
CVE-2020-15368, alias „Wie man einen verwundbaren Treiber ausnutzt“
Exploit und Proof of Concept (PoC) für CVE-2020-15368. Asrock hat den rweverything-Treiber für ihr RGB-Controller-Konfigurationstool neu verpackt und signiert. Sie „schützen“ ihn, indem sie ihre IOCTLs verschlüsseln ... lol. Wir haben diese CVE letzten Sommer zufällig gefunden, und soweit ich weiß, ist der Treiber immer noch nicht gepatcht. Der Impact ist natürlich beliebige Codeausführung im Kernel usw. Also genießt diesen „0day“ lol.
Wenn du mit mir darüber diskutieren willst, ob das eine ECHTE BONA FIDE CVE ist, kannst du mich gerne auf Twitter kontaktieren – wir können einen großen Streit in den sozialen Medien austragen, und es wird für alle Beteiligten wirklich aufregend sein! Ich kaufe sogar eine Domain für diesen Bug, wenn du dazu geneigt bist. Es geht doch nur um Marketing!!!!
Wie auch immer, dieser Bug ist ziemlich beschissen, also werde ich ihn als Tutorial dafür nutzen, wie man einen typischen verwundbaren Treiber pwned. Dieser Beitrag richtet sich also an Anfänger. Du lernst, wie man einen verwundbaren Treiber ausnutzt. Es gibt tonnenweise andere beschissene Treiber da draußen wie diesen. Die Welt liegt dir zu Füßen. Viel Spaß
HAFTUNGSAUSSCHLUSS: Diese Veröffentlichung dient nur zu Bildungszwecken. Es liegt in der Verantwortung des Lesers, alle geltenden lokalen, landes- und bundesrechtlichen Gesetze zu befolgen. Der/die Autor(en) dieser Veröffentlichung übernehmen keine Haftung und sind nicht verantwortlich für jeglichen Missbrauch oder Schäden, die durch die in dieser Veröffentlichung enthaltene Software verursacht werden.
Im Quarantäne-Lockdown hingen meine Mitbewohner (Pear0, Codetector) und ich zusammen herum und spielten mit Pear0s neuem Asrock-Motherboard herum. Die hellen roten LEDs waren extrem nervig und es war nicht möglich, sie unter Linux zu konfigurieren. Unser Plan war es also, den Windows-Treiber zu reversen, der sie steuerte, und die I/O-Operationen unter Linux nachzubilden.
Kurz gesagt, es dauerte nicht lange, bis wir erkannten, dass der Treiber buchstäblich nur ein generischer Treiber ist, der beliebigen Lese-/Schreibzugriff auf alles gewährt. Das umfasst Kontrollregister wie CR3, CR4, physischen Speicher usw. Treiber wie dieser sind für die Verwendung als Debugging-Werkzeug gedacht und die Website des Anbieters sagt das deutlich.

Wir fanden das äußerst witzig. Es ist ziemlich aufregend, wenn man zum ersten Mal von User Mode aus einen Triple Fault auslöst und den Computer hart neu starten lässt. (Beim zwanzigsten Mal vielleicht weniger aufregend.) Wie auch immer, wir meldeten den Bug und vergaßen ihn dann ein Jahr lang.
Als Kernel-Noob fragte ich mich, wie ich den Treiber tatsächlich laden und mit ihm interagieren kann. Wie sich herausstellt, ist das extrem einfach.
Du kannst einfach einen Dienst für den Treiber in Process Hacker erstellen (offensichtlich sind Administratorrechte erforderlich, um Treiber zu laden). Dann kannst du einfach mit der rechten Maustaste klicken und ihn starten. Ja, es ist wirklich so einfach.

Wir können unser Device-Objekt in WinObjEx64 anzeigen.

Wir können sogar in FileTest mit dem Gerät spielen.


