CVE-2018-20169漏洞学习
简介
在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 5286640..f76b2e0a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2251,7 +2251,7 @@ static int usb_enumerate_device_otg(struct usb_device *udev)/* descriptor may appear anywhere in config */err = __usb_get_extra_descriptor(udev->rawdescriptors[0],le16_to_cpu(udev->config[0].desc.wTotalLength), - USB_DT_OTG, (void **) &desc); + USB_DT_OTG, (void **) &desc, sizeof(*desc));if (err || !(desc->bmAttributes & USB_OTG_HNP))return 0;diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 79d8bd7..4ebfbd7 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -832,14 +832,14 @@ 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 < 2) { + if (header->bLength < 2 || header->bLength > size) {printk(KERN_ERR"%s: bogus descriptor, type %d length %d\n",usbcore_name, @@ -848,7 +848,7 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,return -1;}- if (header->bDescriptorType == type) { + if (header->bDescriptorType == type && header->bLength >= minsize) {*ptr = header;return 0;} diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 684d6f0..09a8ebd 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -640,7 +640,7 @@ 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 == -1) {dev_warn(dev, "BUG? WUSB host has no security descriptors\n");return 0; diff --git a/include/linux/usb.h b/include/linux/usb.h index 4cdd515..5e49e82 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -407,11 +407,11 @@ 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[1]; /* 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 < 2) {printk(KERN_ERR"%s: bogus descriptor, type %d length %d\n",usbcore_name,header->bDescriptorType,header->bLength);return -1;}if (header->bDescriptorType == type) {*ptr = header;return 0;}buffer += header->bLength;size -= header->bLength;}return -1; } 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[0],le16_to_cpu(udev->config[0].desc.wTotalLength),USB_DT_OTG, (void **) &desc) == 0)
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的情况,所以最终会造成不正确的数据向上传递。
转载于:https://www.cnblogs.com/likaiming/p/10886154.html
CVE-2018-20169漏洞学习相关推荐
- 大牛预测2018年深度学习走向:大批AI硬件初创将失败
来源:智东西 导语:本文作者Carlos E. Perez是Intuition Machine公司的创始人,曾经编写<人工直觉与深度学习手册>( Artificial Intuition ...
- 打脸!2018年深度学习发展速度被严重高估
打脸!2018年深度学习发展速度被严重高估 https://mp.weixin.qq.com/s/JaqEbgcJA0VCyL6Zt_xUgg 策划编辑 | Debra 作者 | Carlos E. ...
- 2018年强化学习领域十篇重要论文(附源码)
2019-06-06 09:47:57 来自网络 与其他机器学习方法相比,比如监督式学习.迁移学习.甚至非监督式学习学习相比,深度强化学习方法极其需要大量数据,而且常常不稳定,从性能上来说可能不是最好 ...
- Hacking Team Flash 0day漏洞学习笔记
周日的夜晚,与囧桑下载下来Hacking Team之前爆出的flash 0day漏洞,怀着紧张激动的心情,在自己的机子上做了实验,经测试,在我的虚拟机(一个sp3的XP,貌似没装Flash)上根本跑不 ...
- Java开发2018年值得学习的10大技术
转载自 Java开发2018年值得学习的10大技术 作为一个开发人员,我们最大的挑战就是保持自己了解新的技术.技术变化很快,你大概每两年就会看到一个新版本的编程语言和框架. 就拿2017年来说,AR. ...
- log4j2远程代码执行漏洞学习总结
log4j2远程代码执行漏洞学习总结 背景 近期log4j2的漏洞闹得沸沸扬扬,在工作之余也是找了一些资料看一下相关的内容,到现在网上的总结已经很全了,B站上有各种漏洞复现,各大博客类网站关于JNDI ...
- WEB安全全基础漏洞学习
本文省略了SQL注入和xss漏洞,需要的可以网上找资料,资料非常多 web安全全基础漏洞学习 CSRF 简介 跨站请求伪造 (Cross-Site Request Forgery, CSRF),也被称 ...
- 苹果2017年漏洞学习总结
苹果2017年漏洞学习总结 一. 漏洞资料列举: 1. Yalu102 漏洞编号:CVE-2017-2370 作者:kpwn https://github.com/kpwn/yalu ...
- APP安全漏洞学习笔记
APP安全漏洞学习笔记 本文首先明确了APP安全的目标,然后对常见的APP漏洞进行了整理分析,并研究学习了APK的静态分析与动态分析技术,最后介绍了安卓的渗透测试技术和常见的安全评估工具.附录处整理了 ...
- 大数据告诉你:2018年该学习什么技术
前几天,数据科学家Julia Silge在Stack Overflow官方博客上分享了一组分析数据,他在文中揭示了快速增长的技术,快速衰落的技术,稳步增长的技术.我们从中可以看到,2018年你学习什么 ...
最新文章
- 阿里感悟(九)-如何才能晋升
- 2.1/2.2 系统目录结构, 2.3 ls命令, 2.4 文件类型, 2.5 alias命令
- 如何快速查看单链表倒数第K个元素
- 能够抑制网络风暴的是?
- Windows Hello 可绕过漏洞进行身份认证
- BI工具升级动态增量新功能,让大数据量入集市更便捷
- 复现经典:《统计学习方法》第 12 章 监督学习方法总结
- 大容量磁盘分区表、文件系统、分区工具的选择
- 每日程序C语言43-链表原地逆置
- bind简单转发实验
- KNN实现CIFAR-10数据集识别
- OSGI概念理解和入门
- 基于 HanLP 的 ES 中文分词插件
- Graft货币(GRFT)结点搭建
- docker部署php的性能,Docker 学习之部署php + nginx(一)
- R Markdown 使用方法笔记
- webgl渲染Yuv420P图像
- 【元胞自动机】基于matlab元胞自动机城市规划【含Matlab源码 125期】
- ElasticSearch常用的几种查询方式
- android 头像修改
热门文章
- 数据仓库系列1-高质量数据建模
- ACM 未解决的问题
- 为什么要使用设计模式?
- 一步一步学习C#(一)
- 如何:在 Visual Studio 中添加或移除引用(转载)
- mysql性能调优与架构设计_了解架构设计远远不够!一文拆解 Tomcat 高并发原理与性能调优
- springdatajpa命名规则_简单了解下spring data jpa
- mysql子查询日期比较_数据分析系列 16/32 | MySQL中子查询与联合查询
- python新特性赋值_变量与赋值_Python入门视频课程_Python视频-51CTO学院
- java预编译啥意思_java预编译 java jdbc 预编译语句和普通语句的区别