Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-43783 — CVE-2026-43783の概念実証および技術解説。DesktopServicesHelper XPCの任意chownを悪用してroot権限を取得するmacOSのローカル権限昇格脆弱性。 | Kitploit
ツール/GitHubGitHub/andrd3v/cve-2026-43783
特権昇格脆弱性分析エクスプロイトリバースエンジニアリングペネトレーションテストバイナリ解析論文と研究
GitHubandrd3v/cve-2026-43783

CVE-2026-43783

CVE-2026-43783の概念実証および技術解説。DesktopServicesHelper XPCの任意chownを悪用してroot権限を取得するmacOSのローカル権限昇格脆弱性。

リポジトリを見る
214261ヶ月前Kitploit レビュー済み

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2026-43783: Repair Permissions - Get Root: macOS 26.5 の DesktopServicesHelper を介した LPE

初出は PT SWARM または andrd3v.github.io

Finder が iCloud ファイルのパーミッションを「修復」するために使用するデーモンは、あらゆるもののパーミッションを修復する用意があることが判明しました。それにはシステムディレクトリも含まれます。この記事では、DesktopServicesHelper が内部でどのように動作するのか、そしてなぜ単一の XPC リクエストだけで root を取得できたのかを見ていきます。

DesktopServicesHelper の内部

私は com.apple.private.tcc.allow に値 kTCCServiceSystemPolicyAllFiles を持つターゲットを探していたところ、DesktopServicesHelper が目に留まりました。DesktopServicesHelper は /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/Resources/DesktopServicesHelper に存在し、Finder は特権昇格を必要とするファイル操作、すなわちファイルの移動、パーミッションの設定、ゴミ箱の操作にこれを使用しています。

Entitlements DesktopServicesHelper

バイナリを IDA に読み込み、start() に移動しました。いつものタイマーとキューのセットアップを過ぎたところで、XPC リスナーを作成しています。これがデーモンのエントリポイントです。リスナーは特定の名前で mach サービスを登録し、受信接続の待ち受けを開始します。この名前を知っているプロセスは誰でも接続してリクエストを送信できます。ここが重要な部分です:

// get the service name
v16 = (const char *)sub_1000709F4(a1: 0);

// register the listener
mach_service = xpc_connection_create_mach_service(name: v16, targetq: nullptr, flags: 1u);

// set the incoming event handler
xpc_connection_set_event_handler(connection: mach_service, handler: &stru_1000B4B48);
xpc_connection_resume(connection: mach_service);

// start the event loop
CFRunLoopRun();

関数 sub_1000709F4 は mach サービス名を返します。start() 内では引数 0 で呼び出されており、これはデーモンが com.apple.DesktopServicesHelper という名前で登録されることを意味します。

const char *__fastcall sub_1000709F4(int a1)
{
  if ( a1 != 0 )
    return "com.apple.DesktopServicesScriptingHelper";
  else
    return "com.apple.DesktopServicesHelper";
}

stru_1000B4B48 は、デーモンが受信イベントごとに呼び出すハンドラブロック(コールバック)です。XPC はメッセージベースです。クライアントはキーと値を持つ辞書を構築してデーモンに送信し、デーモンはその内容を解析して何を行うかを決定します。受信するすべてのイベントは最終的にこのハンドラに到達します。内部を見てみましょう。

IDA における stru_1000B4B48

このブロックの invoke 関数は sub_100032044 です。新しい接続が確立されると、デーモンはまずクライアントの euid と egid(実効ユーザー ID とグループ ID、つまり接続元プロセスが実行されているユーザーとグループの識別子)を取得します:

euid = xpc_connection_get_euid(connection: v8);
egid = xpc_connection_get_egid(connection: v8);
sub_100071268(a1: euid, a2: egid);

次に、接続上の受信メッセージに対するハンドラを割り当てます:

v15[2] = sub_10003AB2C;  // block's invoke function
xpc_connection_set_event_handler(connection: v14, handler: v15);
xpc_connection_resume(connection: v14);

sub_10003AB2C - ここがクライアントからの実際の XPC リクエストが到着する場所です。次に、デーモンがどのリクエストを実行し、どのリクエストを拒否するかをどのように決定するのかを理解する必要があります。

リクエストディスパッチャ

sub_10003AB2C はメインディスパッチャです。ここでデーモンはリクエストに対して何を行うかを決定します。この関数は大きいため、分解して見ていきましょう。

まず、メッセージから "request" 文字列を取得します。これはクライアントが実行したいコマンド名です:

reply = xpc_dictionary_create_reply(original: v12);
string = xpc_dictionary_get_string(xdict: v12, key: "request");
// ...
// logging: "Got Request '%{public}@'"

次に、デーモンは接続してきたプロセスの監査トークンを取得し、そのプロセスがApp Sandbox内で実行されているかどうかを確認します。App Sandboxは、ファイルシステム、ネットワーク、その他のリソースへのアクセスを制限するmacOSのアプリケーション分離機構です。ここで処理が分岐します:

xpc_dictionary_get_audit_token(a1: v12, a2: buf);
qmemcpy(__p, buf, sizeof(__p));
if ( (unsigned int)sandbox_check_by_audit_token(a1: __p, a2: 0, a3: 0) != 0 )
{
  // client is sandboxed - check against allowlist
  if ( (sub_10003B25C(a1: &v32, a2: "Handshake") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "exit") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "SetDefaultPermissionOnHomeSubdirectories") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "MoveAsideICloudToArchiveInHome") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "RepairPermissionsForCloudItems") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "CopyClientActive") & 1) == 0
    && (sub_10003B25C(a1: &v32, a2: "DeleteItem") & 1) == 0 )
  {
    sub_100034250(a1: v12, a2: &v32); // not in the list - kill the daemon
  }
}
else
{
  // client is not sandboxed - check entitlement
  *(_QWORD *)buf = sub_1000343C0(a1: v12, a2: CFSTR("com.apple.private.tcc.allow"));
  v19 = (const __CFArray *)sub_10003B1E0();
  v20 = v19;
  if ( v19 == nullptr
    || (v35.length = CFArrayGetCount(theArray: v19),
        v35.location = 0,
        CFArrayContainsValue(theArray: v20, range: v35, value: CFSTR("kTCCServiceSystemPolicyAllFiles")) == 0) )
  {
    sub_100034250(a1: v12, a2: &v32); // no entitlement - kill the daemon
  }
}

クライアントがサンドボックス化されている場合、リクエスト名は7つの文字列の許可リストと照合されます。一致すればチェックは通過し、リクエストは通されます。一致しなければ、sub_100034250 がデーモンを強制終了します。 クライアントがサンドボックス化されていない場合、デーモンは値 kTCCServiceSystemPolicyAllFiles を持つ com.apple.private.tcc.allow エンタイトルメントをチェックします。エンタイトルメントがない場合も同様に終了します。

チェックを通過した後、リクエストは2つのハンドラテーブルを順に通過します。まず sub_10002F7C8、次に sub_10002A194 です:

// first table (8 commands, two arguments)
v21 = sub_10002F7C8(a1: buf);
// ...
if ( v21 != 0 )
  v22(a1: v12, a2: v11); // found - invoke

// second table (21 commands, three arguments)
v27 = sub_10002A194(a1: buf);
// ...
if ( v27 != nullptr )
  v27(a1: v12, a2: reply, a3: v11); // found - invoke

次に、ハンドラテーブルを見てみましょう。最初のテーブル(sub_10002F7C8)は次のとおりです。

__int64 __fastcall sub_10002F7C8(__int64 a1)
{
  __int64 result; // x0
  void **v3; // x21
  void **v4; // x8
  _BYTE v5[32]; // [xsp+8h] [xbp-128h] BYREF
  __int64 v6; // [xsp+28h] [xbp-108h] BYREF
  __int64 v7; // [xsp+48h] [xbp-E8h] BYREF
  __int64 v8; // [xsp+68h] [xbp-C8h] BYREF
  __int64 v9; // [xsp+88h] [xbp-A8h] BYREF
  __int64 v10; // [xsp+A8h] [xbp-88h] BYREF
  __int64 v11; // [xsp+C8h] [xbp-68h] BYREF
  __int64 v12; // [xsp+E8h] [xbp-48h] BYREF
  __int64 v13; // [xsp+108h] [xbp-28h] BYREF

  if ( (atomic_load_explicit((atomic_uchar *volatile)&qword_1000BD8C8, memory_order_acquire) & 1) == 0
    && __cxa_guard_acquire(a1: &qword_1000BD8C8) != 0 )
  {
    sub_10002AA78(a1: (int)v5, __s: "OperationSizing");
    sub_10002AA78(a1: (int)&v6, __s: "SetChildPermissions");
    sub_10002AA78(a1: (int)&v7, __s: "RunTrashOperation");
    sub_10002AA78(a1: (int)&v8, __s: "ChildCreateLock");
    sub_10002AA78(a1: (int)&v9, __s: "RunSetRootMetadata");
    sub_10002AA78(a1: (int)&v10, __s: "RunCopyMoveOperation");
    sub_10002AA78(a1: (int)&v11, __s: "DeleteBackup");
    sub_10002AA78(a1: (int)&v12, __s: "DeleteItem");
    sub_10003BDDC(a1: &unk_1000BD8A0, a2: v5, a3: 8);
    v3 = (void **)&v13;
    do
    {
      v4 = v3;
      v3 -= 4;
      if ( *((char *)v4 - 9) < 0 )
        operator delete(__p: *v3);
    }
    while ( v3 != (void **)v5 );
    __cxa_guard_release(a1: &qword_1000BD8C8);
  }
  result = sub_10003BCF0(a1: &unk_1000BD8A0, a2: a1);
  if ( result != 0 )
    return *(_QWORD *)(result + 40);
  return result;
}

ここには特に興味深いものはありません。8つのハンドラ(OperationSizing、SetChildPermissions、...)はすべて、エンタイトルメントチェックの背後で制御されています。 しかし、2つ目のテーブルがあります - v27 = sub_10002A194(a1: buf):

ツールをダウンロード