0

华为 P9 Lite Trust Core TA 解密

 1 year ago
source link: https://xuanxuanblingbling.github.io/trustzone/2023/05/29/p9lite/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

华为 P9 Lite Trust Core TA 解密

2023-05-29

| TrustZone

| 14

更新中…是议题 Unearthing the TrustedCore: A Critical Review on Huawei’s Trusted Execution Environment的子部分,解密TA的大概流程主要有三步:(1)模拟运行TEE中的白盒密码算法解出RSA私钥prikeyx。(2)使用RSA的私钥prikeyx解密TA头部的manifest。(3)使用解密后manifest中的AES key 解密 TA 正文。但作者没有给出解密过程中的一些细节,例如RSA和AES密钥的组织方法。所以我尝试复现了这个解密,并给出解密过程中的所有细节。

image

目标固件VNS-L31C432B160是2016年的版本,也是作者开源在github上的相关工具tckit的示例固件。固件解压后的UPDATE.APP可以使用在之前CVE-2021-39994:HUAWEI SMC SE Factory Check OOB Access中的提到过的Android Image Tools的emui_extractor进行提取,此工具在我本机的mac环境下编译此工具需要添加制定C++11的编译选项-std=c++11:

emui_extractor: image.h image.cc emui_extractor.cc
        g++ -std=c++11 -o emui_extractor image.cc emui_extractor.cc

.PHONY: clean
clean:
        rm emui_extractor

然后即可使用其list子功能查看UPDATE.APP中的不同镜像:

➜  ./emui_extractor ./UPDATE.APP list
=====================================================================
Sequence            File.img            Size            Type   Device
=====================================================================
fe000000       SHA256RSA.img       256.00  B       SHA256RSA   HW7x27
fe000000             CRC.img       197.10 KB             CRC   HW7x27
fffffff0          CURVER.img        15.00  B          CURVER   HW7x27
fffffff1         VERLIST.img         3.14 KB         VERLIST   HW7x27
00000000             EFI.img        17.00 KB             EFI   HW7x27
00000018         XLOADER.img        69.25 KB         XLOADER   HW7x27
00000017         FW_LPM3.img       164.44 KB         FW_LPM3   HW7x27
00000013        FASTBOOT.img         2.53 MB        FASTBOOT   HW7x27
00000016 MODEMNVM_UPDATE.img        19.97 MB MODEMNVM_UPDATE   HW7x27
0000001a           TEEOS.img         2.30 MB           TEEOS   HW7x27
00000012   TRUSTFIRMWARE.img        94.56 KB   TRUSTFIRMWARE   HW7x27
00000019       SENSORHUB.img       416.44 KB       SENSORHUB   HW7x27
00000014         FW_HIFI.img         2.51 MB         FW_HIFI   HW7x27
0000000c            BOOT.img        13.67 MB            BOOT   HW7x27
0000000a        RECOVERY.img        32.34 MB        RECOVERY   HW7x27
0000000a       RECOVERY2.img        32.34 MB       RECOVERY2   HW7x27
00000008             DTS.img        18.12 MB             DTS   HW7x27
00000011        MODEM_FW.img        96.00 MB        MODEM_FW   HW7x27
0000000e           CACHE.img         6.10 MB           CACHE   HW7x27
0000000d          SYSTEM.img         2.38 GB          SYSTEM   HW7x27
0000000f            CUST.img       418.13 MB            CUST   HW7x27
00000010        USERDATA.img        67.33 MB        USERDATA   HW7x27
=====================================================================

主要关注两个镜像:

  • TEEOS.img:TEEOS和静态TA,其中包含对动态TA解密的关键代码和数据
  • SYSTEM.img:Android侧文件系统,其中包含加密后的动态TA

将二者提取出来:

➜  ./emui_extractor ./UPDATE.APP dump TEEOS.img TEEOS.img
➜  ./emui_extractor ./UPDATE.APP dump SYSTEM.img SYSTEM.img

SYSTEM.img是android文件系统的主要部分,加密后的TA就在其中,但其格式是 Android sparse image,还需要再次进行处理:

