Позвольте setuid на сценариях оболочки

Вы хотите использовать Экран GNU. Это является супер потрясающим!

ssh me@myserver.com
screen               #start a screen session
run-a-long-process

CTRL+a, d для отсоединений от экранной сессии

exit                 #disconnect from the server, while run-a-long-process continues

Когда Вы возвращаетесь к своему ноутбуку:

ssh me@myserver.com
screen -r            #resume the screen session

Затем проверьте прогресс своего продолжительного процесса!

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

  • CTRL+a, c для создания нового окна
  • CTRL+a, n для переключения на следующее окно на экранной сессии
  • CTRL+a, p для переключения на предыдущее окно на экранной сессии
  • если Вы входите в систему от набора различных систем, Вы, возможно, случайно оставили себя присоединенными к активной экранной сессии на другом компьютере. по этой причине я всегда возобновляюсь с screen -d -r гарантировать, что, если другая оболочка присоединена к моей экранной сессии, это будет отсоединено, прежде чем я возобновлю его на своей существующей системе.
192
12.08.2010, 18:08
10 ответов

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

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


Хижина Setuid

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

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

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

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

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

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

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


Интерпретаторы Setuid

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

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

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

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

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

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

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


TL, DR:

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

¹ Это обсуждение применяется одинаково, если Вы заменяете “setgid” “setuid”; они оба проигнорированы ядром Linux на сценариях

210
27.01.2020, 19:28
  • 1
    @Josh: Безопасные setuid сценарии оболочки возможны, но только если и реализатор оболочки и писатель сценария очень осторожны. Вместо собственного кода, я рекомендую Perl, где реализаторы заботились, что setuid сценарии должны быть безопасными с небольшим усилием на части писателя сценария. –  Gilles 'SO- stop being evil' 11.12.2010, 15:52
  • 2
    , по-видимому, suidperl материал был удержан от использования и отмечен для удаления в течение многих лет (но сохраняется неменьше), –  jmtd 12.05.2011, 17:32
  • 3
    На самом деле suidperl был удален с жемчуга 5.11 (5,12 стабильных): perl5110delta:> "suidperl" был удален. Это раньше обеспечивало механизм для эмуляции setuid битов полномочий в системах, которые не поддерживают его правильно. perl5120delta:> "suidperl" больше не является частью Perl. Это раньше обеспечивало механизм для эмуляции setuid битов полномочий в системах, которые не поддерживают его правильно. –  Randy Stauner 14.07.2011, 20:40
  • 4
    Также отметьте эту строку в жемчуге 5.6.1 документов (почти десятилетие назад)... perl561delta:> Отмечают, что suidperl ни не создан, ни установлен по умолчанию в любой последней версии жемчуга. Использованию suidperl высоко препятствуют. Если Вы думаете, что нуждаетесь в нем, пробуете альтернативы, такие как sudo сначала. См. courtesan.com/sudo. –  Randy Stauner 14.07.2011, 20:45
  • 5
    , который я не понимаю: это, кажется, объяснение причин проблем, но является там фактическим ответом на вопрос OP здесь? Существует ли способ сказать моей ОС выполнять setuid сценарии оболочки? –  Tom 31.03.2015, 16:36

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

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

57
27.01.2020, 19:28
  • 1
    , который я отговорил бы от предложения, чтобы позволить передавать сценария как параметр командной строки как тот по существу, дает любому, кто может выполнить программу способность запустить любой скрипт как тот созданный пользователь. –  dsp 12.08.2010, 11:05
  • 2
    Обратите внимание, что ЭТО НЕБЕЗОПАСНО, даже если полный путь к сценарию является hardcoded. Оболочка наследует переменные от среды, и многие из них позволяют invoker вводить произвольный код. PATH и LD_LIBRARY_PATH очевидные векторы. Некоторые оболочки выполняются $ENV или $BASHENV или ~/.zshenv даже, прежде чем они начинают выполнять надлежащий сценарий, таким образом, Вы не можете защитить от них вообще из сценария. Единственный безопасный способ вызвать сценарий оболочки с полномочиями состоит в том, чтобы очистить среду. Sudo знает, как сделать это безопасно. Не пишите свою собственную обертку, используйте sudo. –  Gilles 'SO- stop being evil' 08.10.2010, 23:26
  • 3
    я плохо себя чувствую, что он внезапно получает downvoted для этого - я действительно конкретно говорил, что хотел услышать небезопасные версии также, и я воображал исполняемый файл, который взял аргумент сценария оболочки, когда я сказал это. Очевидно, это в широком масштабе небезопасно, но я хотел знать, какие возможности существуют –  Michael Mrozek♦ 09.10.2010, 01:58
  • 4
    @Gilles: к вашему сведению, сбросы Linux LD_LIBRARY_PATH среди прочего, когда это встречается, setuid укусил. –  user1686 15.02.2012, 17:31
  • 5
    Вместо использования system, можно найти это более простым (и более эффективный) для использования одного из exec семейство - скорее всего, execve. Тем путем Вы не создаете новый процесс или запускаете оболочку, и можно передать аргументы (предполагающий, что привилегированный сценарий может безопасно обработать аргументы). –  Toby Speight 23.07.2015, 21:52

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

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

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

