首页
社区
课程
招聘
[原创]CVE-2015-3456 Qemu逃逸复现
发表于: 2025-12-1 08:49 1679

[原创]CVE-2015-3456 Qemu逃逸复现

2025-12-1 08:49
1679

参考文章:4ffK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6k6h3y4H3N6h3I4K6k6g2)9J5k6h3y4G2L8g2)9J5c8X3q4J5j5$3S2A6N6X3g2K6i4K6u0r3y4U0j5H3x3W2)9J5k6h3S2@1L8h3H3`.

QEMU版本:1.5.3

FDC(Floppy Disk Controller)是模拟 Intel 82078 软盘控制器的硬件设备,用于:

当 offset 为 5则会调用 fdctrl_write_data

FIFO是FDC的核心缓冲区,用于:

我们可以看见 fdctrl->fifo 被分配了较为固定的512字节大小的存储。

一共这里我们可以看见分为两个板块,一个板块是接收数据

一个板块是判断数据接收完毕调用函数,然后设置标识位

那么根据映射表我们可以知道 fdctrl_handle_drive_specification_command 函数会被调用

当我们调用0x3f5,并且传入0x8e这种情况的时候,会去调用**fdctrl_handle_drive_specification_command函数,**然后在参数传递完毕后该函数并没有很好的处理fifo的状态,并且 后续的参数传递依赖的 data_pos,而data_pos是无限增加的。

QEMU启动
    
设备注册 (fdc_register_types)
    
设备实例化 (qdev_create)
    
设备初始化 (qdev_init_nofail)
    
调用设备特定的init函数:
    ├─→ isabus_fdc_init1 (ISA设备)
    ├─→ sysbus_fdc_init1 (SysBus设备)
    └─→ sun4m_fdc_init1 (Sun4m设备)
    
fdctrl_init_common(fdctrl)
    
初始化command_to_handler查找表
    
分配fifo缓冲区: qemu_memalign(512, 512)
    
设置fifo_size = 512
    
初始化其他控制器状态
    
连接驱动器 (fdctrl_connect_drives)
QEMU启动
    
设备注册 (fdc_register_types)
    
设备实例化 (qdev_create)
    
设备初始化 (qdev_init_nofail)
    
调用设备特定的init函数:
    ├─→ isabus_fdc_init1 (ISA设备)
    ├─→ sysbus_fdc_init1 (SysBus设备)
    └─→ sun4m_fdc_init1 (Sun4m设备)
    
fdctrl_init_common(fdctrl)
    
初始化command_to_handler查找表
    
分配fifo缓冲区: qemu_memalign(512, 512)
    
设置fifo_size = 512
    
初始化其他控制器状态
    
连接驱动器 (fdctrl_connect_drives)
/* FDC I/O端口映射表 - outb机制的关键
 * c
 * 结构说明:
 * { offset, length, size, .read = func, .write = func }
 * - offset: 相对于iobase的偏移量
 * - length: 端口范围长度
 * - size: 访问大小(1=字节,2=字,4=双字)
 *
 * 端口映射(假设iobase=0x3f0):
 * - 0x3f1-0x3f5: 偏移1-5,映射到fdctrl_read/fdctrl_write
 *   * 0x3f1 (offset=1): FD_REG_SRA (状态寄存器A)
 *   * 0x3f2 (offset=2): FD_REG_DOR (数字输出寄存器)
 *   * 0x3f3 (offset=3): FD_REG_TDR (磁带驱动器寄存器)
 *   * 0x3f4 (offset=4): FD_REG_MSR (主状态寄存器)
 *   * 0x3f5 (offset=5): FD_REG_FIFO (FIFO数据寄存器) ← 漏洞触发端口!
 * - 0x3f7: 偏移7,映射到fdctrl_read/fdctrl_write
 *   * 0x3f7 (offset=7): FD_REG_DIR (数字输入寄存器) 或 FD_REG_CCR (配置控制寄存器)
 *
 * outb(0x8e, 0x3f5) 的完整路径:
 * 1. 主机执行outb(0x8e, 0x3f5)
 * 2. QEMU CPU模拟层:cpu_outb(0x3f5, 0x8e)
 * 3. I/O端口层:ioport_write(0, 0x3f5, 0x8e)
 * 4. 查找注册的handler:ioport_write_table[0][0x3f5]
 * 5. 调用:portio_writeb_thunk() → fdctrl_write(offset=5, value=0x8e)
 * 6. fdctrl_write识别offset=5为FD_REG_FIFO
 * 7. 调用fdctrl_write_data(fdctrl, 0x8e)
 * 8. fdctrl_write_data识别data_pos=0,进入命令模式
 */
static const MemoryRegionPortio fdc_portio_list[] = {
    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },  /* 0x3f1-0x3f5 */
    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },  /* 0x3f7 */
    PORTIO_END_OF_LIST(),
};
/* FDC I/O端口映射表 - outb机制的关键
 * c
 * 结构说明:
 * { offset, length, size, .read = func, .write = func }
 * - offset: 相对于iobase的偏移量
 * - length: 端口范围长度
 * - size: 访问大小(1=字节,2=字,4=双字)
 *
 * 端口映射(假设iobase=0x3f0):
 * - 0x3f1-0x3f5: 偏移1-5,映射到fdctrl_read/fdctrl_write
 *   * 0x3f1 (offset=1): FD_REG_SRA (状态寄存器A)
 *   * 0x3f2 (offset=2): FD_REG_DOR (数字输出寄存器)
 *   * 0x3f3 (offset=3): FD_REG_TDR (磁带驱动器寄存器)
 *   * 0x3f4 (offset=4): FD_REG_MSR (主状态寄存器)
 *   * 0x3f5 (offset=5): FD_REG_FIFO (FIFO数据寄存器) ← 漏洞触发端口!
 * - 0x3f7: 偏移7,映射到fdctrl_read/fdctrl_write
 *   * 0x3f7 (offset=7): FD_REG_DIR (数字输入寄存器) 或 FD_REG_CCR (配置控制寄存器)
 *
 * outb(0x8e, 0x3f5) 的完整路径:
 * 1. 主机执行outb(0x8e, 0x3f5)
 * 2. QEMU CPU模拟层:cpu_outb(0x3f5, 0x8e)
 * 3. I/O端口层:ioport_write(0, 0x3f5, 0x8e)
 * 4. 查找注册的handler:ioport_write_table[0][0x3f5]
 * 5. 调用:portio_writeb_thunk() → fdctrl_write(offset=5, value=0x8e)
 * 6. fdctrl_write识别offset=5为FD_REG_FIFO
 * 7. 调用fdctrl_write_data(fdctrl, 0x8e)
 * 8. fdctrl_write_data识别data_pos=0,进入命令模式
 */
static const MemoryRegionPortio fdc_portio_list[] = {
    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },  /* 0x3f1-0x3f5 */
    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },  /* 0x3f7 */
    PORTIO_END_OF_LIST(),
};
enum {
    FD_REG_SRA = 0x00,
    FD_REG_SRB = 0x01,
    FD_REG_DOR = 0x02,
    FD_REG_TDR = 0x03,
    FD_REG_MSR = 0x04,
    FD_REG_DSR = 0x04,
    FD_REG_FIFO = 0x05,
    FD_REG_DIR = 0x07,
    FD_REG_CCR = 0x07,
};
static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
    FDCtrl *fdctrl = opaque;
 
    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
 
    reg &= 7;
    switch (reg) {
    case FD_REG_DOR:
        fdctrl_write_dor(fdctrl, value);
        break;
    case FD_REG_TDR:
        fdctrl_write_tape(fdctrl, value);
        break;
    case FD_REG_DSR:
        fdctrl_write_rate(fdctrl, value);
        break;
    case FD_REG_FIFO:
        fdctrl_write_data(fdctrl, value);
        break;
    case FD_REG_CCR:
        fdctrl_write_ccr(fdctrl, value);
        break;
    default:
        break;
    }
}
enum {
    FD_REG_SRA = 0x00,
    FD_REG_SRB = 0x01,
    FD_REG_DOR = 0x02,
    FD_REG_TDR = 0x03,
    FD_REG_MSR = 0x04,
    FD_REG_DSR = 0x04,
    FD_REG_FIFO = 0x05,
    FD_REG_DIR = 0x07,
    FD_REG_CCR = 0x07,
};
static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
{
    FDCtrl *fdctrl = opaque;
 
    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
 
    reg &= 7;
    switch (reg) {
    case FD_REG_DOR:
        fdctrl_write_dor(fdctrl, value);
        break;
    case FD_REG_TDR:
        fdctrl_write_tape(fdctrl, value);
        break;
    case FD_REG_DSR:
        fdctrl_write_rate(fdctrl, value);
        break;
    case FD_REG_FIFO:
        fdctrl_write_data(fdctrl, value);
        break;
    case FD_REG_CCR:
        fdctrl_write_ccr(fdctrl, value);
        break;
    default:
        break;
    }
}
/* FDC FIFO数据写入处理函数 - 漏洞核心函数
 *
 * 函数调用链:
 * fdctrl_write (FD_REG_FIFO) → fdctrl_write_data
 *
 * 整体流程:
 * 1. 检查控制器状态(RESET、RQM、DIO)
 * 2. 判断当前模式:
 *    a) 非DMA数据传输模式 (FD_MSR_NONDMA): 处理数据传输
 *    b) 命令模式 (data_pos == 0): 识别命令,设置data_len
 *    c) 参数接收模式: 接收命令参数,写入FIFO
 * 3. 当data_pos == data_len时,调用命令处理器
 *
 * 状态机转换:
 * 空闲 → 命令接收 → 参数接收 → 命令处理 → 数据传输/结果返回 → 空闲
 *
 * 漏洞触发条件:
 * 1. 控制器处于非RESET状态
 * 2. MSR_RQM标志置位(请求主模式)
 * 3. MSR_DIO标志未置位(写方向)
 * 4. 不在非DMA数据传输模式(否则会走另一个分支)
 * 5. 攻击者发送命令后,继续发送超过data_len的数据
 *
 * 漏洞触发流程:
 * Step 1: 发送命令字节(如0x8e)
 *   - data_pos = 0,识别命令,设置data_len = 6
 * Step 2: 发送参数(正常5个参数)
 *   - data_pos: 1 → 2 → 3 → 4 → 5 → 6
 * Step 3: 攻击:继续发送数据
 *   - data_pos继续增长:7 → 8 → ... → 512 → 513 → ...
 *   - 当data_pos >= 512时,fifo[data_pos]越界写入堆内存
 *
 * 关键变量:
 * - fdctrl->fifo: 堆上分配的512字节缓冲区
 * - fdctrl->data_pos: 当前写入位置(可能超过512)
 * - fdctrl->data_len: 预期数据长度(根据命令设置)
 * - fdctrl->msr: 主状态寄存器,控制状态机
 */
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
{
    FDrive *cur_drv;
    int pos;
 
    /* 检查1: 控制器必须在非RESET状态 */
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
    /* 检查2: 控制器必须准备好接收数据
     * - MSR_RQM: 请求主模式标志,必须置位
     * - MSR_DIO: 数据方向标志,0=写(主机→控制器),1=读(控制器→主机)
     * 如果DIO置位,说明控制器在等待主机读取,不应该写入
     */
    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
        FLOPPY_DPRINTF("error: controller not ready for writing\n");
        return;
    }
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
    /* Is it write command time ? */
    if (fdctrl->msr & FD_MSR_NONDMA) {
 
        pos = fdctrl->data_pos++;
        pos %= FD_SECTOR_LEN;
        fdctrl->fifo[pos] = value;
        if (pos == FD_SECTOR_LEN - 1 ||
            fdctrl->data_pos == fdctrl->data_len) {
            cur_drv = get_cur_drv(fdctrl);
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
                FLOPPY_DPRINTF("error writing sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                FLOPPY_DPRINTF("error seeking to next sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
        }
        /* Switch from transfer mode to status mode
         * then from status mode to command mode
         */
        if (fdctrl->data_pos == fdctrl->data_len)
            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
        return;
    }
 
    /* ========================================================================
     * 命令识别阶段 - 当data_pos==0时,识别这是一个新命令
     * ========================================================================
     *
     * 触发条件:data_pos == 0(FIFO为空,准备接收新命令)
     *
     * 命令识别流程:
     * 1. 通过command_to_handler[value]查找命令对应的handler索引
     *    - command_to_handler数组在fdctrl_init_common()中初始化
     *    - 建立命令字节值(0x00-0xff)到handlers数组索引的映射
     *
     * 2. 设置预期数据长度:data_len = parameters + 1
     *    - parameters: 命令需要的参数数量(不包括命令字节本身)
     *    - +1: 包括命令字节本身
     *    - 例如:0x8e命令parameters=5,所以data_len=6
     *
     * 3. 设置MSR_CMDBUSY标志
     *    - 表示控制器正在处理命令,防止其他操作干扰
     *
     * 示例:outb(0x8e, 0x3f5)
     * - value = 0x8e
     * - pos = command_to_handler[0x8e] → 找到handlers数组索引
     * - handlers[pos].name = "DRIVE SPECIFICATION COMMAND"
     * - handlers[pos].parameters = 5
     * - data_len = 5 + 1 = 6
     * - 设置MSR_CMDBUSY标志
     *
     * 后续流程:
     * - 命令字节0x8e被写入fifo[0]
     * - data_pos变为1
     * - 等待接收5个参数
     */
    if (fdctrl->data_pos == 0) {
        /* Command - 命令识别 */
        pos = command_to_handler[value & 0xff];  /* 查找命令对应的handler索引 */
        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
        /* 设置预期数据长度:命令字节 + 参数数量 */
        fdctrl->data_len = handlers[pos].parameters + 1;
        /* 设置命令忙标志,防止其他操作干扰 */
        fdctrl->msr |= FD_MSR_CMDBUSY;
    }
 
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
 
    /* ========================================================================
     * CVE-2015-3456 漏洞核心代码 - 无边界检查的FIFO写入
     * ========================================================================
     *
     * 漏洞点:直接使用data_pos作为数组索引,没有检查是否超过fifo_size(512)
     *
     * 正常流程:
     * - data_pos从0开始,每次写入后递增
     * - 当data_pos == data_len时,调用命令处理器
     * - 命令处理器通常会调用fdctrl_set_fifo或fdctrl_reset_fifo重置data_pos=0
     *
     * 攻击流程(0x8e命令):
     * 1. 发送命令字节0x8e,data_pos=0,设置data_len=6
     * 2. 发送5个参数,data_pos: 1→2→3→4→5→6
     * 3. 当data_pos==6时,调用fdctrl_handle_drive_specification_command
     * 4. 如果第5个参数(fifo[5])的bit7=0且bit6=0,函数直接返回
     * 5. data_pos保持为6,不会被重置
     * 6. 攻击者继续发送数据:
     *    - data_pos=7: fifo[7] = value  ← 仍在FIFO范围内
     *    - ...
     *    - data_pos=512: fifo[512] = value  ← 越界写入!开始覆盖堆内存
     *    - data_pos=513: fifo[513] = value  ← 继续越界写入
     *    - ...
     *
     */
    fdctrl->fifo[fdctrl->data_pos++] = value;  /* 漏洞:直接使用data_pos,无边界检查! */
     
    /* ========================================================================
     * 命令处理阶段 - 当data_pos == data_len时,所有参数接收完毕
     * ========================================================================
     *
     * 触发条件:data_pos == data_len(所有参数已接收)
     *
     * 处理流程:
     * 1. 检查是否为格式化命令(特殊处理)
     * 2. 根据命令字节(fifo[0])查找handler索引
     * 3. 调用对应的命令处理函数
     *
     * 函数调用链:
     * fdctrl_write_data → handlers[pos].handler(fdctrl, direction)
     *
     * 可能的处理器:
     * - fdctrl_start_transfer: READ/WRITE等数据传输命令
     * - fdctrl_handle_drive_specification_command: 0x8e命令 ← 漏洞利用的关键
     * - fdctrl_handle_seek: SEEK命令
     * - fdctrl_handle_specify: SPECIFY命令
     * - 其他命令处理器
     *
     * 示例:0x8e命令处理
     * - fifo[0] = 0x8e(命令字节)
     * - fifo[1-5] = 5个参数
     * - data_pos = 6,data_len = 6
     * - pos = command_to_handler[0x8e]
     * - handlers[pos].handler = fdctrl_handle_drive_specification_command
     * - 调用:fdctrl_handle_drive_specification_command(fdctrl, ...)
     *
     * 漏洞利用的关键:
     * - 如果handler函数不调用fdctrl_set_fifo()或fdctrl_reset_fifo()
     * - data_pos不会被重置,可以继续写入数据
     * - 导致data_pos超过512,触发缓冲区溢出
     */
    /* 检查是否接收完所有参数 */
    if (fdctrl->data_pos == fdctrl->data_len) {
        /* 所有参数接收完毕,可以处理命令了 */
        if (fdctrl->data_state & FD_STATE_FORMAT) {
            /* 格式化命令的特殊处理 */
            fdctrl_format_sector(fdctrl);
            return;
        }
 
        /* 根据命令字节查找并调用相应的处理器 */
        pos = command_to_handler[fdctrl->fifo[0] & 0xff];  /* 从fifo[0]获取命令字节 */
        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
        /* 调用命令处理函数 */
        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
    }
    /* 注意:如果data_pos != data_len,函数返回,等待下一次写入
     * 攻击者可以继续发送数据,使data_pos继续增长,直到超过512
     */
}
/* FDC FIFO数据写入处理函数 - 漏洞核心函数
 *
 * 函数调用链:
 * fdctrl_write (FD_REG_FIFO) → fdctrl_write_data
 *
 * 整体流程:
 * 1. 检查控制器状态(RESET、RQM、DIO)
 * 2. 判断当前模式:
 *    a) 非DMA数据传输模式 (FD_MSR_NONDMA): 处理数据传输
 *    b) 命令模式 (data_pos == 0): 识别命令,设置data_len
 *    c) 参数接收模式: 接收命令参数,写入FIFO
 * 3. 当data_pos == data_len时,调用命令处理器
 *
 * 状态机转换:
 * 空闲 → 命令接收 → 参数接收 → 命令处理 → 数据传输/结果返回 → 空闲
 *
 * 漏洞触发条件:
 * 1. 控制器处于非RESET状态
 * 2. MSR_RQM标志置位(请求主模式)
 * 3. MSR_DIO标志未置位(写方向)
 * 4. 不在非DMA数据传输模式(否则会走另一个分支)
 * 5. 攻击者发送命令后,继续发送超过data_len的数据
 *
 * 漏洞触发流程:
 * Step 1: 发送命令字节(如0x8e)
 *   - data_pos = 0,识别命令,设置data_len = 6
 * Step 2: 发送参数(正常5个参数)
 *   - data_pos: 1 → 2 → 3 → 4 → 5 → 6
 * Step 3: 攻击:继续发送数据
 *   - data_pos继续增长:7 → 8 → ... → 512 → 513 → ...
 *   - 当data_pos >= 512时,fifo[data_pos]越界写入堆内存
 *
 * 关键变量:
 * - fdctrl->fifo: 堆上分配的512字节缓冲区
 * - fdctrl->data_pos: 当前写入位置(可能超过512)
 * - fdctrl->data_len: 预期数据长度(根据命令设置)
 * - fdctrl->msr: 主状态寄存器,控制状态机
 */
static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
{
    FDrive *cur_drv;
    int pos;
 
    /* 检查1: 控制器必须在非RESET状态 */
    if (!(fdctrl->dor & FD_DOR_nRESET)) {
        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
        return;
    }
    /* 检查2: 控制器必须准备好接收数据
     * - MSR_RQM: 请求主模式标志,必须置位
     * - MSR_DIO: 数据方向标志,0=写(主机→控制器),1=读(控制器→主机)
     * 如果DIO置位,说明控制器在等待主机读取,不应该写入
     */
    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
        FLOPPY_DPRINTF("error: controller not ready for writing\n");
        return;
    }
    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
    /* Is it write command time ? */
    if (fdctrl->msr & FD_MSR_NONDMA) {
 
        pos = fdctrl->data_pos++;
        pos %= FD_SECTOR_LEN;
        fdctrl->fifo[pos] = value;
        if (pos == FD_SECTOR_LEN - 1 ||
            fdctrl->data_pos == fdctrl->data_len) {
            cur_drv = get_cur_drv(fdctrl);
            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
                FLOPPY_DPRINTF("error writing sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
                FLOPPY_DPRINTF("error seeking to next sector %d\n",
                               fd_sector(cur_drv));
                return;
            }
        }
        /* Switch from transfer mode to status mode
         * then from status mode to command mode
         */
        if (fdctrl->data_pos == fdctrl->data_len)
            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
        return;
    }
 
    /* ========================================================================
     * 命令识别阶段 - 当data_pos==0时,识别这是一个新命令
     * ========================================================================
     *
     * 触发条件:data_pos == 0(FIFO为空,准备接收新命令)
     *
     * 命令识别流程:
     * 1. 通过command_to_handler[value]查找命令对应的handler索引
     *    - command_to_handler数组在fdctrl_init_common()中初始化
     *    - 建立命令字节值(0x00-0xff)到handlers数组索引的映射
     *
     * 2. 设置预期数据长度:data_len = parameters + 1
     *    - parameters: 命令需要的参数数量(不包括命令字节本身)
     *    - +1: 包括命令字节本身
     *    - 例如:0x8e命令parameters=5,所以data_len=6
     *
     * 3. 设置MSR_CMDBUSY标志
     *    - 表示控制器正在处理命令,防止其他操作干扰
     *
     * 示例:outb(0x8e, 0x3f5)
     * - value = 0x8e
     * - pos = command_to_handler[0x8e] → 找到handlers数组索引
     * - handlers[pos].name = "DRIVE SPECIFICATION COMMAND"
     * - handlers[pos].parameters = 5
     * - data_len = 5 + 1 = 6
     * - 设置MSR_CMDBUSY标志
     *
     * 后续流程:
     * - 命令字节0x8e被写入fifo[0]
     * - data_pos变为1
     * - 等待接收5个参数
     */
    if (fdctrl->data_pos == 0) {
        /* Command - 命令识别 */
        pos = command_to_handler[value & 0xff];  /* 查找命令对应的handler索引 */
        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
        /* 设置预期数据长度:命令字节 + 参数数量 */
        fdctrl->data_len = handlers[pos].parameters + 1;
        /* 设置命令忙标志,防止其他操作干扰 */
        fdctrl->msr |= FD_MSR_CMDBUSY;
    }
 
    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
 
    /* ========================================================================
     * CVE-2015-3456 漏洞核心代码 - 无边界检查的FIFO写入
     * ========================================================================
     *
     * 漏洞点:直接使用data_pos作为数组索引,没有检查是否超过fifo_size(512)
     *
     * 正常流程:
     * - data_pos从0开始,每次写入后递增
     * - 当data_pos == data_len时,调用命令处理器
     * - 命令处理器通常会调用fdctrl_set_fifo或fdctrl_reset_fifo重置data_pos=0
     *
     * 攻击流程(0x8e命令):
     * 1. 发送命令字节0x8e,data_pos=0,设置data_len=6
     * 2. 发送5个参数,data_pos: 1→2→3→4→5→6
     * 3. 当data_pos==6时,调用fdctrl_handle_drive_specification_command
     * 4. 如果第5个参数(fifo[5])的bit7=0且bit6=0,函数直接返回
     * 5. data_pos保持为6,不会被重置
     * 6. 攻击者继续发送数据:
     *    - data_pos=7: fifo[7] = value  ← 仍在FIFO范围内
     *    - ...
     *    - data_pos=512: fifo[512] = value  ← 越界写入!开始覆盖堆内存
     *    - data_pos=513: fifo[513] = value  ← 继续越界写入
     *    - ...
     *
     */
    fdctrl->fifo[fdctrl->data_pos++] = value;  /* 漏洞:直接使用data_pos,无边界检查! */
     
    /* ========================================================================
     * 命令处理阶段 - 当data_pos == data_len时,所有参数接收完毕
     * ========================================================================
     *
     * 触发条件:data_pos == data_len(所有参数已接收)
     *
     * 处理流程:
     * 1. 检查是否为格式化命令(特殊处理)
     * 2. 根据命令字节(fifo[0])查找handler索引
     * 3. 调用对应的命令处理函数
     *
     * 函数调用链:
     * fdctrl_write_data → handlers[pos].handler(fdctrl, direction)
     *
     * 可能的处理器:
     * - fdctrl_start_transfer: READ/WRITE等数据传输命令
     * - fdctrl_handle_drive_specification_command: 0x8e命令 ← 漏洞利用的关键
     * - fdctrl_handle_seek: SEEK命令
     * - fdctrl_handle_specify: SPECIFY命令
     * - 其他命令处理器
     *
     * 示例:0x8e命令处理
     * - fifo[0] = 0x8e(命令字节)
     * - fifo[1-5] = 5个参数
     * - data_pos = 6,data_len = 6
     * - pos = command_to_handler[0x8e]
     * - handlers[pos].handler = fdctrl_handle_drive_specification_command
     * - 调用:fdctrl_handle_drive_specification_command(fdctrl, ...)
     *
     * 漏洞利用的关键:
     * - 如果handler函数不调用fdctrl_set_fifo()或fdctrl_reset_fifo()
     * - data_pos不会被重置,可以继续写入数据
     * - 导致data_pos超过512,触发缓冲区溢出
     */
    /* 检查是否接收完所有参数 */
    if (fdctrl->data_pos == fdctrl->data_len) {
        /* 所有参数接收完毕,可以处理命令了 */
        if (fdctrl->data_state & FD_STATE_FORMAT) {
            /* 格式化命令的特殊处理 */
            fdctrl_format_sector(fdctrl);
            return;
        }
 
        /* 根据命令字节查找并调用相应的处理器 */
        pos = command_to_handler[fdctrl->fifo[0] & 0xff];  /* 从fifo[0]获取命令字节 */
        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
        /* 调用命令处理函数 */
        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
    }
    /* 注意:如果data_pos != data_len,函数返回,等待下一次写入
     * 攻击者可以继续发送数据,使data_pos继续增长,直到超过512
     */
}
enum {
    FD_CMD_READ_TRACK = 0x02,
    FD_CMD_SPECIFY = 0x03,
    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
    FD_CMD_WRITE = 0x05,
    FD_CMD_READ = 0x06,
    FD_CMD_RECALIBRATE = 0x07,
    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
    FD_CMD_WRITE_DELETED = 0x09,
    FD_CMD_READ_ID = 0x0a,
    FD_CMD_READ_DELETED = 0x0c,
    FD_CMD_FORMAT_TRACK = 0x0d,
    FD_CMD_DUMPREG = 0x0e,
    FD_CMD_SEEK = 0x0f,
    FD_CMD_VERSION = 0x10,
    FD_CMD_SCAN_EQUAL = 0x11,
    FD_CMD_PERPENDICULAR_MODE = 0x12,
    FD_CMD_CONFIGURE = 0x13,
    FD_CMD_LOCK = 0x14,
    FD_CMD_VERIFY = 0x16,
    FD_CMD_POWERDOWN_MODE = 0x17,
    FD_CMD_PART_ID = 0x18,
    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
    FD_CMD_SAVE = 0x2e,
    FD_CMD_OPTION = 0x33,
    FD_CMD_RESTORE = 0x4e,
    FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
    FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
    FD_CMD_FORMAT_AND_WRITE = 0xcd,
    FD_CMD_RELATIVE_SEEK_IN = 0xcf,
};
static const struct {
    uint8_t value;
    uint8_t mask;
    const char* name;
    int parameters;
    void (*handler)(FDCtrl *fdctrl, int direction);
    int direction;
} handlers[] = {
    { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
    { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
    { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
    { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
    { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
    { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
    { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
    { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
    { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
    { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
    { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY },
    { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
    { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
    { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
    { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
    { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
    { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
    { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
    { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
    { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
    { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
    { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
    { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
    { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
    { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
    { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
    { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
    { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
    { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
    { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
    { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
};
enum {
    FD_CMD_READ_TRACK = 0x02,
    FD_CMD_SPECIFY = 0x03,
    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
    FD_CMD_WRITE = 0x05,
    FD_CMD_READ = 0x06,
    FD_CMD_RECALIBRATE = 0x07,
    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
    FD_CMD_WRITE_DELETED = 0x09,
    FD_CMD_READ_ID = 0x0a,
    FD_CMD_READ_DELETED = 0x0c,
    FD_CMD_FORMAT_TRACK = 0x0d,
    FD_CMD_DUMPREG = 0x0e,
    FD_CMD_SEEK = 0x0f,
    FD_CMD_VERSION = 0x10,
    FD_CMD_SCAN_EQUAL = 0x11,
    FD_CMD_PERPENDICULAR_MODE = 0x12,
    FD_CMD_CONFIGURE = 0x13,
    FD_CMD_LOCK = 0x14,
    FD_CMD_VERIFY = 0x16,
    FD_CMD_POWERDOWN_MODE = 0x17,
    FD_CMD_PART_ID = 0x18,
    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回