Lösung der Aufgabe mit pwnable.kr 07 - Eingabe. Pwntools verstehen

Bild

In diesem Artikel analysieren wir die Lösung einer mehrstufigen Aufgabe mithilfe der pwntools- Bibliothek.

Organisationsinformationen
Speziell für diejenigen, die etwas Neues lernen und sich in einem der Bereiche Informations- und Computersicherheit entwickeln möchten, werde ich über die folgenden Kategorien schreiben und sprechen:

  • PWN;
  • Kryptographie (Krypto);
  • Netzwerktechnologien (Netzwerk);
  • Reverse (Reverse Engineering);
  • Steganographie (Stegano);
  • Suche und Ausnutzung von WEB-Schwachstellen.

Darüber hinaus werde ich meine Erfahrungen in den Bereichen Computerforensik, Analyse von Malware und Firmware, Angriffe auf drahtlose Netzwerke und lokale Netzwerke, Durchführung von Pentests und Schreiben von Exploits teilen.

Damit Sie sich über neue Artikel, Software und andere Informationen informieren können, habe ich in Telegram einen Kanal und eine Gruppe eingerichtet, um alle Probleme im Bereich ICD zu diskutieren . Außerdem werde ich Ihre persönlichen Anfragen, Fragen, Vorschläge und Empfehlungen persönlich prüfen und alle beantworten .

Alle Informationen werden nur zu Bildungszwecken bereitgestellt. Der Autor dieses Dokuments übernimmt keine Verantwortung für Schäden, die jemandem durch die Verwendung von Kenntnissen und Methoden entstehen, die durch das Studium dieses Dokuments erworben wurden.

Joblösung eingeben


Wir klicken auf das Symbol mit der Signatureingabe und es wird uns mitgeteilt, dass wir uns über SSH mit dem Passwort Gast verbinden müssen.

Bild

Wenn verbunden, sehen wir das entsprechende Banner.

Bild

Lassen Sie uns herausfinden, welche Dateien sich auf dem Server befinden und welche Rechte wir haben.

ls -l 

Bild

Auf diese Weise können wir den Quellcode des Programms lesen, da für jeden ein Leserecht besteht, und das Eingabeprogramm mit den Rechten des Eigentümers ausführen (das Sticky-Bit ist gesetzt). Lassen Sie uns das Ergebnis des Codes sehen.

Quellcode input.c
 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> int main(int argc, char* argv[], char* envp[]){ printf("Welcome to pwnable.kr\n"); printf("Let's see if you know how to give input to program\n"); printf("Just give me correct inputs then you will get the flag :)\n"); // argv if(argc != 100) return 0; if(strcmp(argv['A'],"\x00")) return 0; if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0; printf("Stage 1 clear!\n"); // stdio char buf[4]; read(0, buf, 4); if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0; read(2, buf, 4); if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0; printf("Stage 2 clear!\n"); // env if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0; printf("Stage 3 clear!\n"); // file FILE* fp = fopen("\x0a", "r"); if(!fp) return 0; if( fread(buf, 4, 1, fp)!=1 ) return 0; if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0; fclose(fp); printf("Stage 4 clear!\n"); // network int sd, cd; struct sockaddr_in saddr, caddr; sd = socket(AF_INET, SOCK_STREAM, 0); if(sd == -1){ printf("socket error, tell admin\n"); return 0; } saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons( atoi(argv['C']) ); if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){ printf("bind error, use another port\n"); return 1; } listen(sd, 1); int c = sizeof(struct sockaddr_in); cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c); if(cd < 0){ printf("accept error, tell admin\n"); return 0; } if( recv(cd, buf, 4, 0) != 4 ) return 0; if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0; printf("Stage 5 clear!\n"); // here's your flag system("/bin/cat flag"); return 0; } 


Aus dem Code folgt, dass wir fünf Ebenen durchlaufen müssen. Wir werden sie der Reihe nach nehmen.

Bild

Auf der ersten Ebene wird überprüft, ob die Anzahl der Programmargumente 100 beträgt. In diesem Fall sollte das 65. Element die Zeichenfolge "\ x00" und das 66. Element "\ x20 \ x0a \ x0d" sein. Erstellen Sie Ihr eigenes Verzeichnis im Verzeichnis / tmp / und erstellen Sie dort ein Python-Skript.

Bild

Um die erste Ebene zu lösen, erstellen wir ein Array mit hundert Zeilen 'A'. Und weisen Sie den gewünschten Werten die erforderlichen Argumente zu. Wir können den Prozess mit diesen Argumenten wie folgt starten:

 from pwn import * a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d' ex = process(argv=a) ex.interactive() 

Bild

Wir haben das erste Level bestanden. Schauen Sie sich die zweite an.

Bild

Auf dieser Ebene werden zwei Zeilen gelesen, eine der Standardeingaben stdin und die andere von stderr. Wir müssen zwei Dateien erstellen, die diese Zeilen enthalten.

Bild

Öffnen Sie Streams für diese Dateien und geben Sie den Deskriptor für den Stream einer Datei als Deskriptor stdin und eine andere Datei als Deskriptor stderr an.

 from pwn import * a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d' fin = open('/tmp/ex/in.txt', 'r') ferr = open('/tmp/ex/err.txt', 'r') ex = process(argv=a, stdin=fin, stderr=ferr) fin.close() ferr.close() ex.interactive() 

Bild

Wir fahren mit der Entscheidung der dritten Ebene fort.

Bild

Die Funktion getenv () gibt den Wert einer Umgebungsvariablen zurück, der dem Referenzwert entsprechen sollte. Daher müssen Sie eine Umgebungsvariable mit einem bestimmten Wert erstellen.

 from pwn import * a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d' fin = open('/tmp/ex/in.txt', 'r') ferr = open('/tmp/ex/err.txt', 'r') e={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'} ex = process(argv=a, stdin=fin, stderr=ferr, env=e) fin.close() ferr.close() ex.interactive() 

Bild

Wir haben die Umgebungsvariable verwaltet. Beginnen wir jetzt mit der vierten Ebene.

Bild

Entsprechend dem Code können wir sagen, dass das Programm eine Datei mit dem Namen "\ x0a" öffnet und 4 Zeichen daraus liest, wonach es sie mit den Bytes "\ x00" vergleicht. Da sowohl die Zeichen selbst als auch der Dateiname aus nicht druckbaren Zeichen bestehen, verwenden wir Python.

Bild

Bild

Das letzte Level bleibt. Fangen wir an.

Bild

Das Programm öffnet den Socket an dem im 66. Argument angegebenen Port. Dann empfängt es 4 Bytes über das Netzwerk und vergleicht es mit der Referenzzeichenfolge. Wir müssen dem Programm ein weiteres Argument hinzufügen - die Portnummer, eine Verbindung herstellen und die erforderlichen 4 Bytes senden.

 from pwn import * a = ['A']*100 a[0] = '/home/input2/input' a[ord('A')] = '\x00' a[ord('B')] = '\x20\x0a\x0d' a[ord('C')] = '1234' fin = open('/tmp/ex/in.txt', 'r') ferr = open('/tmp/ex/err.txt', 'r') e={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'} ex = process(argv=a, stdin=fin, stderr=ferr, env=e) fin.close() ferr.close() ex.interactive() 

Bild

Bild

Das ist alles, hol deine Punkte.

Bild

Wir sehen uns in den folgenden Artikeln!

Wir befinden uns in einem Telegrammkanal: einem Kanal im Telegramm .

Source: https://habr.com/ru/post/de460249/


All Articles