Дозволити налаштування на скриптах оболонок


184

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

Однак це стосується лише виконуваних файлів; сценарії оболонки ігнорують встановлений біт:

> 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

Відповіді:


202

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

Якщо ви не дбаєте про безпеку і хочете дозволити встановлені сценарії, під Linux, вам потрібно буде виправити ядро. Щодо ядер 3.x, я думаю, вам потрібно додати дзвінок install_exec_credsу load_scriptфункцію до виклику open_exec, але я не перевіряв.


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

Існує умова перегонів, властива тому, як #!звичайно реалізується shebang ( ):

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

Якщо дозволено встановити сценарії з цією реалізацією, зловмисник може викликати довільний скрипт, створивши символічне посилання на існуючий сценарій встановлення, виконавши його та організувавши змінити посилання після того, як ядро ​​виконало крок 1 і до того, як інтерпретатор обійдеться відкриваючи свій перший аргумент. З цієї причини більшість одноосібників ігнорують встановлений біт, коли виявляють шебанг.

Одним із способів убезпечити цю реалізацію буде ядро, щоб заблокувати файл сценарію до тих пір, поки інтерпретатор не відкриє його (зауважте, що це повинно запобігати не лише від’єднанню або перезапису файлу, а й перейменуванню будь-якого каталогу на шляху). Але системи Unix, як правило, уникають обов'язкових блокувань, і символічні посилання роблять правильну функцію блокування особливо важкою та інвазивною. Я не думаю, що хтось робить це так.

Кілька систем Unix (в основному OpenBSD, NetBSD і Mac OS X, для всіх яких потрібно включити налаштування ядра) реалізують захищений налаштування shebang, використовуючи додаткову функцію: шлях стосується файлу, який уже відкрився в дескрипторі файлу N (тому відкриття є приблизно рівнозначно ). Багато систем Unix (включаючи Linux) мають, але не встановлені сценарії./dev/fd/N/dev/fd/Ndup(N)/dev/fd

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

На сторінці шебанга Свена Маскека є багато інформації про shebang через unice, включаючи підтримку налаштування.


Перекладачі-сетуїди

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

  • Будь-який динамічно пов'язаний нативний бінарний виконуваний файл певним чином інтерпретується динамічним завантажувачем (наприклад /lib/ld.so), який завантажує динамічні бібліотеки, необхідні програмі. У багатьох варіантах ви можете налаштувати шлях пошуку для динамічних бібліотек через середовище ( LD_LIBRARY_PATHце загальна назва змінної середовища) і навіть завантажити додаткові бібліотеки у всі виконані бінарні файли ( LD_PRELOAD). Зухвалий програми може виконати довільний код у контексті цієї програми шляхом розміщення спеціально створений libc.soв $LD_LIBRARY_PATH(серед інших тактик). Всі здорові системи ігнорують LD_*змінні у встановленому виконуваному файлі.

  • У оболонках, таких як sh, csh та похідні, змінні середовища автоматично стають параметрами оболонки. За допомогою таких параметрів, як PATH, IFSта багато іншого, виклик сценарію має багато можливостей виконувати довільний код у контексті сценаріїв оболонки. Деякі оболонки встановлюють ці змінні як певні за замовчуванням, якщо вони виявляють, що скрипт викликав привілеї, але я не знаю, що існує якась конкретна реалізація, якій я би довіряв.

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

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

Зауважте, що використання вбудованої бінарної обгортки нічого не означає, щоб запобігти цим проблемам . Насправді, це може зробити ситуацію ще гірше , тому що це може запобігти середовища виконання від виявлення того, що вона викликається з правами і в обхід його виконання налаштування.

Народний двійковий оболонку може зробити сценарій оболонки безпечним, якщо обгортка захищає навколишнє середовище . Сценарій повинен дбати про те, щоб не робити занадто багато припущень (наприклад, про поточний каталог), але це стосується. Ви можете використовувати судо для цього за умови, що він налаштований на оздоровлення навколишнього середовища. Змінні в чорному списку сприйнятливі до помилок, тому завжди дозволені. З Судо, переконайтеся , що env_resetопція включена, то setenvвимкнений, і що env_fileі env_keepмістять тільки нешкідливі змінні.


