29
Feb
2012

CODEGATE 2012 – Binary 300

In this challenge we’re asked to figure out the number of attacks (and related ports) carried out by a zombie host, provided a zombie client binary and an associated data file.

Like all binaries encountered up until Bin300, this Win32 executable is packed (Upack v0.39 final, according to CFF explorer). Luckily reaching OEP isn’t all that hard. Simply step through the code until you encounter the first far jump, which takes you to 00407E93. From there, either scroll down or search for the first RETN – which you should find at 0040802F. Place a breakpoint on it – this is the JMP which takes us to OEP. Dump the process and optionally fix the IAT, then open the binary in IDA.

The unpacked executable looks like a typical botnet client and the data file provided with it doesn’t have any immediately obvious format. Specifically looking for some code which makes use of the provided data file and connects to arbitrary remote hosts, we end up at 00401DE0 (to which a filename is passed):

  File = fopen(Filename, "rb");
  if ( File )
  {
    fseek(File, 0, 2);
    Count = ftell(File);
    rewind(File);
    memory = malloc(Count);
    fread(memory, 1u, Count, File);
    fclose(File);
    _memory = memory;
    if ( *(_BYTE *)memory == 1
      && *(_DWORD *)((char *)_memory + 1) == *(_DWORD *)inet_addr_0
      && *(_WORD *)((char *)_memory + 5) == port_0 )
    {
      lpHandles = (HANDLE *)operator new(4 * *(_DWORD *)((char *)_memory + 9));
      v31 = GetTickCount();
      lpParameter = (char *)memory + 13;
      for ( i = 0; i < *(_DWORD *)((char *)_memory + 9); ++i )
      {
        v30 = lpParameter;
        do_xor((int)((char *)lpParameter + 4), 0x15u, *(_DWORD *)lpParameter);
        lpHandles[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)StartAddress, lpParameter, 0, 0);
        lpParameter = (char *)lpParameter + *(_WORD *)((char *)v30 + 23) + *(_WORD *)((char *)v30 + 13) + 25;
      }
      WaitForMultipleObjects(*(_DWORD *)((char *)_memory + 9), lpHandles, 1, 0xFFFFFFFFu);
...

Having opened the passed file, the code checks for a magic byte (1) and whether the C&C IP/port is specified in the file. If this check passes, a number of threads is launched, each of which attempts connects to a single remote port. A trivial xor cipher is used to obscure the data used by the ‘attack’ threads. We have partly reversed the data file format and have written some code to parse it and print the ports ‘attacked’:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

struct command {
    uint32_t key;           // 0
    uint32_t time0, time1;  // 4 8
    uint8_t mode;           // 12
    uint16_t size0;         // 13
    uint8_t addr[4];        // 15
    uint16_t port;          // 19
    uint8_t unk[2];         // 21
    uint16_t size1;         // 23
} __attribute__ ((__packed__));

void crypt(unsigned char *data, unsigned int num, unsigned int key)
{
    int i;

    if(key) {
        for(i = 0; i < num; i++) {
            data[i] ^= key;
        }
    }
}

void parse(unsigned char *buf)
{
    unsigned char *p = (buf+13);
    unsigned int sum;
    int i;

    struct command *c;
    sum = 0;

    for(i = 0; i < 8; i++) {
        c = (struct command*)p;

        crypt((unsigned char *)&c->time0, 0x15, c->key);

        printf("port %d\n", c->port);
        sum += c->port;

        p += c->size0 + c->size1 + 25;
    }

    printf("Sum of ports: %u\n", sum * 8);
}

int main(int argc, char *argv[])
{
    unsigned char buf[4096];

    if(argc < 2) {
        printf("usage: parse <file>\n");
        return 1;
    }

    FILE *fp = fopen(argv[1], "rb");

    fread(buf, sizeof(buf), 1, fp);

    parse(buf);

    fclose(fp);

    return 0;
}

Running this, yields the challenge flag (the number of attacks multiplied by the sum of all ports):

$ ./parse dRcw.ziq 
port 80
port 443
port 21
port 23
port 55
port 110
port 3389
port 1521
Sum of ports: 45136

Flag: 45136

Comments are closed.