时时勤拂拭,勿使惹尘埃

TOC

Categories

Android(十一)fuzzy_fastboot


fuzzy_fastboot是AOSP官方支持的fastboot fuzzer工具,主要用于给三方手机厂商进行出厂时安全测试。

0x1 编译fuzzy_fastboot

AOSP master分支自带fuzzy_fastboot源码,位于AOSP/system/core/fastboot/fuzzy_fastboot/目录,编译fuzzy_fastboot工具,在AOSP根目录执行以下指令编译即可(Android源码编译可以参考Android(七)源码编译):
$ make -j8 fuzzy_fastboot
编译完成后会在out/host/linux-x86/testcases/fuzzy_fastboot/x86_64目录下生成fuzzy_fastboot文件。

0x2 使用fuzzy_fastboot

通用测试使用方式很简单,手机需要在开发者选项里开启OEM解锁,连上电脑并进入bootloader模式,直接执行fuzzy_fastboot即可;不过测试时候需要根据提示对oem进行上锁/解锁操作:
$ adb reboot bootloader
$ ./fuzzy_fastboot 
<Waiting for Device>
[==========] Running 51 tests from 5 test cases.
[----------] Global test environment set-up.
[----------] 1 test from USBFunctionality
[ RUN      ] USBFunctionality.USBConnect
[       OK ] USBFunctionality.USBConnect (116 ms)
[----------] 1 test from USBFunctionality (116 ms total)
[----------] 24 tests from Conformance
[ RUN      ] Conformance.GetVar
PLEASE RESPOND TO PROMPT FOR 'unlocking' BOOTLOADER ON DEVICE
WAITING FOR DEVICE...............
.SUCCESS
[       OK ] Conformance.GetVar (18269 ms)
[ RUN      ] Conformance.GetVarVersionBootloader
[       OK ] Conformance.GetVarVersionBootloader (310 ms)
[ RUN      ] Conformance.GetVarVersionBaseband
[       OK ] Conformance.GetVarVersionBaseband (300 ms)
[ RUN      ] Conformance.GetVarSerialNo
[       OK ] Conformance.GetVarSerialNo (300 ms)
...

0x3 fuzzy_fastboot原理

fuzzy_fastboot工具通用测试会尝试进行51项测试,内容如下,包括usb连接、fastboot相关参数获取、oem锁状态、flash等基础功能,此外还有其他fuzz测试相关:
#usb连接测试
USBFunctionality.USBConnect
#获取配置参数
Conformance.GetVar
Conformance.GetVarVersionBootloader
Conformance.GetVarVersionBaseband
Conformance.GetVarSerialNo
Conformance.GetVarSecure
Conformance.GetVarOffModeCharge
Conformance.GetVarVariant
Conformance.GetVarRevision
Conformance.GetVarBattVoltage
Conformance.GetVarBattVoltageOk
Conformance.GetVarDownloadSize
Conformance.GetVarAll
Conformance.UnlockAbility
Conformance.PartitionInfo
Conformance.Slots
Conformance.SetActive
Conformance.LockAndUnlockPrompt
#擦除测试
Conformance.SparseBlockSupport0
Conformance.SparseBlockSupport1
Conformance.SparseDownload0
Conformance.SparseDownload1
Conformance.SparseDownload2
Conformance.SparseDownload3
Conformance.SparseVersionCheck
#oem解锁状态测试
UnlockPermissions.Download
UnlockPermissions.DownloadFlash
#oem锁状态测试
LockPermissions.DownloadFlash
LockPermissions.Erase
LockPermissions.SetActive
LockPermissions.Boot
#fuzz测试
Fuzz.DownloadSize
Fuzz.DownloadPartialBuf
Fuzz.DownloadOverRun
Fuzz.DownloadInvalid1
Fuzz.DownloadInvalid2
Fuzz.DownloadInvalid3
Fuzz.DownloadInvalid4
Fuzz.DownloadInvalid5
Fuzz.DownloadInvalid6
Fuzz.DownloadInvalid7
Fuzz.DownloadInvalid8
Fuzz.GetVarAllSpam
Fuzz.BadCommandTooLarge
Fuzz.CommandTooLarge
Fuzz.CommandMissingArgs
Fuzz.SparseZeroLength
Fuzz.SparseTooManyChunks
Fuzz.USBResetSpam
Fuzz.USBResetCommandSpam
Fuzz.USBResetAfterDownload
以下为原生fastboot支持的功能指令,其中带:号的如download:erase:flash:getvar:set_active:都是支持附加数据的,而fuzzy_fastboot即基于这些指令输入特定数据进行fuzz,尤其download指令可以直接将数据加载到内存里:
const std::vector<std::string> CMDS{"boot",    "continue", "download:",   "erase:", "flash:",
                                    "getvar:", "reboot",   "set_active:", "upload"};
这些指令来自于fastboot协议,相关说明如下:
getvar:%s          Read a config/version variable from the bootloader.
                   The variable contents will be returned after the
                   OKAY response. If the variable is unknown, the bootloader
                   should return a FAIL response, optionally with an error
                   message.

                   Previous versions of this document indicated that getvar
                   should return an empty OKAY response for unknown
                   variables, so older devices might exhibit this behavior,
                   but new implementations should return FAIL instead.

download:%08x      Write data to memory which will be later used
                   by "boot", "ramdisk", "flash", etc.  The client
                   will reply with "DATA%08x" if it has enough
                   space in RAM or "FAIL" if not.  The size of
                   the download is remembered.

