Разрешить setuid для сценариев оболочки

Бит разрешения setuid указывает Linux запускать программу с эффективным идентификатором пользователя владельца вместо исполнителя:

 > cat setuid-test.c #include  #include  int main(int argc, char** argv) { printf("%d", geteuid()); return 0; } > gcc -o setuid-test setuid-test.c > ./setuid-test 1000 > sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test > ./setuid-test 65534 

Однако это относится только к исполняемым файлам; сценарии оболочки игнорируют бит setuid:

 > cat setuid-test2 #!/bin/bash id -u > ./setuid-test2 1000 > sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2 > ./setuid-test2 1000 

Википедия говорит :

Из-за повышенной вероятности недостатков безопасности многие операционные системы игнорируют атрибут setuid при применении к исполняемым сценариям оболочки.

Предполагая, что я готов принять эти риски, есть ли способ заставить Linux обрабатывать бит setuid таким же образом в сценариях оболочки, как и в исполняемых файлах?

Если нет, есть ли общий способ решения этой проблемы? Мое текущее решение состоит в том, чтобы добавить запись sudoers чтобы позволить ALL запускать данный сценарий от имени пользователя, которого я хочу запустить, с помощью NOPASSWD чтобы избежать запроса пароля. Основным недостатком этого является необходимость записи sudoers каждый раз, когда я хочу это сделать, и необходимость для вызывающей стороны sudo some-script вместо просто some-script

8 Solutions collect form web for “Разрешить setuid для сценариев оболочки”

