2018-12-06

The QEMU 3.1 introduced a very serious security issue in SMBus implementation.

The corresponding commit is following:

i2c: pm_smbus: Add block transfer capability

And the fix is in i2c: pm_smbus: check smb_index before block transfer write

The issue is the processing of SMBHSTSTS command in smb_ioport_writeb() function.

Here we see the s->smb_index is increased without bounding check. The read is from ‘s->smb_addr’ and can be controlled by SMBHSTADD command. So it is easy to bypass the if (!read…). As the ‘s->smb_index’ is a ‘uint_32’, this means we can add it to 0xffffffff theoretically. This ‘s->smb_index’ is used to index the memory in ‘s->smb_data’.

case SMBHSTSTS:
    s->smb_stat &= ~(val & ~STS_HOST_BUSY);
    if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
        uint8_t read = s->smb_addr & 0x01;

        s->smb_index++;
        if (!read && s->smb_index == s->smb_data0) {
            uint8_t prot = (s->smb_ctl >> 2) & 0x07;
            uint8_t cmd = s->smb_cmd;
            uint8_t addr = s->smb_addr >> 1;
            int ret;

            if (prot == PROT_I2C_BLOCK_READ) {
                s->smb_stat |= STS_DEV_ERR;
                goto out;
            }

            ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data,
                                    s->smb_data0, !s->i2c_enable);
            if (ret < 0) {
                s->smb_stat |= STS_DEV_ERR;
                goto out;
            }
            s->op_done = true;
            s->smb_stat |= STS_INTR;
            s->smb_stat &= ~STS_HOST_BUSY;
        } else if (!read) {
            s->smb_data[s->smb_index] = s->smb_blkdata;
            s->smb_stat |= STS_BYTE_DONE;
        } else if (s->smb_ctl & CTL_LAST_BYTE) {
            s->op_done = true;
            s->smb_blkdata = s->smb_data[s->smb_index];
            s->smb_index = 0;
            s->smb_stat |= STS_INTR;
            s->smb_stat &= ~STS_HOST_BUSY;
        } else {
            s->smb_blkdata = s->smb_data[s->smb_index];
            s->smb_stat |= STS_BYTE_DONE;
        }
    }
    break;

Look at this code snippet more, there are three ‘else’ after the ‘s->smb_index’ increased. The next important data appears ‘s->smb_blkdata’. This data can be assign by write and write using ‘SMBBLKDAT’ command. In the first ‘else’ we can assign ‘s->smb_data[s->smb_index]’ with ‘s->smb_blkdata’, this means we can write arbitrary bytes out of ‘s->smb_data’ array. In the second and last ‘else’, the ‘s->smb_data[s->smb_index]’ is assigned to ‘s->smb_blkdata’, this means we can read bytes out of ‘s->smb_data’ array.

So we can read/write a lot of (4G theoretically) memory after ‘s->smb_data’ array. This gives us a lot of power and room to make exploit.

Following is the demo of VM escape.



blog comments powered by Disqus