合约攻防比赛--- tctf 《NFT Market》

凌晨12点半左右,咖啡突然让我帮忙打个合约安全比赛。我想着先看看题,然后,不小心就通宵了~~~~~

背景

这道题的难度设计非常有意思,最难的地方利用了0.8.15以下的编译器漏洞。注意:0.8.15以下的编译器都会有这种漏洞。原因:0.8.15在动态内存管理的时候,由于太过着急清除内存,导致了数据丢失,在特定的情况下可以被黑客利用,从而绕开检验,从而进行攻击。由于攻击条件太过苛刻,所以一直没有被黑客利用。但,我们在设计calldata结构的时候,还是要知道有这个的存在~~

题目分析

题目非常简单:

存在一个NFT Market合约,在初始化的时候会产出3个NFT,分别是NFT 1,NFT 2,和NFT 3。黑客需要攻击NFT Market,从而窃取这三个NFT。这三个NFT都分别上架道NFT Market上,价格为:1, 1337, 133333337。

这个时候,NFT Order为:[ #1, #2, #3]

攻击 NFT 1

NFT 1是最容易获取的,我们先触发一下airdrop,拿到5个TcffToken,然后用1个Token来买NFT 1就可以了。

此时,

黑客余额:4 Token

NFT Order为:[ #3, #2]

攻击 NFT 2

Tips:

NFT Market有余额:1337

用户拥有的NFT: NFT1

窃取NFT 2需要用到 purchase Test函数(如下),注意到NFT 2和 NFT 3的owner是NFT Market合约本身。所以,这里刚好利用purchase Test函数,让黑客创建一个NFT 1价值1337的order,然后让NFT Market通过purchase Test函数利用1337余额去买用户手里的NFT 1。这样,用户就可以获得 1337 + 4的余额。

然后,就有足够的金额去购买NFT 2。(价值1337,用户的余额是1337 + 4 )。这里不着急把NFT 1买回,因为攻击3的时候会用到。但为了让用户可以把NFT 1买回,这里需提前插一个NFT 1的订单(因为此时NFT 1还是属于黑客)。

攻击步骤:

此时,

NFT Order为:[ #1, #3]

攻击 NFT 3

攻击NFT 3 非常困难,需要利用到0.8.15以下的编译器漏洞。这个漏洞的详细报告在:

总结一下就是:

这个错误源于,编译器在ABI编码期间将calldata数组复制到内存中时过度急于清理的结果。内存中的数组总是占用32字节的倍数,当基本类型没有填满整个字时,未使用的空间保证被清零,并在所有高级Solidity操作后保持清洁。在受影响的情况下,编译器会发出代码,错误地清理存储在元组最后一个组件中的数组的末端,将属于元组第一个动态组件的32字节清零。

我们要利用这个漏洞,绕过verifiCoupon的合约验签,从而黑客产生一个可以修改价格的Coupon。(当时我和薛神在纠结如何通过合约地址,暴力破解反推出私钥。看来思路是错的,应该是攻击calldata从而绕过签名验证

攻击步骤:

a、创建一个FakeNFT ,并且插入到NFT Market之中。此时,NFT OrderList:

NFT Order为:[ #1, #3, #fakeNFT]

然后我们把#1用1 Token买回,此时:

NFT Order为:[#fakeNFT,#3]

b、call data攻击,攻击的步骤很简单,就是利用上面说的编译器漏洞,然后coupon的orderId传1,但是orderInfo传0。由于编译器漏洞,在verify的时候,orderId1,会以order 0进行验证。

然后 NFT 3的价格就可以被我们修改成1 Token了。我们买回NFT 3即可。

源码

Subscribe to shaneson.eth
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.