|
|
Dit document is beschikbaar in: English Castellano Deutsch Francais Nederlands Russian |
door Frédéric Raynal, Christophe Blaess, Christophe Grenier <pappy/at/users.sourceforge.net, ccb/at/club-internet.fr, grenier/at/nef.esiea.fr> Over de auteur: Christophe Blaess is een onafhankelijke luchtvaart ingenieur. Hij is een Linux fan en werkt veel met dit systeem. Hij coördineert de vertaling van de man pages zoals die te vinden zijn op de site van het Linux Documentation Project. Christophe Grenier is een 5e jaars student aan de ESIEA, hij werkt daar ook als systeembeheerder. Hij is gek van computer beveiligingssystemen. Frédéric Raynal gebruikt Linux, gecertificeerd zonder enige vorm van patent. Maar iets geheel anders: je moet echt Dancer in the Dark gaan zien: behalve Björk die geweldig is, moet deze film je wel raken (Ik kan niet meer zeggen zonder het einde weg te geven, maar het is zowel tragisch als fantastisch). Vertaald naar het Nederlands door: Hendrik-Jan Heins <hjh/at/passys.nl> Inhoud: |
Het vermijden van veiligheidslekken bij het ontwikkelen van een applicatie - Deel 1Kort:
Dit artikel is het eerste in een serie over de voornaamste typen veiligheidslekken in applicaties. We zullen laten zien hoe ze vermeden kunnen worden door je ontwikkelgewoontes enigzins te veranderen. |
Het duurt over het algemeen niet langer dan 2 weken voordat er in een belangrijke applicatie die onderdeel is van de meeste Linux applicaties een veiligheidslek gevonden is, zoals bijvoorbeeld een gat waardoor een locale gebruiker root kan worden. Ondanks de hoge kwaliteit van het grootste deel van deze software, is het lastig om de veiligheid van een programma te garanderen: de software moet zodanig ontwikkeld zijn dat een inbreker niet op een legale manier gebruik kan maken van systeembronnen. De beschikbaarheid van de broncode van applicaties is hierbij een grote hulp, en wordt als zodanig ook gewaardeerd door programmeurs, maar het nadeel hiervan is dat de kleinste foutjes in de software voor iedereen zichtbaar worden. Bovendien worden deze defecten toevallig aangetroffen en de mensen die deze defecten aantreffen, hebben niet altijd de beste bedoelingen.
Voor de systeembeheerder betekent dit dat zijn dagelijks werk bestaat uit
het lezen van de lijsten met bekende veiligheidsproblemen en het onmiddelijk
updaten van de betreffende pakketten. Voor een programmeur kunnen deze
veiligheidslekken een goede oefening zijn om deze veiligheidslekken uit te
proberen, aangezien het beter is om veiligheidslekken te vermijden bij het
programmeren, zodat ze ook later niet gedicht hoeven te worden. We zullen
proberen enkele "klassieke" gevaarlijke gedragingen te definiëren en oplossingen geven om de risico's te beperken. We gaan het niet hebben over
netwerkbeveiligings-problemen, aangezien die vaak het gevolg van problemen
met de configuratie zijn (gevaarlijke cgi-bin scripts, ...) of van systeembugs
die DOS (Denial Of Service) type aanvallen toelaten en aldus ervoor
zorgen dat een machine niet meer luistert naar de eigen clienten. Dit type
probleem is de zorg van de systeembeheerder of de kernel-ontwikkelaars. Maar
de applicatie-programmeur moet ook z'n eigen code beschermen zodra hij externe
gegevens gaat gebruiken. Sommige versies van pine
,
acroread
, netscape
, access
,...
stonden toegang met meer rechten toe of hadden onder sommige omstandigheden
last van informatie lekken. Dus eigenlijk is veilig programmeren
ieders zorg.
Deze serie artikelen laat methodes zien die gebruikt kunnen worden om een Unix systeem te beschadigen. We hadden ze ook alleen kunnen noemen of er slechts enkele woorden aankunnen wijden, maar we prefereren de complete uitleg om mensen de risico's te laten begrijpen. Dus, als je een programma "debugged" of je eigen programma ontwikkelt, kan je nu deze fouten vermijden of verbeteren. We gaan ieder veiligheidslek op dezelfde manier benaderen. We gaan beginnen met de details omtrend de manier waarop het werkt, daarna laten we zien hoe ze te vermijden zijn.Bij ieder voorbeeld zullen we gebruik maken van veiligheidslekken die ook nu nog in veel gebruikte software voorkomen.
Dit eerste artikel gaat over de basiskennis die benodigd is om
veiligheidslekken te begrijpen, daarom gaat het vooral over
privileges en de Set-UID of de Set-GID bit.
Daarna gaan we de gaten die gebaseerd zijn op de system()
functie analyseren, omdat deze makkelijker te begrijpen zijn.
We zullen veel gebruik maken van kleine C programmaatjes om dat
waarover we praten te illustreren. De aanpakken die in deze reeks
artikelen worden genoemd zijn echter ook van toepassing op andere
programmeertalen, zoals java, perl, commandoregelscripts....
Sommige veiligheidslekken zijn afhankelijk van de gebruikte taal,
maar dit geldt niet voor alle veiligheidslekken zoals we zullen
zien met system()
.
op een Unix systeem zijn gebruikers niet gelijk aan elkaar, en ook
applicaties zijn dat niet. De toegang tot de bestandssysteem-nodes
en daarmee de randapparatuur vertrouwt op een stricte identiteits-
check. Sommige gebruikers mogen gevoelige operaties uitvoeren om
het systeem te onderhouden. Een getal genaamd UID (User Identifier)
staat identificatie toe. Om dit wat eenvoudiger te maken correspondeert
dit nummer met een gebruikersnaam, hierbij wordt deze associatie gelegd
in het /etc/passwd
bestand.
De UID 0, met de standaardnaam root, heeft overal in
het systeem toegang. Hij kan systeemnodes aanleggen, veranderen
verwijderen, maar hij kan ook de fysieke configuratie van de
machine beheren, partities "mounten", netwerkconnecties activeren
en de bijbehorende configuratie veranderen (bijvoorbeeld het IP-adres),
of gebruik maken van systeemaanroepen zoals mlock()
om
fysiek geheugen te veranderen, of sched_setscheduler()
om
het volgorde-mechanisme te veranderen. In een nog te schrijven artikel
zullen we de Posix.1e features gaan bestuderen die ons toestaan om de
privileges van een applicatie die draait als root te beperken,
maar voor nu gaan we er vanuit dat de "super-user" alles kan op een
machine.
De aanvallen die we zullen noemen, zijn interne aanvallen hierbij gaat het dus om een geautoriseerde gebruiker die op een machine privileges probeert te verkrijgen die hij niet heeft. Netwerkaanvallen zijn externe aanvallen die komen van mensen die proberen een verbinding te leggen met een machine waarop ze geen toegang hebben.
Om gebruik te maken van privileges die gereserveerd zijn voor een andere gebruiker zonder dat er ingelogd kan worden onder de identiteit van die gebruiker, moet iemand tenminste de mogelijkheid hebben om te communiceren met een applicatie onder het UID van het slachtoffer. Als een applicatie, een proces, onder Linux draait, heeft het een strak gedefinieerde identiteit. Allereerst heeft een programma een attribuut dat een RUID (Real UID) genoemd, dat correspondeert met de ID van de gebruiker die het programma gestart heeft. Deze gegevens worden beheerd door de kernel en kunnen over het algemeen niet veranderd worden. een tweede attribuut completeert deze informatie: het EUID veld (Effective UID) dat correspondeert met de identiteit die de kernel aanneemt bij het beheren van de toegangsrechten (het openenen van bestanden, gereserveerde systeemaanroepen).
De privileges van een andere gebruiker aannemen betekent dat alles uitgevoerd zal worden onder de UID van die gebruiker en niet onder de "correcte", eigen UID. Een cracker probeert natuurlijk de root ID te verkrijgen, maar veel andere gebruikers-accounts zijn ook interessant, hetzij doordat zij toegang geven to systeem- gegevens (news, mail, lp...) hetzij doordat ze het lezen van prive-gegevens toestaan (mail, personal files, etc) of omdat ze gebruikt kunnen worden om illegale activiteiten zoals aanvallen op andere sites kunnen maskeren.
Om een applicatie te draaien met de privileges van een effectieve UID
die anders is dan z'n echte UID (de gebruiker die hem gestart heeft), moet
het uitvoerbare bestand een specifieke bit hebben die aan staat en Set-UID
heet. deze bit kan gevonden worden in het bestandstoegangsattribuut (evenals
de bits die de uitvoerbaarheid, schrijfbaarheid bits, groepsleden en andere
toegangsrechten bevat) en hij heeft de octale waarde 4000. De Set-UID bit
wordt gerepresenteerd door een s
als de rechten met een
ls
commando worden opgevraagd:
Het commando ">> ls -l /bin/su -rwsr-xr-x 1 root root 14124 Aug 18 1999 /bin/su >>
find / -type f -perm +4000
" geeft een
lijst van de systeemapplicaties weer met hun Set-UID bit ingesteld
op 1. Wanneer de kernel een applicatie draait met de Set-UID bit in de
aan-stand, maakt hij gebruik van de identiteit van de programma-eigenaar
als EUID voor het proces. De RUID aan de andere kant, verandert niet en
correspondeert met de gebruiker die het programma startte. Iedere gebruiker
heeft bijvoorbeeld toegang tot het /bin/su
commando, maar het
commando draait onder de identiteit van z'n eigenaar (root) met
alle privileges die op het systeem aanwezig zijn.. Het moge duidelijk zijn
dat de programmeur zeer voorzichtig moet zijn als hij een programma schrijft
dat dit attribuut bevat.
Ieder proces heeft ook een effectief groeps-ID, EGID, en een echte
identifier met de RGID. De Set-GID bit (2000 in octalen) in de toegangs
rechten van een uitvoerbaar bestand, vraagt de kernel om de groep waartoe
de applicatie behoort als EGID en niet de GID van de gebruiker die het
programma gestart heeft. Een vreemde combinatie verschijnt soms wanneer de
Set-GID op 1 staat, maar zonder de groepsuitvoer bit. Dit is eigenlijk een
conventie die niets te maken heeft met privileges in relatie tot applicaties,
maar dit geeft aan dat het bestand geblokkeerd kan worden met de functie
fcntl(fd, F_SETLK, lock)
. Normaal gesproken maakt
een applicatie geen gebruik van de Set-GID bit, maar soms gebeurt het wel.
Enkele spelletjes bijvoorbeeld gebruiken het om de beste scores in een
systeemdirectory te bewaren.
Er zijn verschillende typen aanvallen tegen een systeem. Vandaag zullen we de mechanismen om een extern commando uit te voeren vanuit een applicatie bestuderen. Dit is meestal een commandoregel die draait onder de identiteit van de eigenaar van de applicatie. Een tweede type aanval vertrouwt op de buffer overflow die de aanvaller de mogelijkheid geeft om persoonlijke code instructies te geven. En tenslotte is de derde belangrijke aanvalsmethode is gebaseerd op race condition - een "tijdsgat" tussen twee instructies waarin een systeemcomponent wordt veranderd (normaal gesproken is dat een bestand), terwijl de applicatie denkt dat er niets veranderd is.
De eerste twee aanvalstypes proberen meestal een commandoregel uit
te voeren met de privileges van de eigenaar van de applicatie, terwijl
de derde in plaats daarvan is gericht op het verkrijgen van schrijftoegang
naar beschermde systeembestanden. Leestoegang wordt soms gezien als een
zwakte in de systeembeveiliging (persoonlijke bestanden, emails, password
bestand /etc/shadow
, en pseudo kernel configuratiebestanden
in /proc
).
De doelen van aanvallen op de systeembeveiliging zijn over het algemeen
programma's die een Set-UID (of Set-GID) bit aan hebben staan. Dit heeft
echter ook effect op alle applicaties die draaien onder een andere ID dan
die van z'n eigen gebruiker. De systeem daemons vertegenwoordigen een grote
groep van dit type programma's. Een daemon is een applicatie die over het
algemeen tijdens het opstarten wortd gestart en draait op de achtergrond
zonder enige controle terminal en die gepriviligeerd werk voor alle
gebruikers doet. Bijvoorbeeld de lpd
daemon, die alle
gebruikers toestaat om documenten naar de printer te sturen,
sendmail
verstuurt en her-adresseert e-mail, of
apmd
vraagt de Bios voor de status van de battery van een
notebook. Sommige daemons beheren de communicatie met externe gebruikers via
het netwerk (Ftp, Http, Telnet... services). Een service die
inetd
heet beheert de connecties voor veel van deze services.
We kunnen nu concluderen dat een programma aangevallen kan worden zodra het communiceert, hoe kort ook, met een gebruiker die niet dezelfde is als degene die het programma gestart heeft. Als je dit type applicatie ontwikkelt, moet je vooral op de risico's letten die de functies die we hier bestuderen met zich meebrengen.
Wanneer een applicatie draait met een EUID die anders is dan z'n RUID, dan is dat om de gebruiker privileges te geven die hij nodig heeft, maar zelf niet heeft (bestandstoegang, gereserveerde systeemaanroepen...). Deze privileges zijn echter maar korte tijd nodig, bijvoorbeeld voor het openen van een bestand, afgezien daarvan kan de applicatie draaien onder de gebruikersprivileges, Het is mogelijk om tijdelijk de EUID van een applicatie te veranderen met behulp van een systeemaanroep:
int seteuid (uid_t uid);Een proces kan z'n EUID altijd veranderen en het een RUID geven. In dat geval wordt de oude UID bewaard in een veld genaamd SUID (Saved UID) anders dan SID (Session ID) gebruikt voor controle terminal beheer.Het is altijd mogelijk om de SUID terug te krijgen en te gebruiken als EUID. Natuurlijk kan een programma dat een null EUID heeft (root) wanneer hij dat wil zowel z'n EUID en RUID veranderen (dit is hoe
/bin/su
werkt).
Om de risico's van een aanval te verkleinen, wordt voorgesteld om de EUID te veranderen en de RUID van de gebruikers in plaats daarvan te gebruiken. Wanneer een deel van de code privileges nodig heeft die corresponderen met die van de bestandseigenaar, is het mogelijk om de bewaarde UID in de EUID te zetten. Hier is een voorbeeld:
uid_t e_uid_initial; uid_t r_uid; int main (int argc, char * argv []) { /* bewaart de verschillende UIDs */ e_uid_initial = geteuid (); r_uid = getuid (); /* limiteert de toegangsrechten tot die van de * gebruiker die het programma start */ seteuid (r_uid); ... privileged_function (); ... } void privileged_function (void) { /* Krijgt de initiële privileges terug */ seteuid (e_uid_initial); ... /* Deel dat de privilegs nodig heeft */ ... /* Terug naar de rechten van degene die het programma heeft gestart */ seteuid (r_uid); }
Deze methode is veiliger dan de, helaas, veel gebruikte methode waarbij de initiële EUID wordt gebruikt en dat deze tijdens een "risicovolle operatie" tijdelijk gereduceerde privileges krijgt. Deze reductie in privileges is echter waardeloos tegen buffer-overflow aanvallen. Zoals we in het volgende artikel zullen zien, willen deze aanvallen de applicatie vragen een persoonlijke instructie uit te voeren en deze kan systeemaanroepen bevatten die nodig zijn om het privilegeniveau te verhogen. Maar ondanks dat beschermt deze aanpak tegen externe commando's van de meeste race conditions.
Een applicatie moet vaak een externe systeemfunctie aanroepen. Een
bekend voorbeeld betreft het mail
commando dat e-mail
beheert (het draaien van rapporten, alarmfunctie, statistiek etc.)
dit alles zonder dat er een complexe dialoog met het mail systeem
bestaat. De eenvoudigste oplossing is gebruik maken van de
bibliotheekfunctie:
int system (const char * command)
Deze functie is vrij gevaarlijk: hij roept de commandoregel aan om
het commando dat gegeven is als argument uit te voeren. Het gedrag van
de commandoregel hangt af van de keuzes van de gebruiker. Een typisch
voorbeeld komt van de PATH
omgevingsvariabele.
Laten we eens kijken naar een applicatie die de mail
functie aanroept. Bijvoorbeeld het nu volgende programma dat z'n broncode
naar de gebruiker die het starttte stuurt:
Laten we zeggen dat het programma Set-UID root is:/* system1.c */ #include <stdio.h> #include <stdlib.h> int main (void) { if (system ("mail $USER < system1.c") != 0) perror ("system"); return (0); }
Om dit programma uit te voeren, draait het systeem een commandoregel (met>> cc system1.c -o system1 >> su Password: [root] chown root.root system1 [root] chmod +s system1 [root] exit >> ls -l system1 -rwsrwsr-x 1 root root 11831 Oct 16 17:25 system1 >>
/bin/sh
) en met de optie -c
, het geeft
hem de uit te voeren instructie. Daarna gaat de commandoregel door de
directoryboom volgens de PATH
omgevingsvariabele om een
uitvoerbaar bestand te vinden dat mail
heet. Om het programma
te compromiteren, hoeft de gebruiker alleen maar de inhoud van de variabelen
te veranderen voor het draaien van de applicatie. Bijvoorbeeld:
Zoekt naar het>> export PATH=. >> ./system1
mail
commando, maar alleen binnen de huidige
directory. Er hoeft nu alleen maar een uitvoerbaar bestand worden gemaakt
(bijvoorbeeld een script dat een nieuwe commandoregel start) en noem het
mail
en het programma zal nu uitgevoerd worden met de EUID
van de eigenaar van de hoofdapplicatie! Hier draait ons script
/bin/sh
. Echter, doordat het is uitgevoerd met een
doorgestuurde standaard input ( net als het initiële mail
commando), we moeten het terug in de terminal zien te krijgen. Daarom maken
we nu het volgende script:
Hier is het resultaat:#! /bin/sh # "mail" script draait een commandoregel # en krijgt de standaardinput terug. /bin/sh < /dev/tty
>> export PATH="." >> ./system1 bash# /usr/bin/whoami root bash#
Deze eerste oplossing bestaat er natuurlijk uit dat de volledige
padnaam van het programma wordt gegeven, bijvoorbeeld
/bin/mail
. Hierdoor verschijnt er een nieuw probleem:
de applicatie vertrouwt op de systeeminstallatie. Een applicatie
als /bin/mail
is normaal gesproken op ieder systeem
beschikbaar, maar is bijvoorbeeld GhostScript? (staat het in
/usr/bin
, /usr/share/bin
,
/usr/local/bin
?). Een ander type aanval wordt mogelijk
met enkele oude commanodoregels: het gebruik van de omgevingsvariabele
IFS
. De commandoregel gebruikt deze om de woorden in de
commandoregel te ontleden. Deze variabele bevat de afscheiders. De
standaardwaardes hiervoor zijn de spatie, tab en return. Als de gebruiker
de slash toevoegt /
, begrijpt de commandoregel het commandos
"/bin/mail
" als "bin mail
". Een uitvoerbaar bestand
geheten bin
in de huidige directory kan nu worden uitgevoerd
door PATH
in te stellen, precies zoals we eerder al zagen, en
het staat toe om het programma te draaien met de EUID van de hoofdapplicatie.
Onder Linux is de IFS
omgevingsvariabele geen probleem meer,
omdat bash en pdksh hem beiden met de standaard karakters uitvoeren tijdens het
opstarten. Maar omdat je aan de portabiliteit van de applicatie moeten denken,
moet je erop letten dat sommige systemen minder veilig zijn met deze variabele.
Enkele andere omgevingsvariabelen kunnen onverwachte problemen veroorzaken.
Bijvoorbeeld de mail
applicatie, deze staat gebruikers toe om
een commando te draaien terwijl ze een bericht samenstellen door middel van
een escape sequentie "~!
". Als de gebruiker de string
"~!commando
" aan het begin van de regels schrijft,
wordt het commando uitgevoerd. Het programma /usr/bin/suidperl
wordt gebruikt om perl scripts uit te voeren met een Set-UID bit aanroep
/bin/mail
om een bericht naar de root te sturen als het
een probleem vindt. Aangezien /bin/mail
een Set-UID
root, aanroep is, wordt /bin/mail
uitgevoerd met root
privileges en bevat het de naam van het "beschadigde" bestand.
Een gebruiker kan nu een bestand maken met een naam die een "carriage return"
gevolgd door een ~!command
sequence en nog een "carriage return"
bevat. Als een perl script dat suidperl
aanroept faalt op een
"low-level" probleem dat gerelateerd is aan dit bestand, wordt er een bericht
gestuurd onder de identiteit van de root dat de escape sequence van
de mail
applicatie bevat en het commando in de bestandsnaam
wordt uitgevoerd met root privileges.
Dit probleem zou niet moeten bestaan, aagezien het mail
programma geen escape sequences zou moeten mogen accepteren wanneer deze
automatisch worden gedraaid (dus niet vanaf een terminal). Helaas staat een
ongedocumenteerde "feature" van deze applicatie (waarschijnlijk voor debuggen),
deze escape sequence toe zodra de omgevingsvariabele interactive
is
aangezet. Het resultaat? Een veiligheidslek dat eenvoudig geëxploiteerd
kan worden (en dat gebeurt dus ook) in een applicatie die de systeemveiligheid
zou moeten verbeteren. De schuld moet gedeeld worden. Allereerst bevat
/bin/mail
een ongedocumenteerde optie die vooral gevaarlijk is
omdat het code-uitvoering toestaat terwijl het alleen de verzonden data
controleert, wat a priori verdacht zou zijn voor een mail applicatie
Ten tweede, zelfs wanneer de /usr/bin/suidperl
ontwikkelaars
zich niet bewust waren van de interactieve
variabele, zouden ze de
uitvoerbare omgeving niet moeten hebben laten zoals hij was wanneer er een
extern commando werd aangeroepen, vooral niet wanneer dit programma schrijft
als Set-UID root.
Eigenlijk negeert Linux de Set-UID en Set-GID bit wanneer scripts uitgevoerd
worden (lees /usr/src/linux/fs/binfmt_script.c
en
/usr/src/linux/fs/exec.c
). Maar sommige trucs staan je toe om deze
regel over te slaan, zoals Perl doet met z'n eigen scripts door gebruik te maken
van /usr/bin/suidperl
om deze bits ook in aanmerking te nemen.
Het is niet altijd eenvoudig om een vervanger te vinden voor de
system()
functie. De eerste variant is gebruik maken van systeem
aanroepen zoals execl()
of execle()
. Echter, dit zal
heel anders werken, aangezien een extern programma niet langer aangeroepen wordt
als een subroutine, maar in plaats daarvan worden de commando's aangeroepen in
plaats van het al draaiende proces. Je moet het proces dus splitsen en de
commandoregel argumenten ontleden. Vandaar het volgende programma:
wordt:if (system ("/bin/lpr -Plisting stats.txt") != 0) { perror ("Printing"); return (-1); }
De code wordt duidelijk zwaarder! In sommige situaties wordt hij dan ook vrij complex, bijvoorbeeld wanneer je de standaard input van de applicatie opnieuw moet verwijzen, zoals in:pid_t pid; int status; if ((pid = fork()) < 0) { perror("fork"); return (-1); } if (pid == 0) { /* kindproces */ execl ("/bin/lpr", "lpr", "-Plisting", "stats.txt", NULL); perror ("execl"); exit (-1); } /* moederproces */ waitpid (pid, & status, 0); if ((! WIFEXITED (status)) || (WEXITSTATUS (status) != 0)) { perror ("Printing"); return (-1); }
Dit betekent dat de verwijzing gedefinieerd doorsystem ("mail root < stat.txt");
<
wordt gedaan
vanuit de commandoregel. Je kan hetzelfde doen met een gecompliceerde sequence
zoals fork()
, open()
, dup2()
,
execl()
, etc. In dat geval zou het gebruik van de functie
system()
een acceptabele oplossing zijn, maar dan moet wel de
hele omgeving veranderd worden.
In Linux worden de omgevingsvariabelen bewaard in de vorm van een pointer
naar een tabel van karakters: char ** environ
. Deze tabel eindigt
met NULL. De strings hebben de vorm: "NAME=value
".
We beginnen met het verwijderen van de omgeving met behulp van de GNU extensie:
of dwing de pointerint clearenv (void);
Om de Null waarde te kiezen. Nu worden de belangrijke omgevingsvariabelen geïnitialiseerd, met behulp van gecontroleerde waardes, in de functies:extern char ** environ;
Voor het aanroepen van deint setenv (const char * name, const char * value, int remove) int putenv(const char *string)
system()
functie.
bijvoorbeeld :
Als het nodig is, kan je de inhoud van enkele bruikbare variabelen bewaren voordat je de omgeving verwijdert (clearenv (); setenv ("PATH", "/bin:/usr/bin:/usr/local/bin", 1); setenv ("IFS", " \t\n", 1); system ("mail root < /tmp/msg.txt");
HOME
,
LANG
, TERM
, TZ
,etc.). De
inhoud, de vorm en het formaat van deze variabelen moeten streng
worden gecontroleerd. Het is belangrijk dat je de hele omgeving moet verwijderen
voordat je de benodigde variabelen opnieuw gedefinieerd worden. Het
suidperl
veiligheidsgat zou nooit zijn verschenen als de omgeving
correct was verwijderd.
Analoog hieraan betekent dit dat de bescherming van een machine op een netwerk inhoudt dat iedere connectie geweigerd dient te worden. Hierna activeert de systeembeheerder de benodigde en bruikbare services. Op dezelfde manier als bij het programmeren de omgeving opgeschoond moet worden en vervolgens gevuld moet worden met de benodigde variabelen bij een Set-UID applicatie.
Een parameter format wordt gecontroleerd door de verwachtte waarde te vergelijken met de toegestane formats. Als de vergelijking een match oplevert, wordt de parameter gevalideerd. Anders wordt hij afgewezen. Wanneer je de test draait terwijl je gebruik maakt van een lijst van ongeldige format waardes, wordt het risico van een verkeerde waarde verhoogd en dat kan een ramp betekenen voor het systeem.
We moeten begrijpen dat wat gevaarlijk is met system()
ook
gevaarlijk is voor daarvan afgeleide functies zoals popen()
, of
systeem-aanroepen zoals execlp()
of execvp()
als de
PATH
variable in aanmerking wordt genomen.
Om de bruikbaarheid van een programma te verbeteren, is het makkelijk
om de gebruiker de mogelijkheid te bieden om het grootste deel van de
software te configureren, met behulp van macro's bijvoorbeeld. Om
variabelen of "generic" patronen, te beheren zoals de commandoregel doet,
bestaat er een krachtige functie genaamd wordexp()
. Je moet
er zeer voorzichtig mee zijn, aangezien een string als $
(command)
de uitvoer van het genoemde terminal commando
toestaat. Het geven van de string "$(/bin/sh)
" creëert een
Set-UID commandoregel. Om dit te vermijden heeft wordexp()
een attribuut genaamd WRDE_NOCMD
die de interpretatie van
de $( )
sequence deactiveert.
Als je een extern commando initieert moet je oppassen dat je geen
programma aanroept dat de mogelijkheid biedt om te "ontsnappen" naar
de commandoregel (zoals de vi :!commando
sequence).
Het is lastig om ze allemaal te noemen, van sommige applicaties is het
duidelijk (tekst editors, bestandsmanagers...), maar anderen zijn lastiger
te detecteren (zoals we hebben gezien met /bin/mail
) of ze
hebben gevaarlijke debug-modi.
Dit artikel illustreert verschillende aspecten:
Het volgende artikel zal gaan over gehuegen, de organisatie en de functie aanroepen voordat de buffer overflows bereikt worden. We zullen ook gaan zien hoe shellcode gebouwd moet worden.
|
Site onderhouden door het LinuxFocus editors team
© Frédéric Raynal, Christophe Blaess, Christophe Grenier, FDL LinuxFocus.org |
Vertaling info:
|
2004-01-03, generated by lfparser version 2.43