Linux игнорирует бит setuid¹ во всех интерпретируемых исполняемых файлах (т.е. исполняемых файлах, начинающихся со строки #! ). Часто задаваемые вопросы о comp.unix.questions объясняют проблемы безопасности сценариев оболочки setuid. Эти проблемы бывают двух видов: связанные с Шебангом и связанные с оболочкой; Я вхожу в более подробную информацию ниже.

Если вы не заботитесь о безопасности и хотите разрешить сценарии setuid, в Linux вам нужно будет исправить kernel. Что касается ядер 3.x, я думаю, вам нужно добавить вызов install_exec_creds в функцию load_script перед вызовом open_exec , но я не проверял.


Сетуид Шебанг

Существует способ состязания, свойственный тому, как обычно реализуется шебанг ( #! ):

  1. Ядро открывает исполняемый файл и обнаруживает, что оно начинается с #! ,
  2. Ядро закрывает исполняемый файл и открывает вместо него интерпретатор.
  3. Ядро вставляет путь к сценарию в список аргументов (как argv[1] ) и выполняет интерпретатор.

Если в этой реализации разрешены сценарии setuid, злоумышленник может вызвать произвольный сценарий, создав символическую ссылку на существующий сценарий setuid, выполнив ее и организовав изменение ссылки после того, как kernel ​​выполнит шаг 1 и до того, как интерпретатор дойдет до открывая свой первый аргумент. По этой причине большинство юнитов игнорируют бит setuid при обнаружении шебанга.

Одним из способов защиты этой реализации было бы то, чтобы kernel ​​блокировало файл сценария до тех пор, пока интерпретатор не откроет его (обратите внимание, что это должно предотвратить не только удаление или перезапись файла, но также переименование любого каталога в пути). Но Unix-системы, как правило, уклоняются от обязательных блокировок, а символические ссылки делают правильную блокировку особенно трудной и агрессивной. Я не думаю, что кто-то так делает.

Несколько систем Unix (в основном OpenBSD, NetBSD и Mac OS X, для всех из которых требуется включить параметр ядра) реализуют безопасный набор настроек с помощью дополнительной функции: путь /dev/fd/ N относится к файлу, уже открытому в файле. дескриптор N (поэтому открытие /dev/fd/ N примерно эквивалентно dup( N ) ). Многие системы Unix (включая Linux) имеют /dev/fd но не имеют скриптов setuid.

  1. Ядро открывает исполняемый файл и обнаруживает, что оно начинается с #! , Допустим, дескриптор файла для исполняемого файла равен 3.
  2. Ядро открывает интерпретатор.
  3. Ядро вставляет /dev/fd/3 список аргументов (как argv[1] ) и выполняет интерпретатор.

Страница Шебанга Свена Машика содержит много информации о Шебанге в разных отделениях, включая поддержку setuid .


Сетуид переводчики

Предположим, вам удалось запустить программу от имени root, либо потому, что ваша ОС поддерживает setuid shebang, либо потому, что вы использовали встроенную двоичную оболочку (например, sudo ). Вы открыли дыру в безопасности? Может быть Проблема здесь не в интерпретируемых, а в скомпилированных программах. Вопрос в том, будет ли ваша среда выполнения работать безопасно, если она выполняется с привилегиями.

  • Любой динамически связанный собственный двоичный исполняемый файл интерпретируется динамическим загрузчиком (например, /lib/ld.so ), который загружает динамические библиотеки, необходимые для программы. Во многих устройствах вы можете настроить путь поиска динамических библиотек в среде ( LD_LIBRARY_PATH – это общее имя переменной среды) и даже загрузить дополнительные библиотеки во все исполняемые двоичные файлы ( LD_PRELOAD ). Вызывающий программу может выполнить произвольный код в контексте этой программы, поместив специально созданный libc.so в $LD_LIBRARY_PATH (среди других тактик). Все здравомыслящие системы игнорируют переменные LD_* в исполняемых файлах setuid.

  • В shellх, таких как sh, csh и производные, переменные среды автоматически становятся параметрами оболочки. Через такие параметры, как PATH , IFS и многие другие, у инициатора сценария есть много возможностей выполнить произвольный код в контексте сценариев оболочки. Некоторые оболочки устанавливают эти переменные в разумные значения по умолчанию, если обнаруживают, что скрипт был вызван с привилегиями, но я не знаю, есть ли какая-то конкретная реализация, которой я бы доверял.

  • Большинство сред выполнения (будь то нативное, байт-код или интерпретируемое) имеют сходные функции. Мало кто принимает особые меры предосторожности в исполняемых файлах setuid, хотя те, которые запускают нативный код, часто не делают ничего более хитрого, чем динамическое связывание (которое требует предосторожности)

  • Perl является заметным исключением. Он явно поддерживает сценарии setuid безопасным способом. Фактически, ваш скрипт может запускать setuid, даже если ваша ОС игнорирует бит setuid в скриптах. Это связано с тем, что perl поставляется с корневым помощником setuid, который выполняет необходимые проверки и повторно вызывает интерпретатор для нужных сценариев с желаемыми привилегиями. Это объясняется в руководстве perlsec . Раньше для сценариев setuid perl требовалось #!/usr/bin/suidperl -wT вместо #!/usr/bin/perl -wT , но в большинстве современных систем достаточно #!/usr/bin/perl -wT ,

Обратите внимание, что использование встроенной двоичной оболочки ничего не делает само по себе, чтобы предотвратить эти проблемы . Фактически, это может усугубить ситуацию, потому что это может помешать вашей среде выполнения обнаружить, что она вызывается с привилегиями, и обойти свою конфигурируемость во время выполнения.

Собственная двоичная shell может сделать сценарий оболочки безопасным, если shell дезинфицирует среду . Сценарий должен позаботиться о том, чтобы не делать слишком много предположений (например, о текущем каталоге), но это так. Вы можете использовать sudo для этого при условии, что он настроен для очистки среды. Переменные в черном списке подвержены ошибкам, поэтому всегда белый список. При использовании sudo убедитесь, что опция env_reset , что setenv выключен, а env_file и env_keep содержат только безобидные переменные.


TL, DR:

  • Сетуид Шебанг небезопасен, но обычно игнорируется.
  • Если вы запускаете программу с привилегиями (либо через sudo, либо через setuid), пишете собственный код или perl, либо запускаете программу с оболочкой, которая дезинфицирует среду (например, sudo с опцией env_reset ).

Discussion Это обсуждение в равной степени применимо, если вы замените «setgid» на «setuid»; они оба игнорируются kernelм Linux в сценариях

Одним из способов решения этой проблемы является вызов сценария оболочки из программы, которая может использовать бит setuid.
это что-то вроде sudo. Например, вот как вы могли бы сделать это в программе на C:

 #include  #include  #include  #include  int main() { setuid( 0 ); // you can set it at run time also system( "/home/pubuntu/setuid-test2.sh" ); return 0; } 

Сохраните его как setuid-test2.c.
компилировать
Теперь сделайте setuid для этой двоичной программы:

 su - nobody [enter password] chown nobody:nobody a.out chmod 4755 a.out 

Теперь вы должны быть в состоянии запустить его, и вы увидите, что ваш скрипт выполняется без прав доступа.
Но и здесь вам нужно либо жестко закодировать путь к скрипту, либо передать его как аргумент командной строки вышеупомянутому exe.

Я префикс нескольких сценариев, которые находятся в этой лодке, таким образом:

 #!/bin/sh [ "root" != "$USER" ] && exec sudo $0 "$@" 

Обратите внимание, что здесь не используется setuid а просто выполняется текущий файл с помощью sudo .

Если вы хотите избежать вызова sudo some_script вы можете просто сделать:

  #!/ust/bin/env sh sudo /usr/local/scripts/your_script 

Программы SETUID должны разрабатываться с особой тщательностью, так как они работают с привилегиями root и пользователи имеют над ними большой контроль. Им нужно здравомыслие – все проверить. Вы не можете сделать это с помощью сценариев, потому что:

  • Оболочки – это большие части программного обеспечения, которые активно взаимодействуют с пользователем. Практически невозможно все проверить, особенно потому, что большая часть кода не предназначена для работы в таком режиме.
  • Скрипты – это в основном быстрое и грязное решение, и обычно они не подготовлены с такой осторожностью, чтобы допустить setuid. У них много потенциально опасных функций.
  • Они сильно зависят от других программ. Недостаточно, чтобы shell была проверена. sed , awk и т. д. также необходимо проверить

Обратите внимание, что sudo обеспечивает некоторую проверку работоспособности, но этого недостаточно – проверяйте каждую строку в своем собственном коде.

И последнее замечание: подумайте об использовании возможностей. Они позволяют вам дать процессу, работающему от имени пользователя, специальные привилегии, которые обычно требуют привилегий суперпользователя. Однако, например, хотя ping должен манипулировать сетью, ему не нужен доступ к файлам. Я не уверен, однако, если они унаследованы.

Вы можете создать псевдоним для sudo + имя скрипта. Конечно, это еще большая работа по настройке, так как тогда вам также нужно настроить псевдоним, но это избавляет вас от необходимости вводить sudo.

Но если вы не возражаете против ужасных угроз безопасности, используйте оболочку setuid в качестве интерпретатора для сценария оболочки. Не знаю, сработает ли это для вас, но я думаю, что может.

Позвольте мне заявить, что я советую не делать этого, хотя. Я просто упоминаю это в образовательных целях 😉

команда супер [-r reqpath] [аргументы]

Super позволяет указанным пользователям выполнять сценарии (или другие команды), как если бы они были пользователем root; или он может установить uid, gid и / или дополнительные группы для каждой команды перед выполнением команды. Он предназначен для безопасной альтернативы созданию скриптов setuid root. Super также позволяет обычным пользователям предоставлять команды для выполнения другими; они выполняются с uid, gid и группами пользователей, предлагающих команду.

Super просматривает файл “ super.tab ”, чтобы узнать, разрешено ли пользователю выполнять запрошенную команду. Если разрешение предоставлено, super выполнит pgm [args], где pgm – программа, связанная с этой командой. (Root разрешено выполнение по умолчанию, но все еще может быть отказано, если правило исключает root. Обычные пользователи по умолчанию запрещены к выполнению.)

Если команда является символической ссылкой (или жесткой ссылкой тоже) на супер программу, то ввод% command args эквивалентен вводу% super command args (команда не должна быть супер, иначе супер не распознает, что она вызывается через ссылка на сайт.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html

Если по какой-то причине sudo недоступен, вы можете написать тонкий скрипт-обертку на C:

 #include  int main() { setuid(0); execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL); } 

И как только вы скомпилируете его, установите его как setuid с помощью команды chmod 4511 wrapper_script .

Это похоже на другой опубликованный ответ, но запускает сценарий с чистой средой и явно использует /bin/bash вместо оболочки, вызываемой system() , и поэтому закрывает некоторые потенциальные дыры в безопасности.

Обратите внимание, что это полностью отбрасывает окружающую среду. Если вы хотите использовать некоторые переменные окружения, не открывая уязвимости, вам просто нужно использовать sudo .

Очевидно, вы хотите убедиться, что сам сценарий доступен для записи только пользователю root.

Сначала я обнаружил, что этот вопрос, не будучи убежденным во всех ответах, вот гораздо лучший, который также позволяет вам полностью запутать ваш bash-скрипт, если вы так склонны!

Это должно быть само за себя.

 #include  #include  template  T &replace ( T &str, const U &from, const U &to) { size_t pos; size_t offset = 0; const size_t increment = to.size(); while ((pos = str.find(from, offset)) != T::npos) { str.replace(pos, from.size(), to); offset = pos + increment; } return str; } int main(int argc, char* argv[]) { // Set UUID to root setuid(0); std::string script = R"(#!/bin/bash whoami echo $1 )"; // Escape single quotes. replace(script, std::string("'"), std::string("'\"'\"'")); std::string command; command = command + "bash -c '" + script + "'"; // Append the command line arguments. for (int a = 0; a < argc; ++a) { command = command + " " + argv[a]; } return system(command.c_str()); } 

Тогда ты бежишь

 g++ embedded.cpp -o embedded sudo chown root embedded sudo chmod u+s embedded 
  • Как найти номер строки в Bash при возникновении ошибки?
  • Bash: Как сохранить конкретную строку вывода CLI в файл?
  • Как удалить существующие элементы из массива и впоследствии добавлять новые элементы
  • Извлечь имена файлов дампов
  • deepin-терминал: не удалось выполнить дочерний процесс
  • Linux и Unix - лучшая ОС в мире.