Wednesday, December 21, 2011

Ruxcon DSD challenge

At the Ruxcon (an Australian computer security forum) the Defence Signals Directorate (Australian intelligence agency) had the following text on their stand :

Apply Now
6AAAAABbi8uDwx4zwDPSigOK
ETLCiAM8AHQrg8EBg8MB6+wz
/7/z+TEct0SlpGf5dRyl53US
YQEE56Ri7Kdkj8IAABkcOsw=

It looks so simple that we will impair ourselves a little and try to solve it using only a web browser and an Internet access. As we will see it isn't much of a problem though.

We are going to use only the following 4 sites.

    From Base64 to Asm

    The text is Base64 encoded. For those who don't know it is easy to spot since Base64 only uses alphanumeric characters plus + and /. The number of characters is always multiple of three padded with '=' if necessary.

    Using the translator we convert it to hexadecimal.
    E8 00 00 00 00 5B 8B CB 83 C3 1E 33 C0 33 D2 8A 03 8A 11 32 C2 88 03 3C 00 74 2B 83 C1 01 83 
    C3 01 EB EC 68 74 74 70 3A 2F 2F 77 77 77 2E 64 73 64 2E 67 6F 76 2E 61 75 2F 64 65 63 6F 64 
    65 64 2E 33 FF A6 EF C3 FD
    
    It may be not clear on the spot but it is x86 code (74 is a je instruction , E8 is a call instruction).
    So we feed it to our online disassembler.
    [0x00000000]  call 0x00000005 
    [0x00000005]  pop ebx  
    [0x00000006]  mov ecx,ebx  
    [0x00000008]  add ebx,0x1e
    [0x0000000b]  xor eax,eax  
    [0x0000000d]  xor edx,edx  
    [0x0000000f]  mov al,[ebx]  
    [0x00000011]  mov dl,[ecx]  
    [0x00000013]  xor al,dl
    [0x00000015]  mov [ebx],al  
    [0x00000017]  cmp al,0x0  
    [0x00000019]  jz 0x00000046 
    [0x0000001b]  add ecx,0x1  
    [0x0000001e]  add ebx,0x1  
    [0x00000021]  jmp near 0x0000000f 
    [...]
    [0x00000046]  int3 
    

    Code inspection

    Let's look closer :
    [0x00000000]  call 0x00000005
    It jumps to 0x5 which is the next instruction but before it pushes on the stack the address of the instruction following call. That is 5.
    [0x00000005]  pop ebx
    We get the address pushed by call into ebx. (ebx=5)
    [0x00000006]  mov ecx,ebx
    ecx=ebx(=5)
    [0x00000008]  add ebx,0x1e
    ebx=ebx+0x1e(=0x23)
    [0x0000000b]  xor eax,eax  
    [0x0000000d]  xor edx,edx
    eax and edx are set to zero.
    [0x0000000f]  mov al,[ebx]  
    [0x00000011]  mov dl,[ecx]
    al is set to the value residing at the address pointed by ebx.
    dl is set to the value residing at the address pointed by ecx.
    [0x00000013]  xor al,dl
    [0x00000015]  mov [ebx],al
    They are XORed together and the result is put in memory at the address pointed by ebx.
    [0x00000017]  cmp al,0x0
    [0x00000019]  jz 0x00000046
    We jump at the end of the program when al is zero. It would be the case only if al and dl were the same before the XOR operation.
    [0x0000001b]  add ecx,0x1
    [0x0000001e]  add ebx,0x1
    [0x00000021]  jmp near 0x0000000f
    
    Our two memory pointers ecx and ebx are incremented and we loop back to instruction [0x0000000f].

    The rest of the code is just data used for XORing. It wasn't decoded by the disassembler because it is not valid x86 machine code.

    With that quick inspection we see that the program does just a simple XOR between the code at 0x5 and the data beginning at 0x23, just after the code. The XORed result is put in memory starting at offset 0x23 so that it does not overwrite the code.

    Solution

    There are two ways to solve it then.

    Solution 1

    The first one is to write a C program that does exactly the same and use codepad to compile and execute it. The program is pretty much a line of C for a line of asm not counting the final printf to see the decoded result. It could be simplified to get rid of the unnecessary eax and edx.
    unsigned char mem[]={
    0xE8,0x00,0x00,0x00,0x00,0x5B,0x8B,0xCB,0x83,0xC3,0x1E,0x33,0xC0,0x33,0xD2,
    0x8A,0x03,0x8A,0x11,0x32,0xC2,0x88,0x03,0x3C,0x00,0x74,0x2B,0x83,0xC1,0x01,
    0x83,0xC3,0x01,0xEB,0xEC,0x33,0xFF,0xBF,0xF3,0xF9,0x31,0x1C,0xB7,0x44,0xA5,
    0xA4,0x67,0xF9,0x75,0x1C,0xA5,0xE7,0x75,0x12,0x61,0x01,0x04,0xE7,0xA4,0x62,
    0xEC,0xA7,0x64,0x8F,0xC2,0x00,0x00,0x19,0x1C,0x3A,0xCC}; //Hex dump
    
    void main()
    {
     unsigned char eax, edx;
     unsigned char ecx=5;
     unsigned char ebx=0x23;
    
     while(1)
     {
      eax=mem[ebx];
      edx=mem[ecx];
      eax^=edx;
      mem[ebx]=eax;
      if (eax == 0)
       break;
      ecx++;
      ebx++;
     }
     printf("%s",mem+0x23); //Dump mem starting at the offset it was modified
    }
    
    No fear about printf, we know the string will be null terminated because the loop is exited only when the XORed result is zero.

    The output is http://www.dsd.gov.au/decoded.html.

    Solution 2

    The other way is to use a XOR calculator to get the result but there is a trick. Let's first dump the XORed data.
    From address 0x23 to the end :
    33FFBFF3F9311CB744A5A467F9751CA5E775 12610104E7A462ECA7648FC2 0000191C3ACC
    
    From address 0x5 :
    5B8BCB83C31E33C033D28A038A1132C28803 3C00742B83C10183C301EBEC 33FFBFF3F931

    The trick is that memory zones overlap. The stroke-through part must not be dealt with immediately. When we arrive there the underlined part (which is referring to the same address than the strokethrough part) will have been overwritten.

    Let's take care of the first part and feed it to the XOR calculator. (Since it can't deal with more than 64 hex digits we have to cut it in two).It gives us :
    687474703a2f2f7777772e6473642e676f76 2e61752f6465636f6465642e
    The underlined part is the data with which the strokethrough part should be replaced.

    Now we do the replacement and XOR the last bytes which are :
    0000191C3ACC
    687474703A2F
    It gives
    68746d6c00e3

    Then we just concatenate the two and trim all that is after the first 0x00 digit since it is the end of the string.
    687474703a2f2f7777772e6473642e676f762e61752f6465636f6465642e68746d6c

    Finally we feed it to the Translator Hex to String and voila. We get this : http://www.dsd.gov.au/decoded.html.

    1 comment:

    1. Thank you for this post - this has been a long time in the making, but this post inspired me a decade ago to try to build a commandline solution (which I did) and now, with much more experience, a complete R solution. I've detailed it here: https://jcarroll.com.au/2021/12/23/adventures-in-x86-asm/

      Best wishes and Merry Christmas!

      ReplyDelete