➜  file SYSTEM.img
SYSTEM.img: Android sparse image, version: 1.0, Total of 655320 4096-byte output blocks in 4845 input chunks.

使用https://github.com/anestisb/android-simg2img,将其转化为正常的ext4文件系统格式:

➜  ./simg2img ./SYSTEM.img ./system.ext4
➜  file ./system.ext4
./system.ext4: Linux rev 1.0 ext4 filesystem data, volume name "system" (extents) (large files)

然后在linux中挂载此文件系统即可:

➜  mkdir rootfs
➜  sudo mount ./system.ext4 ./rootfs
➜  ls ./rootfs
app  build.prop  emui    fonts      global  lib64       phone.prop  themes  vendor
asr  cdrom       etc     fpgaice40  hw_oem  lost+found  priv-app    tts     watermark
bin  delapp      extras  framework  lib     media       screenlock  usr     xbin

加密的TA就在bin目录下,是以UUID格式和.sec后缀为名字的文件:

➜  ls -al ./rootfs/bin | grep sec
-rw-r-----.  1 root xuan   12756 11月  9  2016 6c8cf255-ca98-439e-a98e-ade64022ecb6.sec
-rw-r-----.  1 root xuan   26696 11月  9  2016 79b77788-9789-4a7a-a2be-b60155eef5f4.sec
-rw-r-----.  1 root xuan    9012 11月  9  2016 868ccafb-794b-46c6-b5c4-9f1462de4e02.sec
-rw-r-----.  1 root xuan  417224 11月  9  2016 883890ba-3ef8-4f0b-9c02-f5874acbf2ff.sec
-rw-r-----.  1 root xuan   23940 11月  9  2016 9b17660b-8968-4eed-917e-dd32379bd548.sec
-rw-r-----.  1 root xuan   41500 11月  9  2016 b4b71581-add2-e89f-d536-f35436dc7973.sec
-rw-r-----.  1 root 1004   12776 11月  9  2016 fd1bbfb2-9a62-4b27-8fdb-a503529076af.sec
-rw-r-----.  1 root 1004 1326040 11月  9  2016 fpc_1021_ta.sec
-rw-r-----.  1 root 1004 1329000 11月  9  2016 fpc_1021_ta_venus.sec
-rw-r-----.  1 root 1004 1208040 11月  9  2016 fpc_1022_ta.sec
-rwxr-x---.  1 root root   10096 11月  9  2016 secure_storage
-rw-r-----.  1 root 1004 1711896 11月  9  2016 syna_109A0_ta.sec

给出示例,之后以868ccafb-794b-46c6-b5c4-9f1462de4e02.sec为例,目标就是解密这个TA,使用binwalk可以看出这个目标确实是加密的:

➜  binwalk -E ./868ccafb-794b-46c6-b5c4-9f1462de4e02.sec 

DECIMAL       HEXADECIMAL     ENTROPY
--------------------------------------------------------------------------------
0             0x0             Rising entropy edge (0.968520)

image

接下来就用TEEOS.img来解密868ccafb-794b-46c6-b5c4-9f1462de4e02.sec:

拆分TEEOS.img

使用https://github.com/teesec-research/tckit(python2),即可从TEEOS.img拆出RSA的私钥,首先使用splitteeos.py从TEEOS.img拆分出globaltask这个PTA:

➜  python2 tckit/splitteeos/splitteeos.py ./TEEOS.img
➜  ls -al ./tas_out
drwxrwxr-x 2 xuan xuan    4096 6月   4 15:12 .
drwxrwxr-x 4 xuan xuan    4096 6月   4 15:12 ..
-rw-rw-r-- 1 xuan xuan 1642624 6月   4 15:12 globaltask
-rw-rw-r-- 1 xuan xuan   18212 6月   4 15:12 task_gatekeeper
-rw-rw-r-- 1 xuan xuan   75216 6月   4 15:12 task_keyboard
-rw-rw-r-- 1 xuan xuan  107132 6月   4 15:12 task_keymaster
-rw-rw-r-- 1 xuan xuan   14792 6月   4 15:12 task_reet
-rw-rw-r-- 1 xuan xuan   10232 6月   4 15:12 task_secboot
-rw-rw-r-- 1 xuan xuan   15540 6月   4 15:12 task_storage

