1

Using Ghidra I have acquired the following Psuedo-code decompilation output from an ELF 32-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, not stripped.

From this decompilation I can see the key generation is based on a SHA1 of a MAC address

n = 12; // Not sure where 12 comes from, perhaps 2 * number of bytes in a MAC address?
SHA1_Init(&sha1_ctx);
SHA1_Update(&sha1_ctx,macAddr,6);
SHA1_Final((uchar *)&messageDigest,&sha1_ctx);
memset(&key,0,n);
j = 0;
// Why 
for (i = 0; (j < 20 && (i < (int)(n - 2))); i = i + 2) {
    sprintf((char *)((int)&key + i),"%02X",(ulonglong)*(byte *)((int)&messageDigest + j));
    j = j + 1;
}
memcpy(iv,&key,17);
aes_set_key(aesCtx,&key,128,0);
memcpy(buffer, encryptedBuffer, 116);
aesOut = buffer + 4; // Skip first 4 magic bytes
AES_cbc_encrypt(aesCtx,aesOut,aesOut,112,iv,0);

Using this decompilation I am trying create my own implementation of a decryption algorithm with the Tiny AES Encyption library in C.

uint8_t messageDigest[SHA_DIGEST_LENGTH];
uint8_t key[SHA_DIGEST_LENGTH * 2];
uint8_t iv[24];

memset(key, 0x0, 12); // Why memset 12 bytes? Is this key a 12 byte character string or is it 11 bytes with an extra byte zero'ed for the null terminator? 

SHA1((uint8_t *)  ethhdr->h_source, ETH_ALEN, messageDigest);

int i;
int j = 0;
for (i = 0; (j < SHA_DIGEST_LENGTH && i < 10); i += 2) {
    sprintf((char *) &key[i], "%02X", messageDigest[j]);
    j++;
}

memcpy(iv, key, 17); // Why 17? Only 12 bytes of key has been set
printf("%s\n", iv);
printf("%s\n", key);

uint8_t *data = encryptedBuffer;
memcpy(dsBuffer, data, 112);

struct AES_ctx aesCtx;
AES_init_ctx_iv(&aesCtx, key, iv);
// Skip first 4 bytes (magic bytes)
AES_CBC_decrypt_buffer(&aesCtx, (uint8_t *) dsBuffer + 4, 112);

This code appears correct as a line for line rewrite but appears logically flawed to me. In particular memcpy(iv, key, 17); from the decompilation it only appears 10 bytes are set in the key - how can we copy 17 bytes into the IV? Even more so why would we initialise 12 bytes to 0's with the first call to memset?

I searched standard for using SHA1 as key & IV for AES128 but couldn't find anything that matched.

Thanks in advance.

2
  • The key and IV were getting initialised to 0's. When I looked into the assembly in detail I could see stp xzr,xzr,[x7]=>key[0] which zero'ed all the bytes in the key and the IV. Lesson learnt, assembly is king in decompilation
    – Seb
    Apr 12 at 10:33
  • With this info in mind it was clear that the key was the first 10 characters of the SHA1 of the MAC Address and the other 6 bytes were just 0's Now confident it was only creating a 10 character key I was then decrypt using this. Thanks again for the help.
    – Seb
    Apr 12 at 10:39

1 Answer 1

1

ELF 32-bit LSB executable, ARM aarch64, version 1 (SYSV)

I guess you meant 64-bit ELF, but it's not important.

n = 12; // Not sure where 12 comes from, perhaps 2 * number of bytes in a MAC address? // Why for (i = 0; (j < 20 && (i < (int)(n - 2))); i = i + 2) { sprintf((char )((int)&key + i),"%02X",(ulonglong)(byte *)((int)&messageDigest + > j)); j = j + 1; }

The MAC address is 6 byte long, as you mentioned 12 is double this size, if you look to the iteration of the loop: sprintf(key + i, "%02X", messageDigest + j); it's actually "hexlify" the MAC address, meaning it will convert the raw MAC address into a hexdecimal uppercase format, i.e. "\xaa\xbb\xcc\xdd\xee\xff" will be "AABBCCDDEEFF".

memcpy(iv, key, 17); // Why 17? Only 12 bytes of key has been set

You pointed out a good question, and from the decompiled code, I think there's something wrong. At first the algorithm seems to:

  • Perform a SHA-1 for the 6 byte long MAC address
  • key is memset with the size of 12, double the size of the MAC address, so theoretically to store the MAC address in the hexlify form
  • the loop uses 20, the size of the SHA-1 digest, you didn't share the size of the key buffer, but the memset seems useless, and (i < (int)(n - 2))) is a big redflag, this condition will break the for loop earlier
  • key is duplicated to the iv buffer, 17 could be 128-bit + null char, but it doesn't make sense, because the key and iv have a fixed size, they don't have to be null terminated, maybe it's related to the hexlify and string operations, but the loop never null terminate the buffer anyway
  • aes_set_key(aesCtx,&key,128,0);, I wonder what's the last parameter 0, same with AES_cbc_encrypt(aesCtx,aesOut,aesOut,112,iv,0);

I also think a strcpy with fixed sizes could be optimized into a memcpy with string length + 1.

Overall, I think your analysis and the decompiled code is accurate, but the original code is really sketchy. There's a chance the IV is actually based on uninitialized data, but one of the 0 parameter tells the IV size is 0, and thus ignored.

If you can share more details or a use case, like inputs and expected output, it will help to figure out what happened.

BTW, did you identify the AES library used for this code?

1
  • I just send the output of file mybinary.bin and it showed the above output so to me it seems like a 32-bit binary (32 bit pointers) compiled for aarch64 platform. Perhaps was an ARM32 and they've ported it half-cooked to aarch64? The binary itself has been extracted from a router. This appears to be the AES implementation being used. These are the C Bindings for the ARM hardware encyption elixir.bootlin.com/linux/latest/source/arch/arm64/crypto/… I will see what I can share in terms of input/output. Would prefer not to share the MAC address if possible.
    – Seb
    Apr 9 at 11:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.