简介

在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. redis的incr和incrby命令

    Redis Incr 命令将 key 中储存的数字值增一,如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作. Redis Incrby 命令将 key 中储存的 ...

  2. Java-JVM 类加载机制

    类的生命周期中的第一步,就是要被 JVM 加载进内存,类加载器就是来干这件事. 一.类加载器种类 系统提供了 3 种类加载器: 1.启动类加载器(Bootstrap ClassLoader) 由 C ...

  3. php的core问题

    我们在编写代码的时候,可能会发现,路径下多了一个类似这样的文件, 拿php举例  :  core.php_bin.4528.1379650721 这是因为我们在编写代码时候,出现了指针溢出,段错误等问 ...

  4. Linux安全工具之fail2ban防爆力破解

    一:简单介绍 fail2ban是一款实用软件,可以监视你的系统日志,然后匹配日志的错误信息(正则式匹配)执行相应的屏蔽动作 在企业中,有些很多人会开放root登录,这样就有机会给黑客造成暴力破解的机会 ...

  5. Zabbix - LINUX下CPU,硬盘,流量,内存监控

    转载自:https://blog.csdn.net/jxzhfei/article/details/47191431 1.LINUX下zabbix客户端安装 [root@mongodb114 ~]# ...

  6. 图片加载框架之fresco

    FaceBook推出的图片处理框架主页: https://github.com/facebook/fresco中文文档:http://fresco-cn.org/docs/index.html 功能 ...

  7. vue-template-compiler作用

    vue-template-compiler的作用是什么: 看起来 template-compiler是给parse函数使用的, 那么parse函数是干什么的呢 先看一下parse的结果: 结论:使用v ...

  8. 联想 ThinkPad 笔记本 Fn 键 关闭与启用方法

    联想 ThinkPad 笔记本 Fn 键 关闭与启用方法 [最快捷的方式] 按 Fn + Esc 键,进行切换启用或者关闭 Fn 功能键 So easy!!! ^_^

  9. ANSI C遍历二维数组指针地址

    #include <stdio.h> int main() { ][] = {,,,}; //等价于{{1,2},{3,4}}; ; i < ; i++) { ; j < ; ...

  10. delete file SVN commit error has no URL

    在提交SVN的时候遇到这个提交失败的提示: delete file SVN commit error has no URL 我的提交顺序是: 先在自己工程的文件夹删除 ->工程中删除 -> ...