24
27.01.2020, 19:28

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

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

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

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

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

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

12
27.01.2020, 19:28
  • 1
    я предполагаю /ust/bin/env должен быть /usr/bin/env. –  Bob 18.04.2017, 19:07

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

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

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

4
27.01.2020, 19:28
  • 1
    Это будет работать. Ужасно, поскольку Вы заявили. SETUID укусил, позволяет выполнение с правом владельца. Оболочка Setuid (если это не было разработано для работы с setuid) будет работать, как поддерживают любого пользователя. Т.е. любой может работать rm -rf / (и другие команды от серии DO не ДЕЛАЮТ IT ДОМА). –  Maciej Piechotka 17.08.2010, 13:52
  • 2
    @MaciejPiechotka не ДЕЛАЕТ IT ДОМА, Вы имеете в виду, Не стесняются делать это на работе? :) –  peterph 27.02.2014, 23:22

Команда Super [-R Reqpath] Super [args]

Super позволяет указывать пользователей выполнять скрипты (или другие команды), как если бы они были root; Или он может установить UID, GID и / или дополнительные группы на основе в соответствии с командой, прежде чем выполнять команду. Предназначен, чтобы быть безопасной альтернативой для создания скриптов SETUID ROOT. Super также позволяет обычным пользователям поставлять команды для выполнения других; Они выполняют с помощью UID, GID и групп пользователей, предлагающих команду.

Super Обратитесь к файлу «Super.tab», чтобы увидеть, разрешено ли пользователю выполнить запрошенную команду. Если предоставляется разрешение, супер будет работать PGM [args], где PGM - это программа, связанная с этой командой. (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

5
27.01.2020, 19:28

Я впервые нашел этот вопрос, меня не убедили все ответы. Вот гораздо лучший вопрос, который также позволяет полностью запутать ваш сценарий 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
-5
20.08.2021, 13:40

Если по какой-то причине 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.

6
20.08.2021, 13:40

Во-первых, отказ от ответственности :ОП сказал: «Предполагая, что я готов принять на себя эти риски…». Я оказался в похожей ситуации, когда у меня было одно -нерабочее задание, где я не беспокоился о рисках безопасности, потому что это была краткосрочная -ситуация... Прежде чем вы, ненавистники, понизите голосование, это то, что хотел ОП....

Таким образом, все мое решение является расширением того, что Хемант придумал выше . Единственным отличием от его решения является небольшая автоматизация, так что вы можете как жестко -кодировать конкретный исполняемый файл, который хотите запустить, так и с помощью одного -лайнера, что-то вроде комбинации lnи setuid(. ] напримерchmod 6755).

Кто-то скажет, что это излишество для чего-то, что вы «не должны делать в первую очередь» --, но опять же, это то, чего хотел ОП... Около половины кода можно было бы удалить, если вы не заботитесь об отладке. опции; Я хотел возможность включения xtrace,и некоторые из функций отладки были просто для того, чтобы помочь мне в его разработке...

И повторяю, если это уже было неясно :просто sudoявляется лучшим вариантом почти во всех случаях, за исключением явного обмена -между ленью и скоростью... (I согласен с большинством других постов)

Вот код:

cat > makeSetUIDExecutable <<\ENDSCRIPT
#!/bin/bash

usage() { echo "Usage: $0 -c <command> -o <output> [-u <user>] [-v (verbose)] [-x (xtrace)] [-X (xtrace inherit)]" 1>&2; exit 1; }

PrintArray() {
    local -n arr=$1
    echo -n "$1: "
    for elem in "${!arr[@]}"; do
        echo -n "[${elem@Q}]=${arr[$elem]@Q}, "
    done | sed 's/, $//'; echo
}

