RSS Feed
更好更安全的互联网

LCTF2018 ggbank 薅羊毛实战

2018-11-21
作者:LoRexxar'@知道创宇404区块链安全研究团队
时间:2018年11月20日
11.18号结束的LCTF2018中有一个很有趣的智能合约题目叫做ggbank,题目的原意是考察弱随机数问题,但在题目的设定上挺有意思的,加入了一个对地址的验证,导致弱随机的难度高了许多,反倒是薅羊毛更快乐了,下面就借这个题聊聊关于薅羊毛的实战操作。

分析

源代码
https://ropsten.etherscan.io/address/0x7caa18d765e5b4c3bf0831137923841fe3e7258a#code

首先我们照例来分析一下源代码

和之前我出的题风格一致,首先是发行了一种token,然后基于token的挑战代码,主要有几个点

跟着看checkfriend函数

checkfriend就是整个挑战最大的难点,也大幅度影响了思考的方向,这个稍后再谈。

空投函数没看有什么太可说的,就是对每一个新用户都发一次空投。

然后就是goodluck函数

然后只要余额大于200000就可以拿到flag。

其实代码特别简单,漏洞也不难,就是非常常见的弱随机数问题。

随机数的生成方式为

另一个的生成方式为

其实非常简单,这两个数字都是已知的,msg.sender可以直接控制已知的地址,那么左值就是已知的,剩下的就是要等待一个右值出现,由于block.number是自增的,我们可以通过提前计算出一个block.number,然后写脚本监控这个值出现,提前开始发起交易抢打包,就ok了。具体我就不详细提了。可以看看出题人的wp。

https://github.com/LCTF/LCTF2018/tree/master/Writeup/gg%20bank

但问题就在于,这种操作要等block.number出现,而且还要抢打包,毕竟还是不稳定的。所以在做题的时候我们关注到另一条路,薅羊毛,这里重点说说这个。

合约薅羊毛

在想到原来的思路过于复杂之后,我就顺理成章的想到薅羊毛这条路,然后第一反正就是直接通过合约建合约的方式来碰这个概率。

思路来自于最早发现的薅羊毛合约https://paper.seebug.org/646/

这个合约有几个很精巧的点。

首先我们需要有基本的概念,在以太坊上发起交易是需要支付gas的,如果我们不通过合约来交易,那么这笔gas就必须先转账过去eth,然后再发起交易,整个过程困难了好几倍不止。

然后就有了新的问题,在合约中新建合约在EVM中,是属于高消费的操作之一,在以太坊中,每一次交易都会打包进一个区块中,而每一个区块都有gas消费的上限,如果超过了上限,就会爆gas out,然后交易回滚,交易就失败了。

上述的poc中,有一个很特别的点就是我加入了checkfriend的判断,因为我发现循环中如果新建合约的函数调用revert会导致整个交易报错,所以我干脆把整个判断放上来,在判断后再发起交易。

可问题来了,我尝试跑了几波之后发现完全不行,我忽略了一个问题。

让我们回到checkfriend

checkfriend只接受地址中带有7d7ec的地址交易,光是这几个字母出现的概率就只有1/36*1/36*1/36*1/36*1/36这个几率在每次随机生成50个合约上计算的话,概率就太小了。

必须要找新的办法来解决才行。

python脚本解决方案

既然在合约上没办法,那么我直接换用python写脚本来解决。

这个挑战最大的问题就在于checkfriend这里,那么我们直接换一种思路,如果我们去爆破私钥去恢复地址,是不是更有效一点儿?

其实爆破的方式非常多,但有的恢复特别慢,也不知道瓶颈在哪,在换了几种方式之后呢,我终于找到了一个特别快的恢复方式。

我们拿到了地址之后就简单了,首先先转0.01eth给它,然后用私钥发起交易,获得空投、转账回来。

需要注意的是,转账之后需要先等到转账这个交易打包成功,之后才能继续下一步交易,需要多设置一步等待。

有个更快的方案是,先跑出200个地址,然后再批量转账,最后直接跑起来,不过想了一下感觉其实差不太多,因为整个脚本跑下来也就不到半小时,速度还是很可观的。

脚本如下

最终效果显著


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/747/

作者:Nanako | Categories:安全研究技术分享 | Tags:

发表评论