简介

在4.19.9之前的Linux内核中发现了一个问题。USB子系统在读取与驱动程序/ USB /core/usb.c中的_usb_get_extra_descriptor相关的额外描述符时错误地检查了大小。

补丁分析

补丁见这里:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=704620afc70cf47abb9d6a1a57f3825d2bca49cf

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ..f76b2e0a
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -, +, @@ static int usb_enumerate_device_otg(struct usb_device *udev)
/* descriptor may appear anywhere in config */
err = __usb_get_extra_descriptor(udev->rawdescriptors[],
le16_to_cpu(udev->config[].desc.wTotalLength),
- USB_DT_OTG, (void **) &desc);
+ USB_DT_OTG, (void **) &desc, sizeof(*desc));
if (err || !(desc->bmAttributes & USB_OTG_HNP))
return ; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 79d8bd7..4ebfbd7
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -, +, @@ EXPORT_SYMBOL_GPL(usb_get_current_frame_number);
*/ int __usb_get_extra_descriptor(char *buffer, unsigned size,
- unsigned char type, void **ptr)
+ unsigned char type, void **ptr, size_t minsize)
{
struct usb_descriptor_header *header; while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer; - if (header->bLength < ) {
+ if (header->bLength < || header->bLength > size) {
printk(KERN_ERR
"%s: bogus descriptor, type %d length %d\n",
usbcore_name,
@@ -, +, @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
return -;
} - if (header->bDescriptorType == type) {
+ if (header->bDescriptorType == type && header->bLength >= minsize) {
*ptr = header;
return ;
}
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
index 684d6f0..09a8ebd
--- a/drivers/usb/host/hwa-hc.c
+++ b/drivers/usb/host/hwa-hc.c
@@ -, +, @@ static int hwahc_security_create(struct hwahc *hwahc)
top = itr + itr_size;
result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
- USB_DT_SECURITY, (void **) &secd);
+ USB_DT_SECURITY, (void **) &secd, sizeof(*secd));
if (result == -) {
dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
return ;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 4cdd515..5e49e82
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -, +, @@ struct usb_host_bos {
}; int __usb_get_extra_descriptor(char *buffer, unsigned size,
- unsigned char type, void **ptr);
+ unsigned char type, void **ptr, size_t min);
#define usb_get_extra_descriptor(ifpoint, type, ptr) \
__usb_get_extra_descriptor((ifpoint)->extra, \
(ifpoint)->extralen, \
- type, (void **)ptr)
+ type, (void **)ptr, sizeof(**(ptr)))

总共修改了四个文件,但是修改都围绕着__usb_get_extra_descriptor这个函数,包括这个函数的定义以及引用这个函数的位置。补丁中位这个函数增加了一个参数minsize,然后在__usb_get_extra_descriptor的逻辑中增加了判断,在__usb_get_extra_descriptor正常退出也就是返回0的逻辑之中,让bLength必须大于minsize

源码分析

USB中的5种描述符中,都有共同的两个字段,这个两个字段放在描述符的头部,表示描述符长度,描述符类型编号,用usb_descriptor_header来表示

struct usb_descriptor_header {
__u8 bLength;
__u8 bDescriptorType;
} __attribute__ ((packed)); struct usb_device_descriptor {
__u8 bLength;
__u8 bDescriptorType; __le16 bcdUSB;
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0;
__le16 idVendor;
__le16 idProduct;
__le16 bcdDevice;
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations;
} __attribute__ ((packed)); struct usb_config_descriptor {
__u8 bLength;
__u8 bDescriptorType; __le16 wTotalLength;
__u8 bNumInterfaces;
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed)); struct usb_string_descriptor {
__u8 bLength;
__u8 bDescriptorType; __le16 wData[]; /* UTF-16LE encoded */
} __attribute__ ((packed)); struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType; __u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__ ((packed)); struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType; __u8 bEndpointAddress;
__u8 bmAttributes;
__le16 wMaxPacketSize;
__u8 bInterval; /* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));

在include\linux\usb\ch9.h中,可以找到上述这些描述符的定义,然后在该文件中,同样可以找到这些描述符中各个字段的取值,我们看前两个字段bLength和bDescriptorType。

有USB_DT_CONFIG_SIZE和USB_DT_DEVICE_SIZE等这些宏,这就是bLength的取值,表示这个描述符中占多少个字节,__u8就表示一个字节,__lel16就表示两个字节。

bDescriptorType同样,取值是USB_DT_DEVICE,USB_DT_CONFIG等,就是单纯用来区分这些描述符的类型

但是除了上述几种描述符之外,还有一类设备定义的描述符和厂商为设备特别定义的描述符。在内核中描述设备、接口、配置、端口等信息的时候,使用的是另外的结构,他们以usb_host_开头,看include\linux\usb.h中关于端口的描述,描述符被存放在第一个字段

struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
struct usb_ss_ep_comp_descriptor ss_ep_comp;
struct list_head urb_list;
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */ unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled;
};