Alle diese 3 Werkzeuge sind erstaunlich, besonders PH und FileTest. Sie sind wie ein Schweizer Taschenmesser und sollten in der Toolbox jedes Windows-Reversers stecken. Zum Beispiel hat Jonas L meines Wissens nach unzählige Windows-LPE-Schwachstellen gefunden, nur indem er in FileTest herumgespielt hat. Windows hat also wirklich einige großartige Werkzeuge zum Herumspielen. Ich wünschte, es gäbe diesen Scheiß unter Linux.
Rweverything hat einen IOCTL, der einen IOCTL als Parameter entgegennimmt, der steuert, welche Operation ausgeführt werden soll (Speicher lesen, Speicher schreiben, MSR lesen usw.), sowie eine Union aus operationsspezifischen Parametern wie Quelladresse, Zieladresse usw. Wenn wir den Code der beiden Treiber vergleichen:

🤔🤔🤔🤔🤔🤔🤔🤔🤔
Nichtsdestotrotz unternimmt der Treiber einen stümperhaften Versuch von Sicherheit durch Obskurität, indem er verlangt, dass alle IOCTL-Aufrufe mit einem hartcodierten AES-Schlüssel entsprechend verschlüsselt werden. Der Code (nach einiger Bereinigung) sieht so aus:
if ( IoControlCode == 0x22EC00 )
{
char enc_key[32];
memset(enc_key, 0, sizeof(enc_key));
memmove(enc_key, "C110DD4FE9434147B92A5A1E3FDBF29A", 32ui64);
memcpy(enc_key + 13, ioctl_args->key, 16);
size_t cb_decrypted = 0;
my_decrypted_cmd* decryptedCmd = NULL;
DWORD iv_size = ioctl_args->iv_size;
DWORD input_size = *(DWORD*)(ioctl_args + bufferLen - 6);
// really just calls BCrypt API to get an AES implementation
if ( (unsigned int)decrypt_ioctl_params(enc_key, 32, ioctl_args->iv, iv_size, ioctl_args + bufferLen - input_size - 6, input_size, &decryptedCmd, &cb_decrypted) )
{
// Decryption failed
if ( decryptedCmd )
ExFreePoolWithTag(decryptedCmd, 0);
irp->IoStatus.Status = 0xC000000D;
goto Fail_Out;
}
IoControlCode = decryptedCmd->opcode;
Rweverything_Args = &decryptedCmd->args;
}
else // not 0x22EC00
{
if ( IoControlCode != 0x22E858 &&
IoControlCode != 0x22E860 &&
IoControlCode != 0x22E800 &&
IoControlCode != 0x22E804 )// whitelisted control codes
IoControlCode = 0; // block everything else
}
Der Treiber erlaubt einige langweilige Operationen, die etwas PMIO machen, aber alle „lustigen“ Steuercodes sind hinter dieser Entschlüsselungsroutine versteckt. Trotz dieser expliziten Allow-Liste enthält er immer noch die gesamte gefährliche Rweverything-Funktionalität. Statt diese gefährlichen Funktionen zu verstecken, hätten sie wahrscheinlich einfach ganz entfernt werden sollen.
Interessanterweise erlaubt der Treiber dem Benutzer auch, einen Teil des Schlüssels anzugeben (???), aus welchem Grund auch immer – ich habe keine Ahnung. Der Code ist einfach sehr schlecht geschrieben.
Wie auch immer, es ist relativ einfach, den Client-Code zu schreiben, um diese seltsame verschlüsselte API zu nutzen und ihr beliebige IOCTL-Aufrufe zu übergeben, die wir wollen. Ich werde dich nicht mit den Details langweilen.
Wir öffnen ein Handle auf den Treiber und verwenden DeviceIoControl, um den IOCTL aufzurufen – ganz Standard.
HANDLE hDevice = CreateFileA("\\\\.\\GlobalRoot\\Device\\AsrDrv104", GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// ... set up the encrypted ioctl data
BOOL result = DeviceIoControl(hDevice, 0x22EC00, ioctl_data, in_buf_size, out_buf, sizeof(out_buf), &bytes_returned, NULL);