然后使用globaltaskgencode.py从拆分出的globaltask中,提取白盒密码算法代码,并封装为C代码:

➜  python2 tckit/globaltaskgencode.py ./tas_out/globaltask
➜  file tas_out/globaltask_extract_keys.c 
tas_out/globaltask_extract_keys.c: ASCII text

编译并使用QEMU模拟运行此白盒密码算法:

➜  arm-linux-gnueabihf-gcc tas_out/globaltask_extract_keys.c -o test 
➜  qemu-arm -L /usr/arm-linux-gnueabihf ./test 

运行后即可打印RSA私钥:

private_key = '\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
public_key = '\xc4\x11\xcc\x98\xce\x92\x0d\x78\x7e\xbb\x7a\xa4\xff\x5f\x60\x82\xa9\x4d\xb5\xe7\x75\x9d\xfd\x7d\xa7\xcf\xa3\xbb\x25\x83\xf5\x24\xf9\x31\x65\x5a\x5c\xea\xab\x88\xb9\x1b\x94\xc8\x5a\x75\x44\x4e\x17\x50\x76\xa5\xa4\x39\xdd\x79\x5b\xf4\xcc\xd0\x11\xb8\x52\xe0\x8d\x4f\x49\xd4\x6b\xb8\x5b\x4a\xdf\x51\x53\xef\x61\x75\xc4\x43\xe1\xfd\x8c\x18\x9a\x3f\x45\x11\x69\x31\xb4\x9c\x2c\x2f\xdb\x5a\xe9\x09\x4d\x99\xf5\xdc\x95\x34\xde\x1a\xc6\x34\xd7\xbf\x86\x27\xce\x94\x4f\xc8\x03\xd1\x47\x24\x02\x8e\x49\x0b\x22\xe6\x82\x42\xf9\xa7\x1b\x85\x29\xb1\x90\x4e\x22\xd0\x48\x4a\x56\x63\xee\x93\x75\x9d\x25\xbc\x02\xa0\x23\x55\xe6\xd4\x67\xa0\x76\x22\x23\x8b\x5a\x7b\x4d\x24\x7a\x28\x71\x83\x4c\xc0\xa1\x28\x9c\x14\x45\x47\x75\xdb\x12\x42\xfd\x94\x05\xc6\xa3\xb9\xcc\xf7\x48\x8c\xe9\x55\xac\x1f\x01\xca\x6b\xc5\xe5\x1c\xa8\xf4\xc7\x09\x7d\x5b\x4b\x2c\x1d\xcb\xc5\x4e\x12\xfb\x46\x76\x23\xb2\x58\x94\x4b\x7b\x66\xbb\xbb\x18\x8b\x7e\x10\x3c\xbc\x2d\xd8\x1d\x0d\xa2\x7b\x60\xa6\xb9\x1a\x20\x40\x96\xa2\x01\xc9\x09\x06\xe6\xb6\xb0\x7f\x44\xa3\xa9\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

私钥的解析以及后续的解密TA,需要逆向globaltask以及TEEOS本身,可以使用globaltask2elf.py恢复globaltask的ELF结构:

➜  python2 tckit/globaltask2elf.py ./tas_out/globaltask
➜  file ./tas_out/globaltask.elf 
globaltask.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped

以及使用tos2elf.py恢复TEEOS的ELF结构:

➜  python2 tckit/tos2elf.py ./TEEOS.img
➜  file Rtosck
Rtosck: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), no program header, not stripped

接下来就对这两个ELF进行逆向,主要是globaltask.elf:

RSA私钥解析

根据作者PPT,可以确定QEMU运行打印出的两个密钥中第一个私钥(x)为解密TA所需要的关键密钥:

image

但对于这个RSA的私钥解析,可以看出来这里的打印的公私钥没有什么openssl的格式,也就不是标准格式公私钥数据,所以猜测其打印的数据就是p、q、n、e、d等RSA相关数据,因此需要逆向分析解析私钥的代码。

