通过破解固件,让三星手机变身NFC安全研究利器(三)
2020-08-31 10:50:06 Author: www.4hou.com(查看原文) 阅读量:346 收藏

通过破解固件,让三星手机变身NFC安全研究利器(一)

通过破解固件,让三星手机变身NFC安全研究利器(二)

本文是本系列当中的最后一篇,我们将继续为读者介绍如何通过破解三星手的固件,让其变身为NFC安全研究的利器。

改变漏洞利用方式

此外,我们还发现I2C通信也无法通过Logcat打印输出了,这个选项好像已经从手机的配置中完全剔除了。这样的话,我们就很难追踪更新过程中的任何变化了。最终,我找到了一个名为“/proc/nfclog”的文件,其中包含了关于何时执行IOCTL以及发送至芯片的有效载荷大小方面的数据。

有了这个文件,我可以跟踪新的固件更新过程了。最终,我注意到在固件更新开始时,发送的哈希块的大小为0x24。这是4个字节的命令头和32个字节的哈希值的长度。我认为,这意味着芯片使用的SHA-256算法,而不是SHA-1算法,幸运的是,情况确实如此。

 1.png

摸着石头过河

由于没有相关代码可供使用,所有的漏洞利用方法都是摸着石头过河的,准确来说,都是基于芯片工作原理的假设和从原始芯片中获得的知识进行的。

首先,我想证明缓冲区溢出仍然存在。为此,我开始不断增加哈希值的大小,直到芯片由于接收到太多数据而没有反应为止。我可以通过观察芯片在发送最后一条命令后没有响应,还是响应后又崩溃,来找到堆栈的具体大小。如果出现后一种情况,就说明它还在芯片的RAM范围内。

既然已经知道了堆栈的大小,我就可以用32位指针来填充它。这样就能以最快的速度找到所有链接寄存器的值,并且,即使这些寄存器被这样篡改过了,也不太可能引起问题。 1.png

到目前为止,我还不知道需要在引导加载器中跳转到哪里,因为我无法看到它,所以我决定采取蛮力攻击的方法。

从芯片上最低的地址0x0001开始,开始填充堆栈,然后,像在S3FWRN5中那样执行固件。如果此时芯片没有响应NCI请求,则说明没有找到正确的地方。这时,可以给芯片加电,将地址递增2,然后再次尝试。最终,我们发现这种攻击方法可以在地址0x0165处奏效。

提交漏洞

当我复现该漏洞后,我向三星披露了这个漏洞,因为我觉得他们也急于对其进行补救。截至2020年4月为止,他们已经在所有新生产的芯片以及当前正在开发的芯片上修复了该漏洞。尽管如此,对于之前出厂的芯片来说,仍然有可能受到该漏洞的威胁,并且可以在这些芯片上运行自定义固件。

创建自定义固件

虽然修改版本号是一个很好的POC,但它并没有充分利用这种类型的嵌入式芯片的全部功能。通过修改固件,我不仅可以仿真13.56MHz的NFC标签,嗅探NFC通信,甚至可以发动基于读卡器的攻击。

因此,我决定把初始目标变得简单:转储引导加载器。我想通过I2C接口来实现这一点,因为这样便于以后进行调试。

我打算修改一个现有的固件,并添加自己的功能。所以,我首先要找到一个空闲空间比较多的固件。于是,我下载了一个旧的三星S8 ROM,并提取了其S3NRN82固件的二进制文件。在这个文中,我找到了大约0x1800字节的FF值。这表明闪存中存在空闲的内存,因此,我可以根据需要转储自己的代码,而不会因为覆盖操作而破坏现有代码。

 1.png

我想用C语言编写我的自定义代码,因为这比编写汇编代码要容易得多。为此,我借助了ARM gcc编译器,并使用了“-c”标志。这个标志的作用是,让编译器不要进行链接或重新定位处理,而只是编译一个基本的目标文件。这种方法的缺点是,你无法访问标准库,也不能使用堆——堆的处理是要自己操心。下面,让我从一个基本的C函数开始介绍。

 1.png

在C语言中,函数调用是以分支和链接指令的形式进行的,它将当前的程序计数器存储在链接寄存器中,然后以相对寻址方式跳转到当前位置。之所以这样做,主要是为了从被调用的函数中返回。由于我的C函数在编译时对返回地址和堆栈进行了相应的处理,我可以根据需要通过覆盖实际固件中现有的Branch和Link指令跳转到相应的地址。

 1.png

