Burner Exploit Analysis
💰

Burner Exploit Analysis

Published
May 30, 2024
Author
daleewong

Intro

time 2024-05-21
 

Detail

flashloan 70000000000000000000 eth
notion image
把eth 全部换成PNT Token
notion image
换完之后 hacker contract PNT Token balance 1868727451213386974206315
line 371 是还钱,这个闪电贷真好,不用手续费
问题肯定就出在这几行里
notion image
在调用了convertAndBurn之后, hacker contract PNT Token balance 没变,之后用同样的token去换eth
notion image
换出来的eth平白增多了,变成了71726285094628080033,多了1.7个eth
有必要dig into convertAndBurn里面看看
notion image

漏洞点

三明治攻击
这是一次三明治攻击,通俗的说就是低买高卖。中间夹着的那个交易就是让价格升高的交易。
举个例子,你用100块买10斤苹果,也就是一斤10元。
你还没走呢,卖家就说:卖回给我,我可以给你15一斤(正常的情况是由于交易费用,卖家只会以小于10元一斤来回收)。
你做地卖给他,赚钱走人。至于卖家为什么抽风,就具体情况具体分析了,来看代码
notion image
function burn() public { require(!paused, 'cannot burn when paused'); uint total = token.balanceOf(address(this)); uint toBurn = total.mul(percentageToBurn).div(100); token.burn(toBurn, ''); uint notBurned = token.balanceOf(address(this)); require(token.transfer(unburnedDestination, notBurned), 'cannot transfer unburned tokens'); emit Burn(toBurn, notBurned); } function convertAndBurn(address [] calldata tokens) external { for (uint i = 0; i < tokens.length; i++) { _convert(tokens[i]); } burn(); } function _convert(address srcToken) internal { uint srcAmount; uint converted; if (srcToken == ETHER || srcToken == address(0)) { srcAmount = address(this).balance; converted = kyberNetwork.trade .value(srcAmount)(ETHER, srcAmount, address(token), address(uint160(address(this))), BIG_LIMIT, 1, kyberFeeWallet); } else { srcAmount = IERC20(srcToken).balanceOf(address(this)); if (IERC20(srcToken).allowance(address(this), address(kyberNetwork)) > 0) { IERC20(srcToken).safeApprove(address(kyberNetwork), 0); } IERC20(srcToken).safeApprove(address(kyberNetwork), srcAmount); converted = kyberNetwork.trade(srcToken, srcAmount, address(token), address(uint160(address(this))), BIG_LIMIT, 1, kyberFeeWallet); } emit TokenTrade(srcToken, srcAmount, converted); }
第一个地址是address(0),所以会走到_convert函数的这行。问题就出在这行。
这行的作用是:把eth 换成 PNT Token
kyper network trade func // @notice use token address ETH_TOKEN_ADDRESS for ether /// @dev makes a trade between src and dest token and send dest token to destAddress /// @param src Src token /// @param srcAmount amount of src tokens /// @param dest Destination token /// @param destAddress Address to send tokens to /// @param maxDestAmount A limit on the amount of dest tokens /// @param minConversionRate The minimal conversion rate. If actual rate is lower, trade is canceled. /// @param walletId is the wallet ID to send part of the fees /// @return amount of actual dest tokens function trade( ERC20 src, uint srcAmount, ERC20 dest, address destAddress, uint maxDestAmount, uint minConversionRate, address walletId )
converted = kyberNetwork.trade .value(srcAmount)(ETHER, srcAmount, address(token), address(uint160(address(this))), BIG_LIMIT, 1, kyberFeeWallet);
我们继续跟踪它是怎么swap的。因为它代码太多了,就不追代码了,还是看debug吧。
notion image
它七拐八拐,最后还是到了Uniswap V2: Router 2这了。kyper 的trade不是自己实现,它只是个dex的搬运工。它是个其他交易所的聚合。
本质就是这个函数通过Uniswap V2 swap了一些eth 到 PNT Token。继而抬高了PNT Token的价格。
 

End

这个防不胜防,估计开发者没有意识到kyper交易的本质。凡是涉及到swap的,一定要小心,弄懂每个swap背后的原理。