the samsung galaxy s8 is missing a hrng
This commit is contained in:
@@ -43,7 +43,10 @@ to take state snapshots etc. on a physical device.
|
||||
Moreover, we just published [Polypyus](https://github.com/seemoo-lab/polypyus).
|
||||
It enables binary-only binary diffing, independent from *IDA* and *Ghidra*. However,
|
||||
it integrates into that workflow by identifying good starting points for further
|
||||
analysis. We already tried it across various *Broadcom* Wi-Fi and Bluetooth firmware.
|
||||
analysis. We already tried it across various *Broadcom* Wi-Fi and Bluetooth firmware.
|
||||
|
||||
Looking for our random number generator measurements that we did within the analysis
|
||||
of CVE-2020-6616? You can find them [here](doc/rng.md).
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -23,9 +23,10 @@ than 20 scripts in total.
|
||||
* Raspberry Pi 3+/4: [PRNG](../examples/rpi3p_rpi4/randp.py), [HRNG](../examples/rpi3p_rpi4/rand.py)
|
||||
* iPhone 6: [PRNG](../examples/iphone6/randp.py), [HRNG](../examples/iphone6/rand.py)
|
||||
* iPhone 7: [HRNG](../examples/iphone7/rand.py) (didn't measure PRNG as HRNG was used)
|
||||
* Samsung Galaxy S8: [PRNG](../examples/s8/randp.py) __(no HRNG present)__
|
||||
|
||||
|
||||
Note that this list does not (yet) contain the device where the HRNG was found to be missing.
|
||||
We also have a [full list of firmware and hardware analysis results](rng.md) of the HRNG and PRNG.
|
||||
|
||||
|
||||
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
HRNG and PRNG Details (CVE-2020-6616)
|
||||
-------------------------------------
|
||||
|
||||
This is a joint work of @naehrdine, @matedealer, and @fxrh.
|
||||
|
||||
|
||||
We collected at least 1GB of data from the following devices and all of them passed the
|
||||
*Dieharder* tests.
|
||||
|
||||
Chip | Device | Samples | Dieharder
|
||||
-----------| ----------------- | ---------- | -----------
|
||||
BCM4335C0 | Google Nexus 5 | 2.7GB | Passed
|
||||
BCM43430A1 | Raspberry Pi 3/Zero W | 1.3GB | Passed
|
||||
BCM4345B0 | iPhone 6 | 1.8GB | Passed
|
||||
BCM4355C0 | iPhone 7 | 1.0GB | Passed
|
||||
BCM4345C0 | Raspberry Pi 3+/4 | 1.4GB | Passed
|
||||
BCM4358A3 | Samsung Galaxy S6, Nexus 6P | 2.1GB | Passed
|
||||
CYW20719B1 | Evaluation board | 1.4GB | Passed
|
||||
CYW20735B1 | Evaluation board | 1.6GB | Passed
|
||||
CYW20819A1 | Evaluation board | 1.2GB | Passed
|
||||
|
||||
|
||||
The chip in the *iMac Late 2009* is very slow and memory-limited, thus, we only
|
||||
checked if the HRNG is present. The same is the case for the *Samsung Galaxy S10*
|
||||
and *S20* chip, as it has a few more security features that make runtime analysis
|
||||
harder. On the *iPhone 11*, we currently only have `BlueTool` support, which also
|
||||
limits our analysis capabilities.
|
||||
We assume that the presence of a HRNG is sufficient, because all devices on that
|
||||
we were able to perform measurements had good results.
|
||||
|
||||
Chip | Device | HRNG present
|
||||
-----------| ----------------- | -----------
|
||||
BCM2046A2 | iMac Late 2009 | Yes
|
||||
BCM4375B1 | Samsung Galaxy S10/S20 | Yes
|
||||
BCM4378B1 | iPhone 11 | Yes
|
||||
|
||||
|
||||
We found that the firmware of the *Samsung Galaxy S8* does not even reference the HRNG.
|
||||
Also, we were not able to access the HRNG using known register locations. Each time we
|
||||
triggered a RNG-related action such as pairing, a breakpoint we set within the PRNG
|
||||
function was triggered. Since this issue
|
||||
was already visible inside the firmware without performing measurements on the hardware itself,
|
||||
we checked all firmware dumps we had. Overall, we identified five different implementation
|
||||
variants. Those that are not included in the lists above might still have HRNG issues, but
|
||||
it is way more unlikely. However, *Broadcom* and *Cypress* produced even more chips than
|
||||
listed here, and they might be missing a HRNG similar to the *Samsung Galaxy S8*.
|
||||
|
||||
|
||||
Chip | Device | Build Date | RNG Variant | HRNG Location | PRNG | Cache
|
||||
----------|-----------------|------------|-------------|---------------|------|------
|
||||
BCM2046A2 | iMac Late 2009 | 2007 | 1 | 0xE9A00, 3 regs | Minimal (inline) | No
|
||||
BCM2070B0 | MacBook 2011 | Jul 9 2008 | 1 | 0xE9A00, 3 regs | Minimal (inline) | No
|
||||
BCM20702A1 | Asus USB Dongle | Feb (?) 2010 | 1 | 0xEA204, 3 regs | Minimal (inline) | No
|
||||
BCM4335C0 | Google Nexus 5 | Dec 11 2012 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4345B0 | iPhone 6 | Jul 15 2013 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM43430A1 | Raspberry Pi 3/Zero W | Jun 2 2014 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
BCM4345C0 | Raspberry Pi 3+/4 | Aug 19 2014 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4358A3 | Samsung Galaxy S6, Nexus 6P | Oct 23 2014 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4345C1 | iPhone SE | Jan 27 2015 | 2 | 0x314004, 3 regs | Yes (inline) | No
|
||||
BCM4364B0 | MacBook/iMac 2017-with2019 | Aug 21 2015 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
BCM4355C0 | iPhone 7 | Sep 14 2015 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
BCM20703A2 | MacBook/iMac 2016-2017 | Oct 22 2015 | 2 | 0x314004, 3 regs |Yes (inline) | No
|
||||
CYW20719B1 | Evaluation board | Jan 17 2017 | 2 | 0x352600, 3 regs | Yes (inline) | No
|
||||
CYW20735B1 | Evaluation board | Jan 18 2018 | 3 | 0x352600, 3 regs | Yes (`rbg_get_psrng`), 8 regs | Yes, breaks after 32 elements
|
||||
CYW20819A1 | Evaluation board | May 22 2018 | 3 | 0x352600, 3 regs | Yes (`rbg_get_psrng`), 5 regs | Yes, with minor fixes
|
||||
BCM4347B0 | Samsung Galaxy S8 | Jun 3 2016 | 4 | __None__ | Only option | No
|
||||
BCM4347B1 | iPhone 8/X/XR | Oct 11 2016 | 5 | 0x352600, 4 regs | None | Asynchronous 32x cache
|
||||
BCM4375B1 | Samsung Galaxy S10/Note 10/S20 | Apr 13 2018 | 5 | 0x352600, 4 regs | None | Asynchronous 32x cache
|
||||
BCM4378B1 | iPhone 11 | Oct 25 2018 | 5 | 0x602600, 4 regs | None| Asynchronous 32x cache
|
||||
Executable
+225
@@ -0,0 +1,225 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
# Jiska Classen, Secure Mobile Networking Lab
|
||||
|
||||
import sys
|
||||
|
||||
from pwn import *
|
||||
from internalblue.adbcore import ADBCore
|
||||
import internalblue.hci as hci
|
||||
import internalblue.cli as cli
|
||||
import numpy as np
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Measure the RNG of the Nexus 6.
|
||||
Similar to matedealer's thesis, p. 51.
|
||||
|
||||
Changes:
|
||||
|
||||
* Every 5th byte is now 0x42 to ensure that no other process wrote
|
||||
into this memory region in the meantime. Does it job and cheaper
|
||||
than checksums.
|
||||
|
||||
* When we are done, we send an HCI event containing 'RAND'. We catch
|
||||
this with a callback. Way more efficient than polling.
|
||||
|
||||
* We overwrite the original `rbg_rand` function with `bx lr` to
|
||||
ensure we're the only ones accessing the RNG.
|
||||
|
||||
* Disable Wi-Fi as the RNG might be shared.
|
||||
|
||||
"""
|
||||
|
||||
ASM_LOCATION_RNG = 0x215000 # load our snippet here
|
||||
MEM_RNG = ASM_LOCATION_RNG + 0xf0 # store results here
|
||||
MEM_ROUNDS = 0x1000 # run this often (x5 bytes) ... 0x1000 doesn't crash immediately but somewhen later :/
|
||||
FUN_RNG = 0x9C460 # original RNG function that we overwrite with bx lr
|
||||
PRAND = 0x41079C # the pseudo random register we want to benchmark
|
||||
# !!! other mapping, follows CYW20719
|
||||
# 0x318088 dc_nbtc_clk_adr
|
||||
# 0x32A004 timer1value_adr
|
||||
# 0x3186A0 dc_fhout_adr
|
||||
# 0x410434 agcStatus_adr # 1 byte but at least changes
|
||||
# 0x41079C rxInitAngle_adr # this changes a bit
|
||||
# 0x4100AC spurFreqErr1_adr
|
||||
# 0x410548 rxPskPhErr5_adr_0
|
||||
# 0x20066C *mm_top TODO needs special memcpy but is only used once for init
|
||||
|
||||
ASM_SNIPPET_RNG = """
|
||||
|
||||
// use r0-r7 locally
|
||||
push {r0-r7, lr}
|
||||
|
||||
// enter RNG dumping mode
|
||||
ldr r0, =0x%x // run this many rounds
|
||||
ldr r1, =0x%x // dst: store RNG data here
|
||||
bl dump_pseudo
|
||||
|
||||
// done, let's notify
|
||||
bl notify_hci
|
||||
|
||||
// back to lr
|
||||
pop {r0-r7, pc}
|
||||
|
||||
|
||||
//// the main RNG dumping routine
|
||||
dump_pseudo:
|
||||
|
||||
|
||||
// dst is in r1, dump RNG value here
|
||||
ldr r2, =0x%x
|
||||
ldr r3, [r2]
|
||||
str r3, [r1]
|
||||
add r1, 4
|
||||
|
||||
// add a test byte to ensure that no other process wrote here
|
||||
mov r3, 0x42
|
||||
str r3, [r1]
|
||||
add r1, 1
|
||||
|
||||
// loop for rounds in r0
|
||||
subs r0, 1
|
||||
bne dump_pseudo
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
//// issue an HCI event once we're done
|
||||
notify_hci:
|
||||
|
||||
push {r0-r4, lr}
|
||||
|
||||
// allocate vendor specific hci event
|
||||
mov r2, 243
|
||||
mov r1, 0xff
|
||||
mov r0, 245
|
||||
bl 0xE628 // malloc_hci_event_buffer
|
||||
mov r4, r0 // save pointer to the buffer in r4
|
||||
|
||||
// append buffer with "RAND"
|
||||
add r0, 10 // buffer starts at 10 with data
|
||||
ldr r1, =0x444e4152 // RAND
|
||||
str r1, [r0]
|
||||
add r0, 4 // advance buffer by 4
|
||||
|
||||
// send hci event
|
||||
mov r0, r4 // back to buffer at offset 0
|
||||
bl 0xE418 // bthci_event_AttemptToEnqueueEventToTransport
|
||||
|
||||
pop {r0-r4, pc}
|
||||
|
||||
|
||||
""" % (MEM_ROUNDS, MEM_RNG, PRAND)
|
||||
|
||||
|
||||
internalblue = ADBCore(log_level='info', serial=True)
|
||||
internalblue.interface = internalblue.device_list()[0][1] # just use the first device
|
||||
|
||||
# setup sockets
|
||||
if not internalblue.connect():
|
||||
log.critical("No connection to target device.")
|
||||
exit(-1)
|
||||
|
||||
progress_log = log.info("installing assembly patches...")
|
||||
|
||||
|
||||
# Install the RNG code in RAM
|
||||
code = asm(ASM_SNIPPET_RNG, vma=ASM_LOCATION_RNG)
|
||||
if not internalblue.writeMem(address=ASM_LOCATION_RNG, data=code, progress_log=progress_log):
|
||||
progress_log.critical("error!")
|
||||
exit(-1)
|
||||
|
||||
# Disable original RNG
|
||||
patch = asm("bx lr; bx lr", vma=FUN_RNG) # 2 times bx lr is 4 bytes and we can only patch 4 bytes
|
||||
if not internalblue.patchRom(FUN_RNG, patch):
|
||||
log.critical("Could not disable original RNG!")
|
||||
exit(-1)
|
||||
|
||||
|
||||
|
||||
log.info("Installed all RNG hooks.")
|
||||
|
||||
adb.process(["su", "-c", "svc wifi disable"])
|
||||
|
||||
log.info("Disabled Wi-Fi core.")
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
We cannot call HCI Read_RAM from this callback as it requires another callback (something goes wrong here),
|
||||
so we cannot solve this recursively but need some global status variable. Still, polling this is way faster
|
||||
than polling a status register in the Bluetooth firmware itself.
|
||||
"""
|
||||
# global status
|
||||
internalblue.rnd_done = False
|
||||
def rngStatusCallback(record):
|
||||
hcipkt = record[0] # get HCI Event packet
|
||||
|
||||
if not issubclass(hcipkt.__class__, hci.HCI_Event):
|
||||
return
|
||||
|
||||
if hcipkt.data[0:4] == bytes("RAND", "utf-8"):
|
||||
log.info("Random data done!")
|
||||
internalblue.rnd_done = True
|
||||
|
||||
# add RNG callback
|
||||
internalblue.registerHciCallback(rngStatusCallback)
|
||||
|
||||
|
||||
# enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
|
||||
|
||||
# read for multiple rounds to get more experiment data
|
||||
rounds = 100
|
||||
i = 0
|
||||
data = bytearray()
|
||||
while rounds > i:
|
||||
log.info("RNG round %i..." % i)
|
||||
|
||||
# launch assembly snippet
|
||||
internalblue.launchRam(ASM_LOCATION_RNG)
|
||||
|
||||
# wait until we set the global variable that everything is done
|
||||
while not internalblue.rnd_done:
|
||||
continue
|
||||
internalblue.rnd_done = False
|
||||
|
||||
sleep(2) # FIXME
|
||||
# and now read and save the random
|
||||
random = internalblue.readMem(MEM_RNG, MEM_ROUNDS*5)
|
||||
data.extend(random)
|
||||
i = i + 1
|
||||
|
||||
log.info("Finished acquiring random data!")
|
||||
|
||||
# every 5th byte i 0x42
|
||||
check = data[4::5]
|
||||
pos = 0
|
||||
for c in check:
|
||||
pos = pos + 1
|
||||
if c != 0x42:
|
||||
log.error("!!!! data was corrupted !!! %i" % pos)
|
||||
|
||||
# uhm and for deleting every 5th let's take numpy (oh why??)
|
||||
#data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
# FIXME we didn't remove the 0x42 in this data set!! something is wrong here
|
||||
data = np.delete(data, np.arange(4, data.__len__(), 5))
|
||||
|
||||
|
||||
f = open("s8_randomdata_pseudo-%irounds-reg0x%x-2s-corrected.bin" % (rounds, PRAND), "wb")
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
#log.info("--------------------")
|
||||
#log.info("Entering InternalBlue CLI to interpret RNG.")
|
||||
|
||||
## enter CLI
|
||||
#cli.commandLoop(internalblue)
|
||||
|
||||
Reference in New Issue
Block a user