Правильное использование бита setuid

45

У меня есть процесс, которому нужны права root при запуске обычным пользователем. Очевидно, я могу использовать «бит setuid» для достижения этой цели. Как правильно сделать это в системе POSIX?

Кроме того, как я могу сделать это со скриптом, который использует интерпретатор (bash, perl, python, php и т. Д.)?

лютик золотистый
источник

Ответы:

53

УИП бит может быть установлен на исполняемый файл , так что при запуске, программа будет иметь привилегии владельца файла вместо реального пользователя, если они различны. В этом разница между эффективным 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 без запроса пароля? для примера.

лютик золотистый
источник
1
многие оболочки будут вести себя по-разному, если их вызывать как -privilegedоболочку, и, если они вызваны из ответственного скрипта, их также можно безопасно вызывать suid rootтаким образом. Я боюсь, что понятие ответственного сценария является чем-то вроде чепухи в реальном мире. В любом случае, вероятно, стоит упомянуть, насколько важно избегать
записей
@mikeserv Я не знаком с понятиями « -privilegedоболочка» и «ответственный сценарий». Вы хотите сделать еще один ответ о снарядах? Конечно, я не могу обещать вам галочку;) Для этого не так много веских причин sudo- это гораздо лучший вариант, если это возможно, - я написал его как общий ответ, вытекающий из раннего вопроса об аутентификации пароля системы в php. ,
Златовласка
1
Я не хочу , чтобы сделать это - ответственные скрипты являются очень трудно - за меня, наверное. Но я должен сказать , что я не согласен с вашим анализом sudo- я считаю , sudoкак психотические в том , что она делает вид не быть root. Вы можете даже поместить шары оболочки в свой sudoers. suНа мой взгляд, лучше для сценариев, хотя sudoи для живых приложений. Но ни один из них не является таким явным, как сценарий с #!/bin/ksh -pbangline или что-то еще suid kshв $PATHэтом.
mikeserv
Хотя этот ответ действительно отвечает на этот вопрос, сценарии setuid - это рецепт, позволяющий открыть себя для локальной уязвимости повышения привилегий. Есть причина, по которой скрипты не могут быть легко настроены. Правильный ответ здесь - sudo.
mdadm
Я позволил себе отредактировать ваш ответ, чтобы хотя бы упомянуть огромную уязвимость с помощью переменных среды. Мне не очень нравится этот канонический ответ, потому что в нем много отступлений от небезопасного метода (обертка setuid для скриптов). Лучше всего порекомендовать sudo и оставить расширенную дискуссию для другой ветки ( кстати, я ее там и освещал ).
Жиль "ТАК - перестань быть злым"
11

Да, вы можете, но это, вероятно, очень плохая идея . Обычно вы не устанавливаете бит SUID непосредственно на свой исполняемый файл, а используете sudo (8) или su (1) для его выполнения (и ограничиваете, кто может его выполнять)

Однако обратите внимание, что существует много проблем с безопасностью, позволяющих обычным пользователям запускать программы (и особенно сценарии!) От имени пользователя root. Большинству из них приходится делать это, если программа специально и очень и очень тщательно не написана, чтобы справиться с этим, злоумышленники могут легко использовать ее для получения корневой оболочки.

Поэтому, даже если бы вы сделали это, было бы очень полезно сначала очистить ВСЕ входные данные для программы, которую вы хотите запустить от имени пользователя root, включая ее среду, параметры командной строки, STDIN и любые файлы, которые он обрабатывает, данные, поступающие из сетевых подключений. он открывается и т. д. и т. д. Это очень трудно сделать, и еще труднее сделать правильно.

Например, вы можете разрешить пользователям редактировать только некоторые файлы с помощью редактора. Но редактор позволяет выполнять команды внешних помощников или приостанавливать работу, тем самым предоставляя пользователям корневую оболочку, которую они хотят делать. Или вы можете выполнять сценарии оболочки, которые выглядят очень безопасными для вас, но пользователь может запустить его с измененными переменными $ IFS или другими переменными среды, полностью изменив его поведение. Или это может быть PHP-скрипт, который должен выполнять «ls», но пользователь запускает его с измененным $ PATH и таким образом заставляет запускать оболочку, которую он называет «ls». Или десятки других проблем безопасности.

Если программа действительно должна выполнять часть своей работы с правами суперпользователя, вероятно, было бы лучше изменить ее так, чтобы она работала как обычные пользователи, а просто выполнить (после максимально возможной очистки) часть, которая абсолютно нуждается в root с помощью вспомогательной программы suid.

Обратите внимание, что даже если вы полностью доверяете всем своим локальным пользователям (например, вы предоставляете им пароль root), это плохая идея, как и любой непреднамеренный контроль безопасности со стороны ЛЮБОГО из них (например, наличие скрипта PHP в сети или слабый пароль, или что угодно) вдруг позволяет удаленному злоумышленнику полностью скомпрометировать всю машину.

Матия Налис
источник
1
прикомандирован. см. пример безопасного вызова в серии программистов POSIX для man sh- и даже это не может сделать command -p env -i .... Это довольно сложно сделать в конце концов. Тем не менее, если это сделано правильно, для явных целей это может быть лучше для suidспособного интерпретатора, чем для использования suили sudo- так как поведение любого из них всегда будет вне контроля скрипта (хотя с suменьшей вероятностью будет бросать кривые шары, когда он стремится) быть немного менее безумным) . Объединение всего пакета - единственная верная ставка - лучше быть уверенным, что это все.
mikeserv