---
# System prepended metadata

title: AIZPT攻击事件分析
tags: [Security, Smart Contract]

---

## 0x00：背景介绍

昨天，从DeFiHackLabs的[漏洞仓库](https://github.com/SunWeb3Sec/DeFiHackLabs/blob/014e23d0ebc9c8563e772d27672f05ed2063b36f/src/test/2024-10/AIZPTToken_exp.sol#L20)中，了解到BNB Smart Chain上的ERC314标准代币AIZPT的攻击事件。

攻击Tx：https://bscscan.com/tx/0x5e694707337cca979d18f9e45f40e81d6ca341ed342f1377f563e779a746460d

目前找不到关于AIZPT代币的相关背景，猜测是一个土狗项目。

<br>

## 0x01：攻击流程

![image-20241007100651497](https://hackmd.io/_uploads/SkkC_MFi1x.png)
<br>
首先，攻击者通过PancakeSwap利用闪电贷服务借了8000 WBNB，然后兑换成8000 BNB。
![image-20241007094321454](https://hackmd.io/_uploads/B18GFGKi1e.png)

<br>

随后，攻击者用8000 BNB调用AIZPT代币合约的`buy`函数去swap，攻击者得到805,227,658个AIZPT代币。（`buy`函数是通过`receive()`回调函数内部进行调用的）
![image-20241007094953854](https://hackmd.io/_uploads/Sk8QtfKsyl.png)

<br>

接着，攻击者大量调用`sell`函数(由transfer内部调用)，每次调用将一部分的AIZPT代币swap回BNB。
![image-20241007095917533](https://hackmd.io/_uploads/H1HNKfYjJx.png)

调用`sell`函数swap成的BNB数量，会逐渐减少：`2431 -> 1473 -> 960 -> 660 ……`

但攻击者总共得到了8039个BNB，偿还8004.1个给闪电贷后，获利34.9个BNB。

<br>

## 0x02：漏洞分析

在上述过程中，攻击者用8000 BNB购买成AIZPT代币，再将AIZPT代币出售回BNB，最终多了39个BNB。这其中到底发生了什么？让我们深入合约代码的`buy`和`sell`函数去看看

#### buy函数：

```solidity
function buy() internal {
    require(tradingEnable, 'Trading not enable');
	// 用户发送的BNB数量
    uint256 swapValue = msg.value;
	// 根据恒定乘积公式，计算用户得到的AIZPT代币数量
    uint256 token_amount = (swapValue * _balances[address(this)]) / (address(this).balance);

    require(token_amount > 0, 'Buy amount too low');
	// 用户获得1/2的AIZPT代币
    uint256 user_amount = token_amount * 50 / 100;
    uint256 fee_amount = token_amount - user_amount;
	
    _transfer(address(this), msg.sender, user_amount);
    // 另外的1/2则是发送到feeReceiver, 应该是项目方的地址
    _transfer(address(this), feeReceiver, fee_amount);

    emit Swap(msg.sender, swapValue, 0, 0, user_amount);
}
```

代码逻辑很简单，根据恒定乘积公式进行swap，然后转移代币。1/2的代币发送到项目方地址，这应该是项目方的盈利手段。


#### sell函数：

```solidity
function sell(uint256 sell_amount) internal {
    require(tradingEnable, 'Trading not enable');
	// 根据恒定乘积公式，计算用户得到的BNB数量
    uint256 ethAmount = (sell_amount * address(this).balance) / (_balances[address(this)] + sell_amount);

    require(ethAmount > 0, 'Sell amount too low');
    require(address(this).balance >= ethAmount, 'Insufficient ETH in reserves');
	// 将输入的AIZPT代币分成两份
    uint256 swap_amount = sell_amount * 50 / 100;
    uint256 burn_amount = sell_amount - swap_amount;
	// 一份转移回合约
    _transfer(msg.sender, address(this), swap_amount);
    // 一份进行销毁
    _transfer(msg.sender, address(0), burn_amount);
	// 发送BNB给用户
    payable(msg.sender).transfer(ethAmount);

    emit Swap(msg.sender, 0, sell_amount, ethAmount, 0);
}
```

在sell函数中，会把1/2的AIZPT代币销毁，但是发送给用户的BNB数量却是以所有的AIZPT代币数量进行计算的。这就是攻击发送的根本原因，AIZPT数量随着调用变少，恒定乘积公式将不再恒定。以至于后续用AIZPT兑换成的BNB将比预期更多，直至把池子掏空。

<br>

## 0x03：总结

该攻击事件的根本原因是：AIZPT兑换BNB时，恒定乘积公式出现问题，导致可以获得更多的BNB。在对攻击事件分析时，当站在攻击者的角度，会发现更多的攻击细节和疑惑点：

1. 为什么是8000 BNB？是通过计算得来的吗？如何得到最优解呢？
2. 每次调用sell，为什么是发送3000k个AIZPT代币？是否是考虑迭代次数的问题，以至于可以在一次交易中执行完成？







