CVE-2017-17558漏洞学习
简介
这是USB core中的一个拒绝服务漏洞。带有精心设计的描述符的恶意USB设备可以通过在配置描述符中设置过高的bNumInterfaces值来导致内核访问未分配的内存。虽然在解析期间调整了该值,但是在其中一个错误返回路径中跳过了该调整。该漏洞出现在4.15之前
补丁分析
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 55b198b..78e92d2
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -, +, @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
unsigned iad_num = ; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
+ nintf = nintf_orig = config->desc.bNumInterfaces;
+ config->desc.bNumInterfaces = ; // Adjusted later
+
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
config->desc.bLength < USB_DT_CONFIG_SIZE ||
config->desc.bLength > size) {
@@ -, +, @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
buffer += config->desc.bLength;
size -= config->desc.bLength; - nintf = nintf_orig = config->desc.bNumInterfaces;
if (nintf > USB_MAXINTERFACES) {
dev_warn(ddev, "config %d has too many interfaces: %d, "
"using maximum allowed: %d\n",
漏洞出在usb_parse_configuration函数中,而usb_parse_configuration是驱动中解析设备配置的函数。补丁将下面这一行提前了几行
nintf = nintf_orig = config->desc.bNumInterfaces;
然后再讲config->desc.bNumInterfaces置为0了
下面,先介绍USB的驱动的整体架构,然后给出一个调usb_parse_configuration的例子,解析usb_parse_configuration函数
USB驱动架构
四个重要的描述符
为了方便USB设备驱动的编写,USB核心将USB设备抽象成4个层次,自顶向下依次是
1、设备:代表整个设备,一个设备会有相对应一个文件等
2、配置:一个设备有一个或是多个配置,不同的配置使设备表现出不同的功能组合,在连接期间必须从中选择一个。比如说带话筒的耳机可以可以有3个配置,让硬件有3中工作模式:耳机工作、话筒工作、耳机和话筒同时工作。
3、接口:一个配置包含多个接口。比如一个USB音响可以有音频接口和开关按钮的接口,一个配置可以使所有的接口同时有效,并且被不同的驱动同时连接
4、端点:一个接口可以有多个端点。端点是主机与硬件设备通信的最基本形式,每个端点有属于自己的地址,并且数据的传输是单向的,每一个USB设备在主机看来就是多个端点的集合
在include/linux/usb/ch9.h中,定义了几个结构体:usb_device_descriptor、usb_config_descriptor 、usb_interface_descriptor、usb_endpoint_descriptor。分别表示上面这些含义的描述符。每个设备均会分配一个或是多个。
看一个usb_config_descriptor函数的内容,总而言之就是和配置相关的信息:
struct usb_config_descriptor {
__u8 bLength;//描述符的长度,值为USB_DT_CONFIG_SIZE=9
__u8 bDescriptorType;//描述符类型 __le16 wTotalLength;//向设备请求配置描述时获得数据包的长度
__u8 bNumInterfaces;//接口数
__u8 bConfigurationValue;//用这个值表示将要激活的配置
__u8 iConfiguration;//配置描述信息的字符串描述符的索引值
__u8 bmAttributes;//一些属性
__u8 bMaxPower;//最大的电流
} __attribute__ ((packed));
而usb_parse_configuration函数中参数之一是usb_host_config结构,定义如下,可以知道主要的结构就是上面的usb_config_descriptor内容
struct usb_host_config {
struct usb_config_descriptor desc; char *string; /* iConfiguration string, if present */ /* List of any Interface Association Descriptors in this
* configuration. */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; /* the interfaces associated with this configuration,
* stored in no particular order */
struct usb_interface *interface[USB_MAXINTERFACES]; /* Interface information available even when this is not the
* active configuration */
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; unsigned char *extra; /* Extra descriptors */
int extralen;
};
USB设备中的几个重要概念
USB驱动整体的架构如下
在硬件层面,USB设备以主机控制器为根形成一颗树,主机控制器连接在PCI总线上作为一个PCI设备,而连接实际物理设备的节点又叫Hub
USB设备状态
当一个USB设备插入接口,USB设备需要经过几个步骤才能正常开始运作,比如说要让主机和设备相互认识一下,主机给设备分配个地址什么的。在USB协议中,定义了几个状态来描述主机和设备的交互的状态:
- Attached:设备连接上Hub,表示初始状态
- Powered:,连接上设备之后,加电
- Default:加电后,设备收到复位信号,才能使用默认的地址回应主机发送过来的设备和配置描述符的请求
- Address:主机分配了一个唯一地址给设备
- Configured:表示设备以及被主机给配置过了
- Suspended:为了省电,设备可以被挂起
usb_parse_configuration函数解读
usb_parse_configuration完全由usb_get_configuration调用,从名字就可以看,usb_get_configuration就是获取USB设备的配置信息(参考前面USB设备的状态)。当一个设备被插入Hub的时候,内核最终就会调用这个函数,为了保持逻辑完整性,从usb_get_configuration函数开始看起,这个函数的参数只有一个,就是struct usb_device *dev,usb_device结构就是内核中对于一个usb设备的抽象,这个函数就是获取这个设备的配置
int usb_get_configuration(struct usb_device *dev)
{
struct device *ddev = &dev->dev;
int ncfg = dev->descriptor.bNumConfigurations;
int result = ;
unsigned int cfgno, length;
unsigned char *bigbuffer;
struct usb_config_descriptor *desc; cfgno = ;
if (dev->authorized == ) /* Not really an error */
goto out_not_authorized;
result = -ENOMEM;
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
} if (ncfg < ) {
dev_err(ddev, "no configurations\n");
return -EINVAL;
} length = ncfg * sizeof(struct usb_host_config);
dev->config = kzalloc(length, GFP_KERNEL);
if (!dev->config)
goto err2; length = ncfg * sizeof(char *);
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
if (!dev->rawdescriptors)
goto err2; desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
if (!desc)
goto err2; result = ;
for (; cfgno < ncfg; cfgno++) {
/* We grab just the first descriptor so we know how long
* the whole configuration is */
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
desc, USB_DT_CONFIG_SIZE);
if (result < ) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s: %d\n", cfgno, "start", result);
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
dev->descriptor.bNumConfigurations = cfgno;
break;
} else if (result < ) {
dev_err(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno,
USB_DT_CONFIG_SIZE, result);
result = -EINVAL;
goto err;
}
length = max((int) le16_to_cpu(desc->wTotalLength),
USB_DT_CONFIG_SIZE); /* Now that we know the length, get the whole thing */
bigbuffer = kmalloc(length, GFP_KERNEL);
if (!bigbuffer) {
result = -ENOMEM;
goto err;
}
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
bigbuffer, length);
if (result < ) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s\n", cfgno, "all");
kfree(bigbuffer);
goto err;
}
if (result < length) {
dev_warn(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno, length, result);
length = result;
} dev->rawdescriptors[cfgno] = bigbuffer; result = usb_parse_configuration(dev, cfgno,
&dev->config[cfgno], bigbuffer, length);
if (result < ) {
++cfgno;
goto err;
}
}
result = ; err:
kfree(desc);
out_not_authorized:
dev->descriptor.bNumConfigurations = cfgno;
err2:
if (result == -ENOMEM)
dev_err(ddev, "out of memory\n");
return result;
}
上面的函数逻辑也比较的简单,不断的获取配置,然后调用usb_parse_configuration去解析,其他部分的实现就不具体看了,这里主要是弄清楚,传递给usb_parse_configuration函数的几个参数的含义,dev指usb设备,cfgno指当前第几个配置,&dev->config[cfgno]指需要解析的配置,后面是buffer和长度
在usb_parse_configuration就是解析从USB设备读取过来的配置信息,判断数据正确性。
先看for循环前面的一部分,memcpy函数将buffer里面的值放入描述符内,紧接着的一个if条件判断如果失败,则会造成这个函数的结束。然而这个config->desc里面的值却没有被更改,所以可以利用恶意设备来操纵,造成前面所说的访问无效内存
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
config->desc.bLength < USB_DT_CONFIG_SIZE) {
dev_err(ddev, "invalid descriptor for config index %d: "
"type = 0x%X, length = %d\n", cfgidx,
config->desc.bDescriptorType, config->desc.bLength);
return -EINVAL;
}
cfgno = config->desc.bConfigurationValue; buffer += config->desc.bLength;
size -= config->desc.bLength; nintf = nintf_orig = config->desc.bNumInterfaces;
if (nintf > USB_MAXINTERFACES) {
dev_warn(ddev, "config %d has too many interfaces: %d, "
"using maximum allowed: %d\n",
cfgno, nintf, USB_MAXINTERFACES);
nintf = USB_MAXINTERFACES;
}
所以最后的补丁就是将config->desc.bNumInterfaces先赋值成0,这样在错误退出的路径之上就不会有无效内存的访问出现,并且在最后的大for循环退出之后,会有如下语句将这个值恢复,所以正常运行退出的时候也不会有什么错误:
config->desc.bNumInterfaces = nintf = n;
CVE-2017-17558漏洞学习的更多相关文章
- PWN二进制漏洞学习指南
目录 PWN二进制漏洞学习指南 前言 前置技能 PWN概念 概述 发音 术语 PWN环境搭建 PWN知识学习途径 常见漏洞 安全机制 PWN技巧 PWN相关资源博客 Pwn菜鸡小分队 PWN二进制漏洞 ...
- JWT漏洞学习
JWT漏洞学习 什么是JWT? JWT是JSON Web Token的缩写,它是一串带有声明信息的字符串,由服务端使用加密算法对信息签名,以保证其完整性和不可伪造性.Token里可以包含所有必要的信息 ...
- XSS漏洞学习笔记
XSS漏洞学习 简介 xss漏洞,英文名为cross site scripting. xss最大的特点就是能注入恶意的代码到用户浏览器的网页上,从而达到劫持用户会话的目的. 说白了就是想尽办法让你加载 ...
- Typecho-反序列化漏洞学习
目录 Typecho-反序列化漏洞学习 0x00 前言 0x01 分析过程 0x02 调试 0x03 总结 0xFF 参考 Typecho-反序列化漏洞学习 0x00 前言 补丁: https://g ...
- 2017年Java学习总结
2017年Java学习 Java,是我学习的第三种计算机编程语言,刚拿到这本教材时,我被它的厚度与书中字体的密集程度吓了一跳,不过在学习过程中,有Python,C语言的学习基础上,加上老师的 ...
- XXE漏洞学习笔记
XXE 参考文章 名称 地址 一篇文章带你深入理解漏洞之 XXE 漏洞 https://xz.aliyun.com/t/3357 Web Hacking 101 https://wizardforce ...
- FastJson远程命令执行漏洞学习笔记
FastJson远程命令执行漏洞学习笔记 Fastjson简介 fastjson用于将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean.fastjson.ja ...
- CVE补丁安全漏洞【学习笔记】
更新安卓系统的CVE补丁网站:https://www.cvedetails.com/vulnerability-list/vendor_id-1224/product_id-19997/version ...
- 【翻译】 Windows 内核漏洞学习—空指针解引用
Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/windo ...
- Windows 内核漏洞学习—空指针解引用
原标题:Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/w ...
随机推荐
- selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
抓取网页代码后,由于是在同一个li标签下,所以使用一次性抓取,所有的a标签,然后循环做不同的操作,但是抛出找不到元素异常. def office_page(_chrome: Chrome): sn = ...
- 微信小程序_(校园视)开发视频的展示页_上
微信小程序_(校园视) 开发用户注册登陆 传送门 微信小程序_(校园视) 开发上传视频业务 传送门 微信小程序_(校园视) 开发视频的展示页-上 传送门 微信小程序_(校园视) 开发视频的展示页-下 ...
- ARTS打卡计划第三周
Algorithms: https://leetcode-cn.com/problems/4sum/ 算法是先排序,然后按照一次循环按照三个数和两边逼中,考虑去重. Review: https://w ...
- CentOS7中使用GitBlit搭建自己的Git服务器
1.搭建依赖库 yum install java yum install git yum install -y gcc-c++ curl-devel expat-devel gettext-devel ...
- fastadmin后台视频文件上传,受限制,修改php.ini配置即可
post_max_size = 50M(根据情况)upload_max_filesize = 50M(根据情况)
- docker Swarm mode集群
基本概念 Swarm 是使用 SwarmKit 构建的 Docker 引擎内置(原生)的集群管理和编排工具. 使用 Swarm 集群之前需要了解以下几个概念. 节点 运行 Docker 的主机可以主动 ...
- ubuntu下如何高速下载?
答: 使用uget工具 1.安装uget sudo apt-get install uget -y 2.下载时在设置里指定最大连接数 笔者指定最大连接数为10,可以适当调整此值
- Oracle 基表 X$KSMLRU
Oracle 基表 X$KSMLRU 该表是Oracle的一个内部表.当SQL或者PL/SQL块向shared pool中请求一个大的连续的空间时,如果shared pool中连续的可用空间 不足,就 ...
- Dev中GridView——背景颜色改变
DevExpress.XtraGrid.Views 设置指定行的背景颜色 1.事件:CustomDrawCell2.示例: private void gridView1_CustomDrawCell( ...
- 客户端连接oracle11出现提示ORA-12514:错误解决方法
近来安装oracle11g,使用后发现plsql和sqldeveloper等客户端工具不能用,提示以下错误: 1.ORA-12514: TNS: 程序无法监听 原因:OracleOraDb11g_ho ...