但这里的密钥数据其实也直接可以通过公私钥的特性猜出来(就当CTF做),首先可以将私钥数据保存成二进制文件方便查看:

private_key  = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
open('private_key.bin','wb').write(private_key)

使用010editor对私钥数据相面,可以看出来其长度总共0x140字节:

image

对于RSA的私钥,在理论上一般表达为以下两种:

  • p、q、dp、dq、qinv (中国剩余定理优化的RSA计算 RSA-CRT)

关于RSA的中国剩余定理:

但在实际中,RSA私钥一般以PKCS#1和PKCS#8格式存储(ASN.1),其中不仅包括理论上的私钥数据,还包含公钥(n、e),例如使用openssl生成私钥包括(n、e、d、p、q、dp、dq、qinv),对应关系如下:

ASN.1 key structures in DER and PEM

RSAPrivateKey ::= SEQUENCE {
    version Version,
    modulus INTEGER, -- n
    publicExponent INTEGER, -- e
    privateExponent INTEGER, -- d
    prime1 INTEGER, -- p
    prime2 INTEGER, -- q
    exponent1 INTEGER, -- dp: d mod (p-1)
    exponent2 INTEGER, -- dq: d mod (q-1)
    coefficient INTEGER, -- qinv: (inverse of q) mod p
    otherPrimeInfos OtherPrimeInfos OPTIONAL
}

可以自己使用openssl生成一个1024bit的RSA私钥并查看:

➜  openssl genrsa -out rsa_private_key.pem 1024
Generating RSA private key, 1024 bit long modulus
.........++++++
......................++++++
e is 65537 (0x10001)

➜  openssl rsa -in ./rsa_private_key.pem -text 
Private-Key: (1024 bit)
modulus:
    00:d7:1f:f9:0f:d4:f8:00:91:fd:9e:d9:66:b1:12:
    d4:73:20:ee:50:06:8a:e9:f7:28:d6:e0:76:78:60:
    e6:96:cc:a0:4b:db:78:bb:56:8d:ae:0f:d9:33:d2:
    82:92:11:49:25:83:67:58:77:93:b5:68:4c:ec:7c:
    d6:4c:b1:9f:18:ce:93:c7:3e:d4:b4:cc:26:ec:59:
    1f:8c:aa:52:b8:9b:9f:e0:86:8a:53:65:6c:47:0f:
    de:40:bb:8a:28:53:df:81:da:61:97:32:56:3a:80:
    40:8c:27:38:58:3a:97:09:f7:8b:d7:59:37:c2:2c:
    39:8e:2d:c5:cd:30:b8:35:01
publicExponent: 65537 (0x10001)
privateExponent:
    26:b7:8f:68:c5:08:99:79:ac:ee:b0:eb:e5:84:a1:
    0d:d3:68:70:a8:ac:c9:ac:fd:01:a7:46:4b:26:0d:
    7a:28:7b:d5:0b:3b:f0:63:84:7e:46:45:ee:28:bd:
    ed:32:05:3b:26:2a:2c:66:e1:03:ae:30:e2:03:19:
    c2:95:d9:2f:16:2e:1e:b1:34:11:8b:22:02:e5:1e:
    ef:0e:21:59:fc:c1:6b:9a:da:24:eb:98:2d:fd:a0:
    19:2a:e1:1d:3e:a6:2b:af:03:48:14:f9:69:b0:73:
    ba:f7:c6:05:db:f7:ad:5a:b2:3e:80:3e:9f:2d:54:
    5a:2c:33:81:20:58:a5:11
prime1:
    00:f7:c1:40:a3:b5:20:a0:7d:f9:e7:38:df:ec:4d:
    2d:f1:17:00:23:d9:fa:ff:03:c1:e8:78:01:36:2c:
    a9:55:3f:67:d6:89:93:ca:b7:bf:b1:a1:2b:aa:90:
    1a:1c:a6:5a:df:2b:b4:4f:6d:60:04:45:30:b4:cc:
    3b:3f:07:5c:95
