УИП бит может быть установлен на исполняемый файл , так что при запуске, программа будет иметь привилегии владельца файла вместо реального пользователя, если они различны. В этом разница между эффективным UID (идентификатор пользователя) и реальным UID.
Некоторые распространенные утилиты, такие как passwd
, принадлежат root и настраиваются таким образом по необходимости ( passwd
требуется доступ, /etc/shadow
который может быть прочитан только root).
Лучшая стратегия при этом - делать все, что вам нужно, в качестве суперпользователя, а затем снижать привилегии, чтобы избежать ошибок или неправильного использования при запуске root. Чтобы сделать это, вы устанавливаете эффективный uid процесса к его реальному uid. В POSIX C:
#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void report (uid_t real) {
printf (
"Real UID: %d Effective UID: %d\n",
real,
geteuid()
);
}
int main (void) {
uid_t real = getuid();
report(real);
seteuid(real);
report(real);
return 0;
}
Соответствующие функции, которые должны иметь эквивалент в большинстве языков более высокого уровня, если они обычно используются в системах POSIX:
getuid()
: Получить настоящий UID.
geteuid()
: Получить эффективный идентификатор.
seteuid()
: Установить эффективный идентификатор пользователя.
Вы не можете ничего сделать с последним, неподходящим для реального uid, за исключением того, что в исполняемом файле был установлен бит setuid . Чтобы попробовать это, скомпилируйте gcc test.c -o testuid
. Затем вам нужно, с привилегиями:
chown root testuid
chmod u+s testuid
Последний устанавливает бит setuid. Если вы теперь работаете ./testuid
как обычный пользователь, вы увидите, что процесс по умолчанию запускается с эффективным uid 0, root.
Как насчет сценариев?
Это варьируется от платформы к платформе , но в Linux вещи, которые требуют интерпретатора, включая байт-код, не могут использовать бит setuid, если он не установлен на интерпретаторе (что было бы очень глупо). Вот простой Perl-скрипт, который имитирует код C выше:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n";
Верные корням * nixy, perl имеет встроенные специальные переменные для эффективного uid ( $>
) и реального uid ( $<
). Но если вы попробуете то же самое chown
и будете chmod
использовать скомпилированный (из C, предыдущий пример) исполняемый файл, это не будет иметь никакого значения. Скрипт не может получить привилегии.
Ответ на это заключается в использовании двоичного файла setuid для выполнения скрипта:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[]) {
if (argc < 2) {
puts("Path to perl script required.");
return 1;
}
const char *perl = "perl";
argv[0] = (char*)perl;
return execv("/usr/bin/perl", argv);
}
Скомпилируйте это gcc --std=c99 whatever.c -o perlsuid
тогда chown root perlsuid && chmod u+s perlsuid
. Теперь вы можете выполнить любой Perl-скрипт с эффективным uid 0, независимо от того, кому он принадлежит.
Аналогичная стратегия будет работать с php, python и т. Д. Но ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
ПОЖАЛУЙСТА , ПОЖАЛУЙСТА , НЕ оставить такого рода вещи , лежащие вокруг . Скорее всего, вы действительно хотите скомпилировать имя скрипта как абсолютный путь , т. Е. Заменить весь код на main()
:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Убедитесь, что /opt/suid_scripts
все в нем доступно только для пользователей без полномочий root. Иначе кто-то может поменяться на что угодно whatever.pl
.
Кроме того, помните, что многие языки сценариев позволяют переменным среды изменять способ выполнения сценария . Например, переменная среды может привести к загрузке библиотеки, предоставленной вызывающей стороной, что позволит вызывающей стороне выполнить произвольный код от имени пользователя root. Таким образом, если вы не знаете, что интерпретатор и сам скрипт устойчивы ко всем возможным переменным окружения, НЕ ДЕЛАЙТЕ ЭТОГО .
Так что мне делать вместо этого?
Более безопасный способ разрешить пользователю без полномочий root запускать сценарий от имени root - добавить правило sudo и запустить пользователя sudo /path/to/script
. Sudo удаляет большинство переменных среды, а также позволяет администратору точно выбирать, кто может выполнить команду и с какими аргументами. См. Как запустить определенную программу от имени пользователя root без запроса пароля? для примера.
-privileged
оболочку, и, если они вызваны из ответственного скрипта, их также можно безопасно вызыватьsuid root
таким образом. Я боюсь, что понятие ответственного сценария является чем-то вроде чепухи в реальном мире. В любом случае, вероятно, стоит упомянуть, насколько важно избегать