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

185

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

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

Михаил Мрозек
источник

Ответы:

203

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

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


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

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

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

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

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

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

  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.

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

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

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

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

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


TL, DR:

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

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

жилль
источник
2
@Josh: безопасные сценарии оболочки setuid возможны, но только если и разработчик оболочки, и автор сценариев очень осторожны. Вместо нативного кода я рекомендую Perl, где разработчики позаботились о том, чтобы сценарии setuid были безопасны без особых усилий со стороны автора сценария.
Жиль
3
очевидно, suidperlматериал был объявлен устаревшим и отмечен для удаления в течение многих лет (но, тем не менее, сохраняется)
jmtd
7
Фактически suidperlбыл удален с perl 5.11 (стабильная версия 5.12): perl5110delta:> "suidperl" был удален. Он использовался для обеспечения механизма эмуляции битов разрешений setuid в системах, которые не поддерживают его должным образом. perl5120delta:> "suidperl" больше не является частью Perl. Он использовался для обеспечения механизма эмуляции битов разрешений setuid в системах, которые не поддерживают его должным образом.
Рэнди Стаунер
5
Также обратите внимание на эту строку из документации по perl 5.6.1 (почти десять лет назад) ... perl561delta:> Обратите внимание, что suidperl не собирается и не устанавливается по умолчанию в любой недавней версии perl. Использование suidperl крайне не рекомендуется . Если вы думаете, что вам это нужно, попробуйте сначала альтернативы, такие как sudo. Смотрите courtesan.com/sudo .
Рэнди Стаунер
3
Я не понимаю: это, кажется, объяснение причин проблем, но есть ли реальный ответ на вопрос ОП здесь? Есть ли способ указать моей ОС запускать сценарии оболочки setuid?
Том
55

Одним из способов решения этой проблемы является вызов сценария оболочки из программы, которая может использовать бит 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.
compile
Теперь сделайте setuid для этой двоичной программы:

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

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

Hemant
источник
25
Я бы посоветовал против предложения разрешить передачу сценария в качестве аргумента командной строки, поскольку это, по сути, дает любому, кто может выполнить программу, возможность запуска любого сценария от имени определенного пользователя.
дсп
40
Обратите внимание, что ЭТО НЕ БЕЗОПАСНО, даже если полный путь к сценарию жестко задан. Оболочка будет наследовать переменные из среды, и многие из них позволяют вызывающему вводить произвольный код. PATHи LD_LIBRARY_PATHочевидные векторы. Некоторые оболочки выполняют $ENVили $BASHENVили ~/.zshenvдаже прежде , чем они начнут выполнение сценария собственно, поэтому вы не можете защитить от них вообще из сценария. Единственный безопасный способ вызвать скрипт с привилегиями - это очистить среду . Судо знает, как сделать это безопасно. Так что не пишите свою собственную обертку, используйте sudo .
Жиль
28
Я чувствую себя плохо, потому что ему вдруг за это отказывают - я специально сказал, что тоже хочу услышать небезопасные версии, и я представлял себе исполняемый файл, который принимал аргумент сценария оболочки, когда говорил это. Очевидно, что это крайне небезопасно, но я хотел знать, какие возможности существуют
Майкл Мрозек
8
@Gilles: FYI, Linux отключается LD_LIBRARY_PATHсреди прочего, когда сталкивается с битом setuid.
Гравитация
3
Вместо использования systemможет оказаться более простым (и более эффективным) использование одного из execсемейств - скорее всего execve. Таким образом, вы не создаете новый процесс и не запускаете оболочку, и вы можете пересылать аргументы (при условии, что ваш привилегированный скрипт может безопасно обрабатывать аргументы).
Тоби Спейт
23

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

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

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

rcrowley
источник
5
Это не использует setuid, а просто дает вам sudoподсказку. (Для меня весь смысл setuid в том, что все должно запускаться с правами root без необходимости sudo.)
Luc
12

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

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

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

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

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

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

Мацей Печотка
источник
2
Я полагаю, /ust/bin/envдолжно быть /usr/bin/env.
Боб
4

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

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

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

wzzrd
источник
5
Это будет работать. Ужасно, как вы заявили. Бит SETUID позволяет выполнение с правом владельца. Оболочка setuid (если она не предназначена для работы с setuid) будет работать от имени пользователя root. Т.е. любой может запустить rm -rf /(и другие команды из серии НЕ ДЕЛАЙТЕ ЭТОГО НА ДОМУ ).
Мацей Пехотка
9
@MaciejPiechotka от НЕ ДЕЛАЙТЕ ЭТОГО НА ДОМУ Вы имеете в виду, не стесняйтесь делать это на работе? :)
Петер
4

команда супер [-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

Низам Мохамед
источник
Привет и добро пожаловать на сайт! Мы ожидаем, что ответы будут более подробными здесь. Не могли бы вы отредактировать свой ответ и объяснить, что это за программа и как она может помочь решить проблему ОП?
Terdon
Спасибо, Низам, за несколько часов попыток найти что-то вроде super для учетных записей, которые могут быть выполнены другим пользователем в качестве пользователя файла.
Чад
2

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

#include <unistd.h>
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.

Крис
источник
-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
Теодор Р. Смит
источник
Почему отрицания ???
Теодор Р. Смит
2
Возможно, потому что это слишком сложное решение, которое выполняет манипуляции со строками и имеет встроенный код оболочки. Либо вы делаете простой лаунчер, либо используете sudo. Это худший из всех вариантов.
Сирида