prime2:
    00:de:48:ba:aa:c6:f7:65:ba:65:84:6b:1c:04:92:
    a8:c4:76:55:57:d2:0e:86:60:53:31:fd:34:68:b0:
    1e:f5:85:b9:51:b8:0a:84:a5:ff:23:d8:73:19:1f:
    51:c2:66:fc:70:c8:fc:32:f0:5b:41:56:3d:61:c2:
    2c:91:43:af:bd
exponent1:
    1b:9c:a6:1f:98:a8:32:3a:d8:07:35:07:7f:c6:7a:
    40:4c:57:ef:a6:f3:9a:48:48:ec:27:b3:ba:dd:ef:
    61:58:d7:b1:c9:53:77:5c:53:38:f0:c5:75:14:ea:
    54:17:16:39:99:1d:57:5c:d1:3e:a8:97:6d:0e:f5:
    eb:68:5e:a1
exponent2:
    00:93:de:93:f6:f9:87:28:70:38:0a:3f:ea:92:8c:
    31:a3:08:09:3b:f3:ab:df:ee:82:49:b5:e4:40:64:
    31:24:29:82:1f:7f:ab:d7:94:49:c7:41:bd:47:90:
    13:26:9c:b6:00:1d:63:d0:4b:1e:99:b7:51:fc:0f:
    5c:f0:81:b3:8d
coefficient:
    00:e8:5b:63:26:f9:a2:e7:d2:08:c4:36:25:8c:64:
    8f:69:e6:6c:94:54:dd:8f:ff:d3:e4:56:cb:dd:b1:
    f5:4b:d1:a4:6e:44:9c:7f:77:ae:61:4f:49:e2:a0:
    96:ec:72:05:6b:3d:1a:22:13:a9:49:a1:f1:4b:dc:
    fe:5c:90:66:f0
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDXH/kP1PgAkf2e2WaxEtRzIO5QBorp9yjW4HZ4YOaWzKBL23i7
Vo2uD9kz0oKSEUklg2dYd5O1aEzsfNZMsZ8YzpPHPtS0zCbsWR+MqlK4m5/ghopT
ZWxHD95Au4ooU9+B2mGXMlY6gECMJzhYOpcJ94vXWTfCLDmOLcXNMLg1AQIDAQAB
AoGAJrePaMUImXms7rDr5YShDdNocKisyaz9AadGSyYNeih71Qs78GOEfkZF7ii9
7TIFOyYqLGbhA64w4gMZwpXZLxYuHrE0EYsiAuUe7w4hWfzBa5raJOuYLf2gGSrh
HT6mK68DSBT5abBzuvfGBdv3rVqyPoA+ny1UWiwzgSBYpRECQQD3wUCjtSCgffnn
ON/sTS3xFwAj2fr/A8HoeAE2LKlVP2fWiZPKt7+xoSuqkBocplrfK7RPbWAERTC0
zDs/B1yVAkEA3ki6qsb3ZbplhGscBJKoxHZVV9IOhmBTMf00aLAe9YW5UbgKhKX/
I9hzGR9Rwmb8cMj8MvBbQVY9YcIskUOvvQJAG5ymH5ioMjrYBzUHf8Z6QExX76bz
mkhI7Cezut3vYVjXsclTd1xTOPDFdRTqVBcWOZkdV1zRPqiXbQ7162heoQJBAJPe
k/b5hyhwOAo/6pKMMaMICTvzq9/ugkm15EBkMSQpgh9/q9eUScdBvUeQEyactgAd
Y9BLHpm3UfwPXPCBs40CQQDoW2Mm+aLn0gjENiWMZI9p5myUVN2P/9PkVsvdsfVL
0aRuRJx/d65hT0nioJbscgVrPRoiE6lJofFL3P5ckGbw
-----END RSA PRIVATE KEY-----

那以上导出的这0x140字节的RSA私钥应该包含什么了呢?

  • 如果是n、d,那么n、d各0xa0字节,换算为bit为1280,并非512、1024,不太合理
  • 如果是p、q、dp、dq、qinv,则其各64字节,换算为bit为512,可以为RSA 1024,较为合理

那是不是我们猜的p、q、dp、dq、qinv呢?如果是,这里为什么不包含e?因为按照RSA原始的计算方法:

e * d ≡ 1 (mod (p-1)*(q-1))

