[Back to malware page]

Backdoor Spotcom

Download backdoor binary images for analysis
Download server.c and load.c for testing

Some other references (added Nov 30, 2003)

Backdoor.Spotcom (Symantec)
Win32.Spotcom (CA virus information center)

Program analysis

1. Overview

Spotcom is a backdoor client application that allows a hacker to control infected machine from the internet. It uses a fake DNS server for communication. The server IP address (218.242.252.211) is hard-coded in the program body. This particular piece of software was installed on machine via a bug in IE. Altough it is not limited in this form of distribution, it still needs one as it includes no spreading mechanism in itself. Spotcom installs itself as a Windows NT service by replacing itself with a valid Microsoft service QOS RSVP. Service installation requires system level privileges. If the system is configured properly, this program is not a threat. The program also uses some other NT-specific win32 API calls, which makes it only available on NT/W2K/XP (and maybe later).

The program is distributed in two files:

%sysdir%\msrsvp.exe
%sysdir%\olegui.dll

It may also create one or more files of the following name:

%sysdir%\wins\logX.txt
where X presents a physical drive letter (C, D, and so on).

Uninstallation: replace the path %sysdir%\msrsvp.exe with %sysdir%\rsvp.exe in QOS RSVP service and delete files %sysdir%\msrsvp.exe and %sysdir%\olegui.dll. Note that the installation allows to take over any service, not only RSVP. msrsvp.exe is also capable of creating a service of it's own, named as "IIS publishing service" (more on this in chapter 3.).

Defense: close direct outbound access from clients (DNS, HTTP, FTP..)

2. System installation

The two executable files are packed as one executable using a custom binder application. The binder was named as "web2.exe", it was packed using the UPX executable packer and it's size is 36864 bytes. The binder extracts the files msrsvp.exe and olegui.dll and executes msrsvp.exe with argument "replace rsvp", which replaces the executable path in Microsoft QOS RSVP service (for more information of msrsvp.exe arguments, see chapter 3.). After that, it starts the service (chapter 4.)

3. msrsvp.exe arguments

msrsvp.exe accepts a couple of command line arguments. When executed without argument, it attempts to run as a NT service.

install This makes a new service in the system, named as "IIS publishing service"
replace Replace service service executable path with msrsvp.exe
remove Remove "IIS publishing service"
listen Listen on TCP port port and spawn a cmd.exe
connect Connect cmd.exe to host and TCP port port

4. Running

