Правильне використання встановленого біта


45

У мене є процес, який потребує привілеїв root, коли він працює звичайним користувачем. Мабуть, я можу використовувати "встановлений біт" для цього. Який правильний спосіб зробити це в системі POSIX?

Крім того, як я можу це зробити зі сценарієм, який використовує інтерпретатор (bash, perl, python, php тощо)?

Відповіді:


53

УИП біт може бути встановлений на виконуваний файл , так що при запуску, програма матиме привілеї власника файлу замість реального користувача, якщо вони різні. Це різниця між ефективним uid (ідентифікатор користувача) і реальним uid.

Деякі поширені утиліти, такі як passwd, є власністю root та налаштовані таким чином уникнути необхідності ( passwdдоступ до /etc/shadowякого може бути прочитаний тільки root).

Найкраща стратегія при цьому - робити все, що вам потрібно зробити, як суперпользователь одразу, а потім знизити привілеї, так що помилки або неправильне використання рідше трапляються під час запуску root. Для цього ви встановлюєте ефективний uid процесу на його справжній uid. В POSIX C:

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}

Відповідні функції, які повинні мати еквівалент у більшості мов вищого рівня, якщо вони використовуються в системах POSIX:

  • getuid(): Отримайте справжній uid.
  • geteuid(): Отримайте ефективний uid.
  • seteuid(): Встановіть ефективний uid.

Ви не можете нічого зробити з останнім, невідповідним справжньому uid, за винятком випадків, коли біт setuid був встановлений на виконуваному файлі . Таким чином , щоб спробувати це, узагальнювати gcc test.c -o testuid. Тоді вам потрібно з привілеями:

chown root testuid
chmod u+s testuid

Останній встановлює біт setuid. Якщо ви зараз працюєте ./testuidяк звичайний користувач, ви побачите, що процес працює за замовчуванням з ефективним uid 0, root.

Що з сценаріями?

Це залежить від платформи до платформи , але в Linux речі, для яких потрібен інтерпретатор, включаючи байт-код, не можуть використовувати встановлений біт, якщо він не встановлений на інтерпретаторі (що було б дуже дурно). Ось простий скрипт perl, який імітує код C вище:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n"; 

Вірно, що це * nixy root, perl має вбудовані спеціальні змінні для ефективного uid ( $>) та real uid ( $<). Але якщо ви спробуєте те ж саме chownі chmodвикористовувались зі складеним (із C, попереднього прикладу) виконуваним файлом, це не матиме ніякої різниці. Сценарій не може отримати привілеї.

Відповідь на це полягає у використанні встановленого бінарного файлу для виконання сценарію:

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

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to perl script required.");
        return 1;
    }
    const char *perl = "perl";
    argv[0] = (char*)perl;
    return execv("/usr/bin/perl", argv);
}

Складіть це gcc --std=c99 whatever.c -o perlsuid, значить chown root perlsuid && chmod u+s perlsuid. Тепер ви можете виконати будь-який сценарій perl з ефективним uid 0, незалежно від того, хто ним володіє.

Подібна стратегія буде працювати з php, python тощо. Але ...

# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face

БУДЬ ЛАСКА, БУДЬ НЕ залишайте подібних речей лежачи . Швидше за все, ви насправді хочете компілювати в ім'я сценарію як абсолютний шлях , тобто замінити весь код на main():

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

Вони впевнені, /opt/suid_scriptsі все, що знаходиться в ньому, доступне лише для читання для некористуючих користувачів. Інакше хтось міг би поміняти на що-небудь whatever.pl.

Крім того, будьте обережні, що багато мов сценаріїв дозволяють змінним середовищу змінювати спосіб виконання сценарію . Наприклад, змінна середовища може призвести до завантаження бібліотеки, наданої абонентом, що дозволяє абоненту виконувати довільний код як корінь. Таким чином, якщо ви не знаєте, що і інтерпретатор, і сам сценарій є надійними щодо всіх можливих змінних умов середовища, НЕ РОБИТИ ЦЕ .

То що мені робити замість цього?

Більш безпечний спосіб дозволити користувачеві, котрий не має права доступу до запуску сценарію як root, - додати правило sudo і дозволити користувачеві працювати sudo /path/to/script. Судо позбавляє більшості змінних середовища, а також дозволяє адміністратору чітко вибрати, хто може запускати команду та якими аргументами. Див. Як запустити конкретну програму як root без запиту пароля? для прикладу.