TL, DR:

  • Сетуїд Шебанг небезпечний, але зазвичай ігнорується.
  • Якщо ви запускаєте програму з привілеями (через sudo або setuid), напишіть нативний код або perl або запустіть програму обгорткою, яка дезінфікує середовище (наприклад, sudo з env_resetопцією).

¹ Ця дискусія застосовується однаково, якщо ви заміните "setgid" на "setuid"; вони ігноруються ядром Linux у скриптах


2
@Josh: Захищені встановлені сценарії оболонки можливі, але лише якщо реалізатор оболонки та сценарій сценарію дуже обережні. Я замість рідного коду рекомендую Perl, де реалізатори подбали про те, щоб встановлені сценарії мали бути захищені з невеликими зусиллями з боку сценариста.
Жиль

3
Мабуть, цей suidperlматеріал був застарілим і позначений для видалення протягом багатьох років (але зберігається не менше)
jmtd

7
Фактично suidperlбуло видалено станом на perl 5.11 (5.12 стабільний): perl5110delta:> "suidperl" було видалено. Він використовувався для забезпечення механізму емуляції встановлених дозволів бітів у системах, які не підтримують його належним чином. perl5120delta:> "suidperl" більше не є частиною Perl. Він використовувався для забезпечення механізму емуляції встановлених дозволів бітів у системах, які не підтримують його належним чином.
Ренді Стаунер

5
Також зверніть увагу на цей рядок із perl 5.6.1 docs (майже десятиліття тому назад) ... perl561delta:> Зауважте, що suidperl не будується і не встановлюється за замовчуванням у будь-якій останній версії perl. Використання suidperl вкрай не рекомендує . Якщо ви думаєте, що вам це потрібно, спробуйте спочатку такі альтернативи, як судо. Див. Courtesan.com/sudo .
Ренді Стаунер

3
Я не розумію: це, здається, є поясненням причин проблем, але чи є реальна відповідь на питання ОП? Чи є спосіб сказати моїй ОС запускати встановлені сценарії оболонки?
Том

55

Один із способів вирішення цієї проблеми - викликати скрипт оболонки з програми, яка може використовувати встановлений біт.
це щось на зразок судо. Наприклад, ось як ви це зробили в програмі С:

#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.
компілюйте
Тепер виконайте встановлення на цій програмі:

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

Тепер ви маєте змогу запустити його, і ви побачите, що ваш сценарій виконується без жодних дозволів.
Але тут також або потрібно жорстко закодувати шлях сценарію, або передати його як аргумент командного рядка вище exe.


25
Я б радив проти пропозиції дозволити передачу скрипту як аргументу командного рядка, оскільки це по суті дає кожному, хто може виконати програму, можливість запускати будь-який скрипт як визначений користувач.
dsp

39
Зауважте, що ЦЕ НЕБЕЗПЕЧНО, навіть якщо повний шлях до сценарію є жорстким кодом. Оболонка успадкує змінні з оточуючого середовища, і багато з них дозволяють інікеру вводити довільний код. PATHі LD_LIBRARY_PATHє очевидними векторами. Деякі оболонки виконують $ENVабо $BASHENVабо ~/.zshenvнавіть перш , ніж вони почнуть виконання сценарію власне, тому ви не можете захистити від них взагалі з сценарію. Єдиний безпечний спосіб викликати скрипт оболонки з привілеями - це очищення середовища . Судо знає, як це зробити безпечно. Тому не пишіть власну обгортку, використовуйте судо .
Жиль

28
Мені погано, що він раптом стає прихильним до цього - я спеціально сказав, що хочу почути і небезпечні версії, і я уявляв собі виконуваний файл, який брав аргумент сценарію оболонки, коли я це сказав. Очевидно, це масово небезпечно, але я хотів знати, які існують можливості
Майкл Мрозек

8
@Gilles: FYI, Linux скасовує, LD_LIBRARY_PATHкрім іншого, коли він стикається з встановленим бітом.
grawity

3
Замість використання systemвам може бути простіше (і ефективніше) користуватися однією з execсімей - найімовірніше execve. Таким чином, ви не створюєте новий процес або не запускаєте оболонку, і можете пересилати аргументи (припускаючи, що ваш привілейований сценарій може безпечно обробляти аргументи).
Toby Speight

23

Я приставлю кілька сценаріїв, які знаходяться в цьому човні таким чином:

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

Зауважте, що це не використовується, setuidа просто виконує поточний файл за допомогою sudo.


4
Це не використовує setuid, а просто дає вам sudoпідказку. (Для мене вся суть налаштування дає змогу речам працювати як корінь, не потребуючи судо.)
Люк

12

Якщо ви хочете уникати дзвінків, sudo some_scriptви можете просто зробити:

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

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

  • Оболонки - це великі фрагменти програмного забезпечення, які сильно взаємодіють із користувачем. Зрозуміло перевірити все майже неможливо - тим більше, що більшість кодів не призначені для роботи в такому режимі.
  • Сценарії - це в основному швидке рішення, яке зазвичай не готується з такою обережністю, що дозволило б встановити налаштування. Вони мають багато потенційно небезпечних особливостей.
  • Вони сильно залежать від інших програм. Недостатньо, щоб оболонку перевірили. sed, awkтощо, також потрібно перевірити

Зверніть увагу, що sudoпередбачено перевірку правильності, але це недостатньо - перевіряйте кожен рядок у власному коді.

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


2
Я припускаю, що /ust/bin/envмає бути /usr/bin/env.
Боб

4

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

Але якщо ви не заперечуєте жахливих ризиків для безпеки, використовуйте встановлену оболонку як інтерпретатор сценарію оболонки. Не знаю, чи буде це для вас, але я думаю, що це може бути.

Дозвольте констатувати, що я раджу не робити цього. Я лише згадую це для освітніх цілей ;-)


5
Це спрацює. Жахливо, як ви заявили. Біт SETUID дозволяє виконувати право власника. Оболонка Setuid (якщо вона не була розроблена для роботи з setuid) працюватиме як root для будь-якого користувача. Тобто будь-хто може запускатись rm -rf /(та інші команди із серії НЕ РОБИТИ ДОМА ).
Мацей П'єхотка

9
@MaciejPiechotka від НЕ РОБИТИ ДОМАШТЕ, ви хочете зробити це на роботі? :)
петерф

4

команда super [-r reqpath] [args]

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

Super звертається до файлу `` super.tab '', щоб побачити, чи дозволено користувачеві виконувати запитувану команду. Якщо дозвіл надано, super виконає pgm [args], де pgm - програма, пов'язана з цією командою. (Root дозволено виконувати за замовчуванням, але його все одно можна відмовити, якщо правило виключає root. Звичайним користувачам заборонено виконання за замовчуванням.)

Якщо команда є символічним посиланням (або жорстким посиланням теж) на програму супер, то введення% командних аргументів еквівалентно введенню% супер командних аргументів (команда не повинна бути супер, або супер не визнає, що вона викликається через посилання.)

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

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


Привіт і ласкаво просимо на сайт! Очікуємо, що відповіді тут будуть більш детальними. Чи можете ви відредагувати свою відповідь та пояснити, що це за програма та як вона може допомогти вирішити проблему ОП?
тердон

Дякую, Nizam, що годинами намагався знайти щось на кшталт супер для акаунтів, дозволених виконувати їх іншим користувачем як користувачеві файлу.
Чад

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.

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


-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
Можливо, тому, що це надто складне рішення, яке виконує обробку рядків і вбудований код оболонки. Або ви робите простий пусковий апарат, або використовуєте судо. Це найгірше з усіх варіантів.
siride
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.