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

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

> cat setuid-test.c

#include <stdio.h>
#include <unistd.h>

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

Однако это относится только к исполняемым файлам; shell-скрипты игнорируют бит 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

153 голоса | спросил Michael Mrozek 12 AM00000060000000731 2010, 06:20:07

7 ответов


171

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

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


Setuid shebang

Существует условие гонки, присущее способу shebang (#!):

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

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

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

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

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

Страница shebang Sven Mascheck содержит много информации о shebang через unices, включая поддержку setuid .


Устные переводчики Setuid

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

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

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

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

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

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

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


TL, DR:

  • Setuid shebang небезопасен, но обычно игнорируется.
  • Если вы запускаете программу с привилегиями (через sudo или setuid), записываете собственный код или perl или запускаете программу с помощью обертки, которая санирует среду (например, sudo с опцией env_reset ).

¹ Это обсуждение применяется одинаково, если вы замените «setgid» на «setuid».

ответил Gilles 9 +04002010-10-09T00:18:31+04:00312010bEurope/MoscowSat, 09 Oct 2010 00:18:31 +0400 2010, 00:18:31
49

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

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

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  

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

ответил Hemant 12 AM00000090000002131 2010, 09:20:21
21

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

#!/bin/sh
[ "root" != "$USER" ] && exec sudo $0 "[email protected]"
ответил rcrowley 18 AM00000050000005131 2010, 05:53:51
11

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

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

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

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

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

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

ответил Maciej Piechotka 17 PM00000030000002031 2010, 15:04:20
4

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

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

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

ответил wzzrd 12 AM000000100000001931 2010, 10:38:19
4

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

Супер позволяет указанным пользователям выполнять сценарии (или другие команды), как если бы они были 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/утопично /ен /man1 /super.1.html

ответил Nizam Mohamed 27 Jam1000000amTue, 27 Jan 2015 09:01:37 +030015 2015, 09:01:37
-3

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

Он должен быть понятным.

#include <string>
#include <unistd.h>

template <typename T, typename U>
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
ответил Theodore R. Smith 8 J0000006Europe/Moscow 2016, 21:58:34

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132