
Solidityèšèªããã³Ethereumãã©ãããã©ãŒã å
šäœã®ã¹ããŒãã³ã³ãã©ã¯ãã«åºæã®å
žåçãªè匱æ§ãæ»æãããã³åé¡é åã«å°å¿µããã·ãªãŒãºã®ç¬¬4éšã玹ä»ããŸãã ããã§ã¯ãã¹ããŒãã³ã³ãã©ã¯ãã®ã»ãã¥ãªãã£ãåæããããã®ããŒã«ã®ååšãšãããããå¿
èŠãªïŒäžèŠãªïŒçç±ã«ã€ããŠèª¬æããŸãã
ååã§ã¯ãããã³ãã©ã³ãã³ã°æ»æãããŸããŸãªä¹±æ°çæã¢ã«ãŽãªãºã ãããã³æš©é蚌æã³ã³ã»ã³ãµã¹ã䜿çšãããããã¯ãŒã¯ã®åŸ©å
åã«ã€ããŠèª¬æããŸããã 2çªç®ã¯ãæŽæ°ãªãŒããŒãããŒãABIãšã³ã³ãŒã/ãã³ãŒããåæåãããŠããªãã¹ãã¬ãŒãžãã€ã³ã¿ãŒãã¿ã€ãã®æ··ä¹±ãããã³ããã¯ãã¢ã®äœãæ¹ã«ã€ããŠè©±ããŸããã 第3éšã§ã¯ãSolidityã®ããã€ãã®ç¹åŸŽçãªæ©èœã«è§Šããå¥çŽã§èŠã€ãã£ãè«ççãªè匱æ§ã®ããã€ãã調ã¹ãŸããã ãã®ããŒãã§ã¯ãã¹ããŒãã³ã³ãã©ã¯ããåæããããã®æ¢åã®ããŒã«ã®æŠèŠãæäŸããŸãã
ãã¹ãŠã®ããŒã«ãããã€ãã®ã«ããŽãªã«åé¡ããŠã次ã®ããšãã§ããããã«ããŸããã
次ã«ãããããã«è§ŠããŸãã äžéšã®ããŒã«ã¯ã¹ããŒãã³ã³ãã©ã¯ãã®ãœãŒã¹ã³ãŒãã§ã®ã¿æ©èœããä»ã®ããŒã«ã§ã¯æ©èœããŸããã ããã«ãç§ãã¡ã¯ããããªãã¯ãã¡ã€ã³ã«ããããšãããã£ãŠãããç§ãã¡ãååããããŒã«ã«ã€ããŠã®ã¿è©±ãããšãäºçŽããŸãã ãã®ãããã¯ã«é¢ããä»ã®ããŒã«ã®çµéšãããå Žåã¯ãã³ã¡ã³ãããŠãã ãã:)
ãããã°
çŸåšããœãŒã¹ã³ãŒãã§ã¹ããŒãã³ã³ãã©ã¯ãããããã°ããæ©äŒã¯1ã€ïŒãŸãã¯å¯äžã®äŸ¿å©ãªïŒ Remix IDEã®ã¿ã§ãã ãŸãããã€ãã³ãŒããçè§£ããå¿
èŠãããå Žåã¯ã Radare2ãŸãã¯MythrilTraceExplorerã圹ç«ã¡ãŸã ã
éçºè
ã®å Žåãã»ãšãã©ã®å Žåããããã°ã«Remixã䜿çšããŸãïŒã»ãšãã©ã®å Žåããããè¡ããŸãïŒã ããŒã«ã«å€æ°ïŒã¹ã¿ãã¯ïŒã®ç¶æ
ãã¡ã¢ãªãã¹ãã¬ãŒãžãæ¶è²»ã¬ã¹ãªã©ã®ç¶æ
ãç£èŠããããšã¯éåžžã«äŸ¿å©ã§ãã
 |
---|
å³1. Remix IDEã€ã³ã¿ãŒãã§ãŒã¹ |
[ã¹ãã¬ãŒãžãå®å
šã«ããŒããããŸãã]ã¿ãã®å
容ãäžå®ãªå Žåã¯ã ã¹ãã¬ãŒãžããã€ã¹ãæ±ãããšããå§ãããŸãã ãã€ãã¹é¢ã¯ãJavaScript VMã®å®æçãªãã°ãšãã¢ã»ã³ãã©ãŒæ¿å
¥ã䜿çšããå Žåãè¡ããšã®ãããã°ãæåŸ
ã§ããªãããšã§ãã
Remixãæ°ã«å
¥ããªããã³ã³ãœãŒã«ã奜ããªããRarere2ã詊ããŠãã ããã ããã«ãããã³ã³ãã©ã¯ããåè§£ããã³ãããã°ã§ããŸãã ãã¬ãŒã¹ã¯ããããã°ïŒçºçãããã©ã³ã¶ã¯ã·ã§ã³ã®èšé²ïŒã«äœ¿çšãããŸãã rpcã¬ãŒããŒã¯ããŒãã«æ¥ç¶ãïŒ ganache-cliã䟿å©ïŒããã©ã³ã¶ã¯ã·ã§ã³ãç¹°ãè¿ãã®ã«å¿
èŠãªããŒã¿ãå§çž®ããŸãã
ã°ã©ãã£ã«ã«ã¢ãŒãã§ã¯ãã¹ã¿ãã¯ãšã¡ã¢ãªã®å
容ã§ã¿ã€ã«ãéãããšãã§ããŸãã
 |
---|
å³2. Radare 2ã€ã³ã¿ãŒãã§ãŒã¹ |
ãã¬ãŒã¯ãã€ã³ããèšå®ããŠã¡ã¢ãªã«æžã蟌ãããšã¯ã§ããŸãããã¹ãã¬ãŒãžã調ã¹ãã倿Žãããããæ¹æ³ã¯èŠã€ãããŸããã§ããã
Positive Technologiesã®ç€Ÿå¡ã«ã¯ãRadarã䜿çšããŠå¥çŽãåè§£ããã³ãããã°ããäŸããããŸãã ã¬ãŒããŒã¯æãçŽæçãªããŒã«ã§ã¯ãããŸããã®ã§ãã¬ãŒããŒã®ã³ãã³ãã確èªããŠãã ããã
èªåè匱æ§ã¹ãã£ã³
ç§ãã¡ã®åã«ã3ã€ã®æ¥œåšã®æ¯èŒãæ¢ã«è¡ãããŠããŸã-ãã¹ãªã«ããã³ãã£ã³ã¢ããªã€ãšã³ãã ãã®åæã®çµæã«ãããšãMythrillã¯ãªãŒãããOyenteã¯ææªã®çµæãåºããŸãã ãã¹ãäžã«ãèè
ã¯ããŒã«ã«ã³ã³ãã©ã¯ãã®ãœãŒã¹ã³ãŒãããäŸçµŠããããããçµæã¯çã®ãã€ãã³ãŒããã©ãã ãããŸãåŠçããŠããããåæ ããŠããŸããããåºæ¬çã«ç§ãã¡ã®å°è±¡ãšäžèŽããŸãã
Mythrilã«ã¯ãã¹ããŒãã³ã³ãã©ã¯ãã®ãœãŒã¹ã³ãŒããšãã€ãã³ãŒãã®äž¡æ¹ã®åæãå«ãå¹
åºãæ©èœããããŸãã ãã§ãŒã³äžã§ãæ©èœããããšãã€ãŸãããªã³ã©ã€ã³ã§ããŒãã«ãªã³ã©ã€ã³ã§æ¥ç¶ããå¥çŽãåçž®ãããŠåæã§ããããšã¯æ³šç®ã«å€ããŸãã ãŸããéã¢ã»ã³ãã©ãŒã§ãããé©åãªã°ã©ããäœæã§ããŸãïŒãã ããæ³šæããŠãã ãããããã¯ãã°ãã°æ£ããè¡ãããªããããEthersplayãŸãã¯IDA-EVMã詊ããŠãã ããïŒã Mythrilã¯ãMythrilTraceExplorerãšããããŒã«ã®ç¶æ
ãçæããããšãã§ããŸããããã«ãããæå倿°ã䜿çšããŠã¹ããŒãã³ã³ãã©ã¯ãã®ãã€ãã³ãŒãããããã°ã§ããŸãã
 |
---|
å³3. MythrilTraceExplorerã€ã³ã¿ãŒãã§ãŒã¹ |
Mythilããã³MythrilTraceExplorerãªãã·ã§ã³ã®å®å
šãªãªã¹ãã¯ããã¥ã¡ã³ãã«èšèŒãããŠããã ããã«ã¯ãªãç§å¯ã®ãã©ã°ãèŠã€ããããšãã§ããŸãã
Consensysãã¹ãããeth_tx_order_dependence_minimal
ã³ã³ãã©ã¯ããååŸããŸã
ãœãŒã¹eth_tx_order_dependence_minimal contract Benchmark { address public owner; bool public claimed; uint public reward; function Benchmark() public { owner = msg.sender; } function setReward() public payable { require (!claimed); require(msg.sender == owner); owner.transfer(reward); reward = msg.value; } function claimReward(uint256 submission) { require (!claimed); require(submission < 10); msg.sender.transfer(reward); claimed = true; } }
Mythrilãã©ã®ããã«ãœãŒã¹ã³ãŒããåŠçããããèŠãŠãã ããïŒ
user% myth -x eth_tx_order_dependence_minimal.sol ==== Ether send ==== Type: Warning Contract: Benchmark Function name: claimReward(uint256) PC address: 693 In the function 'claimReward(uint256)' a non-zero amount of Ether is sent to msg.sender. Call value is storage_1. There is a check on storage index 7. This storage slot can be written to by calling the function 'claimReward(uint256)'.
ãããŠä»ããããªãã§ïŒ
user% myth -x -c 60806040523...5f9dc0029 The analysis was completed successfully. No issues were detected.
ã芧ã®ãšãããMythrilã®çã®ãã€ãã³ãŒãã¯åæã§ããŸããã§ããã
Pythonã§èšè¿°ããããã¹ããŒãã³ã³ãã©ã¯ãã®è匱æ§ãæ€åºããããã®ç¬èªã®ã·ã³ããªãã¯å®è¡ãšã³ãžã³ãåããããŒã«ã å°ãåãŸã§ãéçºè
ã¯å
¥åããŒã¿ã«é¢ããæ
å ±ã®åºåãæçµçã«è¿œå ããŸãããããã«ãããåé¡ãããªã¬ãŒãããŸãã ãã®ããŒã«ã¯ããœãŒã¹ã³ãŒãã䜿çšããŠå¥çŽãåæããå Žåã«ã®ã¿ãããçšåºŠé©åãªçµæãçæããŸãã ãã€ãã³ãŒãã«èšå®ãããšããã®äžéšããã«ããŒãããªããããã»ãšãã©ã®å Žåããã°ã¯èŠã€ãããŸããã
Mythrilãšã®é¡æšã«ãããOyenteã«çµéšãç©ãã§ãOyenteããœãŒã¹ã³ãŒãã®æç¡ã«ãããããè匱æ§ãã©ã®çšåºŠèŠãŠãããã確èªããŸãã
user% oyente -s eth_tx_order_dependence_minimal.sol INFO:root:contract eth_tx_order_dependence_minimal.sol:Benchmark: INFO:oyente.symExec: ============ Results =========== INFO:oyente.symExec: EVM Code Coverage: 98.3% INFO:oyente.symExec: Integer Underflow: False INFO:oyente.symExec: Integer Overflow: False INFO:oyente.symExec: Parity Multisig Bug 2: False INFO:oyente.symExec: Callstack Depth Attack Vulnerability: False INFO:oyente.symExec: Transaction-Ordering Dependence (TOD): True INFO:oyente.symExec: Timestamp Dependency: False INFO:oyente.symExec: Re-Entrancy Vulnerability: False INFO:oyente.symExec:Flow1 eth_tx_order_dependence_minimal.sol:14:9: Warning: Transaction-Ordering Dependency. owner.transfer(reward) Flow2 eth_tx_order_dependence_minimal.sol:22:9: Warning: Transaction-Ordering Dependency. msg.sender.transfer(reward) INFO:oyente.symExec: ====== Analysis Completed ======
次ã«ããã®ã³ã³ãã©ã¯ãã®ãã€ãã³ãŒããèšå®ããŸãã
user% oyente -b -s eth_tx_order_dependence_minimal.bin INFO:oyente.symExec: ============ Results =========== INFO:oyente.symExec: EVM Code Coverage: 18.0% INFO:oyente.symExec: Callstack Depth Attack Vulnerability: False INFO:oyente.symExec: Transaction-Ordering Dependence (TOD): False INFO:oyente.symExec: Timestamp Dependency: False INFO:oyente.symExec: Re-Entrancy Vulnerability: False INFO:oyente.symExec: ====== Analysis Completed ======
ã芧ã®ãšãããã«ãã¬ããžã¯å€§å¹
ã«æžå°ãïŒ98ãã18ããŒã»ã³ãïŒãããŒã«ã¯å¥çŽã«é¢ããåé¡ãæ€åºããŸããã
Oyenteã®éçºã¯ãïŒãŸãïŒéåžžã«æªãã³ãŒãã®ããã«ç°¡åã§ã¯ãããŸããã ããšãã°ããã©ã¡ãŒã¿ãŒã¯ã°ããŒãã«å€æ°ãä»ããŠã¢ãžã¥ãŒã«ããã¢ãžã¥ãŒã«ã«æž¡ãããŸãã æãå¯èœæ§ãé«ãã®ã¯ããã®ãããžã§ã¯ãã«åãçµãã§ãã人ã1.5人以äžã ããã§ãã
念ã®ãããããŒã«ããã¯ã¹ã«Oyenteãä¿æãã䟡å€ã¯ãããŸãããOyenteãéçºããã³æ¹åããããã©ããã¯ããããŸããã
ããããæãèªåŒµãããéã³ã³ãã€ã©ã ãã®äž»ãªæ©èœã¯ãæ©èœããªãããšã§ãã
ãã®æ©èœãå®èšŒããããã«ãããã¡ãã®ã³ã³ãã©ã¯ããéã³ã³ãã€ã«ããŸããããããã¯ãå®éšã®çŽåºŠã®ããã«ã2017幎7æ1æ¥ããæ¯èŒçå€ãããŒãžã§ã³ã®solc'a 0.4.12ãåéããŸãã Porosityã2018幎1æã«æåŸã«æŽæ°ããããããããŒã«ãåœç€Ÿã®å¥çŽã«å¯Ÿå¿ã§ãããšèšç®ã§ããããšã«æ³šæããŠãã ããã
ãªãªãžãã«ïŒ
contract sample { uint y; function foobar (uint x) { if (x == 0) { y = 0x1337; } else { y = 0xb33f; } } }
ãŠããŒã¯ãªéã³ã³ãã€ã©åºåïŒ
.\porosity.exe --code $binRuntime --decompile function func_14ba3f12 { if (!msg.value) { } if (arg_4 != 0x0) { store[var_xrpMC] = 0xB33F; } store[var_zdGc8] = 0x1337; return; return; }
ããŒã«ãç°¡åã«ããããã«abiãæž¡ãããšãããšãPorosityãã¯ã©ãã·ã¥ããŸãã
Porosityã®ã¢ããªã±ãŒã·ã§ã³ãèŠã€ããããšãã§ããŸããã§ããã䜿çšãã詊ã¿ã¯ãã¹ãŠãæå³ã®ãªãçµè«ãåãåãããããŒã«ãã¯ã©ãã·ã¥ãããããšã§çµäºããŸããã
ããã°ã©ã ã®ã·ã³ããªãã¯å®è¡ã®ããã®ããŒã«ã å°ãåã«ãEVMãµããŒãã远å ãããéçºãããŠããŸãïŒãããŠã æ°žç¶çãªéçºè
ãç»å Žããã°ãäºæ
ã¯ããã«éããªããŸãïŒã å€ãã®ããããæèŒãããŠããŸããããã¹ããããŠããããã»ãšãã©ã®å Žåæ©èœããŸããã ãããžã§ã¯ãã«ã¯ããªãæªãããã¥ã¡ã³ãããããŸãããã³ãã¥ããã£ããã®æ¬ ç¹ãããã«ä¿®æ£ããããšãé¡ã£ãŠããŸãã
Manticoreã®äœ¿çšäŸãèŠãåã«ãæã§ãã·ã³ããªãã¯å®è¡ãã説æãã䟡å€ããããŸãããŸããMichael Hicksã«ãããã¬ãŒã³ããŒã·ã§ã³ããã°ãèŠã€ããããã®ã·ã³ããªãã¯å®è¡ãã®ã¹ã©ã€ãã圹ç«ã¡ãŸãã
 |
---|
å³4.ãã°ãèŠã€ããããã®ã·ã³ããªãã¯å®è¡ã ãã€ã±ã«ã»ããã¯ã¹ |
ã·ã³ããªãã¯å®è¡ã䜿çšãããšãç¹å®ã®ãã€ã³ãã«é¢æ°/ããã°ã©ã ãäžããããã«å¿
èŠãªå
¥åãçè§£ã§ããŸãã 倧ãŸãã«èšãã°ãã·ã³ããªãã¯å®è¡äžã«ãç¹å®ã®ãã€ã³ããžã®å®è¡æ¹æ³ã«é¢ãããã¹ãŠã®å¶éïŒæ¡ä»¶ïŒãåéããããããã®å¶éããåŒãäœæãããå¿
èŠãªå
¥åãååŸããŸãã
ãã¢ã³ã¹ãã¬ãŒã·ã§ã³ã®ããã«ãPositive Hack Days 8ã§éå¬ãããEtherHackã³ã³ãã¹ãã®ãThe Lockãã¿ã¹ã¯ã解決ããŸããåå è
ã«ã¯ãœãŒã¹ã³ãŒãã¯æž¡ããããå¥çŽã®ã¢ãã¬ã¹ã®ã¿ãæž¡ãããŸããã ã¿ã¹ã¯ã解決ããã«ã¯ãæ£ããPINã³ãŒãã§å¥çŽãåŒã³åºãå¿
èŠããããŸããïŒãã®æ€èšŒã¯ãã©ãŒã«ããã¯ã§å®è£
ãããŸãïŒã ãœãªã¥ãŒã·ã§ã³ã«ã¯ããã³ã³ãŒãæ€èšŒã¢ã«ãŽãªãºã ãçè§£ããããã®ãªããŒã¹ãšã³ãžãã¢ãªã³ã°ãå«ãŸããŠããŸããã
ãã®åé¡ã®è§£æ±ºçãèªååããŸãããã ãã ããå¥çŽã®éã¢ã»ã³ãã«ãããã³ãŒããèŠãŠãåå è
ãæå¹ãªPINã³ãŒããéä¿¡ããå Žåã«äœãèµ·ããïŒãŸãã¯èµ·ãããªãïŒãã調ã¹ãå¿
èŠããããŸãã ã€ãŸããããã°ã©ã ã«ã¯sstoreåœä»€ã1ã€ãããªãããšã«æ³šæãã䟡å€ãããããã§ã¹ãã®ããŒãæ£ããéžæãããå Žåã«å®è¡ããããšå®å
šã«æ³å®ã§ããŸãã
æ®å¿µãªãããManticoreã®ãã°ã¯åé¡ã®æ£çŽãªè§£æ±ºçãæäŸããŸããïŒãã°ã¬ããŒããéçºè
ã«éä¿¡ããŸãããã§ããã°ããã«ãšã©ãŒãä¿®æ£ããããšãé¡ã£ãŠããŸãïŒãåºãŠè¡ããªããã°ãªããŸãããä¹ç®ïŒçŸåšã®Manticoreã®ãã°ã«ãããã·ã³ããªãã¯ããŒã¹ã§ã¹ãä¹ããšãã¥ã¬ãŒãã§ããŸããïŒã ãã¡ãããã³ã³ãã¹ãäžã«ãã®ãããªã²ããããæ¹æ³ã§ã¿ã¹ã¯ã解決ããããšã¯ç¡æå³ã§ãããããŒã«ããã¢ã³ã¹ãã¬ãŒã·ã§ã³ããã ãã§ããããšãå¿ããªãã§ãã ããïŒ ãŸãããœãŒã¹ã³ãŒããååšããŠããã·ã³ããªãã¯å®è¡ã¯ãã€ãã³ãŒãã§ã¯ãªãã·ã³ããªãã¯å®è¡ã§ã¯æ©èœãããããäœæ¥ã¯åçŽåãããŸããã
ååãªèšãèš³ãšä¿è·ããããŸãã®ã§ãã¹ã¯ãªãããèŠãŠã¿ãŸãããã
ããã¯ãœã«ã㌠from manticore.ethereum import ManticoreEVM from manticore.core.smtlib import Operators from struct import pack m = ManticoreEVM() m.verbosity(0) contract_source_code = ''' contract lock { uint public unlocked; function unlock(bytes4 cPincode) payable { uint digitPowers = 0; uint iPincode = 0; for (uint i = 0; i < 4; i++) { if (cPincode[i] >= 0x30 && cPincode[i] <= 0x39) { // manticore can't handle pow with symbolic input at the moment uint digit = (uint(cPincode[i]) - 0x30); digit *= digit; digit *= digit; digitPowers += digit; iPincode += (uint(cPincode[i]) - 0x30) * 10**(3-i); } else { revert(); } } if (uint(iPincode) == 0 || uint(iPincode) == 1) { revert(); } if (digitPowers == uint(iPincode)) { unlocked = 0x31337; } } } ''' user_account = m.create_account(balance=10**18) m.world.set_balance(user_account, 10**18) contract_account = m.solidity_create_contract(contract_source_code, owner=user_account) print "[+] Created a contract account 0x%x" % contract_account print '[+] Sending transaction with symbolic input' symbolic_data = m.make_symbolic_buffer(36) m.transaction(caller=user_account, address=contract_account, data=symbolic_data, value=10**18) pincodes = None for state in m.all_states: world = state.platform for where, what in world.get_storage_items(int(contract_account)): _input = Operators.CONCAT(8*4, *world.human_transactions[-1].data[4:8])
å®è¡ãããšã次ã®çµè«ãåŸãããŸãã
[+] Created a contract account 0x1bfa530d5d685155e98cd7d9dd23f7b6a801cfef [+] Sending transaction with symbolic input [+] Storage write 0x31337 [+] Found 3 pincodes: "1634" "8208" "9474"
ç§ãã¡ã¯äœãèµ·ãã£ãŠããã®ããæ±ããŸãã ãŸããå¥çŽã³ãŒããèŠãŠãã ããã ããã¯è§£é€é¢æ°ã¯ãå
¥åã«4ãã€ãã䜿çšããŸãããã®å
¥åã§ã¯ã4æ¡ã®10鲿°ã®ããã¹ã衚çŸã衚瀺ãããŸãã ãããã®ãã³ã³ãŒãã®ã¿ãé©åã§ããã4çªç®ã®æ¬¡æ°ã®æ°åã®åèšã¯ããã³ã³ãŒãèªäœãšçããã0ãš1ãé€ããŸãã 確ãã«
ãã³ã³ãŒãããããã®æ¡ä»¶ãæºãããŠããå Žåãã¹ãã¬ãŒãžã«ãã
unlocked
ãããŠã
unlocked
倿°ã«æ°å€0x31337ãæžã蟌ãŸããŸãã
ããã§ã¯ãã¹ã¯ãªãããçè§£ããŸãããã æåã®éšåã§äœãèµ·ãããã¯ç°¡åã«çè§£ã§ããŸãããŠãŒã¶ãŒã¢ã«ãŠã³ããäœæãããæ¬¡ã«å¥çŽãäœæãããŸãã ãã©ã³ã¶ã¯ã·ã§ã³ã¯ãã·ã³ããªãã¯å
¥åã§éå§ãããŸãã ãã®ã¢ã¯ã·ã§ã³ã䜿çšãããšãã·ã³ããªãã¯å®è¡ãã¢ã¯ãã£ãã«ãªããæ°åããããŸãã å®äºãããããã¹ãŠã®æçµç¶æ
ã調ã¹ãŠãäœããã¹ãã¬ãŒãžã«æžã蟌ãŸããŠãããã©ããã確èªããæžã蟌ãŸããŠããå Žåã¯ããã©ã³ã¶ã¯ã·ã§ã³ãè¡ãããå
¥åã調ã¹ãŸãã åŒã³åºããã颿°ã®çœ²åã®4ãã€ãåŸã«ããããããPINã³ãŒããæœåºããŸãã åé¡ã解決ããå¥çŽãäºæ³ããPINã³ãŒãã®æ¡æ°ãããããªãå Žåã4ãã€ãã§ã¯ãªã32ãã€ããã¹ãŠïŒEVMã®ã¡ã¢ãªã»ã«ã®ããã©ã«ããµã€ãºïŒãåŸãããŸãã æ¬¡ã«ãç»é¢äžã«åæ§ã®æ°åã衚瀺ãããŸãã
0x3136333434343434343434343434343434343434343434343434343434343434 0x3832303800000000000000000000000000000000000000000000000000000000
Manticoreã®åæçµæã䜿çšããŠãããã¡ãã®ããºã«ãè§£ãã ãã§ãªããããã¡ãå¥çŽã®ããã¡ãã®ãã°ãæ¢ãããšãã§ããŸãããå¥çŽãå«ããã¹ãŠã®ãããŒããã£ã¹ããäœæã«å±ãããã«ãå¥çŽã«äœãéä¿¡ããã°ããã§ããïŒã
EthCC 2018ã¯ãŒã¯ã·ã§ããã®å®å
šã§ãªããŠã©ã¬ããã®äŸãèŠãŠã¿ãŸãããïŒ
ä¿è·ãããŠããªãè²¡åž from manticore.ethereum import ManticoreEVM from manticore.core.smtlib import solver m = ManticoreEVM()
æããã«ãéçºè
ã¯changeOwnerã«å±æ§onlyOwnerãäžããããšãå¿ããŠããŸããã ãã®ããã2ã€ã®ãã©ã³ã¶ã¯ã·ã§ã³ã®æ»æè
ãè³éãåŸãããšãã§ããŸãã ãã€ãã®ããã«ãæãè峿·±ãã®ã¯ã¹ã¯ãªããã®æåŸã§ãã ç¶æ
ãåæããŠããæ»æè
ã¯ã©ãã«ã³ã€ã³ãæã£ãŠããŸããïŒããšãã質åãããŸãã ãããŠããã³ãã£ã³ã¢ã¯åœŒã«çããããšãã§ããŸãã WalletHack_00000000.txãã¡ã€ã«ã®mcore_XXXãã©ã«ããŒã§åçãæ¢ããŸãã
 |
---|
å³5. Manticoreã®äœæ¥ã®çµæã |
圌ããManticoreãæŸæ£ããªããã°ãã¹ããŒãã³ã³ãã©ã¯ãã®æè»ãªåæã®ããã®åªããããŒã«ã«æé·ããŸãããçŸå®ã®äžçãšå®éã®ã¿ã¹ã¯ã«çŽé¢ããæºåã¯ãŸã æŽã£ãŠããŸããã
ãšãããã¯ãã¹ããŒãã³ã³ãã©ã¯ãããã¡ãžã³ã°ããããã«èšèšãããå®éšçãªããŒã«ã§ãã ããããããã¯ããªããèããããšãã§ãããã¡ãºã§ã¯ãããŸããã Echidnaãã¹ããŒãã³ã³ãã©ã¯ãã®æ©èœããã¡ãžã³ã°ããã«ã¯ãç¹å¥ãªæ¹æ³ã§ã³ã³ãã©ã¯ããæºåããå¿
èŠããããŸãã
// contract EchidnaTest { function sensitiveFunc(uint num, bool stop) { if (num > 5 && !stop) { selfdestruct(msg.sender); } } }
åŸ contract EchidnaTest { uint num; bool stop; function num_setter(uint _num) public { num = _num; } function stop_setter(bool _stop) public { stop = _stop; } function echidna_sensitiveFunc() public returns (bool) { if (num > 5 && stop) { selfdestruct(msg.sender); } return true; } }
ã€ãŸãããããã¡ã€ãªã³ã°ããæ©èœããããè§£é€ãããå¿
èŠããããŸãã Echidnaã¯ã num_setter
ããã³stop_setter
ãåŒã³åºããŠããnum_setter
ãåŒã³åºããŸãã ãŸããEchidnaã¯ãã¹ãŠãé 調ã«é²ãã ãšãã«é¢æ°ãtrue
ãè¿ãããšãæåŸ
ããŠããããšã«æ³šæããŠãã ããïŒãã ããæç€ºçã«ã¯äŒããŸããïŒã ãããã£ãŠã false
ã revert()
ã selfdestruct(0)
ãªã©ãselfdestruct(0)
revert()
ã«ããããééã£ããåäœãããŒã¯ã§ããŸãã
p4lex@ubuntu:~/tools/echidna$ echidna-test echid.sol âââ echid.sol âââ â "echidna_sensitiveFunc" failed after 23 tests and 51 shrinks. â Call sequence: num_setter(6); â stop_setter(false); â 1 failed.
ãŸãããšãããã倿Žããç¹å®ã®å
¥åããŒã¿ãèšå®ã§ããªãããšã«ã泚æããŠãã ããããããã£ãŠãè€éãªã¿ã¹ã¯ã®å Žåããã®ãããªãã¡ãžã³ã°ã®æå¹æ§ã«ã¯å€ãã®ããšãæãŸããŸãã ãããããããã¡ãžã³ã°ããå¿
èŠãããå颿°ãæžãçŽããªããã°ãªããªããšããäºå®ã¯ãããããæ°ã¥ããã§ãããã å
¬å¹³ã«èšãã°ãHaskellã§èšè¿°ã§ããå Žåã§ããå¯èœãªå€ã®ã»ããããç¹å®ã®ãã¡ã€ã³ãæäŸããããã«å
¥åå€ãžã§ãã¬ãŒã¿ãŒãèšå®ã§ããããšã«æ³šæããŠãã ããïŒããšãã°ã num
ã¯5以äžã§ãªããã°ãªããªãããšãç¥ã£ãŠããŸãïŒã
äžè¬ã«ããã¡ãžã³ã°ã¯å®ãçµã¶ããšãã§ããŸãã è匱æ§ãé·ãã¢ã¯ã·ã§ã³ãã§ãŒã³ã®åŸã«ã®ã¿çŸããå Žåãã·ã³ããªãã¯å®è¡ã«ã¯ãã¡ãžã³ã°ãšã¯å¯Ÿç
§çã«CPUãšã¡ã¢ãªã®å€§ããªæ¶è²»ãå¿
èŠã«ãªããŸãïŒãã ããããçšåºŠã®ç¢ºçã§ã®ã¿ãã®è匱æ§ãæ€åºããŸãïŒã ãšãããã§äœ¿çšãããŠããã¢ãããŒãã®è©³çްã«ã€ããŠã¯ã ãã¡ããã芧ãã ãã ã
åå
¥å¯èœæ§ã¯ã©ãã«ã§ãããããã«æãããéåžžã«å®éšçãªã¯ããŒãºããœãŒã¹ã®ããŒã«ã§ãã æ°æãè¯ãããïŒå¥çŽãéä¿¡ãã©ãŒã ã«è²Œãä»ããããgithubãžã®ãªã³ã¯ãæå®ã§ããŸãã ãŸããéçã³ãŒãåæã䜿çšããããããå¥çŽãã³ã³ãã€ã«ããå¿
èŠãããããŸããã æ¬¡ã®ããã«ãªããŸãã
 |
---|
å³6. SmartCheckã€ã³ã¿ãŒãã§ãŒã¹ |
ãã®äŸã§ã¯ãEther転éåŸã«æ®é«ããªã»ãããããŸããããã¯SmartCheckã®çœ²åã§ãã ãã ãã send
æ©èœãštransfer
æ©èœã®ã¿ã䜿çšãããšåå
¥å¯èœæ§ã鲿¢ãããããïŒ ç¬¬3éšã§ããã«ã€ããŠèª¬æããŸããïŒãäžã®ã¹ã¯ãªãŒã³ã·ã§ããã§ã¯èª€æ€åºãèŠãããŸãã
ãªããŒã¹ãšã³ãžãã¢ãªã³ã°
çŸåšããªãŒãã³ã¢ã¯ã»ã¹ã«ã¯ãªãŒãã³ãã³ã³ãã€ã©ãååšããªãããããã®ããžãã¯ãŸãã¯ãã®ã³ã³ãã©ã¯ããå®è£
ããããžãã¯ãçè§£ããããã«ãéã³ã³ãã€ã©ã䜿çšããå¿
èŠããããŸãã äžè¬ã«ãéã³ã³ãã€ã©ãååšããå¯èœæ§ããããŸãã æåã®è©Šã¿ã¯Porosityã§è¡ãããŸãããããããã解決çã¯æ©èœããŸãããæ®å¿µãªããšã«ãRET2ã®äººãã¡ã¯éãããã®ãæã£ãŠããŸãã ãããŸã§ã¯ããã€ãã³ãŒãã ããæ®ããŠãã§ããããšãèŠãŠã¿ãŸãããã
æãå€ããæã䟿å©ãªéã¢ã»ã³ãã©ãŒã çŸããã€ã³ã¿ãŒãã§ãŒã¹ãèªæ
¢ããããšã¯ã§ããŸããããä»ã®ããŒã«ã«ã¯ãªããããããããŸãïŒãŸããã·ãŒã±ã³ã¹ã®ä»£ããã«åœä»€ãæãããã¿ããã1ã€ã®èªã¿ãããåŒã衚瀺ããæ¬¡ã«åããŒã¹ãŠãããã«ã€ããŠEVMdisãå
¥åã§ã¹ã¿ãã¯ã衚瀺ããŸãèšå·åããã倿°ãå«ããããã¯ã«ã EVMã¯ã¹ã¿ãã¯ããããã·ã³ã§ããããããã®ããŒã¿ããªããã°ã¹ã¿ãã¯ã颿°ã®æåãã埩å
ããå¿
èŠããããããããã«ããã³ãŒãã®ããžãã¯ãçè§£ããããã»ã¹ã倧å¹
ã«é«éåãããŸãã
äŸïŒ
# Stack: [] 0x33 PUSH(0xFFFFFFFF & CALLDATALOAD(0x0) / 0x100000000000000000000000000000000000000000000000000000000) 0x34 DUP1 0x3E JUMPI(:label5, 0x2F54BF6E == POP()) ... :label36 # Stack: [@0x420 @0x408 0x0 @0x2A1 @0x282 :label22 @0x33] 0x43F POP() 0x440 POP() 0x443 PUSH(MLOAD(0x40)) ...
0x33ã§ã¯ãå€ãcalldataããè¿ãããã¹ã¿ãã¯ã«ããã·ã¥ãããŸãã ãã®å€ã¯äžæã§ãããããèšå·åãããŠããŸãã ããã«ãä»ã®ã·ã³ããªãã¯ããã³ç¹å®ã®å€ïŒçªå·ããã³é·ç§»ã¢ãã¬ã¹ïŒã®äžã§ãããŒã¹ãããã¯ã©ãã«36ã®ã¹ã¿ãã¯ã§ããã芳å¯ã§ããŸãã
ç§ãã¡ã®æèŠã§ã¯ãããã¯å¥çŽã®éå±éã®ããã®æã䟿å©ãªããŒã«ã§ãã
EVMã¢ãŒããã¯ãã£ã®ãµããŒãã远å ãããã€ããªãã³ãžã£ãã©ã°ã€ã³ã EVMã¯ã¹ã¿ãã¯ããããã·ã³ã§ãããããå¿è
ã®äžé衚çŸïŒLLILããã³MLILïŒã¯æ©èœããŸããããããã£ãŠããã¢ã¢ã»ã³ãã©ãŒã®æç€ºãèªãå¿
èŠããããŸãã æ°æãè¯ãããïŒãã©ã°ã€ã³ã«ã¯{ : }
ãã¢ãæã€èŸæžããããããããã«å€ãã®æåãªé¢æ°ãå®çŸ©ã§ããŸãïŒããšãã°ãERCæšæºã®ç°ãªãã€ã³ã¿ãŒãã§ãŒã¹ãå®è£
ããïŒã
 |
