Jeg har følgende opsummerede pseudokode i C89 fra et ssh-serverbibliotek, der kun giver adgang til ting som git-shell (/bin/bash
erstattes med det program, der skal køres, så det er ikke muligt at gøre noget andet) :
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; }
Her er hvordan kommandoen udføres senere (strengen command
er uændret efter 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.
Jeg kan ikke gøre memcpy(command,string.st.data,0)
da det tredje medlem af memcpy
har minimumsstørrelse på 1, og i min sammenhæng bruger size_t
et 64-bit heltal, jeg kan ikke udføre et bufferoverløb, da der er len
.
Alt hvad jeg kan gøre er at indstille len
til en værdi, der er større end den, der er allokeret til string.st.data
. Dette er en bufferunderstrømning, der giver mig mulighed for at læse ikke-allokeret hukommelse.
Jeg kan læse serverhukommelsen, men jeg kan ikke se, hvilke følsomme data en offentlig ssh-server kan indeholde (i mit tilfælde listen over brugere, der kan udføre ssh er offentlig) .
Så tillader en bufferunderstrømning på memcpy
fjernbetjening kodeudførelse?
Kommentarer
Svar
Generelt nej, en bufferunderstrømning kan ikke bruges til fjernkørsel af kode. Da angriberstyrede data aldrig forlader den plads, der er afsat til det, har de aldrig evnen til at overtage programmets eksekveringsstrøm.
En bufferunderstrømning har potentialet for andre typer angreb, f.eks. videregivelse af oplysninger (hvis programmet regner med, at bufferens originale indhold udslettes af de nye data).
Kommentarer
- Eller som jeg så for nylig i git, blive senere til et bufferoverløb.
Svar
Da jeg oprindeligt skrev dette svar , det ser ud til, at jeg glødede over nogle misforståelser, som du holder. Disse misforståelser vil sandsynligvis forhindre dig i at forstå mit svar. For at være klar vil jeg gøre denne tekst stor for at lægge vægt, ikke for at være respektløs:
Mange af de udtryk, du bruger, betyder ikke, hvad du synes at tro, de betyder.
For eksempel:
- “det tredje medlem af
memcpy
” findes ikke, fordimemcpy
er en funktion, ikke enstruct
ellerunion
. - “Jeg kan ikke udføre et bufferoverløb, da der er
len
” og jeg ikke kan løbe tør for benzin, da der er benzin. Tigger jeg spørgsmål? Det virker som en passende analogi for mig, især da din kode teoretisk set kunne påberåbe et bufferoverløb. - “Alt hvad jeg kan gøre er at indstille
len
til en værdi, der er større end den, der er tildeltstring.st.data
. Dette er en bufferunderstrømning … “ Nej, det er ikke definitionen på en bufferunderstrømning. En bufferunderstrømning opstår, når du får adgang til en matrix uden for grænserne ved hjælp af et negativt indeks eller et indeks, der kan forårsage pointer aritmetisk indpakning skal forekomme. Sidstnævnte kan være muligt for nogle konfigurationer (selv på “64-bit systemer”, uanset hvad det betyder), men jeg tvivler på, at det var det, du mente, da du skrev disse ord, fordi du derefter fulgte dem op med: - “… gør det muligt for mig at læse ikke-allokeret hukommelse.” Jeg tror måske du mente “uinitialiseret hukommelse”. For at være klar tror jeg du mente at sige at du “har tildelt et overskud og efterladt det overskydende uinitialiseret , og måske vil du gøre noget ved det (se
calloc
ellermemset
).
Lad os overveje char *fubar = NULL;
et øjeblik … denne type markør har generelt et nul værdi.Dereferencering betragtes som en nul pointer-afvigelse , men hvis vi skriver noget som 0["hello"]
får vi det samme som "hello"[0]
(dvs. "h"
). Således kan en nullpegerdereference bruges i angreb, når angriberen styrer udtrykket mellem de firkantede seler (som de gør i din situation).
Kommer tilbage til fubar
ting; lad os sige, at vi memset(&fubar, UCHAR_MAX, sizeof fubar);
, nu er fubar
alle 1-bits, hvilket betyder, at det måske er en adresse det er den største adresse, som vores system (teoretisk set) kunne rumme. Hvad hvis vi får adgang til fubar[1]
? Hvordan kan du få adgang til elementet efter den største adresse? Gør adresse og derefter vikle tilbage? Teknisk set er alt dette udefineret adfærd, men hvis jeg skulle nævne det på fælles arkitektur, ville det være:
- Aritmetisk overløb af en markør, der fører til …
- Null markørafvigelse og / eller potentielt en bufferunderstrømning .
tillader en bufferunderstrømning på
memcpy
eksekvering af ekstern kode?
En buffer underflow kan muliggøre, at en hacker overskriver funktionspegere, der er placeret i hukommelsesregionerne før det pågældende array. Hvis disse funktionspegere er peget på shellcode, kan udføres, når de “påberåbes senere.
I dette noget uklare stykke kode ser det ud til, at der er en risiko for bufferunderstrømning, når int
har en bredere domæne end uint32_t
, da ntohl(string.st.len)+1
ville medføre, at uint32_t
-værdien konverteres til en int
type. Overvej for eksempel, hvis INT_MIN
er -4294967296
(hvilket er en mindre end 0 - UINT32_MAX
) og INT_MAX
er 4294967295
… dette ville i det væsentlige være en 33-bit int
med polstring til pad ud til bredden af en byte; ualmindeligt, men muligt. Under denne omstændighed er udtrykket ntohl(string.st.len)+1
ikke uint32_t
af typen; det “s int
af typen, og i stedet for at pakke tilbage til 0, når usigneret heltal overløb forekommer, brydes det sandsynligvis til -4294967296
når underskrevet heltalsoverløb opstår.
Hvis du “søger en garanti mod bufferunderstrømning , skal du bruge U
helt bogstaveligt suffiks (dvs. ntohl(string.st.len)+1U
). I den situation ender du med, at udtrykket enten er uint32_t
eller unsigned int
(afhængigt af hvilken type der har den største domæne).
Hvis du mener, at ntohl(string.st.len)
muligvis returnerer en mindre end den maksimale værdi for den usignerede type (uanset hvad den er), så len=ntohl(string.st.len)+1
vil resultere i den maksimale værdi, malloc(len+1)
forårsager usigneret indpakning så du ender med at påkalde malloc(0)
og derefter command[len]=0
skriver godt og sandt ud over slutningen af arrayet. Så selvfølgelig har du “ll også et problem med memcpy(command,string.st.data,len);
(dette er et bufferoverløb).
Bufferunderstrømning og overløb er ikke den eneste risiko. Hvis du ikke “t skal du kontrollere returværdien af malloc
og malloc
returnerer NULL
, så kan en nul pointerdifferens bruges til at forårsage vilkårlig kodeudførelse. Dette betyder, at du skal have en metode til at kommunikere = “e8e339f7c1”>
fejler tilbage til den, der ringer op. Det betyder også, at du kan bruge den samme metode til at kontrollere og kommunikere det indpakningsproblem tilbage til den, der ringer op.
command[len] = 0
så er det et bufferoverløb, da det maksimale indeks for en buffer med længdenlen
erlen-1
. Alternativt, hvis den faktiske kode gør enmalloc(len+1)
i stedet for enmalloc(len)
, kan du lave et massivt bufferoverløb ved at indstillelen
værdi til0xFFFFFFFF
.len+1
, så indstillingen til 0 skal være gyldig.string.st.len
til ‑1.