没有e也就不知道d,也就不能按照原始的办法解密。但其实中国剩余定理优化出的RSA计算方法中多出来的3个数据,即通过dp、dq、qinv,可以和p、q结合直接解密密文,而不需要e的直接参与。并且dp、dq、qinv是和e相关的,可以用过p和dp计算出e。其中dp 不是d*p,而是定义为 d mod(p-1),并且dp、e、p满足一个关系,dp与e在mod(p-1)下在互为逆元,即:

dp * e ≡ 1 (mod (p-1))

自己证明了一下(我也不知道证的对不对):

image

那我们就可以假设0x140字节的私钥数据是p、q、dp、dq、qinv,然后计算一下e:

from Crypto.Util.number import bytes_to_long
import gmpy2

private_key  = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'

p  = bytes_to_long(private_key[:64])
dp = bytes_to_long(private_key[128:192])
e  = gmpy2.invert(dp,(p-1))

print(hex(e))

结果为0x10001,就是常用的e,因此:

  • 以上导出的这0x140字节的RSA私钥,确实是p、q、dp、dq、qinv各64字节
  • dp与e在mod(p-1)下在确实互为逆元

在CTF中也有在不给p、q的情况下,仅通过n、e、dp来破解RSA的题目,大概原理就是利用dp与e在mod(p-1)下在互为逆元的性质,将其变化为等式:

【 dp * e ≡ 1 (mod (p-1)) 】 -> 【 dp * e = 1 + k * (p-1) 】(k为整数)

因为dp定义为d mod (p-1),所以dp小于p-1,因此k = [dp/(p-1)] * e - 1的取值范围为[-1,e-1)。题目一般给出e为0x10001,因此可以爆破k,计算对应的p,当p可以被n整除时,成功拆分n为p和q,即可破解RSA:

但现在我们这里不用这么麻烦,因为已经有p、q、e了,所以我们继续可以算出d,然后就可以对密文进行解密了(当然纯用p、q、dp、dq、qinv也可以解密):

from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
import gmpy2

private_key  = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'

p  = bytes_to_long(private_key[:64])
q  = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e  = gmpy2.invert(dp,(p-1))
d  = gmpy2.invert(e,(p-1)*(q-1))

print(len(long_to_bytes(d)))

其他参考:

当然也可以通过逆向找到这个RSA私钥的格式,从作者给出的封装代码中可以看出这个RSA私钥是通过白盒密码算法对ciphertext1(还是带符号的变量)解密得到的:

https://github.com/teesec-research/tckit/blob/master/globaltaskgencode.py

char *ciphertext1 = data + """ + hex(sym_map['ciphertext1']) + """;
char *ciphertext2 = data + """ + hex(sym_map['ciphertext2']) + """;

所以可以从globaltask.elf中对ciphertext1变量的使用出发进行逆向:

➜  strings globaltask.elf | grep ciphertext1
ciphertext1

可以通过IDA的Names或者Exports窗口搜索此符号,找到ciphertext1变量:

image

然后交叉引用可以跟到get_dx_private_key函数,通过对其中的_CC_CRYS_RSA_Build_PrivKeyCRT函数名字(CRT:中国剩余定理)以及参数,也可以确定RSA私钥的划分,其中的65字节是因为解密后的第一组数据是拷贝的起始地址是buf(v17)第二个字节,原因可能是:Leading 00 in RSA public/private key file

image

所以通过这个参数也能看出来私钥的0x140字节应该是被划成了五份,所以应该是p、q、dp、dq、qinv。如果想继续跟进_CC_CRYS_RSA_Build_PrivKeyCRT分析会发现进了系统调用,所以需要继续逆向TEEOS,即转换成名为Rtosck的ELF文件,在其中可以找到CRYS_RSA_Build_PrivKeyCRT函数,虽然逆向这个函数一眼看不太出来什么:

image

但CRYS_RSA_Build_PrivKeyCRT这个函数可搜到,是mbed-os的库函数,通过函数定义中的参数可以彻底确定私钥私钥的组成确实和猜测一致:

https://os.mbed.com/docs/mbed-os/v6.15/mbed-os-api-doxy/group__crys__rsa__build.html