---|
å³7. Ethersplayãã©ã°ã€ã³ã䜿çšãããã€ããªNinjiaã€ã³ã¿ãŒãã§ãŒã¹ |
ãã©ã°ã€ã³ã«ã¯ãååœä»€ã®ã¹ã¿ãã¯ã®ç¶æ
ãç€ºãæ©èœããããŸãã
 |
---|
å³8. Ethersplayã§è¡šç€ºãããã¹ã¿ãã¯ã¹ããŒã¿ã¹ |
ãã ãããã¹ãŠã®ã·ã³ããªãã¯å€æ°ãUnknown
ãšããåèªã«çœ®ãæããŸãããEVMdisã¯ä»£ããã«ããã®ã·ã³ããªãã¯å€æ°ãã¹ã¿ãã¯äžã§åºçŸããã¢ãã¬ã¹ãæžã蟌ã¿ãŸãã
Ethersplayã¯ç§ãã¡ã®å
µåšåº«ã«å®çããŠããŸããããããããæçšãªããŒã«ã«ãªã段éãŸã§éçºãããã§ãããã
IDA Proãã³ã³ãã©ã¯ããåè§£ã§ããããã«ããããã»ããµã¢ãžã¥ãŒã«ã ãŸããEthersplayã®ãããªé¢æ°ãããã¿ã€ãã®èŸæžããããŸãã
 |
