УИП біт може бути встановлений на виконуваний файл , так що при запуску, програма матиме привілеї власника файлу замість реального користувача, якщо вони різні. Це різниця між ефективним 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 без запиту пароля? для прикладу.
-privileged
оболонку, і, якщо вони викликаються з відповідального сценарію, також можна безпечно викликатиsuid root
таким чином. Я боюся, що поняття відповідального сценарію є дещо передумовою в реальному світі. У будь-якому разі, напевно, варто згадати, як важливо уникати записів усіх видів, якщо взагалі це можливо, тоді як дозволи підвищені ...