0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Polymarket UMA CTF Adapter exploiter のフォレンジック分析 — 30秒drip + 14アドレスpeel + 偽PỌL Unicode poisoning の3段セット

0
Last updated at Posted at 2026-05-24

TL;DR

  • 2026-05-22 に発生した Polymarket の internal operator wallet 秘密鍵漏洩事案 で、Exploiter 1 (0x8F98...) のオンチェーン挙動を Etherscan V2 API で手作業分析しました
  • 漏洩鍵 wallet 0x91430cad... を on-chain で 「Polymarket UMA CTF Adapter (0x65070be9...) を 4 年間 195 回呼んでいた operator EOA」 として裏取り済みです
  • 被害規模は実質 $1M+ (Bubblemaps の公表値 $600K は USDC.e 部分だけと推定。POL native を加えると倍以上)
  • なぜ 30 秒間隔で 5,000 単位なのか」の答えは on-chain で確定: Polymarket treasury (0xf61e39c7...) が operator wallet を 5,000 USDC.e × 30 秒間隔で自動 top up する cadence にロックされていたため。Attacker は top up が来るたびに即座に転送する bot を走らせていました
  • その後 14 アドレスへ structured peel → 確認された 偽 PỌL (Unicode lookalike) を使った address poisoning という多段構え
  • ChainAnalyzer に 3 つの新規 detector (DRIP_EXFILTRATION / UNICODE_LOOKALIKE_TOKEN / STRUCTURED_PEEL_FANOUT) を実装する根拠が揃いました
  • 元 disclosure は @zachxbt、可視化は @bubblemaps、Polymarket 側公式コメントは @ShantikiranC 氏が起点となっています

用語集 (初出語の解説)

本記事に出てくる専門用語を最初にまとめておきます。

Polymarket / オラクル系

  • Polymarket: Polygon 上で動く予測市場 (prediction market) プラットフォーム。「○○は△△日までに起こる?」のような yes/no 市場を立てて、ユーザーが USDC.e でベットできます
  • UMA: Universal Market Access の略。オラクル (= ブロックチェーン外の事実をオンチェーンに持ち込むプロトコル) の一種で、予測市場の最終結果判定などに使われます
  • CTF: Conditional Token Framework の略。Gnosis 開発のフレームワークで、yes/no トークンを生成・管理する標準。Polymarket の市場参加トークンは CTF ベースです
  • UMA CTF Adapter: UMA Oracle と CTF を繋ぐコントラクト。「UMA が判定した結果を CTF トークンの payout に変換する」アダプタ層 = Polymarket の決済の心臓部
  • Operator wallet (運用 wallet): 上記 Adapter contract に対して市場初期化や決済処理を行うための専用 EOA。Polymarket のような大規模 dApp では、こういう運用 wallet を複数持ち、各 wallet には少額しか残高を持たせない (= "working capital" レベル) ことが OPSEC のベストプラクティスとされます
  • Treasury (トレジャリー): 運用 wallet に資金を補充する大本の wallet。本事案では 0xf61e39c7... が該当

Ethereum / 技術系

  • EOA: Externally Owned Account の略 = 個人秘密鍵で管理される普通のウォレット (vs. contract account)。コードを持たず、eth_getCode の返り値が "0x" になります
  • Method selector (4-byte selector): Solidity 関数呼び出しは TX の input 先頭 4 バイトに function signature を keccak256 hash したものを使います。例: transfer(address,uint256)0xa9059cbb
  • chainid 統合 API: Etherscan V2 で導入された設計で、chainid=1 (Ethereum), chainid=137 (Polygon) 等のパラメータを付けるだけで、同一エンドポイント / 同一 API key で複数 EVM chain を扱えます

フォレンジック / AML 系

  • Peel chain (ピールチェーン): 元は Bitcoin のコインミキシングで使われる用語で、「大きな UTXO を小さな単位に少しずつ剥がしていく」追跡攪乱パターン
  • Structured peel: 上の進化形で、大きな額を 複数の wallet に均等分配 することで「ブラックリストに 1 つ入っても残りが動かせる」状態を作る
  • Drip exfiltration: 一定間隔・一定金額で連続的に資金を抜き取るパターン。本事案の特徴
  • Structuring (ストラクチャリング / smurfing): AML 用語で、「検知の閾値を意図的に下回る金額に分割して送金する」洗浄テクニック
  • Address poisoning: 「本物の送金先アドレスと先頭・末尾だけ似た偽アドレス」を生成し、被害者の TX 履歴に dust transfer として混入させて、コピペミスで本物だと思って送金させる詐欺パターン
  • Unicode lookalike (Unicode confusable): 見た目がほぼ同じだが Unicode コードポイントが違う文字を使った詐称。例: POL vs PỌL ( = U+1ECC)
  • KYT: Know Your Transaction の略。AML/コンプライアンス文脈で、リアルタイムの TX レベル監視を指します
  • SAR: Suspicious Activity Report の略。金融機関が当局へ提出する不審取引報告書

