Web3 ์„œ๋ช… ์›Œํฌํ”Œ๋กœ ์นจํ•ด & Safe delegatecall Proxy Takeover

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

๊ฐœ์š”

์ฝœ๋“œ-์›”๋ › ์ ˆ๋„ ์ฒด์ธ์€ **Safe{Wallet} web UI์˜ ๊ณต๊ธ‰๋ง ์นจํ•ด(supply-chain compromise)**์™€ **ํ”„๋ก์‹œ์˜ implementation ํฌ์ธํ„ฐ(slot 0)๋ฅผ ๋ฎ์–ด์“ด on-chain delegatecall ์›์‹œ ๊ธฐ๋Šฅ(delegatecall primitive)**์„ ๊ฒฐํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์š” ์š”์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • dApp์ด ์„œ๋ช… ๊ฒฝ๋กœ์— ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์„œ๋ช…์ž๋ฅผ ๊ณต๊ฒฉ์ž๊ฐ€ ์„ ํƒํ•œ ํ•„๋“œ์— ๋Œ€ํ•ด ์œ ํšจํ•œ EIP-712 ์„œ๋ช…์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋งŒ๋“ค๋ฉด์„œ ์›๋ž˜ UI ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์›ํ•ด ๋‹ค๋ฅธ ์„œ๋ช…์ž๋“ค์ด ๋ˆˆ์น˜์ฑ„์ง€ ๋ชปํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Safe proxies๋Š” masterCopy(implementation)๋ฅผ storage slot 0์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. slot 0์— ์“ฐ๋Š” ๊ณ„์•ฝ์œผ๋กœ์˜ delegatecall์€ ์‚ฌ์‹ค์ƒ Safe๋ฅผ ๊ณต๊ฒฉ์ž ๋กœ์ง์œผ๋กœ โ€œ์—…๊ทธ๋ ˆ์ด๋“œโ€ํ•˜์—ฌ ์ง€๊ฐ‘์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์ œ์–ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Off-chain: Targeted signing mutation in Safe

๋ณ€์กฐ๋œ Safe ๋ฒˆ๋“ค(_app-*.js)์€ ํŠน์ • Safe + signer ์ฃผ์†Œ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๊ณต๊ฒฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์ž…๋œ ๋กœ์ง์€ ์„œ๋ช… ํ˜ธ์ถœ ์ง์ „์— ์‹คํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

// Pseudocode of the malicious flow
orig = structuredClone(tx.data);
if (isVictimSafe && isVictimSigner && tx.data.operation === 0) {
tx.data.to = attackerContract;
tx.data.data = "0xa9059cbb...";      // ERC-20 transfer selector
tx.data.operation = 1;                 // delegatecall
tx.data.value = 0;
tx.data.safeTxGas = 45746;
const sig = await sdk.signTransaction(tx, safeVersion);
sig.data = orig;                       // restore original before submission
tx.data = orig;
return sig;
}

๊ณต๊ฒฉ ํŠน์„ฑ

  • Context-gated: ํ”ผํ•ด์ž Safes/์„œ๋ช…์ž์— ๋Œ€ํ•ด ํ•˜๋“œ์ฝ”๋”ฉ๋œ allowlists๊ฐ€ ๋…ธ์ด์ฆˆ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ํƒ์ง€ ๊ฐ€๋Šฅ์„ฑ์„ ๋‚ฎ์ถค.
  • Last-moment mutation: ํ•„๋“œ(to, data, operation, gas)๊ฐ€ signTransaction ์ง์ „์— ๋ฎ์–ด์จ์ง€๊ณ  ์ดํ›„ ๋ณต์›๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— UI์— ํ‘œ์‹œ๋˜๋Š” proposal ํŽ˜์ด๋กœ๋“œ๋Š” ์ •์ƒ์ ์œผ๋กœ ๋ณด์˜€์ง€๋งŒ ์„œ๋ช…์€ ๊ณต๊ฒฉ์ž ํŽ˜์ด๋กœ๋“œ์™€ ์ผ์น˜ํ•จ.
  • EIP-712 opacity: ์ง€๊ฐ‘์€ ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ–ˆ์ง€๋งŒ ์ค‘์ฒฉ๋œ calldata๋ฅผ ๋””์ฝ”๋“œํ•˜๊ฑฐ๋‚˜ operation = delegatecall์„ ๊ฐ•์กฐํ•˜์ง€ ์•Š์•„ ๋ณ€์กฐ๋œ ๋ฉ”์‹œ์ง€๊ฐ€ ์‚ฌ์‹ค์ƒ blind-signed๋จ.