When started as a NT service, msrsvp.exe starts a hidden IE window (or svchost.exe, it it doesn't find IE) and injects olegui.dll in the running program using the standard techinques (VirtualAllocEx(), WriteProcessMemory(), LoadLibrary(), CreateRemoteThread()). For more information on this, read John Peloquin's excellent paper. Once loaded in host application, olegui.dll starts communicating with the server using a protocol described in chapter 5. This design may fool some personal firewall products, because all the traffic is originated from hidden IE instance. Everyone allows IE to communicate, right? Most importantly, the process does not show up in task manager (of course, the host IE process is visible, but don't we all have always couple of them running? :-).

5. Communication protocol

The communtication protocol is a binary protocol that uses UDP port 53. Although it runs over a standard DNS port, the protocol has little to do with the DNS protocol (the Internet Domain Name System). Basically, the protocol is a simple request-response type protocol. The client sends requests and timeouts if the server does not answer. When timeout occurs, the client sends another query. When the server has command in it's queue for the client, it sends a single packet that includes the command transaction id and all the necessary arguments in predefined locations in the packet payload. The clients executes the command, replies with small reply packet and sends query again. Before the query phase (Phase 2) can happen, the server database has to be synchronized (Phase 1).

The following are example packets of each protocol transaction in tcpdump format:

Phase 1 - synchronization (transaction id 0x46)

After startup, the client sends periodically a synchronization packet to server. Packet's payload is of size 84 bytes and it consists of mostly random bytes. This is because of uninitialized stack variables in the client code. The packet should include the client's IP address (bolded at offset 0x65) but instead of it, there's a pointer to the IP address. This makes no sense, probably it's a bug.

19:13:30.664571 IP 192.168.1.20.1231 > 218.242.252.211.53:  522 [70a] [0q] [2n][6144au] (84)
0x0000   4500 0070 0bd6 0000 8011 9524 c0a8 0114        E..p.......$....
0x0010   daf2 fcd3 04cf 0035 005c 7be8 020a 0000        .......5.\{.....
0x0020   0000 0046 0002 1800 ffff ffff c0f9 d000        ...F............
0x0030   5cc3 fc77 c80b 1800 d800 0000 0906 0200        \..w............
0x0040   0000 0000 0000 0175 9af8 d000 7801 1800        .......u....x...
0x0050   0500 0000 4000 0000 e802 1800 e802 1800        ....@...........
0x0060   d12c f977 78f4 981a 0002 0000 0000 0000        .,.wx...........
Phase 1 - server reply (transaction id 0x15)

The server replies to syncronization request with the following message

19:13:30.666950 IP 218.242.252.211.53 > 192.168.1.20.1231:  21 [0q] (12) (DF)
0x0000   4500 0028 0000 4000 3f11 a242 daf2 fcd3        E..(..@.?..B....
0x0010   c0a8 0114 0035 04cf 0014 612a 0015 0000        .....5....a*....
0x0020   0000 0000 0000 0000 0000 0000 0000             ..............
Phase 2 - query (transaction id 0x24)

After the synchronization, client starts sending periodically query packets. The query packet's payload includes a client name (offset 0x28) as returned by GetComputerName win32 API call.

19:14:20.692468 IP 192.168.1.20.1231 > 218.242.252.211.53:  532 [36a] [2q] [2au][|domain]
0x0000   4500 004e 0bd8 0000 8011 9544 c0a8 0114        E..N.......D....
0x0010   daf2 fcd3 04cf 0035 003a 6f1d 0214 0000        .......5.:o.....
0x0020   0002 0024 0000 0002 4a54 5245 5300 0000        ...$....JTRES...
0x0030   0000 0000 0000 0000 0000 0000 0000 0000        ................
0x0040   0000 0000 0000 0000 0000 0000 0000             ..............
Phase 2 - server command (various transaction ids)

Normally, server doesn't respond to query packets. When it responds, it encapsulates a command to client in packet payload. Following packet is an example of command timeout 16 seconds (id 0x0b):

19:14:20.694464 IP 218.242.252.211.53 > 192.168.1.20.1231:  11 [0q] [16au] (12)(DF)
0x0000   4500 0028 0000 4000 3f11 a242 daf2 fcd3        E..(..@.?..B....
0x0010   c0a8 0114 0035 04cf 0014 6124 000b 0000        .....5....a$....
0x0020   0000 0000 0000 0010 0000 0000 0000             ..............
See the chapter 6. for complete list of commands.

Phase 2 - client reply (transaction id 0x04)

Client always replies to command packets with the following packet:

19:14:20.695026 IP 192.168.1.20.1231 > 218.242.252.211.53:  533 [4a] [3q] Type0(Class 0)? .[|domain]
0x0000   4500 002e 0bd9 0000 8011 9563 c0a8 0114        E..........c....
0x0010   daf2 fcd3 04cf 0035 001a 5f17 0215 0000        .......5.._.....
0x0020   0003 0004 0000 0000 0000 0000 0000             ..............

6. Commands

The following table lists all the commands that are implemented in the client code.

Command (transaction id) Description
synchronize (0x15) Synchronize client for communication
timeout (0x0b) Set timeout for queries. Default is 50 seconds. Basically, this adjusts timout parameters in select() winsock API call.
listen (0x0c0b) Listen on TCP port port and spawn a cmd.exe (simple telnet server)
connect (0x0c0c) Connect cmd.exe to remote host using defined TCP port (reverse telnet)
monitor (0x0c0d) Start filesystem monitoring. For more information, see chapter 7.
getfile (0x0c0e) Connect to remote server using a custom TCP protocol and request a file. Returned file is written over an existing local filesystem file (see chapter 8).

Most of the above commands need no further explanation. We'll take a closer look in file system monitoring and getfile function in the following chapters.

7. File system monitoring

Transaction with id 0x0c0d starts a special fuction that I call a file system monitor. It starts by determining all mounted disks with GetLogicalDrives() win32 API call. Then it starts a new thread for every drive it founds and begins to monitor the drive filesystem activity with ReadDirectoryChangesW() API call. When something happens in filesystem, it writes the action in log file %sysdir%\wins\logX.txt where X is the drive letter (C, D, ..). Example log file logC.txt:
Directory/File added - C:\New Text Document.txt
Directory/File removed - C:\New Text Document.txt
Directory/File added - C:\RECYCLER\S-1-5-21-789336058-838170752-725345543-500\Dc339.txt

The function reads three parameters in command packet payload: server name/address, username and password. These parameters are used later for transferring log files to remote server using the FTP protocol. The transfer takes place two times in a day, about 04 and 12 (the exact time depends on when the client has started execution). The client first changes it's directory as "use". Then it makes two directories, first one named as it's IP address and the second one presenting a current date inside the first directory. Then it uploads all the log files in that directory. Example log:

Sun Nov 23 12:07:41 2003  CWD use
Sun Nov 23 12:07:41 2003  MKD 192.168.1.20
Sun Nov 23 12:07:42 2003  CWD 192.168.1.20
Sun Nov 23 12:07:42 2003  MKD 2003-11-23-12-7
Sun Nov 23 12:07:42 2003  CWD 2003-11-23-12-7
Sun Nov 23 12:07:42 2003  TYPE I
Sun Nov 23 12:07:42 2003  PORT 192,168,1,20,5,47
Sun Nov 23 12:07:42 2003  STOR logC.txt

8. getfile

This function reads four parameters in command packet payload: server IP address, TCP port, authentication string and a local filename. It makes a TCP connection to server and sends the authentication string to server. The server then responds something (no matter what) and waits for next packet from client. After that, the server throws some data in the TCP connection and the clients writes that data in place of the local file determined as an argument.

OK.. What's this? I guess it is some sort of remote file fetching implementation. It may even be a buggy http client implementation (server may request a normal "GET /file.exe HTTP/1.0" stuff in authentication string). But what's strange, the client throws the authentication string with two NULL-bytes in front (\0\0string). My apache server was very upset about that. I was able to fetch files with custom server implementation using netcat. But that sounds very strange, why in earth the author has chosen this instead of normal HTTP? This function may be worth of further examination.

9. Conclusion

I was really impressed about couple of things in this backdoor. First thing was the file system monitoring feature described in chapter 7. Using this simple technique, it is possible to spy on every single filesystem operation in system. In effect, this gives the hacker quite good overview what's interesting in this particular system. Another thing that caught my attention was a fair amount of work that the program does for keeping it stealthy - DLL injection and using DNS port for reverse communication channel. The DLL injection is becoming de-facto in trojan industry and they are now really looking for alternative communication channels instead of normal "direct" connection to client or using reverse channel via HTTP. This is getting scary. What about this type of program that uses real DNS protocol for communication? Registering an anonymous subdomain with full NS records is not a big deal.. Do you allow recursive DNS queries from workstations? Is your network safe? Well, I hope that we will never see this kind of super trojan.

This implementation also suffers from a couple of flaws. First off, it does not implement any kind of code-obscuring techinques. It is all right there, ready to disassemble and read. Or maybe it does, I'm just missing it for that reason.. :-) Second thing, there is at least one entry level programming error in the code (IP address handling of gethostbyname(), see chapter 5). This is very strange, because the IP address is determined successfully with the very same routines elsewhere in the code (see chapter 7. for example). This makes me wonder, is this some development version relesed in hurry or impatient cut-and-paste? Anyway, the idea and design is still very good.

Test environment

Because of the program design, debugging is not so easy as it may first look like. Normally, I use the integrated debugger in IDA Pro, but in this case, it is not an option. The debugging has to be done by attaching the host process in debugger after it has loaded olegui.dll. That is where OllyDbg debugger comes in picture. Just use "Attach" in "File" menu and browse the memory window for olegui.dll. You can set breakpoints in the dumped DLL code and debug it like any "normal" program. Of course, this is normal stuff for all debuggers, but OllyDbg works great and is totally free.

Another thing that complicates the testing is the server. I don't like the idea of testing directly with the real thing (218.242.252.211). What if the server is alive? So, the options are pure static analysis of disassembly or some sort of emulation. That is what I did.

The test environment includes one Windows 2000 pro in VMWare virtual machine (client), a Linux/alpha server and OpenBSD firewall that does a NAT for server IP address (218.242.252.211). During live testing, some custom tools had to be developed:

load.c does just what it sounds like. server.c implements all of the transactions that can be found in the client code. It does not support multiple clients and automatic command queueing, it is a simple single-client server developed solely on debugging purposes. Here's some random "screenshot" of server.c in work:

Sun Nov 23 08:18:35 2003: received packet from 192.168.1.20:1316
  Sequence: 0x0002
  Transaction id: 0x24 (query)
  Client: JTRES
  00  0x02 0x14 0x00 0x00 0x00 0x02 0x00 0x24 
  08  0x00 0x00 0x00 0x02 0x4a 0x54 0x52 0x45 
  10  0x53 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  18  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  20  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  28  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  30  0x00 0x00 

Sun Nov 23 08:18:35 2003: sending packet to 192.168.1.20:1316
  Transaction id: 0x0c

Sun Nov 23 08:18:35 2003: received packet from 192.168.1.20:1316
  Sequence: 0x0003
  Transaction id: 0x04 (reply)
  00  0x02 0x15 0x00 0x00 0x00 0x03 0x00 0x04 
  08  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  10  0x00 0x00 0x00 0x00 0x00 0x00 
Much cleaner look than tcpdump! Using server.c in company with OllyDbg helped greatly in understanding some parts of code.


[Back to malware page] Home