УИП бит может быть установлен на исполняемый файл , так что при запуске, программа будет иметь привилегии владельца файла вместо реального пользователя, если они различны. В этом разница между эффективным 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
таким образом. Я боюсь, что понятие ответственного сценария является чем-то вроде чепухи в реальном мире. В любом случае, вероятно, стоит упомянуть, насколько важно избегать-privileged
оболочка» и «ответственный сценарий». Вы хотите сделать еще один ответ о снарядах? Конечно, я не могу обещать вам галочку;) Для этого не так много веских причинsudo
- это гораздо лучший вариант, если это возможно, - я написал его как общий ответ, вытекающий из раннего вопроса об аутентификации пароля системы в php. ,sudo
- я считаю ,sudo
как психотические в том , что она делает вид не бытьroot
. Вы можете даже поместить шары оболочки в свойsudoers
.su
На мой взгляд, лучше для сценариев, хотяsudo
и для живых приложений. Но ни один из них не является таким явным, как сценарий с#!/bin/ksh -p
bangline или что-то ещеsuid ksh
в$PATH
этом.Да, вы можете, но это, вероятно, очень плохая идея . Обычно вы не устанавливаете бит SUID непосредственно на свой исполняемый файл, а используете sudo (8) или su (1) для его выполнения (и ограничиваете, кто может его выполнять)
Однако обратите внимание, что существует много проблем с безопасностью, позволяющих обычным пользователям запускать программы (и особенно сценарии!) От имени пользователя root. Большинству из них приходится делать это, если программа специально и очень и очень тщательно не написана, чтобы справиться с этим, злоумышленники могут легко использовать ее для получения корневой оболочки.
Поэтому, даже если бы вы сделали это, было бы очень полезно сначала очистить ВСЕ входные данные для программы, которую вы хотите запустить от имени пользователя root, включая ее среду, параметры командной строки, STDIN и любые файлы, которые он обрабатывает, данные, поступающие из сетевых подключений. он открывается и т. д. и т. д. Это очень трудно сделать, и еще труднее сделать правильно.
Например, вы можете разрешить пользователям редактировать только некоторые файлы с помощью редактора. Но редактор позволяет выполнять команды внешних помощников или приостанавливать работу, тем самым предоставляя пользователям корневую оболочку, которую они хотят делать. Или вы можете выполнять сценарии оболочки, которые выглядят очень безопасными для вас, но пользователь может запустить его с измененными переменными $ IFS или другими переменными среды, полностью изменив его поведение. Или это может быть PHP-скрипт, который должен выполнять «ls», но пользователь запускает его с измененным $ PATH и таким образом заставляет запускать оболочку, которую он называет «ls». Или десятки других проблем безопасности.
Если программа действительно должна выполнять часть своей работы с правами суперпользователя, вероятно, было бы лучше изменить ее так, чтобы она работала как обычные пользователи, а просто выполнить (после максимально возможной очистки) часть, которая абсолютно нуждается в root с помощью вспомогательной программы suid.
Обратите внимание, что даже если вы полностью доверяете всем своим локальным пользователям (например, вы предоставляете им пароль root), это плохая идея, как и любой непреднамеренный контроль безопасности со стороны ЛЮБОГО из них (например, наличие скрипта PHP в сети или слабый пароль, или что угодно) вдруг позволяет удаленному злоумышленнику полностью скомпрометировать всю машину.
источник
man sh
- и даже это не может сделатьcommand -p env -i ...
. Это довольно сложно сделать в конце концов. Тем не менее, если это сделано правильно, для явных целей это может быть лучше дляsuid
способного интерпретатора, чем для использованияsu
илиsudo
- так как поведение любого из них всегда будет вне контроля скрипта (хотя сsu
меньшей вероятностью будет бросать кривые шары, когда он стремится) быть немного менее безумным) . Объединение всего пакета - единственная верная ставка - лучше быть уверенным, что это все.