结构体中,还会有extra字段,这就是上面所说的设备定义的描述符和厂商为设备特别定义的描述符。

好,接下来看__usb_get_extra_descriptor的实现,这个函数用来在buffer中取出一个特定类型的描述符,地址写在ptr中。在修改之前,这个函数有4个参数,第一个参数表示描述符数组,第二个参数表示这个buffer中描述符项数,第三个参数为需要寻找的描述符类型,第四个参数表示最终结果,描述符位置

int __usb_get_extra_descriptor(char *buffer, unsigned size,
unsigned char type, void **ptr)
{
struct usb_descriptor_header *header; while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer; if (header->bLength < ) {
printk(KERN_ERR
"%s: bogus descriptor, type %d length %d\n",
usbcore_name,
header->bDescriptorType,
header->bLength);
return -;
} if (header->bDescriptorType == type) {
*ptr = header;
return ;
} buffer += header->bLength;
size -= header->bLength;
}
return -;
}
EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor);

所以上面这个函数的逻辑也比较清楚了,从buffer中不断遍历,直到找到需要类型的描述符为止。

接下来看调用这个函数的位置,也是上述补丁中调用__usb_get_extra_descriptor的两个函数:

usb_enumerate_device_otg中,需要从rawdescriptors中取出usb_otg_descriptor。rawdescriptors是字符指针数组,在USB枚举阶段,主机使用GET_DESCRIPTOR请求去获得配置描述符所得到的结果。所有的配置描述符都放着这里,这个函数中需要取出USB_DT_OTG,类型的描述符,OTG是电源管理相关的配置。从这个函数也可以看得出来。config表示所有的配置描述符,wTotalLength表示USB枚举阶段从设备默认端口获得的配置描述信息的长度

__usb_get_extra_descriptor (udev->rawdescriptors[],
le16_to_cpu(udev->config[].desc.wTotalLength),
USB_DT_OTG, (void **) &desc) == )

hwahc_security_create中同样的用法,actconfig表示的是当前正在使用的配置描述符,其他同上。

result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
USB_DT_SECURITY, (void **) &secd);

漏洞分析

所以该漏洞的核心点在于bLength的值,补丁在bLength异常时会返回-1,有判断bLength小于2的情况,但是没有判断bLength大于size的情况,所以最终会造成不正确的数据向上传递。

CVE-2018-20169漏洞学习的更多相关文章

  1. XXE漏洞学习笔记

    XXE 参考文章 名称 地址 一篇文章带你深入理解漏洞之 XXE 漏洞 https://xz.aliyun.com/t/3357 Web Hacking 101 https://wizardforce ...

  2. PWN二进制漏洞学习指南

    目录 PWN二进制漏洞学习指南 前言 前置技能 PWN概念 概述 发音 术语 PWN环境搭建 PWN知识学习途径 常见漏洞 安全机制 PWN技巧 PWN相关资源博客 Pwn菜鸡小分队 PWN二进制漏洞 ...

  3. XSS漏洞学习笔记

    XSS漏洞学习 简介 xss漏洞,英文名为cross site scripting. xss最大的特点就是能注入恶意的代码到用户浏览器的网页上,从而达到劫持用户会话的目的. 说白了就是想尽办法让你加载 ...

  4. Typecho-反序列化漏洞学习

    目录 Typecho-反序列化漏洞学习 0x00 前言 0x01 分析过程 0x02 调试 0x03 总结 0xFF 参考 Typecho-反序列化漏洞学习 0x00 前言 补丁: https://g ...

  5. JWT漏洞学习

    JWT漏洞学习 什么是JWT? JWT是JSON Web Token的缩写,它是一串带有声明信息的字符串,由服务端使用加密算法对信息签名,以保证其完整性和不可伪造性.Token里可以包含所有必要的信息 ...

  6. FastJson远程命令执行漏洞学习笔记

    FastJson远程命令执行漏洞学习笔记 Fastjson简介 fastjson用于将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean.fastjson.ja ...

  7. CVE补丁安全漏洞【学习笔记】

    更新安卓系统的CVE补丁网站:https://www.cvedetails.com/vulnerability-list/vendor_id-1224/product_id-19997/version ...

  8. 【转】Vulhub - 开源的安全漏洞学习与复现项目

    转载于:https://uk.v2ex.com/t/485611#reply15 Vulhub 是一个面向大众的开源漏洞靶场,无需 docker 知识,简单执行两条命令即可编译.运行一个完整的漏洞靶场 ...

  9. CVE-2017-6920 Drupal远程代码执行漏洞学习

     1.背景介绍: CVE-2017-6920是Drupal Core的YAML解析器处理不当所导致的一个远程代码执行漏洞,影响8.x的Drupal Core. Drupal介绍:Drupal 是一个由 ...

  10. 代码审计之CVE-2017-6920 Drupal远程代码执行漏洞学习

     1.背景介绍: CVE-2017-6920是Drupal Core的YAML解析器处理不当所导致的一个远程代码执行漏洞,影响8.x的Drupal Core. Drupal介绍:Drupal 是一个由 ...

随机推荐

  1. Linux用户以及组的添加与删除

    查看centos中的用户和用户组 1.用户列表文件:/etc/passwd/ 2.用户组列表文件:/etc/group 3.查看系统中有哪些用户: cut -d : -f 1 /etc/passwd ...

  2. PHP反序列化总结

    之前遇到过很多次php反序列化相关的内容,总结一下. (反)序列化给我们传递对象提供了一种简单的方法.serialize()将一个对象转换成一个字符串,unserialize()将字符串还原为一个对象 ...

  3. 邻居子系统 之 邻居项创建__neigh_create

    概述 IP层输出数据包会根据路由的下一跳查询邻居项,如果不存在则会调用__neigh_create创建邻居项,然后调用邻居项的output函数进行输出: __neigh_create完成邻居项的创建, ...

  4. vue事件.navtive 的使用

    我们可以直接在组件标签上绑定事件了 然后在 methods 的对象中调用这个方法了 正常情况下是不可以的,但是我们可以使用事件修饰符 .navtive 就可以实现了 props的对象写法

  5. PHP冒泡排序原生代码

    //冒泡排序 $arr=array(23,5,26,4,9,85,10,2,55,44,21,39,11,16,55,88,421,226,588); $n =count($arr); //echo ...

  6. echo、print、print_r之间的区别

    echo php语句:可以一次输出多个值,多个值之间用逗号隔开:没有返回值,输出标量的值.print 函数:只能打印简单类型变量的值(标量,如int,string),返回值为布尔型print_r 函数 ...

  7. javascript之常用事件

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. OOP、DI、IOC的关系

    此随笔的重点在“Demo分析”一章,以代码的分阶段变化讲述了DI,DIP,IOC的演变,写在前面文字均为铺垫. 希望各位园友拍砖,促使流浪者的进步,现在有很多问题想讨论,即以此文寻找志同道合的园友,另 ...

  9. SELECT * 测试

    描述 大家通常禁止在生产环境直接使用select * 已成常识了,也常常在开发规范中就会规定不允许直接使用select *,那么我们为什么不允许使用select * ,在一些什么场景下select * ...

  10. debain安装文泉驿字体

    sudo apt-get install ttf-wqy-microhei sudo apt-get install ttf-wqy-zenhei