Startup
The binary requires root privileges to run. It will shut itself down if it discovers that it is not root. Upon successful startup, the binary changes its value of argv[0] to "[mingetty]", forks and exits. The forked copy shows up in ps listings as "[mingetty]". On many linux systems, there are five of these virtual consoles already running, so this bit of camouflage may go unnoticed. It is worth noting that lsof output continued to list the program as "the-binary". The binary does not seem to make any modifications to the host system. Following a successful fork, the new process opens a raw socket (see description of the network encoding process below) and enters a command processing loop.
Twelve commands can be sent to this tool. Six of the commands perform administrative tasks, and six commands initiate various denial of service attacks. Each of the functions is detailed below. It should be noted that the agent performs no authentication on incoming commands, making it possible to take over a running agent, or terminate jobs on a running agent independently of a handler. It is also possible to use this weakness to make an agent expose itself, by querying agent status on a machine and seeing if a response is generated.
Administrative Functions
- Command 1 - Query agent status: The agent will report whether it is currently executing a service, and if so, what service it is providing. In the general case, this provides a pong to a handler's ping. Services that can be reported include each of the DoS functions, and the root shell service. It is possible that this report is inaccurate due to improper handling of multiple root shells. It seems likely that the author intended for only one service to be active at any one time, but it is in fact possible to have a root shell active in conjunction with any of the DoS services. Note that no output from this command is possible until the handler has configured itself using command 2 below.
- Command 2 - Handler IP configuration: Used to inform the agent who its handler is. The IP configured in this command will be used by the agent as the destination for all administrative results.
- Command 3 - Execute a command and send encoded output to the handler. The only parameter to this command is the command to be executed on the agent host.
- Command 6 - Open a root shell on the agent host. The shell is opened on port 23281 and must receive the string "SeNiF" as a sequence of bytes in order to remain open. Thus "SeNiF" is the password for the root shell. A bad password will elicit the response "\0xFF\0xFB\0x01\0x00" from the agent before the agent terminates the connection. The agent supports a maximum of 3 concurrent shells. A root shell listener constitutes a running "service" for the agent. It appears that the author intended that no more than one "service" could run at any given time. Each of the available DoS functions constitute services as well, thus it is not possible to launch a DoS while a root shell listener is open. There is a flaw in the code that allows a handler to bypass this restriction.
- Command 7 - Execute a command on the agent host with no output to the handler.
- Command 8 - Kill the currently running service. This is the only way to terminate a DoS session. It is also the way to terminate a root shell listener. If a root shell listener is killed while a root shell is open, it is possible to execute one of the DoS services while maintaining an open root shell.
Denial of Service Functions
- Commands 4 & 9 - DNS response flood:
- Command 5 - ICMP/UDP Floods:
- Commands 10 & 11 - Tcp SYN FLood:
- Command 12 - DNS Query/Response Flood:
void encode(int len, unsigned char *source, unsigned char *dest) { int i; unsigned char prev = 0; for (i = 0; i < len; i++) { prev = dest[i] = source[i] + prev + 23; } }The corresponding decoder function looks like this
void decode(int len, unsigned char *source, unsigned char *dest) { int i; unsigned char offset = 23, prev = 0; for (i = 0; i < len; i++, offset += 23) { prev += dest[i] = source[i] - prev - offset; } }Encoding and decoding could easily be done in place, though the attacker chose not to do so. It should be noted that the attacker's version of the decoding function is very convoluted, leading one to question their programming skills somewhat.
Once decoded, a command buffer received by the agent contains a command byte at location command[1]. Valid commands are in the range 1-12 and may be followed by command specific parameters starting at command[2]. The 12 commands and their associated parameters are detailed in item 2 above. See decoder.c for a pcap based program that decodes traffic between handlers and agents.
ip[0] != 1 && ip[9] != 6 && ip[9] != 17To catch traffic that is neither icmp, tcp, or udp. This filter could be extended to allow any other protocols that are expected on a network.
The author performs unnecessary checksumming on the ip headers of his raw sockets, the kernel does this automatically and he could have saved a lot of time. The programmer perhaps has a poor understanding of ICMP and UDP as evidenced by attack 5 where packets are marked as fragments and in the case of UDP improperly check summed. This function is very sloppily coded. What appeared to be a ping flood at first glance, fails to generate any response from the target because of the "fragmented" icmp echo request.
It also seems that he does things without thinking about why, my personal favorite, found in his data transmit routine is
ipHeader->ip_id = htons(random());I am not sure why he felt compelled to convert a random number to network byte order. Force of habit perhaps. If the author had used a socket of the form socket(AF_INET, SOCK_RAW, 11), instead of socket(AF_INET, SOCK_RAW, IPPROTO_RAW) his data transmit routine could have been much shorter beacuse the ip header would have been taken care of for him.
Another favorite. In many cases the parameters this program revceives from it's handler are ip addresses that are already in network byte order. In several places, he converts this for his use in approximately the following way:
unsigned char ip[4]; //this already contains a network order ip char buf[32]; unsigned long targetIp; sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); targetIp = inet_addr(buf);Much wasted effort there.
In the program's "exec with results" command, there is a buffer overlap problem that causes a random prefix of the plain text output of the just executed command to be exposed to network sniffers. This is somewhat comical given the effort the author has gone through to encode those same results before sending them in an earlier part of the same result packet. See this discussion and example for more information.
Whether intentional or not, 4 of the 9 canned DNS queries the author uses are malformed.
The program uses a large number of unnecessary buffer copy/move operations. The coding and decoding algorithms could easily have been performed in place and are quite convoluted. For every DoS command, the entire input buffer is shifted before passing a pointer to the shifted location to the actual DoS function. He could easily have passed the pointer to the non-shifted data and saved himself some time.
Because the author has failed to authenticate ant input commands, this tool is very simple to hijack through use of command 2. Also if one of these programs is found running in the wild, use of command 1 can assist incident handlers in backtracking the attack.