The primary purpose of the binary is to provide a remotely controllable denial of service (DoS) engine and provide a backdoor into a compromised machine. It can perpetrate different kinds of DoS attacks like DNS flood (both of a DNS server and a reply flood of any host), IP fragment attack, and SYN flood. It also allows arbitrary commands to be executed on the compromised host, and can spawn a backdoor shell listening on TCP port 23281.
The binary is controlled by a connectionless protocol, so it is in fact feasible that a single control program sends the same attack commands to a large number of such attack daemons, amplifying the attacks to strong distributed denial of service (DDoS) attacks.
The binary is a ELF executable, statically linked and stripped. While discovered on a Linux system (and it has been compiled on a Linux system), it wouldn't be surprising to find this tool on other systems: it could run directly on FreeBSD using Linux compatibility, and as it uses only standard socket calls it could easily be recompiled to run on just about any Unix-like system. The binary is controlled by sending packets over IP using a rarely-used protocol number of 11 (assigned to NVP, or Network Voice Protocol). Inside the command packet is an encoded data part that begins with a "command code". This command code is a value from 1 to 12, and we explain the capabilities of each command below. One important capability is that the few commands (1 and 3) that require a response to the controller can obscure the location that replies are sent to by sending 10 duplicate copies of the reply to different addresses, similar to the "decoy scan" available in nmap. All of the attack commands take a target system parameter that is either a host name or a numeric IP address. For detailed information on the command packet structure and the parameters each of these commands takes, see the "technical details" appendix to the technical advisory.
Command | Description | Capabilities |
1 | Give attack status query | Sends a reply packet indicating whether an attack is currently running, and if so which attack is being performed. |
2 | Set communication parameters | Sets the daemon to respond to either a single given IP address, a given set of 10 addresses, or a given IP address plus 9 randomly chosen addresses. |
3 | Execute a command | Executes a command on the compromised system, capturing output and sending back according to the reply method (set in command 2). Times out after 10 seconds. |
4 | DNS reply flood attack (slow) | Sends SOA queries to a sequence of 8000 DNS servers (addresses hard-coded in the attack binary) with a fixed, spoofed source address in order to flood that machine with DNS replies. Can come from a given fixed port, or the attack binary will generate random source ports for each packet if the source port parameter is 0. Sends packets at a fixed, controlled rate (about 100 packets per second on our test machine). |
5 | Fragment attack | Sends many packets with a high fragment offset (this is the "Jolt2" DoS technique). Might cause the remote system to allocate may packet reassembly buffers, for a denial-of-service (see the technical advisory for more details). The packets may be either ICMP or UDP packets, and the source address to use for the packets must be given. |
6 | Spawn backdoor shell | Starts a process listening on TCP port 23281. When a connection
is made, if the first 6 characters read are "SeNiF" followed by a
newline, then /bin/sh is executed and attached to this
TCP connection. |
7 | Silent execute command | Executes a command on the compromised system. Output is not captured unless redirection is given in the command string. Command can run for 20 minutes before timing out. |
8 | Kill the current attack | Stops any currently-running attack or command |
9 | DNS reply flood attack (rate adjustable) | The same as command 4 except that an additional parameter controls the rate at which packets are sent out. Can be adjusted from the slow rate of command 4 all the way up to sending packets almost as fast as possible. |
10 | SYN flood (slow) | Performs a SYN flood on a given target host with a given destination port number. Source addresses can be either fixed or random. Packets were sent at about 100 per second on our test machine. |
11 | SYN flood (rate adjustable) | The same as command 10 except that an additional parameter controls the rate at which packets are sent out. Can be adjusted from the slow rate of command 10 all the way up to sending packets almost as fast as possible. |
12 | DNS request flood | Sends many SOA queries to a single, given DNS server. The source IP and port can either be given or randomly chosen for each packet (address and IP are independently controllable). The rate at which packets can be sent is controlled by an additional parameter, ranging from about 100 packets per second to almost full-rate packet transmission. |
Starting at the 3rd byte of the data portion of the control packet, the data is encoded (the 1st byte of the packet determines the packet type -- 2 for control packets, and 3 for reply packets, and it doesn't seem like the 2nd byte is ever used). For details on the packet formats, including an example using the captured data posted by the honeynet team, see the "technical details" appendix of the technical advisory.
In the encoded part of the packet, the following algorithm is used: the first byte simply has the constant 23 added to it. Second and subsequent bytes have the previous output byte added to it, as well as the constant 23. We'll call this encryption method "Caesar cipher with chaining," and it is terribly insecure! The encoding method is illustrated in the following diagram:
It is easy to reverse this process to get the decoding method.
The encoding and decoding routines are shown below in C code, as we reconstructed them. Note that the implementation below is much simpler than what is in the challenge binary (see bonus question 1 below for more info), but is functionally equivalent.
Encoding Function:
void encode(int len, u_char *in, u_char *out) { int i; if (len <= 0) return; out[0] = in[0]+23; for (i=1; i<len; i++) out[i] = in[i]+out[i-1]+23; }
Decoding Function:
void decode(int len, u_char *in, u_char *out) { int i; if (len <= 0) return; for (i=len-1; i>0; i--) out[i] = in[i]-in[i-1]-23; out[0] = in[0]-23; }
The interaction of the binary with its control program will generate traffic using IP packets with the protocol field set to 11. To detect it, listen for protocol 11 traffic. Since protocol 11 is just one example of a protocol that is rarely used, a more general solution is to have an IDS listen for all traffic that uses a protocol other than TCP, UDP or ICMP (protocols 6, 17, and 1, respectively). Also a firewall could filter all traffic that is not one of these regularly used protocols. The rules must be adapted in the obvious way for organizations that do use other protocols (IPV6, for example).
In addition, it is a good general piece of advice to have an IDS detect signs of outgoing DoS attacks as well, since that's a sign of either a rogue internal user or one of these DoS attack tools.
The following three techniques could have been done to make analysis more difficult:
-f
option to figure out the system calls used by the binary.[mingetty]
to
avoid detection.
strings
output.On the other hand, the first two methods could have been motivated by reasons other than making reverse engineering difficult. In particular, to avoid problems with different library versions, it is common to statically link a binary, and stripping the resulting binary reduces the size substantially. The forks are a fairly standard way of setting up an independent background daemon as well.
There are several distributed denial of service (DDoS) tools that are similar in various ways. We list a few here from most similar to least similar:
argv[0]
, encrypts commands,
provides a variety DoS attacks (ICMP flood, UDP flood, SYN flood,
and Smurf attacks), as well as a backdoor root shell. The main
differences are that TFN2K uses good encryption (blowfish) and does
communication over standard protocols (TCP and UDP).
Using a rarely-used protocol number (protocol 11) for the control
channel seems to be a unique characteristic of this particular tool.
I couldn't find evidence of other tools with such a control channel
except for this one reference on the snort-users mailing list archive:
http://www.ultraviolet.org/mail-archives/snort-users.2001/0515.html
-- this is most likely talking about the same tool that we
analyzed for this challenge!
The person who developed this tool has some knowledge of
networking since they were able to write code to hand-assemble
packets. Furthermore, the control method of using an unused protocol
is fairly clever, especially since
many firewalls and IDS systems will not block or flag NVP traffic.
The rogue process also will not show up on netstat
when
invoked with some commonly used parameters (for example, we often do either
"netstat -tanp
" or "netstat -uanp
" which
will only show TCP or UDP listeners).
On the other hand, the coding skills of the person who developed this tool are very weak. The data encoding scheme is weak and is easily discovered, and while some techniques might have been used to make reverse engineering difficult (see question 5 above) this was not done well. To pick one particular coding example, the following is our C-style reconstruction of how they implemented the decoding routine:
void decode(int len, u_char *in, u_char *out) { char workbuffer[len]; int ch, pos, i; out[0] = 0; for (pos=len-1; pos>=0; pos--) { if(pos == 0) ch = in[0]; else ch = in[pos] - in[pos-1]; ch = ch - 23; while (ch < 0) ch += 256; for (i=0; i<len; i++) workbuffer[i] = out[i]; out[0] = ch; for (i=1; i<len; i++) out[i] = workbuffer[i-1]; sprintf(out, "%c%s", ch, workbuffer); } }There are many aspects of this code that are very poorly done. First, they use a method of decoding a byte, and shifting the output of the buffer to the right in order to make room to store the new byte. This is completely unnecessary, as bytes can be put directly into the correct position (see our decoding routine in the answer to regular question 3 above). Second, in order to shift the buffer they first copy to a temporary buffer and then back, which is not necessary to do this shift! Third, after doing the shift and character insertion they repeat the whole process using sprintf! Not only is this unnecessary, but since the data is binary and not a string at all, using the
%s
format in the sprintf
is not appropriate!
Fourth, to convert their calculated value to the right range (0..255)
for a byte, they use a loop where they repeatedly add 256. This is
completely necessary since they can either mask or simply copy to a
char variable. Finally, they didn't debug the data encoding function
very carefully because the plaintext actually leaks out in the padded
packet (see our analysis file for a
specific example).
Based on these observations with this routine, we'd say that whoever wrote the decode function is a very poor programmer.
This tool is an example of an increasingly popular attack tool: remotely controllable DoS engines. We have seen many advancements in this general class already, and future advancements (over the challenge binary) in similar tools could include:
The TFN and TFN2K DDoS tools already implement many of these advances.