Ho il seguente pseudo codice riassunto in C89 da una libreria del server ssh che fornisce solo accesso a cose come git ‑ shell (/bin/bash
viene sostituito con il programma da eseguire, quindi non è possibile fare qualcosaltro) :
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; }
Ecco come il comando viene eseguito in seguito (la stringa command
rimane invariata dopo 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.
Non posso eseguire memcpy(command,string.st.data,0)
poiché il terzo membro di memcpy
ha un dimensione minima di 1 e, nel mio contesto, size_t
utilizza un numero intero a 64 bit, non posso eseguire un overflow del buffer, poiché è presente len
.
Tutto quello che posso fare è impostare len
su un valore maggiore di quello assegnato a string.st.data
. Si tratta di un underflow del buffer che mi consente di leggere la memoria non allocata.
Posso leggere la memoria del server, tuttavia, non riesco a vedere quali dati sensibili può contenere un server ssh pubblico (nel mio caso lelenco degli utenti che può eseguire ssh è pubblico) .
Così come un buffer underflow su memcpy
consenti remoto esecuzione del codice?
Commenti
Risposta
In generale, no, non è possibile utilizzare un underflow del buffer per lesecuzione di codice remoto. Poiché i dati controllati da un utente malintenzionato non lasciano mai lo spazio assegnato, non hanno mai la capacità di assumere il controllo del flusso di esecuzione del programma.
Un underflow del buffer ha il potenziale per altri tipi di attacchi, come divulgazione di informazioni (se il programma conta sul contenuto originale del buffer cancellato dai nuovi dati).
Commenti
- O come io visto di recente in git, essere trasformato in un secondo momento in un buffer overflow.
Risposta
Quando originariamente ho scritto questa risposta , sembra che abbia sorvolato su alcuni malintesi che hai. Queste incomprensioni probabilmente ti impedirebbero di comprendere la mia risposta. Per essere chiari, renderò questo testo grande per enfatizzare, non per mancare di rispetto:
Molti dei termini che usi non significano quello che sembri pensare che significhino.
Ad esempio:
- “il terzo membro di
memcpy
” è inesistente perchémemcpy
è una funzione, non unstruct
ounion
. - “Non posso eseguire un buffer overflow, poiché cè
len
” e non posso “rimanere senza benzina, perché cè benzina. Sto supplicando Mi sembra unanalogia appropriata, soprattutto perché in teoria, il tuo codice potrebbe invocare un buffer overflow. - “Tutto quello che posso fare è impostare
len
a un valore maggiore di quello allocato astring.st.data
. Questo è un underflow del buffer … “ No, questa non è la definizione di un underflow del buffer. Si verifica un underflow del buffer quando accedi a un array fuori dai limiti utilizzando un indice negativo o un indice che causerebbe che si verifichi il wrapping aritmetico del puntatore. Questultimo può essere possibile per alcune configurazioni (anche su “sistemi a 64 bit”, qualunque cosa significhi), ma dubito che questo sia ciò che intendevi quando stavi scrivendo queste parole, perché poi le hai seguite con: - “… permettendomi di leggere la memoria non allocata.” Penso che forse intendevi “memoria non inizializzata”. Per essere chiari, penso che volessi affermare che tu “ho assegnato un eccesso e lasciato leccesso non inizializzato e forse vuoi fare qualcosa al riguardo (vedi
calloc
omemset
).
Consideriamo char *fubar = NULL;
per un momento … questo tipo di puntatore generalmente ha uno zero valore.Dereferenziare è considerato un dereferenziazione del puntatore nullo , ma se scriviamo qualcosa come 0["hello"]
otteniamo lo stesso di "hello"[0]
(ovvero "h"
). Pertanto, una dereferenziazione del puntatore nullo può essere utilizzata negli attacchi quando lattaccante controlla lespressione tra le parentesi quadre (come fanno nella tua situazione).
Tornando al fubar
cosa; diciamo che memset(&fubar, UCHAR_MAX, sizeof fubar);
, ora fubar
è tutto a 1 bit, il che significa che potrebbe essere un indirizzo questo è lindirizzo più grande che il nostro sistema potrebbe (in teoria) ospitare. E se accediamo a fubar[1]
? Come puoi accedere allelemento dopo lindirizzo più grande? indirizzo e poi tornare indietro? Tecnicamente, tutto questo è un comportamento indefinito, ma se dovessi nominare quello su unarchitettura comune, sarebbe:
- Overflow aritmetico di un puntatore, che conduce a …
- Dereferenziazione del puntatore nullo e / o potenzialmente un buffer underflow .
un buffer underflow su
memcpy
consente lesecuzione di codice in modalità remota?
Un buffer underflow può consentire a un utente malintenzionato di sovrascrivere i puntatori a funzione situati nelle regioni di memoria prima dellarray in questione. Se questi puntatori a funzione sono fatti per puntare allo shellcode, quello shellcode può essere eseguiti quando vengono “nuovamente richiamati in seguito.
In questo pezzo di codice un po oscuro, sembra che ci sia” un rischio di underflow del buffer quando int
ha una più ampia dominio di uint32_t
, poiché ntohl(string.st.len)+1
provocherebbe la conversione del valore uint32_t
in un int
tipo. Considera, ad esempio, se INT_MIN
è -4294967296
(che è uno in meno di 0 - UINT32_MAX
) e INT_MAX
è 4294967295
… questo sarebbe essenzialmente un int
a 33 bit con riempimento per riempire fino alla larghezza di un byte; raro, ma possibile. In questa circostanza, lespressione ntohl(string.st.len)+1
non è uint32_t
nel tipo; è di tipo “s int
e invece di tornare a 0 quando si verifica un overflow di numeri interi senza segno , probabilmente va a capo a -4294967296
quando si verifica un overflow dellintero con segno .
Se “stai cercando una garanzia contro il buffer underflow , utilizza U
suffisso letterale intero (ad esempio ntohl(string.st.len)+1U
). Quindi, in quella situazione, ti ritroverai con lespressione uint32_t
o unsigned int
(a seconda del tipo con il più grande dominio).
Se consideri che ntohl(string.st.len)
può restituire uno in meno del valore massimo per quel tipo senza segno (qualunque esso sia), allora len=ntohl(string.st.len)+1
restituirà il valore massimo, malloc(len+1)
causerà wrapping non firmato quindi” finirai per invocare malloc(0)
e quindi command[len]=0
scriverà bene e veramente oltre la fine dellarray. Quindi, naturalmente, “avrai anche un problema con memcpy(command,string.st.data,len);
(questo è un buffer overflow).
Buffer underflow e gli straripamenti non sono lunico rischio. Se non “t controlla il valore restituito di malloc
e malloc
restituisce NULL
quindi una dereferenziazione del puntatore nullo può essere utilizzata per provocare lesecuzione di codice arbitrario. Ciò implica che dovresti avere un metodo per comunicare malloc
errori di ritorno al chiamante. Implica anche che potresti utilizzare lo stesso metodo per verificare e comunicare il problema di wrapping al chiamante.
command[len] = 0
si tratta di un buffer overflow, poiché lindice massimo per un buffer di lunghezzalen
èlen-1
. In alternativa, se il codice effettivo esegue unmalloc(len+1)
invece di unmalloc(len)
, puoi creare un enorme overflow del buffer impostandolen
valore a0xFFFFFFFF
.len+1
quindi limpostazione a 0 dovrebbe essere valida.string.st.len
su -1.