CRYSError_t CRYS_RSA_Build_PrivKeyCRT	(	CRYS_RSAUserPrivKey_t * 	UserPrivKey_ptr,
uint8_t * 	P_ptr,
uint16_t 	PSize,
uint8_t * 	Q_ptr,
uint16_t 	QSize,
uint8_t * 	dP_ptr,
uint16_t 	dPSize,
uint8_t * 	dQ_ptr,
uint16_t 	dQSize,
uint8_t * 	qInv_ptr,
uint16_t 	qInvSize 
)	

RSA解密manifest

现在即可使用RSA来解密TA头部的manifest,不过在解密之前我们还是通过010editor看一看TA,可以发现开头的0x18字节比较干净,应该不是加密的数据。从0x98字节开始是个字符串,所以也肯定没加密。因此 0x18 到 0x98偏移这0x80字节的数据比较乱,应该就是加密的manifest,也正好是128字节,满足RSA 1024的密文情况:

image

通过之前逆向globaltask.elf中的get_dx_private_key函数往上交叉引用 ,可以找到load_secure_app_image函数,其中调用parse_manifest函数的参数中有一个立即数0x18,这个偏移和我们推测的TA中加密的manifest的偏移一致:

image

跟入parse_manifest函数,可以看到立即数128,和我们推测的manifest密文长度也一致:

image

所以直接尝试使用RSA解密TA开头偏移0x18,长度为0x80字节的数据:

from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
import gmpy2

private_key  = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'

p  = bytes_to_long(private_key[:64])
q  = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e  = gmpy2.invert(dp,(p-1))
d  = gmpy2.invert(e,(p-1)*(q-1))
n  = p * q

c = bytes_to_long(open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x18:0x18+0x80])
m = long_to_bytes(pow(c,d,n))

print(len(m))
print(m.hex())

解密后的数据如下,通过其中连片的00字节,就可以判断出解密成功了:

127

02f7c9f892ef5dbb32fe00fbca8c864b79c646b5c49f1462de4e0201000000000000000000000000400100002000000000000020000000200000001900000031a4679966df8337fb391a9b4bcf1695dbba3037ce89b876165298ac9ab783c813b20f304fb0d2d0e61e37a2fdd6835756803d7ae2367b27ff17dab70f422daf

但需要注意,解密后的数据的开头是02,这是PKCS#1 v1.5 的 padding,也叫 RSA_PKCS1_PADDING,是在数据头部填充padding:

image

所以需要把解密数据开头的0x02 到 0x00 的数据去掉:

from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
import gmpy2

private_key  = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'

p  = bytes_to_long(private_key[:64])
q  = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e  = gmpy2.invert(dp,(p-1))
d  = gmpy2.invert(e,(p-1)*(q-1))
n  = p * q

c = bytes_to_long(open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x18:0x18+128])
m = long_to_bytes(pow(c,d,n))
i = m.find(b'\x00') + 1

open('manifest.bin','wb').write(m[i:])

最终得到的明文manifest,长度0x74字节,具体如下:

manifest.bin

image

接下来就是从这0x74字节找出AES加密的KEY,另外其中也许还包含AES加密的IV。

AES密钥解析逆向

具体来说就是逆向globaltask.elf中的load_secure_app_image函数,以及其调用的两个关键函数:

  • parse_manifest:解密manifest并解析
  • elf_decry:对加密后的TA进行解密

image

确定加密TA的偏移

首先结合868ccafb-794b-46c6-b5c4-9f1462de4e02.sec分析load_secure_app_image,可以确定其调用elf_decry的第一个参数,就是加密TA文件中AES加密后TA_ELF的偏移,本例中偏移为0x1b4具体计算如下:

v15 = en_TA + 0x18;                  // 0x18
v20 = v15 + (0x99 & 0xFFFFFFFC) + 4; // 0x9c
v19 = *(_DWORD *)(en_TA + 0x14);     // 0x100

hex(0x18 + 0x9c + 0x100) == 0x1b4

