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


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舊版**

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 蜜獾

蜜罐合約分析

---
## 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


### 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