PrintVariable() {
    echo "$1: ${!1}"
}

unset makeSetUIDExecutableOpts quotedCommand
declare -A makeSetUIDExecutableOpts

OPTIND=1
while getopts ":c:o:u:vxX" makeSetUIDExecutableOpt; do
    # echo -e "\nProcessing argument...\nmakeSetUIDExecutableOpt = ${makeSetUIDExecutableOpt}\nOPTARG = ${OPTARG}"
    [[ "${makeSetUIDExecutableOpt}" == ":" ]] && usage
    makeSetUIDExecutableOpts["${makeSetUIDExecutableOpt}"]="${OPTARG}"
done

[[ -z "${makeSetUIDExecutableOpts[c]}" ]] || [[ -z "${makeSetUIDExecutableOpts[o]}" ]] && usage

[[ "${makeSetUIDExecutableOpts[x]+-}" ]] &&  {
    set -x
    trap "set +x" EXIT
}

makeSetUIDExecutableOpts[u]="${makeSetUIDExecutableOpts[u]:-root}"
userID=$(id -u "${makeSetUIDExecutableOpts[u]}")

[[ "${makeSetUIDExecutableOpts[X]+-}" ]] &&  quotedCommand="set -x; "
quotedCommand+="${makeSetUIDExecutableOpts[c]//\\\"/\\\\\"}"
quotedCommand="\"${quotedCommand//\"/\\\"}\""

[[ "${makeSetUIDExecutableOpts[v]+-}" ]] &&  {
    echo -e "\nSummary -- Arguments provided:\n"
    PrintArray makeSetUIDExecutableOpts
    echo
    PrintVariable quotedCommand
    echo
}

cat > "${makeSetUIDExecutableOpts[o]}.c" <<ENDSUBSCRIPT
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

int main()
{

    if( setuid( ${userID} ) ) {
        printf("setuid failed, errno = %i\n", errno);
    }
    system( ${quotedCommand} );
    return 0;
 }

ENDSUBSCRIPT

cc "${makeSetUIDExecutableOpts[o]}.c" -o "${makeSetUIDExecutableOpts[o]}"

sudo chown "${makeSetUIDExecutableOpts[u]}" "${makeSetUIDExecutableOpts[o]}"
sudo chmod 6755 "${makeSetUIDExecutableOpts[o]}"

ENDSCRIPT

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

printUidGidInfo='set -- $(cat /proc/self/status | grep -P -o "(?<=Uid:|Gid:).*$"); unset outputString; i=1; while [ ${i} -le 4 ]; do outputString=${outputString}"\n"$(id -nu ${1}); shift; i=$(( i + 1 )); done; while [ ${i} -le 8 ]; do temp=$(getent group ${1}); outputString=${outputString}"\n"${temp%%:*}; shift; i=$(( i + 1 )); done; printf "\nReal UID: %s\nEffective UID: %s\nSaved set UID: %s\nFilesystem UID: %s\nReal GID: %s\nEffective GID: %s\nSaved set GID: %s\nFilesystem UID: %s\n\n" ${outputString}'

./makeSetUIDExecutable -c 'echo; echo "id -u -n:"; id -u -n; echo; echo stat -c "User: %U, Group: %G" /proc/$$/; stat -c "User: %U, Group: %G" /proc/$$/; echo; echo "/proc/self/status"; '"${printUidGidInfo}" -o proveUserInfo -v

./proveUserInfo

Выход:

id -u -n:
root

stat -c User: %U, Group: %G /proc/10977/
User: root, Group: users

/proc/self/status

Real UID: root
Effective UID: root
Saved set UID: root
Filesystem UID: root
Real GID: users
Effective GID: users
Saved set GID: users
Filesystem UID: users
1
20.08.2021, 13:40

ПРИМЕЧАНИЕ.:задача, описанная в исходном вопросе, может быть решена безопасным способом с использованием конфигураций sudoи sudoers. Этот ответ останется для людей, которые все еще привержены запуску сценариев с setuid, даже после того, как их предупредили о последствиях для безопасности. Описанный метод также заложил основу для написания более надежной обработки среды, подобной той, что была описана в sudo. Предложения приветствуются в чате .

Приведенный ниже сценарий оболочки создаст собственную оболочку с setuid и встроенным заданным сценарием.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ :ВНИМАТЕЛЬНО ПРОЧТИТЕ