---|
å³9. IDA-EVMããã»ããµã¢ãžã¥ãŒã«ãåããIDAã€ã³ã¿ãŒãã§ã€ã¹ |
ãã ããããã¯ãããã°ã©ã å
šäœã®ã¹ã¿ãã¯ã®ç¶æ
ãæåã§è§£æ±ºããå¿
èŠããããããã³ã³ãã©ã¯ãã®äŸ¿å©ãªãªããŒã¹éçºã«ã¯ååã§ã¯ãããŸããã ç§ãã¡ã®æèŠã§ã¯ããã®ããŒã«ã¯éçºãåããããšã¯ã»ãšãã©ãªããçŸåšã®åœ¢ã§ã¯åœ¹ã«ç«ããªãããããã®ããŒã«ã¯æ³šç®ã«å€ããŸããã
ãã¹ãäž
ãã®ã»ã¯ã·ã§ã³ã§ã¯ãå®éã«ããªãã®å¥çŽããå®å
šãªå¥çŽãããåŸåããããã®ãããªãã¡ãã¹ãã«ã€ããŠã話ããããšæããŸãã å®è·µã瀺ãããã«ããã¹ãã§å°ãªããšã95ïŒ
ãã«ããŒãããã¹ããŒãã³ã³ãã©ã¯ãã§ã¯ãæ·±å»ãªè匱æ§ã®å¯èœæ§ã¯ãããã§ãã ãã¹ããäœæããããã»ã¹ã§ã¯ãå€ãã®å Žåãå¿
èŠãªãã§ãã¯ãå€ã远å ãããŸãããå¹³æ°ã§ã¯ãããŸãããçµéšè±å¯ãªã»ãã¥ãªãã£ç£æ»å¡ãŸãã¯éçºè
ã®ã¿ãèªãããšãã§ããŸãã ããšãã°ãå¥çŽãå®è£
ãããµããžã§ã¯ããšãªã¢ã®0dayè匱æ§ããŸãã¯ãã®ä»ã®è«çãšã©ãŒãèŠã€ããããç¹å®ã®å¥çŽã¡ã«ããºã ïŒDAppïŒãããå¹ççã«ããæ¹æ³ãææ¡ããŸãã
Truffleã䜿çšããŠããã®å Žã§ããã¹ããå®è¡ããã®ãæã䟿å©ã§ãïŒã»ãšãã©ã®ã¹ããŒãã³ã³ãã©ã¯ããéçºãããŠããŸãïŒã ãã¹ããã©ã«ããŒïŒ truffle init
åŸã«è¡šç€ºããtruffle init
ïŒã§ããã¹ããäœæãã truffle test
ã³ãã³ãã䜿çšããŠå®è¡ã§ããŸãã ãã ããå
¬åŒããã¥ã¡ã³ããããããªã¥ãã䜿çšãããã¹ãã«ã€ããŠè©³ãã説æã§ãã人ã¯ã»ãšãã©ããŸããã ãŸãã ãœãªããã«ãã¬ããžããŒã«ã䜿çšãããã¹ãã§ã«ãã¬ããžã枬å®ããããšãå¿ããªãã§ãã ããã
äœããã®çç±ïŒå€ãã®å ŽåïŒã§Truffleãããªãã«åããªãå Žåãweb3ã©ã€ãã©ãªã䜿çšããŠãã¹ããæžãããšãã§ããŸãïŒãããã¯ç°ãªãèšèªã§å©çšå¯èœã§ãïŒã èšè¿°ããããã¹ãã³ãŒãã¯ãããžãã¯ã®äžéšãšããŠå°æ¥ã®DAppã«ç°¡åã«è»¢éã§ãããããweb3.jsã䜿çšããã®ãæã䟿å©ã§ãã å¯äžã®æ³šæç¹ã¯ãweb3.jsããŒãžã§ã³^ 1.0.0ã䜿çšããããšã§ãã æªæ¥ã¯åœŒå¥³ã®ãã®ã§ãããGoogleã®æåã®è¡ãšãªãããŒãžã§ã³0.14.0ã§ã¯äºææ§ããããŸããã äŸãèããŠã¿ãŸãããïŒ
ãœãŒã¹ã³ãŒã pragma solidity ^0.4.23; contract Testable { address public human; uint private counter; constructor() { human = tx.origin; } event CallmeLog(); function callme(uint times) public { counter++; if (times > 1) { callme(--times); } else { emit CallmeLog(); } } }
ãã¹ãèªäœã«é²ãåã«ããããã¯ãŒã¯ã«æ°ããã³ã³ãã©ã¯ããã¢ããããŒããã颿°ãäœæããå¿
èŠããããŸããããã¯äžåºŠã ãè¡ãå¿
èŠãããããã§ã:)
ãã¹ã const solc = require('solc'); const fs = require('fs'); const Web3 = require('web3'); const web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545')); const code = fs.readFileSync('./testable.sol'); async function main() { // when use unlocked account from node let defaultAccs = await web3.eth.getAccounts(); web3.eth.defaultAccount = defaultAccs[0]; // this is for known private key // web3.eth.accounts.wallet.add(privKey.key); let Testable = await deploy(); human_test(Testable); callme_tx_status_test(Testable, 3); callme_event_test(Testable, 3); // let's got new instance Testable = await deploy(); callme_storage_test(Testable, 5); } async function deploy() { let output = solc.compile(code.toString(), 1); let TestableABI = output.contracts[Object.keys(output.contracts)[0]].interface; let TestableBytecode = output.contracts[Object.keys(output.contracts)[0]].bytecode; let Testable = new web3.eth.Contract(JSON.parse(TestableABI),{ from: web3.eth.defaultAccount }); let gasCount = await Testable.deploy({ data: TestableBytecode }).estimateGas(); Testable = await Testable.deploy({ data: TestableBytecode }).send({ gas: gasCount }); return Testable; } async function human_test(instance) { // let's test that contract has owner (human) // use "call" for public variables and constant methods let humanAddress = await instance.methods.human().call(); if (humanAddress !== web3.eth.defaultAccount) { throw "â human address is wrong" } else { console.log(" human_test passed"); } } ...
, ( ) . web3_utilz/Testing .
, , , , , , CTF.
(reverse engineering), , . , , - ( cryptoKittes ). , reverse engineering. , , , .
, , p4lex Igor1024 .