1
багато оболонок будуть поводитись по-різному, якщо їх викликають як -privilegedоболонку, і, якщо вони викликаються з відповідального сценарію, також можна безпечно викликати suid rootтаким чином. Я боюся, що поняття відповідального сценарію є дещо передумовою в реальному світі. У будь-якому разі, напевно, варто згадати, як важливо уникати записів усіх видів, якщо взагалі це можливо, тоді як дозволи підвищені ...
mikeserv

@mikeserv Мені не знайомі поняття " -privilegedоболонка" та "відповідальний сценарій". Ви хочете зробити ще одну відповідь про снаряди? Я не можу обіцяти вам галочку, звичайно;) Існує не так багато вагомих причин для цього - sudoце набагато кращий варіант, якщо це можливо - я написав це як загальну відповідь, що випливає з раннього запитання про автентифікацію пароля системи в php .
goldilocks

1
Я не хочу , щоб зробити це - відповідальні скрипти є дуже важко - за мене, напевно. Але я повинен сказати , що я НЕ згоден з вашим аналізом sudo- я вважаю , sudoяк психотичні в тому , що вона робить вигляд НЕ бути root. Ви навіть можете помістити в свої кулі оболонки sudoers. suна мій погляд, краще для сценаріїв, хоча sudoце добре для живих програм. Але ні один не так явно , як сценарій з #!/bin/ksh -pbangline або незалежно від suid kshв $PATHце.
mikeserv

Хоча ця відповідь відповідає на це питання, встановлені сценарії є рецептом для відкриття себе до місцевої вразливості ескалації привілеїв. Існує причина, чому сценарії не можуть бути налаштовані легко. Правильна відповідь тут - sudo.
mdadm

Я взяв на себе сміливість відредагувати вашу відповідь, щоб принаймні згадати про величезну вразливість змінних середовища. Мені це не подобається як канонічна відповідь, тому що вона перетворюється на безліч відхилень від небезпечного методу (встановлену обгортку для сценаріїв). Було б краще , щоб рекомендувати Sudo і залишити розширене обговорення іншого потоку (я прикрив його там , до речі).
Жил "ТАК - перестань бути злим"

11

Так, ви можете, але це, мабуть, дуже погана ідея . Зазвичай ви не встановлюєте біт SUID безпосередньо на свій виконуваний файл, але використовуєте sudo (8) або su (1) для його виконання (і обмежуєте, хто може його виконати)

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

Тож навіть якщо ви це зробили, було б дуже добре спершу переосмислити ВСІ входи до програми, яку ви хочете запустити як root, включаючи її оточення, параметри командного рядка, STDIN та будь-які файли, які він обробляє, дані, що надходять із мережевих з'єднань. це відкривається, і т. д. Це зробити надзвичайно важко, а ще важче зробити правильно.

Наприклад, ви можете дозволити користувачам, які редагують лише якийсь файл із редактором. Але редактор дозволяє виконувати команду зовнішніх помічників або призупиняти, тим самим даючи користувачам кореневу оболонку робити так, як вони хочуть. Або ви можете виконувати сценарії оболонки, які виглядають дуже безпечно для вас, але користувач може запускати його з модифікованими $ IFS або іншими змінними середовища, що повністю змінюють його поведінку. Або це може бути сценарій PHP, який повинен виконувати "ls", але користувач запускає його з модифікованим $ PATH і таким чином змушує запустити оболонку, яку він назвав "ls". Або десятки інших проблем із безпекою.

Якщо програма дійсно повинна виконати частину своєї роботи як root, можливо, найкраще буде її змінити, щоб вона працювала як звичайні користувачі, а просто виконала (після максимальної дезінфекції) ту частину, яка абсолютно потребує корінця через програму suid helper.

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


1
відряджений. див. приклад безпечного виклику в серії програмістів POSIX для man sh- і навіть того, що не вдається зробити command -p env -i .... Зрештою, це досить важко зробити. Однак, якщо це зроблено правильно, для явних цілей, може бути краще suidвмілому перекладачеві, ніж використовувати, suабо sudo- оскільки поведінка будь-якого завжди буде поза контролем сценарію (хоча suрідше викидати криві кулі в міру тенденції бути трохи менш божевільним) . З'єднання всього пакету - єдина впевнена ставка - просто краще бути впевненим , що все.
mikeserv
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.