一道积攒了很久才解出来的题,这道题大体不难,但是好多小细节呜呜呜。而且Libcsearcher里的libc库没更新(上篇博客讲了)。
这道题是BUUCTF上的ciscn_2019_c_1。标准的64位ROP流程,我也就分享一下自己的坎坷心路历程。
代码审计

老规矩checksec一下,只开了nx保护。
观察main函数

显然哦,只有输入1才有点用。
关键函数:encrypt()

发现一个栈溢出漏洞。ROP链的入口。
然后就是读取s的长度,在长度范围内对内容进行加密:也就是根据ascii码范围不同来进行异或操作。
这个解密函数会把读入的s给加密,把你的内容替换掉。
解题思路
1.通过gets()函数构造ROP链:
64位程序里面的传参方式是前六位参数为寄存器传参,故不能直接把参数写在栈里,需要通过gadgets来实现控制寄存器的值从而控制函数参数的值。
实用工具:ROPgadgets
ROPgadget --binary 文件名
即可查看程序本身的gadget。这里只会用到第一个寄存器rdi,因此去找pop rdi的指令即可。

pop_rdi_addr = 0x400c83
然后通过 p64(pop_rdi_addr)+p64(参数值)+p64(函数地址) 即可调用函数
2.绕过加密函数,防止输入内容被篡改:
方法一:
关于绕过加密函数,网上异口同声的一种方法:
输入encrypt(s),也就是说先给要输入的内容异或一次,然后程序在给异或过的信息再异或一次。
异或的加法逆元,导致一个数与相同的另一个数异或两次,就会回到最初的值。
例如(a^b)^b=a
这种方法确实可以,不过感觉很繁琐,首先得把加密函数稍微改一下加进自己的exp里。
现在在网上基本上很难找到不同的解法了,感觉大家都流水线过一遍,也不想去思考或者也不愿意去分享自己更好的做法了。很可惜,李杜诗篇万口传,至今已觉不新鲜。
方法二:
也是偶然发现的,并没有加密,发过去代码依然成功运行并且得到了flag。
后来想了想,大概是因为64位里面的地址高位都是0,造成了\x00截断,于是strlen()函数读取的长度变得更短,没有威胁到我们payload后面关键的地址部分。
payload = b'a'*0x58+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
方法三:
根据第二种方法得到启发,直接令payload第一位为’\x00’,这样strlen()读取到的就是0,就不会进行加密。
payload = b'\x00'

本文详细介绍了如何利用64位程序中的栈溢出漏洞,通过ROP技术构造payload,绕过加密函数并获取程序的控制权。作者分享了三种不同的方法,包括使用异或逆元、利用x00截断和直接设置payload首字节为x00来避免加密。在解决过程中,作者遇到了puts函数地址获取错误和堆栈平衡问题,并给出了相应的解决方案。最后,文章讨论了当前网络安全挑战中创新思维的重要性。
最低0.47元/天 解锁文章
927





