An Interesting Problem - RSAOS
0.About
最近自己在BUU上面刷题的时候发现了一个很好玩的题目——RSAOS,虽然本fw还是瞄了几眼WP才写出来的——因为自己tcl,但还是决定跟大家分享一下——因为确实非常的好玩
7月14日,BUUCTF上了5000分,以后还要继续努力。
#define huangx607087 fw
Part 1 Beginning
题目什么附件都没有给,就是一个普普通通的远程题目,直接打开虚拟机远程连接上去(
然后远程连接上去之后,似乎什么东西都没有(,并没有让你输出什么sha256
验证的,也没有输出什么RSA必要的$c,e,n$的必要解密条件。随便输入一个e
,n
,ls
之类的东西都显示”Commond Not Found”。。。。。。
这就是一个很奇怪的玩意了——不仅没有源码,还没给什么其他的东西(
于是本fw毫不要脸地去瞄了一眼这道题的wp,原来第一步要先输入help获取菜单——行吧,怪我自己没有开发经验,谁叫自己是一个连机器人都配不好的fw呢?下亿次我拿到一个未知的系统一定要先输一下help
或者menu
等功能性命令,(拿小本本记下来
输入了help之后,似乎很多事情就好起来了(,得到了一连串的命令——
Part 2 Attempt 1
乱搞阶段
很显然,要输入get-flag
或者get-privatekey
,我们才能获得最终答案。因此先输一下get-flag来看看需要什么条件(
然后它给我冒出来了一个什么foldhash的东西(看来这玩意还是有点东西的。可能权限命令需要某个密码才能完成吧——要记住这是Crypto题目不是Pwn/Re题目,估计其他的内容不会很难
输入get-publickey
,服务器返回了这样一个东西:貌似重连几遍内容一直不变
1 | Public key parameters: |
duck
不必管那么多,或许其他的东西应该试试看(,比如没试过的命令都尝试日一遍看看(,尤其是这个debug,我看你很不对劲[严肃]
果然debug这边确实有什么玄机在里面!或许我们应当enable一下这个debug才对
果然在debug enable
之后,似乎每次输出命令都会给你一个什么DBG-CRC32() SIG()
的模式。不过这个究竟是什么东西啊(
百度了一下,原来CRC32()
是一个$32$个比特位的哈希值,而SIG
是签名结果。
不过这个时候自己的发展又陷入僵局。。。。(于是再一次瞄了眼别人的wp)
结果发现这是个脑洞题——原来help后面还可以跟上命令名称来告诉你这个命令究竟是干什么用的
在help security
这边,我们发现了比较有价值的信息:
简单翻译一下并概括,差不多是这两条内容:
1 | 1.非特权命令的签名内容是对你输入内容的CRC32进行签名,而特权命令的签名内容是其FoldHash进行签名的内容 |
经过查询,CRC32是一种hash方法,其hash后取值范围是$0$到$2^{32}-1$,该方法哈希冲突率比较高。
Part 3 Analyzation
于是乱搞到这里差不多可以结束了(,得正式地做题了(
首先先来回顾一下一下RSA的数字签名
签名方式:
$$
\mathrm{SIG}(x)\equiv x^d \pmod n
$$
验证方式:
$$
\mathrm{Verify}(x)\equiv \mathrm{SIG}^e(x)\pmod n
$$
很显然,用于验证的内容时私钥。因此我们需要尝试伪造一个签名才可以完成。
根据题目提示,不难分析出其foldhash
的原理。
1 | #Sagemath |
关注到foldhash实际上是一个数字,CRC32也是一个数字,只不过foldhash的比特位是$80$,而CRC32的结果是$32$位。如果使用特权命令,那么你需要搞出你输入的那一行内容的数字签名。
注意到上面分析的第三条,暗示着我们可以在特权命令后面加一下东西,我们就可以获取到必要的特权命令foldhash的签名了,也就是特权命令的$\mathrm{foldhash}(x)\equiv x^d \pmod n$的值。
不过我们这边有个问题来了:$x$是$80$位的内容,但CRC32最后是$32$位内容,因此我们需要构造一个字符串,其格式为"get-flag "+padding
,使得该字符串的foldhash值满足$\mathrm{FFFFFFFFH}$光滑,最后根据RSA的同态性将所有SIG值相乘后就是我们需要的内容了
果然,瞄了一眼WP,WP也是这么写的,不过他直接用的get-flag 0
,该命令的foldhash的值为$637185588446528046742504$。其分解质因数结果为:
$$
637185588446528046742504=2^3×250091×244079963×1304805461
$$
不过直接抄wp肯定是没意思的,但问题是这个get-flag 0
真的这么好构造出来吗?于是我在Sagemath中测试了以下代码,随机在字符串get flag
后面填充$8$位十六进制数。
1 | #Sagemath |
下图是一次运行结果。
于是我构造了一个不同并且更简单的字符串:get-flag 8c5fdf74
,其foldhash值为$123923881117703856573281$,质因数分解结果为$2719×574657×931169×85174303$,随机生成了$5$次后得到了这个结果。
然后我又重复运行了这个代码数次,发现最多的一次为$21$次尝试获得符合条件的字符串,最少一次可以一发命中符合条件的字符串,平均大概$6$到$7$次左右的尝试即可产生符合条件的字符串——说明符合条件的字符串并不难找。
于是找字符串这个问题就这样解决了。下面就是寻找$4$个符合条件的字符串$s_1,s_2,s_3,s_4$,让其CRC32值分别为$2719,574657,931169,85174303$——看来这似乎是一个有难度的事情。
实际上枚举$6$位内容的可见字符串在原则上是可行的。不过github上还是能找到一个包([戳这里](theonlypwner/crc32: CRC32 tools: reverse, undo/rewind, and calculate hashes (github.com))),直接下载即可。
Part 4 Attempt 2
真正的尝试阶段
阅读过这个包的内容后,开始了尝试。
然后我连续发送了三个过去,结果发现不对(,那边的验证结果是0xe0db37ee
。这两个内容之间是不是有什么联系在里面啊(
那我找个0xe0db37ee
的值发过去试试看呢?
果然,最后的结果是正确的,此时记录下签名结果
于是重复上面的步骤,我们就可以获得另外三个的内容了(
将得到的$4$个SIG乘起来模$n$,获得最终的结果。发送过去得到flag。
5.More Exploration
搞出来flag之后,本fw很形象再去看看另外两个命令是干什么的,于是决定如法炮制伪造字符串
我们可以使用的字符串如下:
根据上面的内容可以获得其私钥:
1 | N: 0xd888075370effdb016d85de8c894ee7ac2764527210d8ce1d8bd14a06c67de148b4680781366002f9649e3885e18ab950120c660970ab9a499ea74ea7aa38fe732940b5204300ef7b96a608efec1a74007a4b1d592cf9eb23890d8fa416202857d0e0f9ebad79324d03d09db0502ff4bae0b2dfc0b150ddea806a5ff24e2d32f |
而security命令的输出与help-security的输出一模一样。。。
6.Review
一道很好玩的题目还是值得把玩以下的,感觉收获很大。
最后附一下网上给的CRC32加密原理,以后研究。