[LinuxFocus-icon]
<--  | Home  | Map  | Index  | Zoek

Nieuws | Archieven | Links | Over LF
Dit document is beschikbaar in: English  Castellano  Deutsch  Francais  Nederlands  Indonesian  Russian  

[de auteurs]
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 1

[article illustartion]

Kort:

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.


_________________ _________________ _________________

 

Inleiding

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().

 

Privileges

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:

>> ls -l /bin/su
-rwsr-xr-x  1 root  root  14124 Aug 18  1999 /bin/su
>>

Het commando "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.

 

Typen aanvallen en potentiële doelen

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.

 

Het veranderen van privilegeniveau's

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.

 

Het draaien van externe commando's

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)
 

Gevaren van de system() functie

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:

/* system1.c */

#include <stdio.h>
#include <stdlib.h>

int
main (void)
{
  if (system ("mail $USER < system1.c") != 0)
    perror ("system");
  return (0);
}
Laten we zeggen dat het programma Set-UID root  is:
>> 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
>>

Om dit programma uit te voeren, draait het systeem een commandoregel (met /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:
  >> export PATH=.
  >> ./system1

Zoekt naar het 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:
#! /bin/sh
# "mail" script draait een commandoregel
# en krijgt de standaardinput terug.
/bin/sh < /dev/tty

Hier is het resultaat:
>> 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.

 

Oplossingen

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:

  if (system ("/bin/lpr -Plisting stats.txt") != 0) {
    perror ("Printing");
    return (-1);
  }

wordt:
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);
}

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:
system ("mail root < stat.txt");

Dit betekent dat de verwijzing gedefinieerd door < 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:

    int clearenv (void);

of dwing de pointer
    extern char ** environ;

Om de Null waarde te kiezen. Nu worden de belangrijke omgevingsvariabelen geïnitialiseerd, met behulp van gecontroleerde waardes, in de functies:
    int setenv (const char * name, const char * value, int remove)
    int putenv(const char *string)

Voor het aanroepen van de system() functie. bijvoorbeeld :
    clearenv ();
    setenv ("PATH", "/bin:/usr/bin:/usr/local/bin", 1);
    setenv ("IFS", " \t\n", 1);
    system ("mail root < /tmp/msg.txt");

Als het nodig is, kan je de inhoud van enkele bruikbare variabelen bewaren voordat je de omgeving verwijdert (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.

 

Indirecte uitvoer van commando's

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.

 

Conclusie

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.

 

Talkback voor dit artikel

Elk artikel heeft zijn eigen talkback pagina. Daar kan je commentaar geven of commentaar van anderen lezen:
 talkback pagina 

<--, Terug naar de titelpagina van dit nummer

Site onderhouden door het LinuxFocus editors team
© Frédéric Raynal, Christophe Blaess, Christophe Grenier, FDL
LinuxFocus.org
Vertaling info:
fr --> -- : Frédéric Raynal, Christophe Blaess, Christophe Grenier <pappy/at/users.sourceforge.net, ccb/at/club-internet.fr, grenier/at/nef.esiea.fr>
fr --> en: Georges Tarbouriech <georges.t/at/linuxfocus.org>
en --> nl: Hendrik-Jan Heins <hjh/at/passys.nl>

2004-01-03, generated by lfparser version 2.43