DeFi / MEV 系

  • AMM: Automated Market Maker。Uniswap などの DEX の根幹となる、流動性プールを使った自動価格決定の仕組み
  • Slippage: AMM で大きな swap を一発で行うと、プールバランスが歪んで想定より悪いレートで約定する現象
  • MEV: Maximum Extractable Value (旧 Miner Extractable Value)。ブロック内の TX 順序を操作することで得られる利益。今回は「同じ脆弱性 (鍵) を発見した第三者の bot が同じ TX を front-run する」リスク文脈で言及

ChainAnalyzer 内部

  • W9 / W10 / W11 / W12 ...: ChainAnalyzer の Wallet 系 detector の ID。W = Wallet, E = EVM-specific, C = Common 等のカテゴリ prefix
  • Follow Mode: scan した wallet の counterparty を自動的に展開していく機能 (Neo4j BFS ベース、Business/Enterprise tier)
  • Bridge Flow Trace: マルチホップ・クロスチェーンのフォレンジック追跡機能。前作の Qiita 記事を参照(https://qiita.com/Rascal/items/94aa710a9c3bcda77804)

!重要なインサイト (これだけ読めば OK)

Insight 1: これは "contract exploit" ではなく "key compromise" (on-chain でも裏取り済み)

Bubblemaps が当初「Polymarket契約が悪用された」と発信しましたが、その後 community note と Polymarket 関係者の公式投稿で 「内部運用ウォレットの秘密鍵漏洩であり、コントラクトやコアインフラには影響していない」 と訂正されました。

本記事ではこの説明を on-chain データだけで以下のように裏取りしました:

チェック項目 観測値 解釈
漏洩鍵 wallet 0x91430cad... の type EOA (eth_getCode = "0x") 攻撃された contract そのものではない
同 wallet の活動開始 2022-02-11 4 年以上の運用歴 = 使い捨て exploit wallet ではない
同 wallet の最頻 OUT 先 0x65070be9... (= Polygonscan verified label UmaCtfAdapter) を 195 回 call まさに Polymarket UMA CTF Adapter の operator として動いていた
最頻 method selector 0x185d1646 = initialize(bytes,address,uint256,uint256,uint256) UMA CTF Adapter の新規 market 初期化 — 典型的な Polymarket operator 業務

→ つまり 「Polymarket の UMA CTF Adapter を 4 年間運用してきた operator wallet の鍵が漏洩し、攻撃者がその鍵で wallet 残高を抜いた」 という Shantikiran 氏の公式コメントは、on-chain でも完全に整合します。

技術的に重要な含意: 攻撃者は contract の脆弱性を突いたわけではなく、legitimate な秘密鍵を入手して legitimate な transfer 関数を呼んでいるだけ です。

Insight 2: 真の被害規模は公表値より大きい

token 攻撃者からの outflow 合計 USD 換算
POL native 約 1.2M POL (14 アドレスに分散) ≈ $440K (POL ≈ $0.37 想定)
USDC.e 約 588K USDC.e (7 アドレスに分散) ≈ $588K
合計 ≈ $1M+

Bubblemaps の公表値 $600K は USDC.e 部分の小計だった可能性が高いです。

Insight 3: 偽 PỌL (Unicode lookalike) で address poisoning が仕掛けられている

これが今回の 最大の発見 です。攻撃者は peel out 時に、Unicode の look-alike 文字を使った偽 POL トークン "PỌL" (= U+1ECC、ベトナム語の "O with dot below") を混入させています:

偽 PỌL 受領先 本物 POL 受領先 (同 amount) プレフィックス一致
0x4b2037f7d50103f6d5e30ec563e895a36347edd2 (127,169 PỌL) 0x4b20080924b4b999676cf85748fe564a6137edd2 (127,169 POL) 0x4b20
0xe69606bdac852875e0ec3d359aedf563c4a1a7ea (29,992 PỌL) 0xe696cadce07b6be114d19b98c1e8d423c741a7ea (29,992 POL) 0xe696

これは 本物の peel destination とプレフィックスが同じ偽アドレスに、偽 PỌL トークンを送りつけて、後日の追跡分析者 / 受領者がコピー & ペーストで間違える ことを誘発する典型的な address poisoning の進化形です。正規 POL の送金 log と偽 PỌL の送金 log が並んで表示 されることで、token symbol を見ただけでは違いに気づきにくくなっています。

Insight 4: ChainAnalyzer の既存 detector では fire しない

📢 2026-05-25 アップデート: 下記で「専用ルールなし」と書いた W12 / W13 / W14 を実装してデプロイしました。詳細は 第 11 節 を参照。本記事公開時点の状態を残すため、以下の本文はそのままにしておきます。

現状 ChainAnalyzer の AML detector スイート (76+ ルール) は以下のように反応します:

  • W11 DUST_ATTACK_TESTING — 5,000 USDC × 多数回の inbound に対して部分 hit する可能性
  • E17 PHANTOM_EVENT_MIS_ATTRIBUTION — 偽 PỌL の transfer log が address poisoning の派生として警告される可能性
  • Counterparty table — 14 アドレスへの fanout は graph で一目瞭然
  • 30 秒間隔の drip exfiltration — 専用ルールなし
  • Unicode lookalike token — 専用ルールなし
  • STRUCTURED_PEEL_FANOUT (consolidation hub 検出) — 専用ルールなし

つまり、本事案は 「現状の自動検出では拾い切れないが、手作業 + Etherscan V2 API で再現できる」 という、新規 detector の design space を提示している格好の題材になっています (詳細は第 6 節)。

Insight 5: 攻撃者の OPSEC 戦略が見える

  • 検知逃れ: 5,000 単位は KYT alert の閾値を sub-threshold で素通り
  • MEV 回避: 大きい TX を出して mempool で同じ脆弱性を front-run されるリスクを排除
  • 時間稼ぎ: 一気に抜くと即時 detection で stop されるため、少しずつ動かす
  • 分析者の混乱誘発: 偽 PỌL token で address-level の見分けに混乱を仕込む
  • fanout で追跡複雑化: 14 アドレスに均等分配 → 1 つ blacklist 入れても残り 13 が動かせる

1. 事案概要

2026-05-22、X (旧 Twitter) で @bubblemaps が以下のアラートを発信しました:

🚨 Polymarket契約が悪用されました
攻撃者が30秒ごとに5,000 $POLを抜き取っています — 現在までに600kドルが盗まれています
Polymarketの活動を今はすべて一時停止してください

その後、community note と Polymarket 関係者の @ShantikiranC 氏による公式コメントで、以下のように 訂正 がなされました:

報酬の支払いに関連するセキュリティ報告を認識しています。ユーザー資金と市場決済は安全です。
調査結果から、内部運用に使用されたウォレットの秘密鍵が侵害された可能性が指摘されていますが、コントラクトやコアインフラには影響していません。
バックエンドサービスでキーのローテーションを行っています。

つまり実態は 「Polymarket UMA CTF Adapter contract の運用に使われていた内部ウォレットの秘密鍵が漏洩し、攻撃者がそれを使って report payout の権限を行使した」 という性質の事案でした。初期発見は @zachxbt 氏。

本記事では、Bubblemaps が公開した primary attacker address 0x8F98075db5d6C620e8D420A8c516E2F2059d9B91 (Bubblemaps ラベル: "Polymarket Adapter Exploiter 1") のオンチェーン挙動を、Etherscan V2 API で手作業再現し、新たに 偽 PỌL を用いた address poisoning の存在を確認した結果を報告します。


2. データ取得 — Etherscan V2 API で 1 コマンド

Polygon (chainid=137) を Etherscan V2 API で叩きます。

ポイントは、Etherscan V2 の chainid 統合 API によって、Polygon でも Ethereum mainnet でも 同じ API key で同じエンドポイントで取れる ことです。


3. 観測された drip パターン (30 秒間隔)

0x8F98 宛の inbound TX を時刻順に見ると、見事に 30 秒ごとに 5,000 単位 の流入が並びます。

POL native (2026-05-22 17:57 — 18:01 UTC)

timestamp 方向 金額 tx_hash (prefix)
17:57:40 IN 4,999.9940 POL 0x6f0f628e...
17:58:13 IN 4,999.9942 POL 0xe69043ec...
17:58:45 IN 4,999.9942 POL 0x6b52e0f0...
17:59:16 IN 4,999.9942 POL 0x75176e05...
17:59:49 IN 4,999.9940 POL 0xc6e2c326...
18:00:21 IN 4,999.1630 POL 0x9cb3111e...
18:00:54 IN 9,993.9960 POL 0x5562ea09... ← この回だけ 2 倍
18:01:26 IN 4,999.9942 POL 0x289d28dd...

median gap 32 秒、value 一定 (±0.02%) — drip パターンの教科書的シグネチャです。

USDC.e (2026-05-22 16:26 — 16:32 UTC、source = 0x91430cad...)

同じ source address 0x91430cad2d39... から 5,000 USDC.e ずつ、約 30 秒ごと:

16:26:57  IN  5,000.00 USDC.e
16:28:02  IN  5,000.00 USDC.e
16:28:34  IN  5,000.00 USDC.e
16:29:07  IN  5,000.00 USDC.e
16:29:38  IN  5,000.00 USDC.e
16:30:12  IN  10,000.00 USDC.e ← 2倍
16:30:45  IN  5,000.00 USDC.e
16:31:16  IN  5,000.00 USDC.e
16:31:50  IN  5,000.00 USDC.e
16:32:21  IN  5,000.00 USDC.e
16:32:54  OUT 150,000.50 USDC.e → 0xd3cfcd214eb5  ← consolidation 開始

USDC.e と POL の両方が同じ 30 秒間隔で並行して動いているのが特徴的です。


4. なぜ 30 秒 / 5,000 ごとなのか — on-chain で確定した最終解

これが本記事のもう一つの中心的な発見です。Source 0x91430cadinbound 履歴 を取得して比較すると、ぴったり整合する送金元が見つかります。

4-1. Treasury からの top-up cadence (= 確定要因)

0x91430cad への USDC.e の流入を時刻順に並べると:

2026-05-22  16:30:38  IN  5,000 USDC.e  from 0xf61e39c7eb1e... (treasury)
2026-05-22  16:31:08  IN  5,000 USDC.e  from 0xf61e39c7eb1e... (+30 秒)
2026-05-22  16:31:37  IN  5,000 USDC.e  from 0xf61e39c7eb1e... (+29 秒)
2026-05-22  16:32:07  IN  5,000 USDC.e  from 0xf61e39c7eb1e... (+30 秒)
2026-05-22  16:32:37  IN  5,000 USDC.e  from 0xf61e39c7eb1e... (+30 秒)

送金元 0xf61e39c7eb1e2ff5af3a24bca88d40fd11594805 が 30 秒間隔で 5,000 USDC.e ずつ自動 top up していました。これは Polymarket の treasury / settlement orchestrator に相当する wallet で、UMA CTF Adapter の operator wallet (= 漏洩した 0x91430cad) を継続的に補充するための automated funding mechanism と考えられます。

つまり攻撃者の drainage pattern と完全に一致するのは:

treasury → operator: 5,000 USDC.e @ 30 秒間隔   (legitimate top-up)
operator → attacker: 5,000 USDC.e @ 30 秒間隔   (attacker bot で race-out)

Attacker は contract 制約に縛られていたのではなく、自分が抜ける残高の上限 (= operator wallet の working capital level) に縛られていた わけです。Operator wallet は normally 5,000 USDC.e + 5,000 POL 程度の "working balance" しか保持しない設計で、攻撃者は top-up が着いた瞬間に attacker wallet に転送する bot を回しているだけ、というのが on-chain から見える挙動です。

4-2. これは Polymarket 側 OPSEC が「半分機能していた」ことを意味する

設計者観点で見ると、operator wallet を小額・連続 top-up にしておく のは AML / セキュリティ上のベストプラクティスです:

  • 1 回の鍵漏洩で 失われる即時残高は最小 (5,000 USDC.e + 5,000 POL 程度)
  • 異常な大量 outflow があれば treasury 側で即時検知可能 (理屈の上では)

ところが今回は:

  • 鍵漏洩そのものを Polymarket 側が即時検知できなかった
  • 攻撃者の bot が top-up を即座に消費する形で running drain
  • 検知されるまでの数時間で top-up cadence × 時間 = 累計 $1M+ に膨らんだ

逆に言えば、OPSEC 設計の "blast radius limit" が攻撃者の "drainage rate" にもなった という皮肉な結果です。

4-3. ボーナス: 副次的な OPSEC 上の利点 (推察)

設計上の制約が主因ですが、攻撃者にも以下の副次的なメリットがあった可能性があります (これは推察):

  • AML 検知系の閾値回避: 5,000 USDC.e (= $5,000) は KYT / Forta / OpenZeppelin Defender の 典型的 alert threshold ($10K-$50K) を sub-threshold で素通り
  • MEV / front-running 防御: mempool 上で 1 回ごとの TX 規模が小さく、同じ脆弱性 (鍵) を発見した第三者の bot に front-run されにくい
  • 時間 buffer: 一括で抜くより検知後の freeze までの time window を実質長く確保できる

5. 14 アドレスへの structured peel

0x8F98 の outflow を unique recipient 別に集計すると 14 アドレス に均等分散しています。

Peel destination 金額 (POL native)
0xbe311a69cdfb515a116e5dce13eadf21c4b3fd53 140,000
0xe03fd8672d017c97c0d83957532e81979570c3d4 129,992
0x4b20080924b4b999676cf85748fe564a6137edd2 127,169
0x6e01404037245d8446fcc5c15b3cf1e035710658 119,954
0xc49531cd02ab5a04b813b0467400461837a6f3b4 119,981
0xbcbba9672f362f49635efb8d756c19d181a1837e 105,000
0xd7d6a0e2b92d2ac2168b807d3a5436b3006e4c63 104,955
0xca310141c53eda70e8fbb15371f8000971087222 94,966
0x74d1cbd70fdb3b8f60bf56353c94deaa5cf30be6 79,908
0x47b7c11bc90bd767c0f464082236119b9d8a7518 69,987
0x8c4d25d0cf9ce62053bbb6d04fb8bd7812e23ed5 49,980
0xe696cadce07b6be114d19b98c1e8d423c741a7ea 29,992
0x6d1f3eea4f5f2b37239608486877ca4e582f2974 74,990 (2回合算)
...他 1 件

Bubblemaps が community note で「15 アドレスに分割」と述べていたのと、観測値はほぼ一致しました (Bubblemaps は consolidation hub を別カウントしている可能性)。

合計 約 1.2M POL ≈ $440K が POL native だけで peel out されています。


6. 偽 PỌL Unicode lookalike による address poisoning

ここが今回の 最大の発見 です。0x8F98 の ERC-20 transfer log を見ると、トークンシンボル "POL" に混じって "PỌL" (U+1ECC) という偽トークンが出てきます:

2026-05-22T...  OUT  127,169.0000 PỌL  → 0x4b2037f7d50103f6...
2026-05-22T...  OUT   29,992.0000 PỌL  → 0xe69606bdac852875...

これと、本物 POL native の peel out 先を並べると:

プレフィックスが 4 文字 (= 16 ビット) 一致 したペアのアドレスに、同額の偽 PỌL を送りつけている ことが分かります。

これは何を狙ったテクニックか

伝統的な address poisoning は「$0 の dust 送金 で受信者の wallet 履歴に類似アドレスを混入させ、後でコピーされるのを待つ」というシンプルなものでした。これは:

  1. 送金量を本物と同額に揃えている → 受信者の wallet ヒストリーで「同額 → 同 prefix のアドレス」が並び、コピペミスを誘発しやすい
  2. token symbol が POL ではなく PỌL (Unicode lookalike) → フォレンジック分析者が token symbol で grep しても「同じ POL」と思ってフィルタを抜けてしまう
  3. fanout 内に複数組混入 → 複数の peel destination が同時に address poisoning の罠にかかる構造

つまり 「自分の peel destination を保護しつつ、後追いで本物の受信者にコピペミスをさせて追加で資金を吸い取る or 分析者を混乱させる」 という二重目的の trap と解釈できます。

※ 解釈の留保: 上記は on-chain で観測される事実をベースとした 解釈の一つ です。攻撃者の内部意図を断定するものではありません。


7. ChainAnalyzer からの視点 — 新規 detector 3 つを新規追加する必要性

本事案を起点に、ChainAnalyzer の AML detector ファミリーに以下の 3 つを追加実装する根拠が揃いました。

W12 DRIP_EXFILTRATION (HIGH, -20)

発火条件:
  - 同一 sender → 同一 recipient の inbound TX が ≥10 回連続
  - 各 TX の value が均等 (median ±5% 以内)
  - TX 間隔の median gap が安定 (±30% 以内)
  - 累計 value が $50K+
発火対象: recipient wallet (Exploiter)
description:
  "Drip exfiltration pattern: {sender} sent {amount} {token} ×
   {count} times at {interval}s interval (total {usd}) — typical
   structured drainage signature."

Polymarket exploit に加え、過去の多くの DeFi exploit や rug pull で見られるパターンを 1 つの detector で網羅できます。

W13 UNICODE_LOOKALIKE_TOKEN (MEDIUM, -10)

発火条件:
  - 同一 wallet の ERC-20 transfer log に、token symbol が
    既知 native / blue-chip token の Unicode lookalike になっている
    transfer が混入
  - 例: PỌL (U+1ECC), ÚSDC (U+00DA), ÉTH (U+00C9) など
発火対象: 送信側 (poisoner)
description:
  "Lookalike token symbol detected: {fake_symbol} matches
   {real_symbol} via Unicode confusion attack. Likely
   address poisoning variant."

Unicode confusable mapping は Unicode Consortium が公開している confusables.txt を使えば実装可能です。

W14 STRUCTURED_PEEL_FANOUT (MEDIUM, -15)

発火条件:
  - 単一 sender が短期間 (≤24h) に ≥10 unique recipient に均等分配
  - 各 recipient への amount が relative 範囲内 (mean±50% 以内)
  - 受信側 wallet の多くが age < 30 日 (= 使い捨て候補)
発火対象: sender wallet (consolidation hub / peel hub)
description:
  "Structured peel fanout: hub {addr} distributed {total} {token}
   across {n} recipients ({avg_age} day avg recipient age) —
   classic post-exploit dispersal."

これは Phase A.5 で実装した bridge_funnel_detected (Bridge Flow Trace の forensic insight) の逆方向版という位置づけです。

既存 detector との関係

これら 3 つは既存の以下の detector と補完関係になります:

  • W9/W10 BRIDGE_FUNDED / PRIVACY_BRIDGE_FUNDED — exfiltration が bridge 経由になった瞬間に hit
  • W11 DUST_ATTACK_TESTING — Unicode lookalike を含む dust transfer に hit
  • E17 PHANTOM_EVENT_MIS_ATTRIBUTION — 偽 token transfer event の誤帰属に hit
  • Follow Mode — peel destination 14 アドレスを自動展開して下流追跡

8. なぜ Bubblemaps + zachxbt は偽 PỌL を出さなかったのか (推察)

これは推察ですが、いくつかの理由が考えられます:

  1. Bubblemaps の token registry は ERC-20 contract address ベース で集計しているため、symbol が PỌL に変わっていても本物 POL と別 contract として既に分類されている → token symbol 表示で見落としやすい
  2. address poisoning の variant は単発で見ると気づきにくい — peel destination 14 件のうち 2 件だけが poisoning ペア、という割合で発見されにくい
  3. そもそも primary disclosure (@zachxbt) の段階では USDC.e のみフォーカス だった可能性

逆に言えば、本記事の貢献は 「公開された address だけを起点に Etherscan V2 API を叩くだけで、追加の forensic 層が見える」 ことを示した点にあります。


9. ChainAnalyzer で本事案を追う具体的な手順

ここまでは Etherscan V2 API を叩いた手作業分析でしたが、ChainAnalyzer の既存機能を使うと、4 つのステップ でこの調査の大半を再現できます。新規 detector (W12-W14) が未実装の現状でも実用的に追えます。

Step 1: Primary attacker を scan する

https://chain-analyzer.com/account/scan で:

  • 入力: 0x8F98075db5d6C620e8D420A8c516E2F2059d9B91
  • Chain: Polygon
  • 得られるもの:
    • Risk score + 既存 detector の発火状況 (W11 DUST_ATTACK が部分 hit する可能性)
    • Counterparty Table — top inbound/outbound 一覧。ここで 0x91430cad... と 14 件の peel destination が並んで見える
    • ML Anomaly Score — 通常パターンから外れているか自動評価
    • Bridge Crossings セクション — 攻撃者が今後 bridge 経由で逃げ始めたら自動的にここに出る

これだけで「operator wallet → attacker → 14 件 fanout」の hub-and-spoke 構造が一目で確認できます。

Step 2: Operator wallet 側を scan する

0x91430cad2d3975766499717fa0d66a78d814e5c5 を Polygon で scan:

  • Counterparty top に 0x65070be9... (UmaCtfAdapter) が突出 → これだけで「ここは Polymarket operator 系の運用 wallet だ」とすぐ分かる
  • Treasury 0xf61e39c7... が inbound top にあるはず → top-up cadence の起点
  • 短期間に attacker 0x8F98... への outbound が突出 = 異常 outflow パターン

Step 3: Follow Mode + Watchlist で 14 アドレスを自動展開

https://chain-analyzer.com/account/watchlist で attacker 0x8F98 を watchlist に追加し、Follow Mode を有効化 (depth=2):

  • Follow Mode は Neo4j 上で BFS 探索を行い、attacker の downstream の wallet を自動展開します
  • 14 件の peel destination を 1 hop で展開、それぞれの hop 先 (= 攻撃者が将来 bridge やミキサーへ送る出口) を 2 hop で先回り検知
  • 新規 TX が発生したら Email / Webhook 通知 が飛んできます (Pro+/Business/Enterprise)

これは「攻撃者は今 wallet を分散させて、後で個別に動かす」という典型パターンに対するリアルタイム監視として有効です。

Step 4: Bridge Flow Trace で escape route 追跡

https://chain-analyzer.com/account/bridge-flow で 14 件の peel destination のうち動きがあったものを順に Bridge Flow Trace:

  • 攻撃者は最終的に どこかの bridge 経由で Polygon を出る はず (Stargate / Across / Synapse / Wormhole / NEAR Intents 等)
  • Bridge Flow Trace は 7 つのプロトコルに対応していて、bridge crossing を発見すれば自動で counterpart の上流 / 下流 chain を resolve
  • 着金先 (例: Ethereum / Solana の attacker wallet) まで追跡できます

現状の制限とギャップ

やりたいこと ChainAnalyzer 現状 ギャップ
30 秒 / 5,000 単位の drip pattern を自動検出 既存 detector では fire しない W12 DRIP_EXFILTRATION 追加実装が必要
偽 PỌL (Unicode lookalike) を自動検出 E17 が部分的に拾う可能性 W13 UNICODE_LOOKALIKE_TOKEN 追加実装が必要
14 件 fanout を 1 つの hub として認識 Counterparty table と Follow Mode で「見える」が、insight として 1 行にならない W14 STRUCTURED_PEEL_FANOUT 追加実装が必要
Treasury → operator → attacker の三段 cascade を 1 リクエストで描く 現状 Bridge Flow Trace は cross-chain 専用 同一 chain 内の "Capital Drip Trace" 機能の追加が望ましい

自分的推奨事項: PDF Forensic Report (Phase C)

調査結果を PDF レポートにまとめて SAR 等に添付する場合は、ChainAnalyzer 開発中の Bridge Flow PDF Report (Phase C) が将来的に対応予定です。本事案のようなケースを想定して、レポートには以下が含まれる予定:

  • 全 TX の signature 付き graph (matplotlib render)
  • 役割分類 (operator / attacker / peel destination / treasury)
  • timestamp 順の event timeline
  • "Same Operator Likelihood" スコア (= 14 件 peel 先が同一オペレータ所有である可能性)

まずは "scan → counterparty → follow mode" だけで 80% 追える

新規 detector の実装を待たなくても、Step 1-3 の組み合わせで本事案の構造は 80% つかめます。残り 20% (= drip pattern の自動 alerting、Unicode lookalike の自動検出、fanout 集約 insight) が W12-W14 で埋まる、という整理です。


10. まとめ

項目
事案 Polymarket UMA CTF Adapter operator wallet 秘密鍵漏洩 (contract exploit ではない)
発生日 2026-05-22
Primary attacker 0x8F98075db5d6C620e8D420A8c516E2F2059d9B91
漏洩鍵 wallet (operator) 0x91430cad2d3975766499717fa0d66a78d814e5c5 (EOA、2022-02 から運用)
Target contract 0x65070be91477460d8a7aeeb94ef92fe056c2f2a7 (= UmaCtfAdapter、Polygonscan verified)
Treasury (top-up 元) 0xf61e39c7eb1e2ff5af3a24bca88d40fd11594805
観測された drip 単位 5,000 USDC.e / 5,000 POL @ 30 秒間隔
真の 30 秒 / 5,000 cadence の原因 Treasury → operator の自動 top-up cadence (on-chain で確定)
Peel destinations (POL native) 14 アドレス (合計 ~1.2M POL ≈ $440K)
ERC-20 outflow (USDC.e) 7 アドレス (合計 ~$588K)
真の被害規模 (推定) $1M+ (公表値 $600K より大きい)
新発見: 偽 PỌL token Unicode (U+1ECC) lookalike による address poisoning ペアを 2 件確認
提案 detector W12 DRIP_EXFILTRATION / W13 UNICODE_LOOKALIKE_TOKEN / W14 STRUCTURED_PEEL_FANOUT

本記事のオリジナルな貢献は次の 4 点です:

  1. 「contract exploit ではなく key compromise」を on-chain で完全裏取り (operator wallet が 4 年間 UmaCtfAdapter を 195 回呼んでいた事実から確認)
  2. 「なぜ 5,000 単位 / 30 秒間隔か」の正解を発見 — contract 側の制約ではなく、Polymarket treasury の自動 top-up cadence に attacker の drainage rate がロックされていた
  3. Bubblemaps の表示金額 ($600K) より実被害は大きい ($1M+、POL native を含めると)
  4. Unicode lookalike (PỌL) address poisoning が peel chain に混入している多段構えのフォレンジック観察

11. 📢 アップデート (2026-05-25): 提案 detector を実装してデプロイ

本記事公開後、第 7 節で提案した 3 つの detector (W12 / W13 / W14) を ChainAnalyzer 本番環境に実装・デプロイしました。さらに operator wallet 0x91430cad2d3975766499717fa0d66a78d814e5c5 を Polygon で直接 scan したところ、W12 が 2 件、W13 / W14 が各 1 件、計 4 件 fire することを on-chain で確認できました。

スキャン結果

Detector Severity 内容
W12 DRIP_EXFILTRATION HIGH operator wallet が 4,999.9942 POL (native)175 回、約 32 秒間隔で送付 (累計 ≈ $498,617)
W12 DRIP_EXFILTRATION HIGH operator wallet が 5,000.0000 USDC.e74 回、約 33 秒間隔で送付 (累計 ≈ $458,671)
W13 UNICODE_LOOKALIKE_TOKEN MEDIUM "PỌL" が "POL" を Unicode 紛らわせ文字 (U+1ECC) で詐称、3 件の transfer を観測
W14 STRUCTURED_PEEL_FANOUT MEDIUM 1,246,874.55 POL を 13 のユニークな宛先に 2 時間以内で均等分配

W12 が native POLUSDC.e の両方で別個に fire しているのが本件の決定的な指紋になっています — 同じ operator wallet が 2 通貨で同じ cadence の drip を別タイミングで出している様子が、自動 detector レベルで明示されます。

W12 のロバストネス改善 — median + MAD

実装当初の W12 は mean + relative stdev で値の uniformity を判定していました。本ケースでは 1 件だけ "double batch" (9,993 POL ≈ 通常の 2 倍) が混ざっており、それだけで relative stdev が 0% → 26% に跳ね上がり、当初の閾値 (10%) を突破して fire しない という落とし穴が判明しました。

そこで _robust_uniformity 関数を導入し、median + MAD (Median Absolute Deviation) ベースに置換しました:

import statistics

# MAD = median of |v_i - median|
# 1 件の outlier は分布の最大 1/n しか動かさないので、stdev のように爆発しない。
def _robust_uniformity(values, within_tolerance=0.10):
    med = statistics.median(values)
    if med <= 0:
        return (0.0, float("inf"), 0.0)
    mad = statistics.median(abs(v - med) for v in values)
    rel_mad = mad / med
    within = sum(1 for v in values if abs(v - med) / med <= within_tolerance) / len(values)
    return (med, rel_mad, within)

# fire 条件:
#   rel_mad ≤ 0.05  (MAD / median が 5% 以内)
#   AND ≥ 80% の値が median ±10% 以内

検証ケース:

  • ✅ Polymarket case (USDC.e 5K × 20 + 1 件 double batch): fire (rel_mad=0.0, within_10pct=0.95)
  • ✅ ランダム入金の通常 wallet: fire しない (within_10pct が低くて reject)
  • ✅ 小額 uniform drip (< $50K): fire しない (cumulative threshold)
  • ✅ 1/3 が outlier のノイジー分布: fire しない (within_10pct < 80%)

教訓: 「ほぼ均一だけど 1〜2 件だけブレた」というケースは attacker bot 系で頻繁に起きます (treasury 側の 2-batch top-up、retry、手動介入)。stdev でなく median + MAD を使うのが robust です。

Insight 4 のアップデート

第 4 節「ChainAnalyzer の既存 detector では fire しません」という当初の指摘は、本日 (2026-05-25) をもって 過去のもの になりました。同じ wallet 0x91430cad2d3975766499717fa0d66a78d814e5c5 を scan すれば、W12 / W13 / W14 が自動で発火します。

# 確認手順
1. https://chain-analyzer.com にアクセス (無料スキャンあり)
2. Scan ページで chain = Polygon を選択
3. 0x91430cad2d3975766499717fa0d66a78d814e5c5 を入力
4. → W12 × 2 + W13 × 1 + W14 × 1 が detection list に表示されます

参考リンク


免責: 本記事は公開された on-chain データ (Polygonscan / Etherscan V2 API) と公開 X 投稿 (Bubblemaps / Polymarket 関係者) を起点とした技術的フォレンジック分析であり、個別の関係者の法的責任を断定するものではありません。偽 PỌL token を用いた address poisoning の解釈は on-chain の事実観察に基づく一つの解釈であり、攻撃者の内部意図を確定するものではありません。また投資助言でもありません。間違いが含まれる可能性があることをご了承ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?