因此load_secure_app_image函数与868ccafb-794b-46c6-b5c4-9f1462de4e02.sec的对应分析的方法如下:

  • *.sec文件头的0x18字节对应load_secure_app_image中解析出的一些全局变量
  • MANIFEST部分交给parse_manifest函数处理,并在其中也会解析出一些全局变量
  • 解密TA_ELF交给elf_decry函数,但其会使用parse_manifest解析出的一些全局变量

image

确定AES加密模式

跟入elf_decry函数,可以看到其使用的密码函数是符合GP标准的TEE密码函数接口:

GlobalPlatform Technology TEE Internal Core API Specification Version 1.3.1

  • TEE_AllocateOperation
  • TEE_SetOperationKey
  • TEE_CipherInit

image

elf_decry调用TEE_AllocateOperation的第二个参数为0x10000110,通过TEE_AllocateOperation函数的定义:

TEE_Result TEE_AllocateOperation(
TEE_OperationHandle* operation,
uint32_t algorithm,
uint32_t mode,
uint32_t maxKeySize );

可以识别出其使用的加密算法为AES CBC (0x10000110):

optee_os/lib/libutee/include/tee_api_defines.h

#define TEE_ALG_AES_CBC_NOPAD               0x10000110

确定AES的KEY

v12 = *(_DWORD *)(v20[67] + 4);
if ( v12 )
{
    memcpy(v12, dword_156764, dword_156758);
    v13 = TEE_SetOperationKey(v18, (int)v20);
    if ( v13 )
  • dword_156764
  • dword_156758

确定AES的IV

TEE_CipherInit(v18, dword_156764, dword_156758 / 2);
from Crypto.Cipher import AES

iv     =  open('manifest.bin','rb').read()[84:84+16]
key    =  open('manifest.bin','rb').read()[84:84+32]
cipher =  open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x1b4:0x1b4+32]

a = AES.new(key, AES.MODE_CBC,iv)
msg = a.decrypt(cipher)
print(msg)
➜  python3 exp.py
b'\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00'

完整解密脚本

from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
import gmpy2

private_key  = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'

p  = bytes_to_long(private_key[:64])
q  = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e  = gmpy2.invert(dp,(p-1))
d  = gmpy2.invert(e,(p-1)*(q-1))
n  = p * q

c = bytes_to_long(open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x18:0x18+128])
m = long_to_bytes(pow(c,d,n))
i = m.find(b'\x00') + 1

f = open('manifest.bin','wb')
f.write(m[i:])
f.close()

iv     =  open('manifest.bin','rb').read()[84:84+16]
key    =  open('manifest.bin','rb').read()[84:84+32]
cipher =  open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x1b4:]

a   = AES.new(key, AES.MODE_CBC,iv)
msg = a.decrypt(cipher)
open('868ccafb-794b-46c6-b5c4-9f1462de4e02.elf','wb').write(msg)

可以发现解出来的TA是甚至是带符号的:

868ccafb-794b-46c6-b5c4-9f1462de4e02.elf

➜  file 868ccafb-794b-46c6-b5c4-9f1462de4e02.elf 
868ccafb-794b-46c6-b5c4-9f1462de4e02.elf: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped

使用IDA逆向此TA可以发现确实有符号,但这个TA居然是可以调用外部函数的,例如SLog。这和OP-TEE的TA不同,OP-TEE的TA相当于静态链接,所有的函数都在静态链接到TA中,TA ELF 外部的功能只有通过svc系统调用进TEE OS完成:

image

回到华为 Trust Core 方案中的TA,找到SLog函数的本体实现在globaltask中,所以此方案中globaltask和TA运行时应该在同一个内存环境下:

image

另外 P9 Lite 上这套 Trust Core 方案的 TEE还是值得继续研究的,因为2016年极棒国外黑客nick用鼻子解锁的正是这款P9 Lite,固件版本也和目前的示例相近:

Nick Stephens-how does someone unlock your phone with nose

image

P9 Lite是在国外上市的手机,不过闲鱼上也可以买到,我买了一个,但是系统版本在2020年,尝试使用以上的私钥无法解密其中的TA。应该可以刷低版本复现2016年极棒的漏洞,之后有缘再玩:

其他相关:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK