简介

在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. (转载)深入理解Java:内省(Introspector)

    本文转载自:https://www.cnblogs.com/peida/archive/2013/06/03/3090842.html 一些概念: 内省(Introspector) 是Java 语言对 ...

  2. idea 远程代码调试

    声明一点:重要的事情说3遍 本地代码和服务器代码必须一致 本地代码和服务器代码必须一致 本地代码和服务器代码必须一致 第一步,创建remote 第二步.填写服务器信息 第三部.tomcat/bin/s ...

  3. LeetCode 48. 旋转图像(Rotate Image)

    题目描述 给定一个 n × n 的二维矩阵表示一个图像. 将图像顺时针旋转 90 度. 说明: 你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵.请不要使用另一个矩阵来旋转图像. 示例 1: ...

  4. linux开启数据库远程连接

    1.阿里云开启数据库端口 3306 2. 修改 Mysql-Server 用户配置 mysql -uroot -p(注意此用户必须要有最高级权限才行 默认root用户) mysql> USE m ...

  5. Spring Boot Cli下载安装

    本地下载地址: spring-boot-cli-2.1.8.RELEASE-bin.zip  : https://pan.baidu.com/s/1GMyxj1PecsM4BG_hzoteVQ spr ...

  6. Android 使用date set命令修改系统时间

    测试环境:android 7.1.1 在adb shell中试图使用 date -s "yyyymmdd.[[[hh]mm]ss]"修改系统系统时间时,会提示 date: Unkn ...

  7. leetcode378 有序矩阵中第k小的元素

    排序后取数组第k个元素,遍历需要n^2的复杂度,查找插入logn,时间复杂度O(n^2logn).方法很笨,完全就是STL过于牛x运行通过的. class Solution { public: int ...

  8. pandas之数据处理操作

    1.pandas对缺失数据的处理 我们的数据缺失通常有两种情况: 1.一种就是空,None等,在pandas是NaN(和np.nan一样) 解决方法: 判断数据是否为NaN:pd.isnull(df) ...

  9. openstack部署keystone

    环境: 免密钥,域名解析 cat /etc/hosts 192.168.42.120 controller 192.168.42.121 compute 192.168.42.122 storage ...

  10. Node.js的事件轮询Event Loop原理

    Node.js的事件轮询Event Loop原理解释 事件轮询主要是针对事件队列进行轮询,事件生产者将事件排队放入队列中,队列另外一端有一个线程称为事件消费者会不断查询队列中是否有事件,如果有事件,就 ...