Nebezpečenstvá SUID shell skriptov
Thomas Akin
Tento článok sa snaží balansovať na tenkej hranici medzi úplným zverejnením informácií (full
disclosure) a publikovanými exploitmi. Cieľom článku je ukázať, ako fungujú SUID programy, a
pomôcť tak ostatným, ktorí píšu vlastné programy, vyhnúť sa bežným chybám. Uvedené
príklady sú dostatočne podrobné na to, aby ste pochopili jednotlivé nebezpečenstvá, no
nesľubujem, že všetky budú fungovať presne tak, ako je uvedené, ak by ste sa ich pokúsili
použiť škodlivo.
Bežne UNIXové skripty a programy bežia s rovnakými oprávneniami ako používateľ, ktorý ich
spúšťa. Preto bežní používatelia nemôžu zmeniť svoje heslo priamou úpravou súboru/etc/passwd; nemajú oprávnenie zapisovať do /etc/passwd a žiadny príkaz, ktorý spustia, toto
oprávnenie tiež mať nebude. SUID programy však tieto bežné oprávnenia obchádzajú a vždy
bežia s oprávneniami vlastníka programu. Vďaka tomu môžu používatelia použiť príkaz/usr/bin/passwd na zmenu svojho hesla. Príkaz /usr/bin/passwd je SUID a jeho vlastníkom je
root. Vždy beží s rovnakými oprávneniami ako root.
Keď noví administrátori objavia SUID, často v ňom vidia zázračné riešenie, ktoré vyrieši všetky
ich problémy. Okamžite začnú používať SUID skripty a programy, aby si uľahčili prácu. Žiaľ,
väčšinou to robia zle. Pri práci s administrátormi, ktorí sú v SUID nováčikmi, sa často stretávam
so skriptami ako tento:
% ls change-pass
-rwsr-x--- 1 root helpdesk
37 Feb 26 16:35 change-pass
% cat change-pass
#!/bin/csh -b
set user = $1
passwd $user
Tento jednoduchý skript bol vytvorený na to, aby help desku umožnil resetovať heslá
používateľov, čo je bežná potreba. Skript je SUID root a je spustiteľný iba pre roota alebo
členov skupiny helpdesk. Tento jednoduchý skript je však plný dier. Odhalím sedem z nich a
ukážem, či sa im dá predísť.
Prvý problém nastáva preto, že tento skript je napísaný v C-shelli. C-shell skripty sú zraniteľné
voči manipulácii s premennými prostredia (environment variables). Aby to útočník využil, môže
kompromitovať účet help desku (pomerne triviálne) a udeliť si root shell pomocou:
% env TERM='`cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755/tmp/sh`' change-pass
Ponaučenie prvé — Nikdy nepoužívajte C-shell pre SUID skripty.
% cat change-pass
#!/bin/ksh
user=$1
passwd $user
Prepísanie skriptu do Korn shellu nám pomôže vyhnúť sa problému s C-shellom, no stále máme
problémy. Skript je zraniteľný voči manipulácii s premennou PATH. Keďže program používa
relatívne cesty, útočník môže zmeniť svoju premennú PATH tak, aby sa namiesto skutočného
programu /usr/bin/passwd spustil jeho vlastný program:
% export PATH='/tmp'
% echo "cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755/tmp/sh" >/tmp/passwd
% ./change-pass
Premenná PATH bola zmenená a príkaz change-pass teraz namiesto zamýšľaného programu
/usr/bin/passwd spustí program /tmp/passwd.
Ponaučenie druhé — Vždy manuálne nastavte PATH a používajte absolútne cesty.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
/usr/bin/passwd $user
Teraz je PATH bezpečná a používame absolútne cesty; no pri bližšom pohľade zistíme, že tento
skript dokáže zmeniť heslo komukoľvek, dokonca aj rootovi! Nechceme, aby help desk (alebo
útočník) mohol pomocou nášho skriptu zmeniť heslo roota.
Ponaučenie tretie — Pochopte, ako fungujú programy vo vašom skripte.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
rm /tmp/.user
echo "$user" > /tmp/.user
isroot='/usr/bin/grep -c root /tmp/.user'
[ "$isroot" -gt 0 ] && echo "You Can't change root's password!" && exit
/usr/bin/passwd $user
Teraz sa tento skript ukončí, ak niekto ako argument zadá root. Čo sa však stane, ak útočník
spustí program bez zadania argumentu? Program spustí príkaz passwd bez akýchkoľvek
argumentov. Keď príkaz passwd nedostane žiadne argumenty, predvolene sa vzťahuje na
aktuálneho používateľa. Problém je v tom, že v root-owned SUID skripte je aktuálnym
používateľom vždy root. Help desk (alebo útočník) tak stále môže zmeniť heslo roota tým, že
skriptu change-pass nezadá žiadny argument.
Ponaučenie tretie (upravené) — Pochopte, ako fungujú programy vo vašom skripte, najmä ako spracúvajú argumenty.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
[ -z $user ] && echo "Usage: change-pass username" && exit
rm /tmp/.user
echo "$user" > /tmp/.user
isroot='/usr/bin/grep -c root /tmp/.user'
[ "$isroot" -gt 0 ] && echo "You Can't change root's password!" && exit
/usr/bin/passwd $user
Už nikomu neumožňujeme zmeniť heslo roota, všimnite si však, že používame dočasný súbor.
Tento skript dočasný súbor vymaže, znovu ho vytvorí, naplní ho menom používateľa a
nakoniec skontroluje, či dané meno používateľa nie je root.
Čo keby útočník dokázal načasovanie zladiť tak dokonale, že tesne po tom, čo skript odstráni
súbor /tmp/.user, no ešte predtým, než vytvorí nový súbor /tmp/.user, vytvoril prázdny súbor
/tmp/.user? Prepísal by sa súbor útočníka? Možno áno, možno nie, v závislosti od toho, ako bolo
nastavené prepisovanie súborov (file clobbering). Ak sa útočníkov súbor /tmp/.user neprepíše,
útočník obíde kontroly a prinúti skript, aby zmenil heslo roota. Aby si tento typ útoku uľahčil,
útočník by mohol napísať program, ktorý automaticky sleduje aktivitu a nahradí súbor
/tmp/.user.
Ponaučenie štvrté — Nepoužívajte dočasné súbory! Ak dočasné súbory musíte použiť, neumiestňujte ich do verejne zapisovateľného priestoru.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=$1
[ -z $user ] && echo "Usage: change-pass username" && exit
[ "$user" = root ] && echo "You can't change root's password!" && exit
/usr/bin/passwd $user
Dočasné súbory už nie sú prítomné, no teraz môže útočník využiť dobre známy trik s
bodkočiarkou (semi-colon trick). Bodkočiarka umožňuje na jeden riadok umiestniť viac ako
jeden príkaz. Využitím tohto by útočník mohol napísať:
% change-pass "user;cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh"
Náš skript by tento vstup prijal a spustil:
/usr/bin/passwd user;cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh
Každý z týchto príkazov sa vykoná postupne, čím sa poskytne root shell. Aby sme podobným
problémom predišli, musíme zabezpečiť, že žiadne dáta zadané používateľom neobsahujú
bodkočiarku ani žiadny iný metaznak shellu.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
user=${1##*[ \\$/;()|\>\<& ]}
[ -z $user ] && echo "Usage: change-pass username" && exit
[ "$user" = root ] && "You can't change root's password!" && exit
/usr/bin/passwd $user
Teraz zo vstupu používateľa odstraňujeme nasledujúce znaky: medzeru, , $, /, ;, (, ), |, >, <, &
a tabulátor. Nie je to na prvý pohľad vidieť, ale za otváracou zátvorkou ( [ ) je medzera a pred
zatváracou zátvorkou ( ] ) je tabulátor.
Ponaučenie piate — Nedôverujte a kontrolujte všetok používateľský vstup a odstraňujte z neho metaznaky.
Ďalšou bežnou zraniteľnosťou je zraniteľnosť súvisiaca s premennou IFS (Internal Field
Separator) shellu. IFS určuje, ktoré znaky oddeľujú jednotlivé príkazy. Bežne je nastavená na
medzeru, tabulátor alebo nový riadok. Zmenou IFS môže útočník zmeniť, ktoré programy náš
skript spustí. Náš skript volá program /usr/bin/passwd. Zmena IFS na „/“ pomocou
% export IFS=’/‘
spôsobí, že skript už nespustí /usr/bin/passwd, ale namiesto toho spustí usr bin passwd.
Útočník teraz môže vytvoriť skript s názvom usr, ktorý vygeneruje root shell, a náš SUID skript
ho za neho spustí.
Ponaučenie šieste — Vždy manuálne nastavte svoju premennú IFS.
% cat change-pass
#!/bin/ksh
PATH='/bin:/usr/bin'
IFS=' '
user=${1##*[ \\$/;()|\>\<& ]}
[ -z $user ] && echo "Usage: change-pass username" && exit
[ "$user" = root ] && "You can't change root's password!" && exit
/usr/bin/passwd $user
Bohužiaľ stále nie sme v bezpečí. V shell skriptoch existuje inherentná race condition (súbeh
procesov), ktorú nedokážeme vyriešiť ani lepším programovaním. Problém spočíva v tom, že
spustenie shell skriptu je dvojkrokový proces. Najprv systém spustí nový shell. Potom nový
shell prečíta obsah skriptu a vykoná ho. Presným načasovaním môže útočník zneužiť
oneskorenie medzi spustením shellu a momentom, keď je skript prečítaný a vykonaný.
Vytvorením odkazu (link) na SUID skript:
% cd /tmp
% ln -s change-pass rootme
spustením tohto odkazu a jeho rýchlym nahradením iným súborom:
% ./rootme &
% rm rootme && \
echo "cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh" \
>>rootme
je možné ako root spustiť čokoľvek. Takto vykonaný útok má len malú šancu na úspech,
existujú však techniky a programy, ktoré túto šancu zvyšujú a celý postup dokážu
automatizovať. Proti tomuto útoku existujú iba dve obrany.
Po prvé, nepoužívať SUID shell skripty.
Po druhé, niektoré systémy (napr. Solaris) tejto race condition predchádzajú tým, že
novému shellu odovzdajú už otvorený popisovač súboru (file handle), čím sa vyhnú potrebe
znovu otvárať a čítať SUID súbor.
Ponaučenie siedme — Nepoužívajte SUID shell skripty.
Aj po všetkej našej práci je takmer nemožné vytvoriť bezpečný SUID shell skript. (Na väčšine
systémov je to dokonca úplne nemožné.) Práve kvôli týmto problémom niektoré systémy
(napr. Linux) na shell skriptoch SUID vôbec nerešpektujú. Ak potrebujete funkcionalitu SUID,
existujú tri bezpečnejšie možnosti:
wrapper program napísaný v jazyku C, Perl skript alebo program ako sudo.
Ak ste v bezpečnom programovaní nováčikom, odporúčam buď sudo, alebo Perl skript.
SUID Perl skripty majú zabudovanú ochranu, ktorá programátorom zabraňuje robiť
chyby uvedené v tomto článku.
Viac informácií o bezpečnom SUID programovaní nájdete v knihe Practical UNIX & Internet Security (O’Reilly & Associates), alebo na stránke:
http://seclab.cs.ucdavis.edu/~bishop/secprog.html.
Thomas Akin pracuje v oblasti informačnej bezpečnosti už takmer desať rokov a špecializuje sa na bezpečnosť systémov UNIX, sietí a internetu. Okrem certifikátu CISSP má štyri certifikácie zamerané na UNIX a tri sieťové certifikácie. Väčšinu svojho času venuje tvorbe školiacich programov v oblasti informačnej bezpečnosti, výučbe a písaniu. Kontaktovať ho môžete na:
takin@crossrealm.com.
Preložené z: Sys Admin > Dangers of SUID Shell Scripts — http://web.archive.org/web/20071011040421/http://…