๊ฒŒ์ดํŠธ์›จ์ด ๊ฒ€์ฆ ๊ด€๋ จ์„ฑ

Safe ์ œ์•ˆ์€ Safe Client Gateway๋กœ ์ œ์ถœ๋œ๋‹ค. ๊ฐ•ํ™”๋œ ๊ฒ€์‚ฌ ์ด์ „์—๋Š” UI๊ฐ€ ์„œ๋ช… ํ›„ ํ•„๋“œ๋ฅผ ์žฌ์ž‘์„ฑํ•˜๋ฉด safeTxHash/์„œ๋ช…์ด JSON ๋ณธ๋ฌธ๊ณผ ๋‹ค๋ฅธ ํ•„๋“œ์— ๋Œ€์‘ํ•˜๋Š” proposal์„ ๊ฒŒ์ดํŠธ์›จ์ด๊ฐ€ ์ˆ˜๋ฝํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์‚ฌ๊ณ  ์ดํ›„์—๋Š” ๊ฒŒ์ดํŠธ์›จ์ด๊ฐ€ ์ œ์ถœ๋œ ํŠธ๋žœ์žญ์…˜๊ณผ ํ•ด์‹œ/์„œ๋ช…์ด ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” proposal์„ ๊ฑฐ๋ถ€ํ•œ๋‹ค. ์œ ์‚ฌํ•œ ์„œ๋ฒ„ ์ธก ํ•ด์‹œ ๊ฒ€์ฆ์€ ๋ชจ๋“  signing-orchestration API์— ์ ์šฉ๋˜์–ด์•ผ ํ•œ๋‹ค.

2025 Bybit/Safe ์‚ฌ๊ฑด ํ•˜์ด๋ผ์ดํŠธ

  • 2025๋…„ 2์›” 21์ผ Bybit ์ฝœ๋“œ์›”๋ › ํƒˆ์ทจ(~401k ETH)๋Š” ๊ฐ™์€ ํŒจํ„ด์„ ์žฌ์‚ฌ์šฉํ–ˆ๋‹ค: ์†์ƒ๋œ Safe S3 bundle์€ Bybit ์„œ๋ช…์ž์—๊ฒŒ๋งŒ ํŠธ๋ฆฌ๊ฑฐ๋˜์—ˆ๊ณ  operation=0 โ†’ 1๋กœ ๊ต์ฒดํ•˜์—ฌ to๋ฅผ slot 0์„ ์“ฐ๋Š” ๋ฏธ๋ฆฌ ๋ฐฐํฌ๋œ ๊ณต๊ฒฉ์ž ๊ณ„์•ฝ์œผ๋กœ ์ง€์ •ํ–ˆ๋‹ค.
  • Wayback-cached _app-52c9031bfa03da47.js๋Š” Bybit์˜ Safe (0x1db9โ€ฆcf4) ๋ฐ ์„œ๋ช…์ž ์ฃผ์†Œ๋ฅผ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋กœ์ง์„ ๋ณด์—ฌ์ฃผ๊ณ , ์‹คํ–‰ ๋‘ ๋ถ„ ํ›„ ์ฆ‰์‹œ ๊นจ๋—ํ•œ ๋ฒˆ๋“ค๋กœ ๋กค๋ฐฑ๋˜์–ด โ€œmutate โ†’ sign โ†’ restoreโ€ ํŠธ๋ฆญ์„ ๋ฐ˜์˜ํ–ˆ๋‹ค.
  • ์•…์„ฑ ๊ณ„์•ฝ (e.g., 0x9622โ€ฆc7242)์€ ๋‹จ์ˆœํ•œ ํ•จ์ˆ˜ sweepETH/sweepERC20๊ณผ ํ•จ๊ป˜ ๊ตฌํ˜„ ์Šฌ๋กฏ์„ ์“ฐ๋Š” transfer(address,uint256)๋ฅผ ํฌํ•จํ–ˆ๋‹ค. execTransaction(..., operation=1, to=contract, data=transfer(newImpl,0))์˜ ์‹คํ–‰์€ ํ”„๋ก์‹œ ๊ตฌํ˜„์„ ๋ณ€๊ฒฝํ•˜์—ฌ ์™„์ „ํ•œ ์ œ์–ด๊ถŒ์„ ๋ถ€์—ฌํ–ˆ๋‹ค.

