Finished my bruteforecing. No results unfortunately. Guess we need better understanding of how they usually build those keys.
I managed to dump the device storage. This is some weird shit- the kernel partition is entirely empty. This means that uboot is essentially decrypting the kernel from the stock encrypted image and loading it into memory. Interesting to say the least. We need the decrypted kernel image before we'll get anywhere near looking at the rootfs. Most of the partitions in the dump are empty, except for the one where I presume, the encrypted kernel image is stored, along with the regular old stuff like calibration data and WIFI firmware.
Anyways, I have uploaded the stock uboot binary here;
https://github.com/MeisterLone/Verizon-CR1000A/raw/main/Stock-Firmware/uboot.elf
Heres the full storage dump
https://drive.google.com/file/d/1AwKICWd9Iwl14FXxiB-aGQCqn4vQYzUJ/view?usp=sharing
Lets get cracking!
Looks like a "Decrypt" method here.
SHA384 involved
Salt string??: &F5ua#Or6V
Yeah thats definitely decrypting the kernel image in this function. This may be a little bit above my skill level to figure out exactly how its doing the decryption.
Here's the encrypted kernel image
https://github.com/MeisterLone/Verizon-CR1000A/raw/main/Stock-Firmware/encrypted_kernel.img
@meisterlone what was your process to dump eMMC?
These 2 functions are doing AES decryption (highlighted on screenshot), so I presume FUN_4a9202e0
is some kind of key derivation function, maybe HKDF?
I think I reversed engineered this function:
void decryptKernel(int *param_1,void *out)
{
undefined *puVar1;
undefined4 uVar2;
size_t ilen;
undefined4 extraout_r1;
undefined4 extraout_r1_00;
undefined4 uVar3;
int iVar4;
uint32_t uStack_160;
byte abStack_15c [16];
byte abStack_14c [32];
char acStack_12c [32];
byte abStack_10c [240];
uint local_1c;
puVar1 = PTR_DAT_4a903034;
local_1c = *(uint *)PTR_DAT_4a903034;
memset(abStack_14c,0,0x20);
memset(abStack_15c,0,0x10);
debugLog(PTR_s_Decrypting..._4a903038);
if (param_1[0x41] == DAT_4a90303c) {
ilen = strlen(PTR_s_&F5ua#Or6V_4a903044);
csum_wd(PTR_s_&F5ua#Or6V_4a903044,ilen,PTR_s_sha384_4a903048,acStack_12c,&uStack_160);
memcpy(abStack_14c,acStack_12c,0x20);
memcpy(abStack_15c,abStack_10c,0x10);
iVar4 = *param_1;
memcpy(abStack_10c,abStack_14c,0x20);
aes_expand_key(abStack_14c,0x20,abStack_10c);
aes_cbc_decrypt_blocks
(0x20,abStack_10c,abStack_15c,(byte *)(param_1 + 0x80),(byte *)out,iVar4 + 0xfU >> 4);
debugLog(PTR_s_Done._4a90304c);
uVar2 = 0;
uVar3 = extraout_r1_00;
}
else {
debugLog(PTR_s_Fail._4a903040);
uVar2 = 0xffffffff;
uVar3 = extraout_r1;
}
if (local_1c != *(uint *)puVar1) {
FUN_4a9033d0(uVar2,uVar3,local_1c,*(uint *)puVar1);
}
return;
}
and
int csum_wd(char *input,uint32_t ilen,char *algorithm,char *output,uint32_t *outlen)
{
int iVar1;
uint uVar2;
uint32_t uVar3;
iVar1 = strcmp(algorithm,PTR_s_crc32_4a92042c);
if (iVar1 == 0) {
uVar2 = thunk_FUN_4a952df4(0,(uint)input,ilen);
uVar3 = 4;
*(uint *)output =
uVar2 << 0x18 | (uVar2 >> 8 & 0xff) << 0x10 | (uVar2 >> 0x10 & 0xff) << 8 | uVar2 >> 0x18;
}
else {
iVar1 = strcmp(algorithm,PTR_DAT_4a920430);
if (iVar1 == 0) {
FUN_4a94d490(input,ilen,output);
uVar3 = 0x14;
}
else {
iVar1 = strcmp(algorithm,PTR_s_sha256_4a920434);
if (iVar1 == 0) {
FUN_4a94fcec(input,ilen,output);
uVar3 = 0x20;
}
else {
iVar1 = strcmp(algorithm,PTR_s_sha384_4a920438);
if (iVar1 == 0) {
sha384_csum_wd(input,ilen,output,0x4000);
uVar3 = 0x30;
}
else {
iVar1 = strcmp(algorithm,PTR_s_sha512_4a92043c);
if (iVar1 == 0) {
FUN_4a951540((uint8_t *)input,ilen,output);
uVar3 = 0x40;
}
else {
iVar1 = strcmp(algorithm,PTR_DAT_4a920440);
if (iVar1 != 0) {
return -1;
}
FUN_4a94baac(input,ilen,output);
uVar3 = 0x10;
}
}
}
}
}
*outlen = uVar3;
return iVar1;
}
Definitions of other functions are available in the u-boot repo: https://source.denx.de/u-boot/u-boot/-/blob/master/lib/aes.c.
I will try to decrypt the kernel later today.
Great work! How do you where the input params are pulled from? I have a hard time finding offsets in ghidra. How to actually find mmc locations for where its pulling from etc. Are those locations loaded elsewhere?
I mean we know where the encrypted image is located, but i dont actually see references to those locations anywhere. They are in the βdataβ partition, but id expect some offsets in that partition somewhere in the decomp but I dont really see any that make sense
Also, dont we need an initialisation vector for the algorithm pulled from disk somewhere?
I haven't yet figured this part out, but I don't think they hardcoded the location, they seem to be going through the GPT table and then memory mapping the decrypted kernel directly to 0x48000000:
While you try and crack that, im going to work on trying to see if I can get serial enabled again after they disable it. I found where its being disabled, im wondering if we jump over those instruction if we get full serial access
IV seems to be all zeros, and the key is simply a sha384 of the string you found.
@spol-eff In this function here
4a91da20
It seems to be checking for the string "TestMode" somewhere. It then disables the console if TestMode is not found. Any ideas where its checking???
Thanks!!
I tried setting an environment variable TestMode=1 but no luck
It does seem to be using FUN_4a91d020
to make that check, so I bet it does have something to do with env variables.
Also I've tried decrypting, but getting garbage so far - I think IV is probably not zeros like I though, but it's passed in through stack variables so Ghidra isn't very helpful.
It worked!!!!
All I had to do is set
Testmode=mfg
Now my serial port works. Woop!
ill post bootlogs in a minute
Haha, nice! I was just about to post
Damn, it wont boot the kernel now. It needs a test kernel in TestMode. But atleast we're getting somewhere.
Format: Log Type - Time(microsec) - Message - Optional Info
Log Type: B - Since Boot(Power On Reset), D - Delta, S - Statistic
S - QC_IMAGE_VERSION_STRING=BOOT.BF.3.3.1-00163
S - IMAGE_VARIANT_STRING=HAASANAZA
S - OEM_IMAGE_VERSION_STRING=CRM
S - Boot Config, 0x000002e3
B - 201 - PBL, Start
B - 2736 - bootable_media_detect_entry, Start
B - 24763 - bootable_media_detect_success, Start
B - 24767 - elf_loader_entry, Start
B - 26122 - auth_hash_seg_entry, Start
B - 64171 - auth_hash_seg_exit, Start
B - 78795 - elf_segs_hash_verify_entry, Start
B - 141428 - PBL, End
B - 157197 - SBL1, Start
B - 210572 - GCC [RstStat:0x10, RstDbg:0x600000] WDog Stat : 0x4
B - 217251 - pm_device_init, Start
B - 347334 - PM_SET_VAL:Skip
D - 129564 - pm_device_init, Delta
B - 349774 - pm_driver_init, Start
D - 5215 - pm_driver_init, Delta
B - 355965 - clock_init, Start
D - 2135 - clock_init, Delta
B - 360144 - boot_flash_init, Start
D - 8631 - boot_flash_init, Delta
B - 372435 - boot_config_data_table_init, Start
D - 1037 - boot_config_data_table_init, Delta - (575 Bytes)
B - 380030 - Boot Setting : 0x00000619
B - 383873 - CDT version:2,Platform ID:8,Major ID:1,Minor ID:0,Subtype:18
B - 390796 - sbl1_ddr_set_params, Start
B - 394609 - CPR configuration: 0x30c
B - 398086 - cpr_init, Start
B - 400861 - Rail:0 Mode: 5 Voltage: 784000
B - 405406 - CL CPR settled at 784000mV
B - 408974 - Rail:1 Mode: 5 Voltage: 880000
B - 413061 - Rail:1 Mode: 7 Voltage: 888000
D - 16500 - cpr_init, Delta
B - 419924 - Pre_DDR_clock_init, Start
B - 423950 - Pre_DDR_clock_init, End
B - 427335 - DDR Type : PCDDR4
B - 434076 - do ddr sanity test, Start
D - 1067 - do ddr sanity test, Delta
B - 437797 - DDR: Start of HAL DDR Boot Training
B - 442524 - DDR: End of HAL DDR Boot Training
B - 448228 - DDR: Checksum to be stored on flash is -1511448646
B - 458720 - Image Load, Start
D - 345565 - QSEE Image Loaded, Delta - (1381328 Bytes)
B - 804376 - Image Load, Start
D - 366 - SEC Image Loaded, Delta - (0 Bytes)
B - 811879 - Image Load, Start
D - 288286 - DEVCFG Image Loaded, Delta - (32548 Bytes)
B - 1100257 - Image Load, Start
D - 292922 - RPM Image Loaded, Delta - (93060 Bytes)
B - 1393240 - Image Load, Start
D - 310215 - APPSBL Image Loaded, Delta - (556778 Bytes)
B - 1703547 - QSEE Execution, Start
D - 61 - QSEE Execution, Delta
B - 1709372 - USB D+ check, Start
D - 0 - USB D+ check, Delta
B - 1715747 - SBL1, End
D - 1560868 - SBL1, Delta
S - Flash Throughput, 33671 KB/s (2064961 Bytes, 61326 us)
S - DDR Frequency, 600 MHz
S - Core 0 Frequency, 1651 MHz
U-Boot 2016.01-v00.04 (May 06 2022 - 14:40:18 +0800)
DRAM: smem ram ptable found: ver: 1 len: 4
2 GiB
NAND: Could not find nand-flash in device tree
SF: Unsupported flash IDs: manuf ff, jedec ffff, ext_jedec ffff
ipq_spi: SPI Flash not found (bus/cs/speed/mode) = (0/0/48000000/0)
0 MiB
MMC: <NULL>: 0 (eMMC)
PCI Link Intialized
In: serial@78B3000
Out: serial@78B3000
Err: serial@78B3000
Console Enable
machid: 8010012
eth0 MAC Address from ART is not valid
eth1 MAC Address from ART is not valid
eth2 MAC Address from ART is not valid
eth3 MAC Address from ART is not valid
eth4 MAC Address from ART is not valid
eth5 MAC Address from ART is not valid
<DBG> autoboot_command
Hit any key to stop autoboot: 0
MMC read: dev # 0, block # 57378, count 40960 ... 40960 blocks read: OK
Verifying... fail
This is the invalid kernel
bootipq - bootipq from flash device
Net: MAC0 addr:0:3:7f:ba:db:ad
PHY ID1: 0x31c3
PHY ID2: 0x1c13
EDMA ver 1 hw init
Num rings - TxDesc:1 (0-0) TxCmpl:1 (7-7)
RxDesc:1 (15-15) RxFill:1 (7-7)
ipq807x_edma_alloc_rings: successfull
ipq807x_edma_setup_ring_resources: successfull
ipq807x_edma_configure_rings: successfull
ipq807x_edma_hw_init: successfull
eth0
IPQ807x# bootipq
MMC read: dev # 0, block # 57378, count 40960 ... 40960 blocks read: OK
Verifying... fail
This is the invalid kernel
bootipq - bootipq from flash device
IPQ807x# printenv
TestMode=mfg
adminpass=66LTRB7KJ
baseMAC=78:67:0E:33:2F:BE
baudrate=115200
bootargs=console=ttyMSM0,115200n8 vmalloc=900M cnss2.bdf_pci0=0xa4 root=PARTUUID =726de7d6-3fbb-8755-1cbe-68409ea2a227 gpt rootwait root=PARTUUID=726de7d6-3fbb-8 755-1cbe-68409ea2a227 gpt rootwait
bootcmd=bootipq
bootdelay=1
dump_to_flash=0x0010f54e
ethact=eth0
ethaddr=00:03:7f:ba:db:ad
fdt_high=0x4A400000
fdtcontroladdr=4a972e50
fileaddr=44000000
filesize=1c8
flash_type=5
fsbootargs=root=PARTUUID=726de7d6-3fbb-8755-1cbe-68409ea2a227 gpt
hwver=0.0.7
ipaddr=192.168.1.1
machid=8010012
macno=7
model=CR1000A
netmask=255.255.255.0
serialno=ABP22126049
serverip=192.168.1.100
soc_hw_version=200d0200
soc_version_major=2
soc_version_minor=0
ssid=Verizon_XLQ9JV
stderr=serial@78B3000
stdin=serial@78B3000
stdout=serial@78B3000
wifipass=moat-polo6-vie
Environment size: 887/262140 bytes
IPQ807x# help
? - alias for 'help'
aes_256 - AES 256 CBC/ECB encryption/decryption
aq_load_fw- LOAD aq-fw-binary
aq_phy_restart- Restart Aquantia phy
base - print or set address offset
bdinfo - print Board Info structure
bootipq - bootipq from flash device
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootz - boot Linux zImage image from memory
canary - test stack canary
chpart - change active partition
cmp - memory compare
cp - memory copy
crc32 - checksum calculation
dcache - enable or disable data cache
dhcp - boot image via network using DHCP/TFTP protocol
dm - Driver model low level access
echo - echo args to console
env - environment handling commands
erase - erase FLASH memory
exectzt - execute TZT
exit - exit script
false - do nothing, unsuccessfully
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fatsize - determine a file's size
fdt - flattened device tree utility commands
flash - flash part_name
flash part_name load_addr file_size
flasherase- flerase part_name
flinfo - print FLASH memory information
fuseipq - fuse QFPROM registers from memory
go - start application at address 'addr'
help - print command description/usage
i2c - I2C sub-system
icache - enable or disable instruction cache
imxtract- extract a part of a multi-image
ipq_mdio- IPQ mdio utility commands
is_sec_boot_enabled- check secure boot fuse is enabled or not
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
md - memory display
mii - MII utility commands
mm - memory modify (auto-incrementing address)
mmc - MMC sub system
mmcinfo - display MMC info
mtdparts- define flash/nand partitions
mtest - simple RAM read/write test
mw - memory write (fill)
nand - NAND sub-system
nboot - boot from NAND device
nm - memory modify (constant address)
pci - list and access PCI Configuration Space
ping - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
ramboot - Upload image and boot from RAM
reset - Perform RESET of the CPU
run - run commands in an environment variable
runmulticore- Enable and schedule secondary cores
saveenv - save environment variables to persistent storage
secure_authenticate- authenticate the signed image
setenv - set environment variables
sf - SPI flash sub-system
showvar - print local hushshell variables
sleep - delay execution for some time
smeminfo- print SMEM FLASH information
source - run script from memory
test - minimal test like /bin/sh
tftpboot- boot image via network using TFTP protocol
tftpput - TFTP put command, for uploading files to a server
true - do nothing, successfully
uart - UART sub-system
ubi - ubi commands
version - print monitor, compiler and linker version
IPQ807x#
? - alias for 'help'
aes_256 - AES 256 CBC/ECB encryption/decryption
aq_load_fw- LOAD aq-fw-binary
aq_phy_restart- Restart Aquantia phy
base - print or set address offset
bdinfo - print Board Info structure
bootipq - bootipq from flash device
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootz - boot Linux zImage image from memory
canary - test stack canary
chpart - change active partition
cmp - memory compare
cp - memory copy
crc32 - checksum calculation
dcache - enable or disable data cache
dhcp - boot image via network using DHCP/TFTP protocol
dm - Driver model low level access
echo - echo args to console
env - environment handling commands
erase - erase FLASH memory
exectzt - execute TZT
exit - exit script
false - do nothing, unsuccessfully
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fatsize - determine a file's size
fdt - flattened device tree utility commands
flash - flash part_name
flash part_name load_addr file_size
flasherase- flerase part_name
flinfo - print FLASH memory information
fuseipq - fuse QFPROM registers from memory
go - start application at address 'addr'
help - print command description/usage
i2c - I2C sub-system
icache - enable or disable instruction cache
imxtract- extract a part of a multi-image
ipq_mdio- IPQ mdio utility commands
is_sec_boot_enabled- check secure boot fuse is enabled or not
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
md - memory display
mii - MII utility commands
mm - memory modify (auto-incrementing address)
mmc - MMC sub system
mmcinfo - display MMC info
mtdparts- define flash/nand partitions
mtest - simple RAM read/write test
mw - memory write (fill)
nand - NAND sub-system
nboot - boot from NAND device
nm - memory modify (constant address)
pci - list and access PCI Configuration Space
ping - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
ramboot - Upload image and boot from RAM
reset - Perform RESET of the CPU
run - run commands in an environment variable
runmulticore- Enable and schedule secondary cores
saveenv - save environment variables to persistent storage
secure_authenticate- authenticate the signed image
setenv - set environment variables
sf - SPI flash sub-system
showvar - print local hushshell variables
sleep - delay execution for some time
smeminfo- print SMEM FLASH information
source - run script from memory
test - minimal test like /bin/sh
tftpboot- boot image via network using TFTP protocol
tftpput - TFTP put command, for uploading files to a server
true - do nothing, successfully
uart - UART sub-system
ubi - ubi commands
version - print monitor, compiler and linker version
IPQ807x#
? - alias for 'help'
aes_256 - AES 256 CBC/ECB encryption/decryption
aq_load_fw- LOAD aq-fw-binary
aq_phy_restart- Restart Aquantia phy
base - print or set address offset
bdinfo - print Board Info structure
bootipq - bootipq from flash device
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootz - boot Linux zImage image from memory
canary - test stack canary
chpart - change active partition
cmp - memory compare
cp - memory copy
crc32 - checksum calculation
dcache - enable or disable data cache
dhcp - boot image via network using DHCP/TFTP protocol
dm - Driver model low level access
echo - echo args to console
env - environment handling commands
erase - erase FLASH memory
exectzt - execute TZT
exit - exit script
false - do nothing, unsuccessfully
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fatsize - determine a file's size
fdt - flattened device tree utility commands
flash - flash part_name
flash part_name load_addr file_size
flasherase- flerase part_name
flinfo - print FLASH memory information
fuseipq - fuse QFPROM registers from memory
go - start application at address 'addr'
help - print command description/usage
i2c - I2C sub-system
icache - enable or disable instruction cache
imxtract- extract a part of a multi-image
ipq_mdio- IPQ mdio utility commands
is_sec_boot_enabled- check secure boot fuse is enabled or not
itest - return true/false on integer compare
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
md - memory display
mii - MII utility commands
mm - memory modify (auto-incrementing address)
mmc - MMC sub system
mmcinfo - display MMC info
mtdparts- define flash/nand partitions
mtest - simple RAM read/write test
mw - memory write (fill)
nand - NAND sub-system
nboot - boot from NAND device
nm - memory modify (constant address)
pci - list and access PCI Configuration Space
ping - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
ramboot - Upload image and boot from RAM
reset - Perform RESET of the CPU
run - run commands in an environment variable
runmulticore- Enable and schedule secondary cores
saveenv - save environment variables to persistent storage
secure_authenticate- authenticate the signed image
setenv - set environment variables
sf - SPI flash sub-system
showvar - print local hushshell variables
sleep - delay execution for some time
smeminfo- print SMEM FLASH information
source - run script from memory
test - minimal test like /bin/sh
tftpboot- boot image via network using TFTP protocol
tftpput - TFTP put command, for uploading files to a server
true - do nothing, successfully
uart - UART sub-system
ubi - ubi commands
version - print monitor, compiler and linker version
IPQ807x#
Ok wait I can disable TestMode after boot and now the kernel boots, but as soon as it jumps to the kernel, it disabled serial again. Silly OEM
IPQ807x# setenv TestMode
IPQ807x# bootipq
MMC read: dev # 0, block # 57378, count 40960 ... 40960 blocks read: OK
Decrypting... Done.
MMC read: dev # 0, block # 471374, count 81402 ... 81402 blocks read: OK
## Loading kernel from FIT Image at 48000028 ...
Using 'config@hk14' configuration
Verifying Hash Integrity ... OK
Trying 'kernel@1' kernel subimage
Description: ARM64 OpenWrt Linux-4.4.60
Type: Kernel Image
Compression: gzip compressed
Data Start: 0x48000110
Data Size: 11625637 Bytes = 11.1 MiB
Architecture: AArch64
OS: Linux
Load Address: 0x41080000
Entry Point: 0x41080000
Hash algo: crc32
Hash value: fe2896cb
Hash algo: sha1
Hash value: e3efa2d352d4e835f00de5ec0b07dfa15ebee344
Verifying Hash Integrity ... crc32+ sha1+ OK
## Loading fdt from FIT Image at 48000028 ...
Using 'config@hk14' configuration
Trying 'fdt@hk14' fdt subimage
Description: ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
Type: Flat Device Tree
Compression: uncompressed
Data Start: 0x48b6a8a4
Data Size: 84036 Bytes = 82.1 KiB
Architecture: AArch64
Hash algo: crc32
Hash value: 42a1c48a
Hash algo: sha1
Hash value: 6db123ce3eb01d558d2a12e6282773327b971c59
Verifying Hash Integrity ... crc32+ sha1+ OK
Booting using the fdt blob at 0x48b6a8a4
Uncompressing Kernel Image ... OK
Loading Device Tree to 4a3e8000, end 4a3ff843 ... OK
Using machid 0x8010012 from environment
Starting kernel ...
Jumping to AARCH64 kernel via monitor
However, this leaves an opportunity. I believe if I can interrupt uboot after decrypting the kernel and before it jumps to the kernel, the unencrypted kernel should be in memory for me to grab.
The kernel also reset all env variables to default first thing after it runs lol. Silly silly oem