Для безопасного использования следующего фрагмента необходимы некоторые допущения.:

  1. созданные исполняемые файлы setuid должны быть доступны только для чтения;
  2. встроенному сценарию следует доверять, и он не может создавать бреши в безопасности, например выполнять другой произвольный сценарий в файловой системе;
  3. Интерпретатор вашего скрипта и сам скрипт не должны быть чувствительны к атакам с манипулированием средой, таким как изменение переменных среды для принудительного выполнения ненадежного кода;
  4. Система еще не скомпрометирована, например исполняемые файлы в системных PATHдоступных каталогах уже заменены вредоносными версиями.

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

Если вы прочитали приведенное выше предупреждение,и вы думаете, что сможете гарантировать вышеуказанные условия, или вы можете обеспечить безопасность вашей системы другими способами, создайте файл create-setuid-wrapper.shследующим образом:

#!/usr/bin/env bash
set -e

if [[ `id -u` != 0 ]]; then
    echo "Execute this command with super user credentials"
    exit 1
fi

if [[ $# -ne 1 || ! -f $1 ]]; then
    echo "A file argument is necessary"
    exit 1
fi

INPUT=$1
TEMPFOLDER=/tmp/tmpsuidwrapper
TEMPINPUT=$TEMPFOLDER/input.data
TEMPINPUTOBJ=$TEMPFOLDER/input.o
WRAPPERSRC=$TEMPFOLDER/wrapper.c
OUTFILE=$(basename -- "$INPUT")
OUTFILE="${OUTFILE%.*}.bin"

# Create temporary directory and copy the input
mkdir -p $TEMPFOLDER
cp $INPUT $TEMPINPUT

# Write wrapper source
cat > $WRAPPERSRC <<- EOF
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>

extern const char _binary_input_data_start[];
extern  void* _binary_input_data_size;

int main (int argc, char *argv[])
{
    int rc = setuid(0);
    if (rc == -1)
    {
        perror("setuid failed");
        return -1;
    }

    int fd = memfd_create("script", 0);
    if (fd == -1)
    {
        perror("memfd_create failed");
        return -1;
    }

    size_t size = (size_t)&_binary_input_data_size;
    rc = write(fd, _binary_input_data_start, size);
    if (rc == -1)
    {
        perror("write failed");
        return -1;
    }

    // Clean the environment to protect from some common
    // environment manipulation attacks
    // CHECK-ME: Check if some variables unset is actually needed
    // and if more variables should be unset, sanitize
    unsetenv("LD_LIBRARY_PATH");
    unsetenv("LD_PRELOAD");
    unsetenv("BASHENV");
    unsetenv("ENV");
    setenv("PATH", "${PATH}", 1);

    pid_t pid = fork();
    if (pid == 0)
    {
        // Child process, execute the script
        {
            const char * const argv[] = { "script", NULL };
            const char * const envp[] = { NULL };
            int rc = fexecve(fd, (char * const *) argv, (char * const *) envp);
            if (rc == -1)
            {
                perror("fexecve failed");
                return -1;
            }
        }
    }
    if (pid == -1)
    {
        perror("fork failed");
        return -1;
    }

    // Parent process
    wait(NULL);
    return 0;
}
EOF

# NOTE: Crappy 'ld' syntax apparenty doesn't allow to
# specify a name for the symbol being created, so it will
# use path as the name. We move the input to temp folder
# to keep the symbol name short
CWD=`pwd`
cd $TEMPFOLDER
# Create a linkable object with the data to be embedded
ld -r -b binary -o input.o input.data
cd $CWD

# Compile the program and set setuid
cc -fPIC -no-pie -o $OUTFILE $WRAPPERSRC $TEMPINPUTOBJ
chmod 4755 $OUTFILE

# Cleanup
rm -fr $TEMPFOLDER

Затем подготовьте, например, test.pyскрипт, подобный следующему:

#!/usr/bin/env python3
import getpass
print(getpass.getuser())

Позвоните create_setuid_wrapper.shпо указанному выше test.py. Сценарий создаст исполняемый файл test.bin. Убедитесь, что ваша система разрешает выполнение произвольных исполняемых файлов с setuid (. Системы с поддержкой SELinux не будут ). Выполнение напечатает rootили имя вашего суперпользователя.

EDIT1:Удалена копия во временный файл исполняемого файла setuid. Теперь встроенный скрипт просто выполняется непосредственно из памяти с помощью forkи fexecve.

EDIT2:Попытка очистить среду от некоторых распространенных атак манипулирования средой. Оно может быть неправильным и/или недостаточным.

-2
20.08.2021, 13:40

Теги

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