์˜จ์ฒด์ธ: ์Šฌ๋กฏ ์ถฉ๋Œ์„ ํ†ตํ•œ delegatecall ํ”„๋ก์‹œ ํƒˆ์ทจ

Safe ํ”„๋ก์‹œ๋Š” masterCopy๋ฅผ storage slot 0์— ๋ณด๊ด€ํ•˜๊ณ  ๋ชจ๋“  ๋กœ์ง์„ ๊ทธ์ชฝ์— ์œ„์ž„ํ•œ๋‹ค. Safe๊ฐ€ **operation = 1 (delegatecall)**์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋ช…๋œ ์–ด๋–ค ํŠธ๋žœ์žญ์…˜๋„ ์ž„์˜์˜ ๊ณ„์•ฝ์„ ๊ฐ€๋ฆฌ์ผœ ํ”„๋ก์‹œ์˜ ์Šคํ† ๋ฆฌ์ง€ ์ปจํ…์ŠคํŠธ์—์„œ ๊ทธ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ณต๊ฒฉ์ž ๊ณ„์•ฝ์€ ERC-20 transfer(address,uint256)๋ฅผ ํ‰๋‚ด๋‚ด์—ˆ์ง€๋งŒ ๋Œ€์‹  _to๋ฅผ slot 0์— ์ผ๋‹ค:

// Decompiler view (storage slot 0 write)
uint256 stor0; // slot 0
function transfer(address _to, uint256 _value) external {
stor0 = uint256(uint160(_to));
}

Execution path:

  1. ํ”ผํ•ด์ž๊ฐ€ execTransaction์— ์„œ๋ช… โ€” operation = delegatecall, to = attackerContract, data = transfer(newImpl, 0).
  2. Safe masterCopy๊ฐ€ ์ด ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์— ๋Œ€ํ•œ ์„œ๋ช…์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  3. Proxy๊ฐ€ attackerContract๋กœ delegatecall์„ ์ˆ˜ํ–‰; transfer ๋ณธ๋ฌธ์ด slot 0์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
  4. Slot 0 (masterCopy)๊ฐ€ ์ด์ œ ๊ณต๊ฒฉ์ž ์ œ์–ด ๋กœ์ง์„ ๊ฐ€๋ฆฌํ‚ด โ†’ ์ง€๊ฐ‘ ์™„์ „ ํƒˆ์ทจ ๋ฐ ์ž๊ธˆ ์œ ์ถœ.

Guard & version notes (post-incident hardening)

  • Safes >= v1.3.0๋Š” delegatecall์„ ๊ฑฐ๋ถ€(veto)ํ•˜๊ฑฐ๋‚˜ to/selectors์— ๋Œ€ํ•œ ACL์„ ๊ฐ•์ œํ•˜๋Š” Guard๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค; Bybit๋Š” v1.1.1์„ ์‚ฌ์šฉํ•ด Guard ํ›…์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ œ์–ด ํ”Œ๋ ˆ์ธ์„ ํ™•๋ณดํ•˜๋ ค๋ฉด ๊ณ„์•ฝ ์—…๊ทธ๋ ˆ์ด๋“œ(๋ฐ ์†Œ์œ ์ž ์žฌ๋“ฑ๋ก)๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Detection & hardening checklist

  • UI integrity: JS ์ž์‚ฐ์„ pinํ•˜๊ณ  SRI ์ ์šฉ; ๋ฒˆ๋“ค ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ชจ๋‹ˆํ„ฐ๋ง; ์„œ๋ช… UI๋ฅผ ์‹ ๋ขฐ ๊ฒฝ๊ณ„์˜ ์ผ๋ถ€๋กœ ์ทจ๊ธ‰ํ•˜์„ธ์š”.
  • Sign-time validation: EIP-712 clear-signing์„ ์ง€์›ํ•˜๋Š” ํ•˜๋“œ์›จ์–ด ์ง€๊ฐ‘ ์‚ฌ์šฉ; operation์„ ๋ช…์‹œ์ ์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ณ  ์ค‘์ฒฉ๋œ calldata๋ฅผ ๋””์ฝ”๋“œํ•˜์„ธ์š”. ์ •์ฑ…์—์„œ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ํ•œ operation = 1์ผ ๋•Œ ์„œ๋ช…์„ ๊ฑฐ๋ถ€ํ•˜์„ธ์š”.
  • Server-side hash checks: ์ œ์•ˆ์„œ๋ฅผ ์ค‘๊ณ„ํ•˜๋Š” ๊ฒŒ์ดํŠธ์›จ์ด/์„œ๋น„์Šค๋Š” safeTxHash๋ฅผ ์žฌ๊ณ„์‚ฐํ•˜๊ณ  ์„œ๋ช…์ด ์ œ์ถœ๋œ ํ•„๋“œ์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Policy/allowlists: to, selectors, ์ž์‚ฐ ์œ ํ˜•์— ๋Œ€ํ•œ ์‚ฌ์ „ ๊ฒ€์‚ฌ ๊ทœ์น™์„ ์ ์šฉํ•˜๊ณ  ์‹ฌ์‚ฌ๋œ ํ”Œ๋กœ์šฐ๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” delegatecall์„ ๊ธˆ์ง€ํ•˜์„ธ์š”. ์™„์ „ ์„œ๋ช…๋œ ํŠธ๋žœ์žญ์…˜์„ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธํ•˜๊ธฐ ์ „์— ๋‚ด๋ถ€ ์ •์ฑ… ์„œ๋น„์Šค๋ฅผ ์š”๊ตฌํ•˜์„ธ์š”.
  • Contract design: ํ•„์š”ํ•˜์ง€ ์•Š์€ ํ•œ multisig/treasury ์ง€๊ฐ‘์—์„œ ์ž„์˜์˜ delegatecall์„ ๋…ธ์ถœํ•˜์ง€ ๋งˆ์„ธ์š”. ์—…๊ทธ๋ ˆ์ด๋“œ ํฌ์ธํ„ฐ๋ฅผ slot 0์—์„œ ๋ฉ€๋ฆฌ ๋‘๊ฑฐ๋‚˜ ๋ช…์‹œ์  ์—…๊ทธ๋ ˆ์ด๋“œ ๋กœ์ง๊ณผ ์ ‘๊ทผ ์ œ์–ด๋กœ ๋ณดํ˜ธํ•˜์„ธ์š”.
  • Monitoring: ์žฌ๋ฌด ์ž๊ธˆ์„ ๋ณด์œ ํ•œ ์ง€๊ฐ‘์—์„œ์˜ delegatecall ์‹คํ–‰ ๋ฐ ์ผ๋ฐ˜์ ์ธ call ํŒจํ„ด์—์„œ operation์„ ๋ณ€๊ฒฝํ•˜๋Š” ์ œ์•ˆ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”.

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