
Paracosme ist ein Zero-Click-Exploit zur Remote-Speicherkorruption, der ICONICS Genesis64 kompromittiert und erfolgreich auf der Bühne während des Pwn2Own Miami 2022-Wettbewerbs demonstriert wurde.
Paracosme ist ein von mir geschriebener Memory-Corruption-Exploit, der gegen die von ICONICS entwickelte Suite Genesis64 v10.97.1 gerichtet ist, um Remote Code Execution zu erreichen.
Der Exploit wurde während des Pwn2Own 2022 Miami-Wettbewerbs vorgeführt, der auf der S4x22 Conference stattfand. Du kannst darüber in Teilnahme am Pwn2Own ICS 2022 Miami: Ausnutzen einer Zero-Click-Remote-Memory-Corruption in ICONICS Genesis64 lesen.
Die Schwachstelle erreichte einen CVSS-Score von 9,8 und erhielt die Kennungen CVE-2022-33318 / ZDI-22-1041. Sie wurde in Genesis64 10.97.2 behoben. Du kannst außerdem die Advisory ICSA-22-202-04 sowie das Whitepaper zu Sicherheitslücken der ICONICS Suite von ICONICS lesen.
Du findest den Exploit-Code in src/paracosme.py, einen PoC zum Auslösen des Absturzes bzw. zur Überprüfung, ob du betroffen bist, in src/paracosme-poc.py, und das auf dem Zielsystem ausgeführte Payload in src/payload.
Der beste Weg, um herauszufinden, ob du betroffen bist, ist, Page Heap für GenBroker64.exe zu aktivieren, den Dienst neu zu starten, einen Debugger an GenBroker64.exe anzuhängen, paracosme-poc.py gegen deinen Server auszuführen – dann solltest du Abstürze wie im Folgenden sehen:
Du musst einen Debugger an den Zielprozess anhängen, um den Absturz zu sehen; andernfalls ignoriert die Anwendung ihn.
Der Exploit wurde nur unter Windows getestet, sollte aber auch auf Linux-Plattformen funktionieren:
impacket mit: pip3 install impacketsc config lanmanserver start=disabled und starte neusmbserver.py (Teil der impacket-Beispiele) mit: python src\smbserver.py -smb2support x binpython src\paracosme.py --target <ip>
Weitere Details findest du in Teilnahme am Pwn2Own ICS 2022 Miami: Ausnutzen einer Zero-Click-Remote-Memory-Corruption in ICONICS Genesis64.
Paracosme nutzt eine Use-after-Free-Schwachstelle im Prozess GenBroker64 aus, um auf einem Windows-21H2-x64-System Remote Code Execution zu erreichen.
Im Wesentlichen lauscht der GenBroker64-Prozess auf dem TCP-Port 38080 und kann nach einem Handshake mit einem Client verschiedene Pakete deserialisieren. Der Fehler, den ich gefunden habe, befindet sich im Code, der das Lesen einer VARIANT vom Netzwerk-Socket behandelt. Im Grunde besteht ein VARIANT aus einem Typ und einem Wert. Die Funktion wirkt auf den ersten Blick gut geschrieben und bemüht sich, nur bestimmte Typen zu entpacken. So sieht sie aus:
bool CheckVariantType(VARTYPE VarType) {
if((VarType & 0x2FFF) != VarType) {
return false;
}
switch(VarType & 0xFFF) {
case VT_EMPTY:
case VT_NULL:
case VT_I2:
case VT_I4:
case VT_R4:
case VT_R8:
case VT_CY:
case VT_DATE:
case VT_BSTR:
case VT_ERROR:
case VT_BOOL:
case VT_VARIANT:
case VT_I1:
case VT_UI1:
case VT_UI2:
case VT_UI4:
case VT_I8:
case VT_UI8:
case VT_INT:
case VT_UINT:
case VT_HRESULT:
case VT_FILETIME:
return true;
break;
default:
return false;
}
}
size_t VariantTypeToSize(VARTYPE VarType) {
switch(VarType) {
case VT_I1: return 1;
case VT_UI2: return 2;
case VT_UI4:
case VT_INT:
case VT_UINT:
case VT_HRESULT:
return 4;
case VT_I8:
case VT_UI8:
case VT_FILETIME:
return 8;
default:
return 0;
}
}
void Utils::ReadVariant(tagVARIANT *Variant, Archive_t *Archive, int Level) {
TRY {
return ReadVariant_((CArchive *)Archive, (COleVariant *)Variant);
} CATCH_ALL(e) {
VariantClear(Variant);
}
}
HRESULT Utils::ReadVariant_(tagVARIANT *Variant, Archive_t *Archive, int Level) {
VARTYPE VarType = Archive.ReadUint16();
if((VarType & VT_ARRAY) != 0) {
// Special logic to unpack arrays..
return ..;
}
Size = VariantTypeToSize(VarType);
if (Size) {
Variant->vt = VarType;
return Archive.ReadInto(&Variant->decVal.8, Size);
}
if(!CheckVariantType(VarType)) {
// ...
throw Something();
}
return Archive >> Variant;
}
Die Funktion implementiert selbst das Entpacken von Arrays sowie das Lesen einfacher Variant-Typen. Wenn sie jedoch etwas empfängt, das keiner dieser beiden Kategorien entspricht, fällt sie auf den operator>> der Archiv-Instanz zurück. Bei dieser Archiv-Instanz handelt es sich um ein Objekt, das vom Microsoft Foundation Class-Framework bereitgestellt wird und die Serialisierung & Deserialisierung verschiedener Objekte übernimmt. Dieser Code ist eigentlich Open Source und du findest ihn in C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\atlmfc\src\mfc\olevar.cpp, aber hier ist er: