Write-Ups

Reverse Engineering

Area 51 (450 pts)​

This binary was obtained from an encryption device running on some fairly common processor IP.
However, one of the flash readback columns was damaged. Reconstruct the program and find the flag!

Hint: it's a core that survived from the 80s

Area51.bin is a modified version of firmware extracted from a SiLabs C8051 device. Due to the extraction technique, some flash columns may be damaged, and require recovery. The use of a non-standard register use convention makes analysis slightly more complicated.

Disassembly

Area 51 is (as the name implies) an 8051 binary. However, loading the binary in a disassembler does nothing useful.

corrupt-decompile.png

The hint suggests that there is some data corruption due to a column failure. Careful examination of the binary reveals that bit 4 of all bytes is stuck at 0. (Note: the actual binary has bit 4 randomly set. We have simplified the problem by turning it to a stuck-at fault)

misread-bytes.png

Starting at the normal reset vector of 0, and interactively setting/clearing bits until a "reasonable" control flow is available results in a somewhat usable program. Note that some constants will remain corrupt: this is to be expected.

after_correction.png

Program analysis

At some point it is possible to notice that an (error'd) constant 9E3778B9 is loaded. A number of XOR operations follow, with 64 rounds applied, suggesting it is XTEA, used in an ECB mode.

xtea_magic.png

Following dataflow eventually reveals the key and ciphertext used.

data_and_key.png

Decrypting the payload

The key and ciphertext also have bit 4 stuck at 0. However, XTEA is a block cipher. We know that the flag starts with "DSO-NUS{" and only a valid key will produce this from the first 8 bytes. We have 16 key bits, and 8 ciphertext bits to permute due to the stuck-at fault.

 

We can proceed to error correct and test with the listing below:

char corrupt_ciphertext[128] = {0x26, 0x43, 0xe0, 0x67, 0x29, 0xaa, 0x64, 0xa0,

                                    0xcc, 0x60, 0x8f, 0xe8, 0xe9, 0xec, 0x8b, 0xaf,

                                    0xe9, 0x64, 0xaa, 0x4c, 0x42, 0x68, 0x08, 0x48,

                                    0x2f, 0x6e, 0x0a, 0x20, 0x84, 0xcd, 0x2a, 0x84,

                                    0xc1, 0xaf, 0xcc, 0x89, 0x61, 0x01, 0xa2, 0xce, 

                                    0x83, 0x87, 0x6d, 0x6f, 0x23, 0x60, 0x23, 0x81,

                                    0x28, 0xc1, 0x4c, 0x40, 0xc3, 0xe1, 0xee, 0x4b,

                                    0x45, 0x06, 0xcb, 0xef, 0xca, 0xec, 0x4e, 0xcf,

                                    0x86, 0x83, 0xa1, 0x47, 0xeb, 0x20, 0x24, 0x4c,

                                    0xc5, 0xad, 0x45, 0xee, 0xc5, 0xc3, 0xa1, 0x05,

                                    0x4e, 0xc1, 0xc1, 0x49, 0xca, 0x0a, 0x61, 0x8e,

                                    0x4e, 0xc1, 0xc1, 0x49, 0xca, 0x0a, 0x61, 0x8e,

                                    0x4e, 0xc1, 0xc1, 0x49, 0xca, 0x0a, 0x61, 0x8e, 

                                    0x4e, 0xc1, 0xc1, 0x49, 0xca, 0x0a, 0x61, 0x8e,

                                    0x4e, 0xc1, 0xc1, 0x49, 0xca, 0x0a, 0x61, 0x8e,

                                    0x4e, 0xc1, 0xc1, 0x49, 0xca, 0x0a, 0x61, 0x8e};

    uint32_t corrupt_key[4] = {0xa9c4a4a7, 0x0bcf62c4, 0xe2cfc4e9, 0xebccad6e};

 

    uint32_t correction=0xa6f825;

    uint8_t corr[24];

    int i;

    for(correction=0; correction < 0xffffff+1; correction++){

        uint8_t cleartext[8];

        memcpy(cleartext, corrupt_ciphertext,8);

        uint32_t key[4];

        memcpy(key, corrupt_key, 4*sizeof(uint32_t));

        uint32_t c = correction;

        for(i=0;i<24;i++){  //Try the current correction

            if(c&1 == 1){

                corr[i] = 0x10;

            }else{

                corr[i] = 0x00;

            }

            c>>=1;

        }

        uint8_t* dkey = (uint8_t*)key;

        for(i=0;i<16;i++){

            *dkey |= corr[i];

            dkey++;

        }

        uint8_t* dct = cleartext;

        for(i=16;i<24;i++){

            *dct |= corr[i];

            dct++;

        }

        printf("Corr: %x Ciphertext8: %x %x Key: %x %x %x %x ", correction, (uint32_t)cleartext[0], (uint32_t)cleartext[1], key[0], key[1], key[2], key[3]); 

        decipher(64,(uint32_t*)cleartext,key);

        for(i=0; i<8; i++){

            printf("%c", 0xff&cleartext[i]);

        }

        printf("\n");

    }

Eventually, this reveals the correct key as:

    Corr: a6f825 Ciphertext8: 26 53 Key: a9d4a4b7 bcf72c4 f2cfc4e9 fbdcbd7e DSO-NUS{

Applying the key value a9d4a4b7 bcf72c4 f2cfc4e9 fbdcbd7e to the rest of the ciphertext 8 bytes at a time, and correcting the 8 bits will yield an ASCII string containing the cleartext:

    DSO-NUS{d88d02623ed4b4aa853f4f955b3c5213a8da4ac0feda1585b54728ddfe813a44}