Branch和Link指令使用的是补码形式的相对地址,因此,我们可以跳转执行当前位置前后的代码。这个过程可能有点复杂,需要重复进行计算,为此,我专门编写了一个函数,让它来完成这些繁琐的计算。

 1.png

我对这个构建应用程序进行了扩展,这样就可以完成函数重定位操作了。这意味着,我不仅可以从自定义C代码中的函数中调用函数,以实现尽可能多的功能,同时,也更易于在代码中获得相对于自定义函数的地址。

这个工具来说,不仅能够以最基本的形式编译我的C函数,还能将它们转储到固件的有效载荷中,从而覆盖相应的目标函数。

 1.png

1.png

在这里,我决定覆盖一个与供应商密切相关的NCI函数“2F 24”,因为我发现可以向其发送任意长度的参数,同时,该函数在我所考察的设备上没有任何有意义的功能。

为此,我们需要搜索可能设置NCI响应的位置,即以“4F 24”开头的地址——我在IDA中用正则表达式搜索了一下“MOVS.*#0x24”,竟然找到了一个函数调用,这肯定是我需要修改的那个函数。

 1.png

简单的分析表明,“sub_119BE”函数发送的是I2C响应,所以我选择覆盖“sub_11A76”函数,因为这样我就可以修改响应数据了,而不允许其他任何操作。

此外,我希望能够接收参数,例如内存读取的地址,但就目前来说,我还不知道它们会被存储在RAM中的哪些地方。为了找到这些地址,我创建了一个NCI请求,即“2F 24 04 FA CE FA CE”,然后开始搜索这个任意却唯一的值。然后,我让代码转储这个地址,这样我就可以根据需要在函数中读取它了。

 1.png

有了这一切,我就可以写出转储任意内存的代码了。这样,我们就可以用它来转储引导加载器了。

之后,我对新的引导加载器进行了反汇编,以便查找与漏洞有关的代码。

 1.png

我发现地址0x0165处的代码会读取一个存储在R0寄存器中的未知地址,然后攻击就得手了。我决定修改它,使其匹配S3FWRN5的漏洞。

标签仿真

由于我们可以编写自定义固件,所以,我决定先从仿真NFC标签开始入手。

这些芯片支持常见的13.56MHz协议,如ISO14443a和ISO14443b,在读卡器模式下,能够支持基于这些标准的各种标签类型。我想完整地模拟一个Mifare Classic标签,于是开始研究如何利用ISO14443a。为此,我使用proxmark来展开调试过程。

 1.png

为了让手机设置成在默认状态下作为标签运行,我按顺序重放了启动芯片的 NCI 命令。然后,我依次查看了这些命令,并删除了那些不影响NFC通信的命令。

 1.png

我发现最后发送的命令是RF discover命令,我可以将其修改为只仿真ISO14443a标签,而非其他类型的标签。

硬件外设

我需要学习很多关于芯片硬件功能的错综复杂的知识,所以开始对我正在使用的固件进行分析。我首开始搜索ISO14443a SELECT命令(0x93)方面的资料,看看它在哪里被使用。第一个搜索结果就提供了该硬件相关的信息。

 1.png

以0x4xxxxxxx开头的地址一般都是Cortex-M芯片中的硬件外设,这意味着0x93的值是从该内存空间中的某处进行比较的。利用我之前的任意读取命令,我将0x40020000的内存全部转储,因为这很可能是处理芯片上的NFC通信,而每个硬件外设都是单独寻址的。

 1.png

当手机作为NFC读卡器查看这些地址,会显示出NFC WUPA(0x52)命令,该命令是用于枚举的,这意味着这是个正确的通信硬件地址。同时,在较低地址处有一些配置数据。

为了模拟不同的NFC标签,首先需要改变枚举信息。与NFC标签进行通信时,首先需要获取其类型及其唯一标识符的信息,它们通常由以下值组成:

ATQA:由NCI定义。

SAK:由NCI定义。

UID:由手机随机分配,第一个字节总是0x08。

其中,ATQA和SAK这两个值用于定义标签类型和UID大小,虽然这些值可以通过NCI进行设置,但设置特定的位可以防止针对特定标签类型的欺骗。

由于它们是用于枚举的,与正常通信相比,会有一定的时间限制,所以,很可能会借助于特定的硬件寄存器来提高响应速度。

