the samsung galaxy s8 is missing a hrng

This commit is contained in:
Jiska Classen
2020-04-30 23:40:07 +02:00
parent 2d3ff6226d
commit f8b0ad6725
4 changed files with 300 additions and 2 deletions
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+225
View File
@@ -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)