本部分由长亭科技区块链安全负责人于晓航撰写
Writeup 1: Monica’s Bank
0x00 概况
这题是几种Solidity常见经典漏洞的组合,如果对Solidity熟悉的话会相对简单。
0x01 思路
在transferBalance函数里,由于整数溢出require并没啥用,只需要随意transfer任意小余额到其他地址就可以获得大量balance。
然后可以buy 1个credit,这里的随机数是完全可预测的,只需要把随机数那段代码复制到攻击合约里就好了。
最后一步是重入攻击,跟hint里说的一样,这个类似DAO的加强版攻击。大概意思是只需要重入1次就好,而不是多次重入,这样可以触发creditOf[] -= amount的整数溢出。
在DAO攻击中,目标函数和攻击合约的fallback都被调用了很多次,直到gas耗尽,调用链底层抛出异常,由于.call.value特点是返回false而非继续向上抛出异常,所以这整个交易不会被revert,然后.call.value之后的代码们只会被执行1次。
在‘加强版’攻击里,由于我们主动结束第二次fallback,.call.value之后的代码会被执行2次,从而触发整数下溢,达成目标。
0x02 解密Flag
触发SendFlag后,后台脚本会把加密flag数据放在交易里发送给攻击者的地址,即tx.origin。
数据是用tx.origin的公钥加密的,因此可以用私钥,和提供的解密脚本来解密。
0x03 POC
0x04 彩蛋
Writeup 2: Monica’s Casino
0x00 概况
这题属于中等偏难,是个EVM JOP类型蜜罐合约题目。需要逆向合约opcode,找一些gadgets来准备好栈以及操作控制流,利用后门。
0x01 JOP思路
Constructor里有jump指令,跳到参数a,通过分析合约创建交易的input data可以知道是0x9b。
在remix里调试一下可以看到0x9b指向的是参数b的开头,刚好这里有一个jumpdest字节码所以程序可以合法跳到这里,执行后面的指令。b开头这段指令做的事很简单:将b的后半段字节copy到memory上,然后直接return这些字节。所以这一步其实就是替换掉了合约在链上的runtime code,所以runtime code其实跟源码看到的不一样。
严格来说现在runtimecode可以是任意内容,这样的话会需要很多比较无聊的逆向工作,但这里可以尝试的一个技巧是,把源码里constructor的jump一行注释掉,然后编译得到原本的runtime code,然后比较这两个版本。会发现这里我在‘有效字节码部分’只patch了1个字节。
如上,第一处diff是1字节,第二处是metadata,这个理论上来说对不同源码是应该不一样的(但这里是题目有意设置的不同)。
第一个字节把push32(0x7f)改成了dup3(0x82),实际上把后面的字节从‘数据段’解放为了‘代码段’。然后其实后续的这些字节就是源码中salt的值。
于是这部分字节可以被解释为这段opcode:
这里可以看到有了第一个任意跳转,也就是后门的入口,然后还有挺多事要做。通过msg.sender[msg.sender[-1]]这个字节可以控制地址。
接下来目标是控制合约触发event,即log字节码,也就是需要跳到log前的某个jumpdest。
从这里可能会有人尝试直接一步到位跳到log,但其实会发现那个地方的地址要高于0xff没法通过1字节控制。
因此我们需要跳转到小于0x100的某个位置,总之最后落在salt的后半段,然后再通过inputdata的其他字节控制跳转到下一个gadget。
可能会注意到的另一个事情是在最近的‘jumpdest’和‘log’之间,存在一些修改栈内容的操作,因此我们需要利用其他gadgets大概准备一下stack的内容。
接下来的步骤是需要跳转到metadata段来处理一些stack/memory的存取操作(正常情况下metadata段不会被执行)
Ummm,我觉得写到这里差不多,还是希望把其他乐趣留给大家来探索接下来的解法。后面的解题应该还会有几个trick没有提到。
0x02 未初始化结构体变量
在走到后门入口之前有另一个trick需要处理一下。未初始化结构体变量也是一个很典型的蜜罐合约,在旧版编译器下,‘PlayerInfo pi’这种写法其实定义了一个指向0的指针,刚好也是secret存储的位置,因此pi.addr = msg.sender其实悄悄把secret修改成了msg.sender。
所以lottery函数的参数应该是攻击者自己的地址。这个设计像是后门的守卫。总之这样我们就可以触发到后门了。
另一个点是,由于我们需要给lottery提供的inputdata要长于uint256来触发JOP部分,可能需要用web3脚本或者部署一个代理合约来构造发送任意payload。然后为了减轻选手的压力,我写了一个docker image作为完整的独立debug环境,作为题目附件可供下载,启动后里面有一个包含题目环境的私链,与ropsten上的环境几乎一模一样,也提供了几乎所有解题需要用到的库、脚本样例啥的。
0x03 POC
如果你有debug环境的话直接run docker,记得暴露一下rpc/ws接口,然后把remix连到docker里的私链来debug。如果没有的话用ropsten也是一样的。
接下来配置一下ws_provider跑poc就完事儿了,应该会发一条交易触发SendFlag。可以尝试debug poc这条交易来理解一些细节。
关于如何解密flagdata,请参考‘Monica’s Bank Writeups’。
感谢,希望大家喜欢这小破题。
完事儿
FYI: http://xhyumiracle.com/defcon27-village-talk/
本部分由白帽汇高级安全研究专员R3start撰写
Writeup 1: Controllable Database Connection
打开网站提示“Please connect to the database first” 还有一个 Connect的链接 所以应该跟数据库有关
点击链接后发现URI 多了/#mysql.php 看来是个提示
访问mysql.php 提示 Error 常规套路扫一波源码
得到备份文件 /mysql.php.bak 获取到源码
发现连接地址、用户名、密码参数可控,看来考点是mysql伪造恶意服务端读取客户端文件,按照泄露的源码来看,应该是要读 /Flag.php 文件
通过连接自己公网开启的恶意mysql服务端读取成功读取到Flag.php 的源码
Flag.php 是一个简单命令执行绕过,限制使用eval、assert函数和长度不得超过11位,满足以上两个条件进入eval函数执行
我们可以使用反单引号执行命令,并通过$_GET传入我们需要执行的命令,长度刚好11个字符内
成功反弹shell 并获取到Flag
Writeup 2: Unsafe Access
打开网站只有一张logo和一个提示 6_79?很自然的就想到应该跟6379,redis有关!
右键查看源码发现一个被注释的链接
测试发现此接口存在SSRF漏洞
扫描备份文件获取到link.php的源码,只是简单的进行了一些过滤,限制了gopher和127、localhost 等字符
联系首页的6_79提示,看来这题是常规的SSRF配合Redis拿Flag。使用dict协议写入redis、使用0 代表当前IP或域名解析等方式即可绕过访问本地限制。尝试反弹或者写私钥,均没有成功应该是权限过低的原因,于是尝试写webshell,成功写入恶意代码,获取到Flag。
写入恶意代码:(<? 等特殊符号需要转义,不然问号后面会导致截断无法写入)
设置保存路径:
设置保存名字:
保存:
成功写入
本部分由PeckShield漏洞研究总监Edward Lo撰写
Remote DoS
这是公链项目的第一题,参赛者需要找到漏洞并进行攻击造成Remote DoS。服务器上跑的是经过修改的geth结点,而问题点就在EVM中。
首先来看看EVM正常执行流程:
EVM在执行合约时,会一步一步提取其中的OPCode,并做相对应的检查:
OPCode需要的传参是否已放到栈(stack)上,是否有足够空间能放回传值;
EVM目前是否在readonly模式(例如以static_call方式呼叫),若是则需符合规则,不能对链上状态有任何改动(例如转帐);
计算OPCode需要的内存大小,并根据目前的内存情况决定如何收取gas费用并扩容内存。
接下来我们以OPCode mstore8为例,来看看到底怎麽计算。
如上所示,p是offset,v是想写入的内容
EVM会调用memoryMStore8计算回传所需要的内存大小,接着调用gasMStore8来计算所需的gas费用
这道题目就是将memoryMStore8注解掉,如此一来会造成两个问题:
EVM不会计算收取gas,因为memorySize永远为0;
内存不会扩容,攻击者可以在任意offset写入一个可控byte内容;
如此一来,只需布置会调用mstore8的恶意合约便可以进行攻击
RCE
基于上一道题目,我们可以在任意offset写一个byte,理论上便能透过它执行RCE。此题的技术难度跟水平要求较高,攻击者必须结合漏洞并布置巧妙构思过的恶意合约,将shellcode写入远端节点来完成RCE