upload             Read data from memory which was staged by the last
                   command, e.g. an oem command.  The client will reply
                   with "DATA%08x" if it is ready to send %08x bytes of
                   data.  If no data was staged in the last command,
                   the client must reply with "FAIL".  After the client
                   successfully sends %08x bytes, the client shall send
                   a single packet starting with "OKAY".  Clients
                   should not support "upload" unless it supports an
                   oem command that requires "upload" capabilities.

flash:%s           Write the previously downloaded image to the
                   named partition (if possible).

erase:%s           Erase the indicated partition (clear to 0xFFs)

boot               The previously downloaded data is a boot.img
                   and should be booted according to the normal
                   procedure for a boot.img

continue           Continue booting as normal (if possible)

reboot             Reboot the device.

reboot-bootloader
                   Reboot back into the bootloader.
                   Useful for upgrade processes that require upgrading
                   the bootloader and then upgrading other partitions
                   using the new bootloader.
以某一条fuzz代码为例,这次输入的数据为download:01000000\0dkjfvijafdaiuybgidabgybr
TEST_F(Fuzz, DownloadInvalid8) {
    std::string cmd("download:01000000\0dkjfvijafdaiuybgidabgybr",
                    sizeof("download:01000000\0dkjfvijafdaiuybgidabgybr"));
    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
}

0x4 fuzz结果分析

0x41 pixel_9.0

使用fuzzy_fastboot对Android9.0版本的pixel进行测试,在进行Fuzz.DownloadInvalid1项测试时,pixel设备完全无响应,按电源键(包括长按)也无反应,需要长按电源键+音量-以重启进入bootloader才能重新开机,该测试输出数据如下:
[ RUN      ] Fuzz.DownloadInvalid1
system/core/fastboot/fuzzy_fastboot/main.cpp:688: Failure
Expected equality of these values:
  DownloadCommand(0)
    Which is: Success
  DEVICE_FAIL
    Which is: Device Error
Device did not respond with FAIL for malformed download command 'download:0'
<<<<<<<< TRACE BEGIN >>>>>>>>>
[WRITE 0ms](19 bytes): "getvar:is-userspace"
[READ 22ms](19 bytes): "FAILunknown command"
[WRITE 32ms](27 bytes): "flashing get_unlock_ability"
[READ 72ms](26 bytes): "49,4e,46,4f,09,67,65,74,5f,75,6e,6c,6f,63,6b,5f,61,62,69,6c,69,74,79,3a,20,31,"
[READ 82ms](4 bytes): "OKAY"
[WRITE 122ms](15 bytes): "getvar:unlocked"
[READ 132ms](7 bytes): "OKAYyes"
[WRITE 172ms](17 bytes): "download:00000000"
[READ 182ms](12 bytes): "DATA00000000"
<<<<<<<< TRACE END >>>>>>>>>
DEVICE UNRESPONSE, attempting to recover...issued USB reset...FAIL
分析该测试源码,其实只是执行了download:0
TEST_F(Fuzz, DownloadInvalid1) {
    EXPECT_EQ(DownloadCommand(0), DEVICE_FAIL)
            << "Device did not respond with FAIL for malformed download command 'download:0'";
}
其实从前面51项测试内容来看,Fuzz.DownloadInvalid1只是fuzz测试最开始部分,后续都未开始就已经触发dos了。说明只要通过download往fastboot写数据都会导致crash。若要进行后面的测试,可以通过conf文件进行控制。

0x42 某oem手机

测试某oem手机时候,51项测试里面只通过了以下四个(usb连接、oem锁状态下测试),其他测试项均失败,故推测fastboot相关指令应该被该手机尝试修改名称 or 删除掉了:
#usb连接测试
[       OK ] USBFunctionality.USBConnect (116 ms)
#oem锁状态下的下载镜像测试
[       OK ] LockPermissions.DownloadFlash (145 ms)
#oem锁状态下的擦除测试
[       OK ] LockPermissions.Erase (133 ms)
#oem锁状态下的启动测试
[       OK ] LockPermissions.Boot (139 ms)
对比上面导致pixel设备dos的Fuzz.DownloadInvalid1项来看,测试oem失败的原因其实是无法解锁导致的:
[ RUN      ] Fuzz.DownloadInvalid1
system/core/fastboot/fuzzy_fastboot/fixtures.cpp:198: Failure
Expected equality of these values:
  fb->RawCommand("flashing " + cmd, &resp)
    Which is: Device Error
  SUCCESS
    Which is: Success
Attempting to change locked state, but 'flashingunlock' command failed
system/core/fastboot/fuzzy_fastboot/fixtures.cpp:235: Failure
Expected: SetLockState(UNLOCKED) doesn't generate new fatal failures in the current thread.
  Actual: it does.
<<<<<<<< TRACE BEGIN >>>>>>>>>
[WRITE 0ms](19 bytes): "getvar:is-userspace"
[READ 0ms](29 bytes): "FAILGetVar Variable Not found"
[WRITE 0ms](27 bytes): "flashing get_unlock_ability"
[READ 1ms](25 bytes): "INFOget_unlock_ability: 1"
[READ 1ms](4 bytes): "OKAY"
[WRITE 1ms](15 bytes): "getvar:unlocked"
[READ 1ms](6 bytes): "OKAYno"
[WRITE 1ms](15 bytes): "flashing unlock"
[READ 2ms](19 bytes): "FAILunknown command"
<<<<<<<< TRACE END >>>>>>>>>
[  FAILED  ] Fuzz.DownloadInvalid1 (121 ms)