it-swarm-eu.dev

Povolit setuid na skriptech Shell

Bit oprávnění setuid říká Linuxu, aby místo spouštěče spustil program s efektivním uživatelským idem vlastníka:

> 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

To se však týká pouze spustitelných souborů; Skripty Shell ignorují bit setuid:

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> Sudo chown nobody ./setuid-test2; Sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

Wikipedia říká :

Kvůli zvýšené pravděpodobnosti bezpečnostních chyb mnoho operačních systémů ignoruje atribut setuid, když je použito na spustitelné skripty Shell.

Předpokládám, že jsem ochoten přijmout tato rizika, existuje nějaký způsob, jak Linuxu nakládat s setuid bitem na Shell skriptech stejně jako s spustitelnými soubory?

Pokud ne, existuje společné řešení tohoto problému? Mým současným řešením je přidat položku sudoers, aby umožnila ALL spouštět daný skript jako uživatel, který chci, aby se spustil jako, pomocí NOPASSWD se vyhnete výzvě k zadání hesla. Hlavní nevýhodou je potřeba položky sudoers pokaždé, když to chci udělat, a potřeba volajícího Sudo some-script místo pouhého some-script

195
Michael Mrozek

Linux ignoruje bit setuid¹ ve všech interpretovaných spustitelných souborech (tj. Spustitelné soubory začínající řádkem #!). comp.unix.questions FAQ vysvětluje bezpečnostní problémy s skripty setuid Shell. Tyto problémy jsou dvojího druhu: související se Shebangem a Shellem; Níže se podrobněji věnuji.

Pokud vám záleží na zabezpečení a nechcete povolit skripty setuid, musíte pod Linuxem opravit jádro. Od 3.x jádra si myslím, že musíte přidat volání do install_exec_creds ve funkci load_script , před voláním do open_exec, Ale netestoval jsem.


Setuid Shebang

Způsob, jakým je Shebang (#!) Obvykle implementován, je součástí závodu:

  1. Jádro otevře spustitelný soubor a zjistí, že začíná řetězcem #!.
  2. Jádro zavře spustitelný soubor a místo toho otevře interpret.
  3. Jádro vloží cestu ke skriptu do seznamu argumentů (jako argv[1]) A provede interpret.

Pokud jsou u této implementace povoleny skripty setuid, může útočník vyvolat libovolný skript vytvořením symbolického odkazu na existující skript setuid, jeho provedením a uspořádáním změny odkazu poté, co jádro provedlo krok 1 a předtím, než se interpret přiblíží k otevírá svůj první argument. Z tohoto důvodu většina unices ignoruje bit setuid , když detekují Shebang.

Jedním ze způsobů, jak zajistit tuto implementaci, by bylo to, že by jádro uzamklo soubor skriptu, dokud jej interpret neotevře (musí to zabránit nejen odpojení nebo přepsání souboru, ale také přejmenování libovolného adresáře v cestě). Ale unixové systémy mají sklon se vyhýbat povinným zámkům a symbolické odkazy by způsobily, že správný zámek je obzvláště obtížný a invazivní. Nemyslím si, že to někdo takto dělá.

Několik unixových systémů (hlavně OpenBSD, NetBSD a Mac OS X, z nichž všechny vyžadují povolení jádra) implementuje bezpečný setuid Shuang pomocí dalšího feature: cesta /dev/fd/N odkazuje na soubor již otevřený v deskriptoru souboru [~ # ~] n [~ # ~] (takže otevření /dev/fd/N je zhruba ekvivalentní dup(N)). Mnoho unixových systémů (včetně Linuxu) má /dev/fd, Ale nemá nastavené skripty.

  1. Jádro otevře spustitelný soubor a zjistí, že začíná řetězcem #!. Řekněme, že popisovač souboru pro spustitelný soubor je 3.
  2. Jádro otevře interpret.
  3. Jádro vloží /dev/fd/3 Seznam argumentů (jako argv[1]) A provede interpret.

Stránka Shebanga Sven Mascheck obsahuje spoustu informací o Shebang napříč odvětvími, včetně podpora setuid .


Tlumočníci setuidů

Předpokládejme, že se vám podařilo spustit program jako root, buď proto, že váš operační systém podporuje setuid Shebang, nebo protože jste použili nativní binární obal (například Sudo). Otevřeli jste bezpečnostní díru? Možná . Problém zde není o interpretovaných vs kompilovaných programech. Problém je v tom, zda se váš runtime systém chová bezpečně, pokud je spuštěn s oprávněními.

  • Jakýkoli dynamicky propojený nativní binární spustitelný soubor je interpretován způsobem dynamickým zavaděčem (např. /lib/ld.so), Který načte dynamické knihovny vyžadované Program. V mnoha odvětvích můžete konfigurovat vyhledávací cestu pro dynamické knihovny prostřednictvím prostředí (LD_LIBRARY_PATH Je obecný název pro proměnnou prostředí) a dokonce načíst další knihovny do všech spuštěných binárních souborů (LD_PRELOAD) . Vyvolávač programu může vykonávat libovolný kód v kontextu tohoto programu umístěním speciálně vytvořeného libc.so Do $LD_LIBRARY_PATH (Mimo jiné taktiky). Všechny zdravé systémy ignorují proměnné LD_* Ve spustitelných souborech setuid.

  • V skořápkách , jako jsou sh, csh a deriváty, se proměnné prostředí automaticky stanou parametry Shell. Prostřednictvím parametrů, jako je PATH, IFS a mnoho dalších, má invokátor skriptu mnoho příležitostí ke spuštění libovolného kódu v kontextu skriptů Shell. Některé shelly nastavují tyto proměnné na normální výchozí hodnoty, pokud zjistí, že skript byl vyvolán s právy, ale nevím, že existuje nějaká konkrétní implementace, které bych věřil.

  • Většina běhových prostředí (nativní, bytecode nebo interpretovaná) má podobné funkce. Málokdo přijímá speciální opatření v spustitelných souborech setuid, ačkoli ty, které provozují nativní kód, často nedělají nic lepšího než dynamické propojení (které provádí preventivní opatření).

  • Perl je významná výjimka. explicitně podporuje skripty setuid bezpečným způsobem. Ve skutečnosti může váš skript spouštět setuid, i když váš operační systém ignoroval bit setuid na skriptech. Důvodem je, že Perl je dodáván s pomocným kořenovým pomocníkem setuid, který provádí nezbytné kontroly a znovu vyvolává tlumočníka na požadovaných skriptech s požadovanými oprávněními. To je vysvětleno v manuálu perlsec . Bývalo to, že skripty setuid Perl potřebovaly #!/usr/bin/suidperl -wT Místo #!/usr/bin/Perl -wT, Ale na většině moderních systémů stačí #!/usr/bin/Perl -wT.

Všimněte si, že použití nativního binárního wrapperu samo o sobě nezabrání těmto problémům . Ve skutečnosti to může situaci zhoršit , protože by to mohlo zabránit runtime prostředí v detekci, že je vyvoláno s oprávněními a obejít jeho konfigurovatelnost za běhu.

Nativní binární obal může skript Shell zabezpečit, pokud obal dezinfikuje prostředí . Skript musí dávat pozor, aby příliš mnoho předpokladů (např. O aktuálním adresáři), ale to platí. K tomu můžete použít Sudo za předpokladu, že je nastaveno tak, aby dezinfikovalo životní prostředí. Proměnné černé listiny jsou náchylné k chybám, takže vždy na bílé listině. U Sudo se ujistěte, že je zapnuta možnost env_reset, Že setenv je vypnutá a že env_file A env_keep Obsahují pouze nevinné proměnné.


TL, DR:

  • Setuid Shebang je nejistý, ale obvykle se ignoruje.
  • Pokud spustíte program s oprávněními (buď prostřednictvím Sudo nebo setuid), napište nativní kód nebo Perl, nebo spusťte program s obalem, který dezinfikuje prostředí (například Sudo s možností env_reset).

¹ Tato diskuse platí stejně, pokud nahradíte „setgid“ za „setuid“; Linuxové jádro na skriptech oba ignoruje

Jedním ze způsobů řešení tohoto problému je vyvolání skriptu Shell z programu, který může používat bit setuid.
Je to něco jako Sudo. Zde je například způsob, jak toho dosáhnout v programu 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;
 }

Uložte jej jako setuid-test2.c.
sestavit
Nyní udělejte setuid v tomto programu binární:

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

Nyní byste měli být schopni spustit, a uvidíte, že váš skript je spuštěn s nikým povolením.
Ale také zde musíte buď zakódovat cestu skriptu, nebo ji předat jako příkazový řádek arg k výše exe.

57
Hemant

Předponu pár skriptů, které jsou v této lodi tak:

#!/bin/sh
[ "root" != "$USER" ] && exec Sudo $0 "[email protected]"

Všimněte si, že to nepoužívá setuid, ale jednoduše spustí aktuální soubor s Sudo.

24
rcrowley

Pokud se chcete vyhnout volání Sudo some_script stačí udělat:

  #!/ust/bin/env sh

  Sudo /usr/local/scripts/your_script

Programy SETUID musí být navrženy s maximální péčí, protože běží s oprávněními root a uživatelé nad nimi mají velkou kontrolu. Potřebují všechno zkontrolovat. Nemůžete to udělat se skripty, protože:

  • Skořápky jsou velké kusy softwaru, které silně interagují s uživatelem. Je téměř nemožné zkontrolovat vše správně - zejména proto, že většina kódu nemá v takovém režimu běžet.
  • Skripty jsou většinou rychlým řešením a obvykle nejsou připraveny s takovou péčí, že by umožnily setuid. Mají mnoho potenciálně nebezpečných funkcí.
  • Jsou silně závislé na jiných programech. Nestačí, že byl Shell zkontrolován. sed, awk atd. by bylo třeba také zkontrolovat

Vezměte prosím na vědomí, že Sudo poskytuje nějakou kontrolu zdravého rozumu, ale to nestačí - zkontrolujte každý řádek ve svém vlastním kódu.

Poslední poznámka: zvažte využití možností. Umožňují vám udělit proces spuštěný jako uživatelská zvláštní oprávnění, která by normálně vyžadovala oprávnění root. Avšak například, zatímco ping potřebuje manipulovat se sítí, nemusí mít přístup k souborům. Nejsem si však jistý, zda jsou zděděni.

12
Maciej Piechotka

příkaz super [-r reqpath] [args]

Super umožňuje určitým uživatelům spouštět skripty (nebo jiné příkazy), jako by šlo o root; nebo může nastavit uid, gid a/nebo doplňkové skupiny na základě jednotlivých příkazů před provedením příkazu. Má být bezpečnou alternativou k vytváření rootů skriptů setuid. Super také umožňuje běžným uživatelům dodávat příkazy pro provádění ostatními; tyto se spouští s uid, gid a skupinami uživatele, který příkaz nabízí.

Super konzultuje soubor `` super.tab '', aby zjistil, zda má uživatel dovoleno provést požadovaný příkaz. Pokud je uděleno oprávnění, super provede pgm [args], kde pgm je program, který je přidružen k tomuto příkazu. (Kořen je ve výchozím nastavení povolen, ale stále jej lze odepřít, pokud pravidlo vylučuje kořenový adresář. Běžní uživatelé jsou ve výchozím nastavení zakázáni.)

Pokud je příkaz symbolickým odkazem (nebo také pevným odkazem) na super program, pak je psaní% příkaz args ekvivalentní psaní% super příkaz args (Příkaz nesmí být super, nebo super nepozná, že je vyvolán přes odkaz.)

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

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

5
Nizam Mohamed

Můžete vytvořit alias pro Sudo + název skriptu. Nastavení je samozřejmě ještě více práce, protože musíte také nastavit alias, ale ušetří vám to zadávání Sudo.

Ale pokud vám nevadí hrozná bezpečnostní rizika, použijte jako interpret skriptu Shell setuid Shell. Nevím, jestli to pro vás bude fungovat, ale myslím, že by to mohlo být.

Dovolte mi říci, že nedoporučuji to skutečně dělat. Já to jen zmiňuji pro vzdělávací účely ;-)

4
wzzrd

Pokud z nějakého důvodu není Sudo k dispozici, můžete napsat C skript tenkého obalu do C:

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

A jakmile to zkompilujete, nastavíte jej jako setuid s chmod 4511 wrapper_script.

Je to podobné jako jiná vyslaná odpověď, ale spustí skript s čistým prostředím a explicitně používá /bin/bash Namísto Shell nazvaného system(), a tak uzavírá některé potenciální bezpečnostní díry.

Všimněte si, že tím zcela zlikvidujete prostředí. Pokud chcete použít některé proměnné prostředí, aniž byste museli otevírat zranitelnosti, musíte použít pouze Sudo.

Je zřejmé, že chcete zajistit, aby samotný skript byl zapisovatelný pouze rootem.

2
Chris