CVE-2018-19985漏洞學習

簡介

4.19.8以前,在Linux內核中,hso_probe()函數中發現了一個缺陷,該函數從USB設備(做爲u8)讀取if_num值,而且不須要對數組進行長度檢查就使用它來索引數組,從而致使在hso_probe()或hso_get_config_data()中讀取OOB內存。攻擊者使用僞造的USB設備和對系統的物理訪問(須要鏈接這樣的設備)能夠致使系統崩潰和拒絕服務。linux

補丁分析

補丁在這裏:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5146f95df782b0ac61abde36567e718692725c89ios

diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 184c24b..d6916f7 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -2807,6 +2807,12 @@ static int hso_get_config_data(struct usb_interface *interface)
         return -EIO;
     }
 
+    /* check if we have a valid interface */
+    if (if_num > 16) {
+        kfree(config_data);
+        return -EINVAL;
+    }
+
     switch (config_data[if_num]) {
     case 0x0:
         result = 0;
@@ -2877,10 +2883,18 @@ static int hso_probe(struct usb_interface *interface,
 
     /* Get the interface/port specification from either driver_info or from
      * the device itself */
-    if (id->driver_info)
+    if (id->driver_info) {
+        /* if_num is controlled by the device, driver_info is a 0 terminated
+         * array. Make sure, the access is in bounds! */
+        for (i = 0; i <= if_num; ++i)
+            if (((u32 *)(id->driver_info))[i] == 0)
+                goto exit;
         port_spec = ((u32 *)(id->driver_info))[if_num];
-    else
+    } else {
         port_spec = hso_get_config_data(interface);
+        if (port_spec < 0)
+            goto exit;
+    }
 
     /* Check if we need to switch to alt interfaces prior to port
      * configuration */

肯定問題出在drivers/net/usb/hso.c,分析一下這個文件git

源碼分析

文件最後,有模塊的一些操做,文件開頭有一些註釋,得知這是文件是一個驅動的實現,Option High Speed Mobile Devices,還有一個專門的編譯選項CONFIG_USB_HSO數組

module_init(hso_init);
module_exit(hso_exit);

MODULE_AUTHOR(MOD_AUTHOR);
MODULE_DESCRIPTION(MOD_DESCRIPTION);
MODULE_LICENSE(MOD_LICENSE);

初始化函數是hso_init,卸載函數是hso_exit,卸載函數比較簡單,只是註銷了tty和usb的驅動,一樣在hso_init函數中,也註冊了這兩個驅動。函數

static void __exit hso_exit(void)
{
    printk(KERN_INFO "hso: unloaded\n");

    tty_unregister_driver(tty_drv);
    /* deregister the usb driver */
    usb_deregister(&hso_driver);
}

這應該是一個連在USB總線上的串口,下層使用USB來和硬件交流,而後將USB得到的信息傳遞給tty層。從文件中定義的tty_operations這些個函數看下去,最終實現都是USB這邊的實現,usb_submit_urb、usb_control_msg函數源碼分析

static const struct tty_operations hso_serial_ops = {
    .open = hso_serial_open,
    .close = hso_serial_close,
    .write = hso_serial_write,
    .write_room = hso_serial_write_room,
    .ioctl = hso_serial_ioctl,
    .set_termios = hso_serial_set_termios,
    .chars_in_buffer = hso_serial_chars_in_buffer,
    .tiocmget = hso_serial_tiocmget,
    .tiocmset = hso_serial_tiocmset,
    .get_icount = hso_get_count,
    .unthrottle = hso_unthrottle
};

hso_probe函數是定義的usb驅動的probe函數,就是USB設備插上以後,USB驅動第一個被調用的函數,在這個函數中應該要作一些設備的初始化功能spa

static struct usb_driver hso_driver = {
    .name = driver_name,
    .probe = hso_probe,
    .disconnect = hso_disconnect,
    .id_table = hso_ids,
    .suspend = hso_suspend,
    .resume = hso_resume,
    .reset_resume = hso_resume,
    .supports_autosuspend = 1,
};

補丁的修改位置就在hso_probe的開頭,問題出在if_num處code

    if_num = interface->altsetting->desc.bInterfaceNumber;

    /* Get the interface/port specification from either driver_info or from
     * the device itself */
    if (id->driver_info)
        port_spec = ((u32 *)(id->driver_info))[if_num];
    else
        port_spec = hso_get_config_data(interface);

    if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
        dev_err(&interface->dev, "Not our interface\n");
        return -ENODEV;
    }

altsetting,這個值表示可選的設置,bInterfaceNumber表示該配置的接口號。這些值都是USB枚舉階段從外部設備讀入的值,也就是if_num這個值是能夠被惡意的外部設備所控制blog

在port_spec = ((u32 *)(id->driver_info))[if_num];這句話,當id->driver_info==0的時候if_num的超過會形成越界訪問,不過好像看了下hso_ids中的值,會有等於0的狀況嗎?索引

而後是hso_get_config_data函數,開頭一樣有這樣的一段和上述代碼相似

    struct usb_device *usbdev = interface_to_usbdev(interface);
    u8 config_data[17];
    u32 if_num = interface->altsetting->desc.bInterfaceNumber;
    s32 result;

    if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
                0x86, 0xC0, 0, 0, config_data, 17,
                USB_CTRL_SET_TIMEOUT) != 0x11) {
        return -EIO;
    }

    switch (config_data[if_num]) {

一樣是對於interface->altsetting->desc.bInterfaceNumber這個值的數據訪問。因此加上一個越界判斷

相關文章
相關標籤/搜索