我通过NCI写入了非常易于识别的ATQA和SAK值,然后搜索RAM。通过这种方法,可以了解访问了哪些静态存储器,进而在固件中找到设置这些值的函数。

 1.png

我用自己的C函数覆盖了这个函数,然后,从这个新创建的函数中调用它来完成其他的硬件设置,并按照我的要求修改了SAK、ATQA和UID值。

 1.png

我使用proxmark对使用这个自定义固件的手机进行了读取,发现成功了。

 1.png

接下来,我打算覆盖该芯片的全部通信功能。为此,我首先在固件中搜索HALT (0x50)和RATS (0xE0)命令,因为已知当前形式的固件是支持这些命令的。然后,作为对比我又搜索RATS值,得到了四个结果,其中一个是固件中NFC通信的状态机。

 1.png

通过这个函数,我找到了与响应有关的信息,这样一来,我就可以通过编写C代码来为自己完成相应的操作了。

1.png

我覆盖了状态机,以便可以添加自己的功能。

我实现了一个简单的读命令,对于大多数Mifare标签来说,可以使用0x30作为命令值,后面是一个块,最后是CRC。我这样做是为了给标签提供基本的功能,以便将来继续扩展。

 1.png

至此,我完全实现了Mifare Classic通信,并准备好了补丁代码。我还为NFC添加了一些调试功能。

 1.png

然而,我们还要注意HALT命令,它可以对硬件状态机执行一些修改。为了简单起见,我调用了核心固件中处理halts的函数调用。

实现完善的通信功能后,我就可以使用proxmark来认证和读取块了。

 1.png

虽然这在proxmark上可以工作,但在合法的读卡器上却无法工作。这是由于与Mifare Classic的通信是经过加密处理的。

扩展硬件功能

根据ISO14443a协议,每传输8位就有一个额外的位用作奇偶校验以进行错误检查。根据设置位数是奇数还是偶数,这个位被相应的设置为1或0。根据标准做法,芯片会负责生成这个额外的位,并会在响应中使用8位缓冲区。这会带来一些问题,因为这个位在合法的Mifare通信中是加密的。我觉得在芯片的寄存器中很可能有一个未知的配置用以支持这个功能,于是我开始寻找它。

1.png 

我在硬件寄存器的前64个字节中设置和取消设置位,并评估每个位对响应的影响。通过这种方法,我得到了一些有趣的结果:我发现在地址0x40020004处,可以通过设置位0x4000来修改奇偶校验类型。

这样做的目的是设置第九位,然而这意味着我必须将9位二进制数字放入8位空间中。我通过对数据进行移位来解决这个问题的。

 1.png

这样一来,就可以完全实现Mifare Classic了。

为该固件添加的最后一个功能是转储写入。当通过I2C上传标签二进制文件时,我希望它们被读卡器修改的同时,标签中的文件也能发生相应的改变。要做到这一点,我只需在每次执行写入时发送包含新数据的I2C响应即可。

 1.png

为此,我创建了一个简单的demo,通过用另一部手机阅读S9,来演示标签仿真的运行情况。

利用标签仿真技术,不仅可以欺骗13.56MHz的门禁卡,而且还可以发到其他攻击。需要注意的是,启用了这个补丁后,所有其他NFC功能仍然能够正常工作。

我觉得这种方法比专门的攻击工具更加巧妙,如果进行相应的扩展,比如添加离线破解等功能,则可以用于测试各种NFC协议。

我发现,这种仿真技术不仅可以扩展到各种协议,并且通过修改硬件寄存器的特定方式,我还可以进行一些简单的被动型嗅探攻击。有了这个框架之后,我会进一步丰富其具体功能。

小结

截至2020年4月,三星已修复了上述所有漏洞。

要想利用该漏洞,需要root访问权限。

应当将手机视为一种容易遭到入侵的嵌入式设备。

引导加载器漏洞比你想象的更常见,尤其是在手机中。

为专有芯片开发定制固件是一项挑战,同时也能学到很多东西。

如果在旧的芯片中发现了未公开的漏洞,那么大概率也能在新的芯片中找到它。

这个项目的所有代码都可以在GitHub上找到,具体地址为: https://github.com/Iskuri/Samsung-NFC-Controller-Reverse-Engineering。

本文翻译自:https://www.pentestpartners.com/security-blog/breaking-samsung-firmware-or-turning-your-s8-s9-s10-into-a-diy-proxmark/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/EGVk
如有侵权请联系:admin#unsafe.sh