PARADIGM CTF 2022题目分析(5)- Vanity 分析

WriteUp 2年前 (2022) admin
440 0 0

题目分析 


照例先看题目的setup合约: 

 

PARADIGM CTF 2022题目分析(5)- Vanity 分析


创建了一个challenge合约,然后要拿到flag需要让challenge合约的bestScore这个方法的返回值大于等于16。 
进入到challenge合约:

PARADIGM CTF 2022题目分析(5)- Vanity 分析

 
三个函数都是solve,函数可见性不一样。两个外部调用,一个private。先看第一个solve,没有参数,但是读取调用者地址(msg.sender),然后调用内部的solve函数。内部的solve函数先暂时搁置,继续往下看第二个外部调用的solve。这个solve传入两个参数,一个地址,一个bytes字符串,这两个变量都外部可控,然后内部逻辑调用了一个库函数isValidSignatureNow,这个函数需要3个参数,两个是外部传入的变量,还有一个是hash常量。然后需要这个库函数返回true,才继续调用内部的solve。然后就是内部的solve,看到内部调用的solve函数只传入一个地址类型的数据,把这个地址转成 bytes20,然后遍历,如果当前字节是00,则score++,最后如果score>bestscore,则把score赋值给bestscore。看来关键点是这个判断,最后bestscore要>=16,即传入的这个地址的16个字节的内容都要是00。 
首先排除第一个可以外部调用的solve,无法构造一个有16个字节都是0的地址并且让这个地址发起调用。所以主要就是第二个可外部调用的solve,这个函数的主要部分就是isValidSignatureNow,这个库函数的逻辑,由于篇幅问题,只粘贴可利用的代码部分。 

PARADIGM CTF 2022题目分析(5)- Vanity 分析

 
进入到代码逻辑,参数是一个外部可控的地址,一个外部可控的bytes字符串,一个hash常量。 

PARADIGM CTF 2022题目分析(5)- Vanity 分析


先把这个hash打出来: 

PARADIGM CTF 2022题目分析(5)- Vanity 分析


先看第一个调用,调用了ecdsa库函数的tryRecover函数,传入一个hash常量,一个签名的字符串,返回一个签名者的地址,要求签名者的地址和传入的signer地址相等。但是hash不可控,这个hash是一个常量,这一步走不通。接着进行,下面是对传入地址signer的staticcall调用。把函数签名,hash,签名内容进行abi编码之后传入。要求staticcall返回内容的长度是32字节,返回的bytes32转化成bytes4(也就是高位截断之后的前4字节),这前4个字节要等于isValidSignature的函数签名。首先这个signer要求至少16个字节的内容都是0,然后要求调用之后返回32字节的内容,并且返回内容等于一个已知常量。 

bytes4 a= bytes4(keccak256("isValidSignature(bytes32,bytes)" ));
emit log_bytes4(tttt:0x1626ba7e)


整个代码读完之后,有了大致的思路,因为其它函数调用经过分析都走不通,唯一这个staticcall的地方可以尝试。要一个地址有至少16个字节都是0,容易想到evm预编译。首先这个预编译合约它是不在链上的,这部分内容集成在每个节点上,因为调用频繁,所以不在链上计算,节约成本。 具体文档参照:https://www.evm.codes/precompiled。首先这些个预编译合约的地址前面有很多0,满足条件,但是还需要找一个调用返回bytes32的。 

PARADIGM CTF 2022题目分析(5)- Vanity 分析


发现只有0x2合适,返回的内容是32字节。这个函数就是对传入参数做一个sha256。 

那现在这个问题就转化了。首先传一个0x2的地址,符合要求,还需要传入一个bytes字符串。然后把isValidSignature的函数签名,hash常量,这个外部传入的bytes字符串,这3个内容进行abi编码之后,做一个sha256计算,要求返回结果的前4个字节是isValidSignature的函数签名。先看一下abi编码之后的数据。 


PARADIGM CTF 2022题目分析(5)- Vanity 分析

首先是isValidSignature函数签名,然后是MAGIC的hash。这些都是常量,而且已知。 

所以第一个32字节是MAGIC的hash。因为这个hash的长度是固定的,已经写死了bytes32字节,所以在内存中不存长度。然后要看这个外部传入的bytes字符串,这个是不定长的,它在abi编码之后先存一个长度,因为它的上一个参数占用了32字节,所以这个外部传入的字符串只能存在0x20。而且字符串在内存的存储是先存一个长度,然后再存内容。所以abi编码之后的内容大概是这样: 


PARADIGM CTF 2022题目分析(5)- Vanity 分析


大概以这样的数据去暴力,前两行内容固定,因为字符串是存在高位。先假定外部传入的signature是4个字节(如果4字节出不来,再尝试6字节,8字节这样),第三行0x04。 

我们去暴力一个8位的内容,它和前面的数据abi编码之后的sha256的前8位是isValidSignature的函数签名。 

编码的时候一定注意格式,否则很容易出错。 

dat=bytes.fromhex('1626ba7e'+ web3.codec.encode_abi(['bytes32','bytes' ],['19bb34e293bba96bf0xaeea54cdd3d2dad7fdf44cbea855173fa84534fcfb528', h]).hex())


建议直接使用python库函数进行编码。 


PARADIGM CTF 2022题目分析(5)- Vanity 分析

 

主要代码段,写的过于暴力。 最后跑出来signature是8cf1a8bb。 完整调用如下图所示: 


PARADIGM CTF 2022题目分析(5)- Vanity 分析


PARADIGM CTF 2022题目分析(5)- Vanity 分析

 

发现前后bestScore已经发生变化,并且大于等于16。已经完成获得flag的要求。 

 

总结 


这个题的主要目的是考察evm的预编译合约,暴力的代码还有很大的优化空间。还有一个点要注意solidity版本,题目的版本是0.7.6,一开始用的0.8.13去调试,发现已经没有了abi.decode函数,所以再调试的过程中一定要保持与题目环境版本一致。 


PARADIGM CTF 2022题目分析(5)- Vanity 分析


Numen 导航


Numen 官网

https://www.numencyber.com/


GitHub

https://github.com/NumenCyber


Twitter

https://twitter.com/@numencyber


Medium

https://medium.com/@numencyberlabs


LinkedIn

https://www.linkedin.com/company/numencyber/


原文始发于微信公众号(Numen Cyber Labs):PARADIGM CTF 2022题目分析(5)- Vanity 分析

版权声明:admin 发表于 2022年9月2日 下午6:30。
转载请注明:PARADIGM CTF 2022题目分析(5)- Vanity 分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...