Hoe kan een buffer-underflow leiden tot uitvoering van externe code op 64-bits?

Ik heb de volgende samengevatte pseudocode in C89 van een SSH-serverbibliotheek die alleen toegang biedt tot zaken als git-shell (/bin/bash is vervangen door het programma dat moet worden uitgevoerd, dus het is niet mogelijk om iets anders te doen) :

struct _raw_uncapped_ssh_string { // no limit on the size of the string; uint32_t len; char non_null_terminated_string[]; // by protocol design it have a minimum length of 1 }; typedef struct _raw_uncapped_ssh_string raw_ssh_string; union buffer { void * uncapped_zlib_decompressed_network_data; // yes, the size is uncapped, so it’s possible to put 4Gb of // data in it that would be copied later into memory. zlib // allow easily to turn some Mb in Gb of data, but it’s not // the point of the question. raw_ssh_string st; }; get_command (compressed_network_data) { size_t len; char * command; buffer string=uncompress_to_buffer(compressed_network_data); len=ntohl(string.st.len)+1; command=malloc(len+1); command[len]=0; // here’s the point, both the string length and content as // well it’s supplied size is controlled by the attacker. memcpy(command,string.st.data,len); return command; } 

Hier is hoe het commando later wordt uitgevoerd (de string command is ongewijzigd na get_command()) .

const char *args[]={"/bin/bash",command,NULL}; // /bin/bash isn’t the shell, it has been replaced by git‑shell. // redirect the program output to the network. dup2(stdin, 0); dup2(stdout,1); dup2(stdout,2); close(stdin); close(stdout); //if this return execution failed and print an error message return execv(args[0],(char * const *)args); // I don’t know which is the system, so I can’t know about the libc behaviour. 

Ik kan memcpy(command,string.st.data,0) niet doen aangezien het derde lid van memcpy een minimale grootte van 1, en in mijn context gebruikt size_t een 64-bits geheel getal, ik kan geen bufferoverloop uitvoeren, aangezien er len.

Het enige wat ik kan doen is len instellen op een waarde die groter is dan die toegewezen aan string.st.data. Dit is een buffer-underflow waardoor ik niet-toegewezen geheugen kan lezen.
Ik kan het servergeheugen lezen, maar ik kan niet zien welke gevoelige gegevens een openbare ssh-server kan bevatten (in mijn geval de lijst met gebruikers die kan uitvoeren ssh is public) .

Dat geldt ook voor een buffer-underflow op memcpy remote toestaan code-uitvoering?

Reacties

  • Als de code echt doet command[len] = 0 dan is dat een bufferoverloop, aangezien de max index voor een buffer met lengte len len-1 is. Als alternatief, als de eigenlijke code een malloc(len+1) doet in plaats van een malloc(len), dan kun je een enorme bufferoverloop maken door de len waarde naar 0xFFFFFFFF.
  • @ThomasPornin: Nee dit is een fout van mijn kant, in feite roept het verschillende functies op om te doen dit. Neem aan dat de rest waar is.
  • @ThomasPornin: ik heb het over 64 bits, aangezien len size_t is, gebruikt het een 64 bits geheel getal. Er is dus geen manier voor integer overflow.
  • @ThomasPornin Ze wijzen len+1 toe, zodat de instelling op 0 geldig moet zijn.
  • @ RoraΖ: Hij had het over het instellen van string.st.len op ‑1.

Antwoord

In het algemeen, nee, een buffer-underflow kan niet worden gebruikt voor het uitvoeren van externe code. Omdat door een aanvaller gecontroleerde gegevens nooit de daarvoor bestemde ruimte verlaten, kunnen ze nooit de uitvoeringsstroom van het programma overnemen.

Een bufferonderloop heeft het potentieel voor andere soorten aanvallen, zoals openbaarmaking van informatie (als het programma erop rekent dat de originele inhoud van de buffer wordt weggevaagd door de nieuwe gegevens).

Opmerkingen

  • Of zoals ik zag onlangs in git, wordt later omgezet in een bufferoverloop.

Antwoord

Toen ik dit antwoord oorspronkelijk schreef , het lijkt erop dat ik enkele misverstanden die u koestert, heb verdoezeld. Deze misverstanden zouden u er waarschijnlijk van weerhouden mijn antwoord te begrijpen. Voor alle duidelijkheid: ik ga deze tekst groot maken om de nadruk te leggen, niet om respectloos te zijn:

Veel van de termen die u gebruikt, betekenen niet wat u lijkt te denken dat ze betekenen.

Bijvoorbeeld:

  • “het derde lid van memcpy bestaat niet omdat memcpy is een functie, niet een struct of union.
  • “Ik kan geen bufferoverloop uitvoeren, aangezien er len is en ik kan niet zonder benzine komen te zitten, aangezien er benzine is. Smeek ik de vraag? Het lijkt mij een passende analogie, vooral omdat theoretisch gesproken uw code kan een bufferoverloop veroorzaken.
  • “Het enige wat ik kan doen is div id = “82b7b4b452”>

naar een grotere waarde dan die is toegewezen aanstring.st.data. Dit is een buffer-underflow … ” Nee, dat is niet de definitie van een buffer-underflow. Een buffer-underflow treedt op wanneer u een array buiten het bereik opent met een negatieve index, of een index die aanwijzer rekenkundige omloop om plaats te vinden. Dit laatste kan mogelijk zijn voor sommige configuraties (zelfs op “64-bits systemen”, wat dat ook betekent), maar ik betwijfel of dit is wat u bedoelde toen u deze woorden aan het schrijven was, omdat u ze vervolgens opvolgde met:

  • “… waardoor ik niet-toegewezen geheugen kan lezen.” Ik denk dat u misschien “niet-geïnitialiseerde herinnering” bedoelde. Voor alle duidelijkheid: ik denk dat u bedoelde te zeggen dat u “heb een eigen risico toegewezen en het eigen risico niet geïnitialiseerd , en misschien wil je daar iets aan doen (zie calloc of memset).
  • Laten we char *fubar = NULL; even bekijken … dit soort aanwijzer heeft over het algemeen een nul waarde.Dereferentie wordt beschouwd als een null pointer dereference , maar als we iets schrijven als 0["hello"], krijgen we hetzelfde als "hello"[0] (dat is "h"). Zo kan een null-pointer-dereferentie worden gebruikt bij aanvallen wanneer de aanvaller de uitdrukking tussen de vierkante haken controleert (zoals in uw situatie).

    Terugkomend op de fubar ding; laten we zeggen dat we memset(&fubar, UCHAR_MAX, sizeof fubar);, nu fubar allemaal 1-bits is, wat betekent dat het mogelijk een adres is dat is het grootste adres dat ons systeem (theoretisch) zou kunnen bevatten. Wat als we toegang hebben tot fubar[1]? Hoe kun je toegang krijgen tot het element na het grootste adres? adres en dan omwikkelen? Technisch gezien is dit allemaal ongedefinieerd gedrag, maar als ik dat zou noemen op een gewone architectuur, zou het zijn:

    1. Rekenkundige overloop van een pointer, die leidt naar …
    2. Null pointer dereference, en / of mogelijk een buffer underflow .

    Staat een buffer-underflow op memcpy uitvoering van externe code toe?

    Een buffer underflow kan een aanvaller in staat stellen functie-pointers te overschrijven die zich bevinden in de geheugengebieden vóór de array in kwestie. Als die functie-pointers naar shellcode wijzen, kan die shellcode worden uitgevoerd wanneer ze “later opnieuw worden aangeroepen.

    In dit ietwat obscure stuk code lijkt het erop dat er” een risico op buffer-underflow bestaat wanneer int een bredere domein dan uint32_t, aangezien ntohl(string.st.len)+1 ervoor zou zorgen dat de uint32_t -waarde wordt geconverteerd naar een int type. Bedenk bijvoorbeeld of INT_MIN -4294967296 is (dit is één minder dan 0 - UINT32_MAX) en INT_MAX is 4294967295 … dit zou in wezen een 33-bits int zijn met opvulling om uit te vullen tot de breedte van een byte; ongebruikelijk, maar mogelijk. In dit geval is de uitdrukking ntohl(string.st.len)+1 niet uint32_t in type; het is “s int in type, en in plaats van terug te lopen naar 0 wanneer niet-ondertekende integer-overloop optreedt, wordt het waarschijnlijk teruglopen naar -4294967296 wanneer ondertekende integer overflow optreedt.

    Als u “op zoek bent naar een garantie tegen buffer underflow , gebruik dan de U letterlijk geheel getal achtervoegsel (dwz ntohl(string.st.len)+1U). In die situatie “zal je eindigen met de expressie die ofwel een uint32_t of een unsigned int is (afhankelijk van welk type de grootste domein).

    Als u bedenkt dat ntohl(string.st.len) één minder kan retourneren dan de maximumwaarde voor dat niet-ondertekende type (wat het ook is), dan len=ntohl(string.st.len)+1 zal resulteren in de maximale waarde, malloc(len+1) zal niet-ondertekende wrapping veroorzaken, dus je” zult uiteindelijk en dan command[len]=0 zal goed en waar voorbij het einde van de array schrijven. Dan heb je natuurlijk “ ook een probleem met memcpy(command,string.st.data,len); (dit is een bufferoverloop).

    Buffer-underflows en overstorten zijn niet het enige risico. Als je “t niet controleert de retourwaarde van malloc , en malloc retourneert NULL en vervolgens kan een null-pointer-dereferentie worden gebruikt om het uitvoeren van willekeurige code te veroorzaken. Dit houdt in dat u een methode moet hebben om malloc mislukt terug naar de beller. Het impliceert ook dat u dezelfde methode zou kunnen gebruiken om dat wrapping-probleem te controleren en terug te communiceren naar de beller.

    Geef een reactie

    Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *