The very powerful and the very stupid have one thing in common. Instead of altering their views to fit the facts, they alter the facts to fit their views... which can be very uncomfortable if you happen to be one of the facts that needs altering. | |
Dr Who. |
Initially standard Linux (or any Unix for that matter) tools were used to interrogate the binary downloaded late on May 6th 2002. After 2 hours we had concluded the following.
The binary was an ELF binary with file format elf32-i386.
The binary had been statically linked to libc (5.3.12) and the resolv network library
The binary had been stripped of debugging symbols. Not only that, but by compiling an empty stub and statically linking to libc and lresolv we also determined that there must have been --strip-all and possibly -nostartfiles to the linker to try and reduce the information in the file. This serves a dual purpose, it reduces file size to help reduce the risk of detection and it also makes it difficult to find useful sections of code.
Running the executable initially shows no visible output and exits almost immediately. We will discuss this further in later sections. This was done in a semi-controlled environment with no physical network connection to avoid unpleasant repercussions from inadvertently running a nasty binary.
We have several references to shell invocation as shown later.
Has references to some standard (but old) hosts configuration files. There is always the possibility these are from the libc or resolv library.
2 references to "1ÈQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ" which may be an encoded string of some description. Initial thoughts would be this string is possibly used to exploit a vulnerability on another system. Perhaps this is a self-replicating worm?
The binary appears to have been built with GCC (GNU) 2.7.2.1.2
Strings output from wtfu4 reveals some interesting strings we will come back to later.
strings -n 10 ./original ... [mingetty] /tmp/.hj237349 /bin/csh -f -c "%s" 1> %s 2>&1 /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin/:. /bin/csh -f -c "%s" %d.%d.%d.%d %u.%u.%u.%u gethostby*.getanswer: asked for "%s", got "%s" ...
There were other strings, but most were outside the normal ASCII character range. The strings which followed this output we concluded were part of the resolv library we had concluded previously this binary had been statically linked with (due to these strings).
The outcome of this initial investigation was that we were looking at part of a rootkit installed on the honeynet system. The binary had been compiled on a system using gcc 2.7.2.1.2 and had been statically linked to an old version of libc (5.3.12). The combination of the compiler plus the library versions leads to the conclusion this binary had most likely been built on RedHat 4.0 or 4.1 system. We initially concluded that the binary had been compiled on the compromised system from these facts. We have not seen anything to suggest otherwise.
The following night we started what was to be a journey of discovery into the features of wtfu4. To make it easier on ourselves, the goal for the evening was simple (^_^). We were to determine what the binary initially did when it was executed. Did it transmit anything across the web to a remote server somewhere as many DDOS tools have previously done? Did it modify system files and use information on the host to spread itself? Armed to the teeth with an arsenal of Linux tools - ethereal, lsof, ps, netstat, strace and objdump - we set to work deciphering its purpose.
To start with we ran it using strace to determine what system calls we could find wtfu4 making. The output is shown below.
[root@WhiteHat reverse]# strace ./original execve("../reverse/original", ["../reverse/original"], [/* 46 vars */]) = 0 personality(PER_LINUX) = 0 geteuid() = 0 sigaction(SIGCHLD, {SIG_IGN}, {SIG_DFL}, 0x4005d7c8) = 0 fork() = 2214 --- SIGCHLD (Child exited) --- _exit(0)
Well, we have discovered another of the author's little secrets, the fork() system call, but we weren't going to let something like that get in our way. Looking at the strace help we can follow the children, albeit with less accuracy as some calls may be missed during the forking process, but we could follow it, that was the main thing at this stage.
[root@WhiteHat report]# strace -f ../reverse/original execve("../reverse/original", ["../reverse/original"], [/* 46 vars */]) = 0 personality(PER_LINUX) = 0 geteuid() = 0 sigaction(SIGCHLD, {SIG_IGN}, {SIG_DFL}, 0x4005d7c8) = 0 fork() = 2235 [pid 2234] _exit(0) = ? setsid() = 2235 sigaction(SIGCHLD, {SIG_IGN}, {SIG_IGN}, 0x80575a8) = 0 fork() = 2236 [pid 2236] chdir("/") = 0 [pid 2236] close(0) = 0 [pid 2236] close(1) = 0 [pid 2236] close(2) = 0 [pid 2236] time(NULL) = 1022025745 [pid 2236] socket(PF_INET, SOCK_RAW, 0xb /* IPPROTO_??? */) = 0 [pid 2236] sigaction(SIGHUP, {SIG_IGN}, {SIG_DFL}, 0x4005d7c8) = 0 [pid 2236] sigaction(SIGTERM, {SIG_IGN}, {SIG_DFL}, 0x4005d7c8) = 0 [pid 2236] sigaction(SIGCHLD, {SIG_IGN}, {SIG_IGN}, 0x80575a8) = 0 [pid 2236] sigaction(SIGCHLD, {SIG_IGN}, {SIG_IGN}, 0x80575a8) = 0 [pid 2236] recv(0, <unfinished ...> [pid 2235] _exit(0)
So now we have it, it forks twice in quick succession, killing the original processes immediately after, and then sits and waits in a recv loop. Now, this is where we get interesting. A quick look at the man page for recv reveals it to be part of the socket suite of library functions. A quick look in Ethereal had revealed there were no packets transmitted during initial loading so this must be a server process waiting for a connection attempt. Ok, now we just need to find what port it is listening on. The clue is there, in the output from the strace command we have just issued.
[pid 2236] socket(PF_INET, SOCK_RAW, 0xb /* IPPROTO_??? */) = 0
We have a process sat listening on port 11. Excellent, we can now connect to the port and start chatting to the server. A few hours later we were still trying to connect using telnet - we didn't know of any other way at this stage without writing a client ourselves. Had we missed something here or were we being stupid? We didn't know. It is definitely port 11, right?
[root@WhiteHat root]# netstat -a | grep -i listen tcp 0 0 *:6000 *:* LISTEN tcp 0 0 *:ssh *:* LISTEN unix 2 [ ACC ] STREAM LISTENING 19109 /tmp/.X11-unix/X0 unix 2 [ ACC ] STREAM LISTENING 19003 /tmp/.font-unix/fs-1 unix 2 [ ACC ] STREAM LISTENING 19192 /tmp/ksocket-root/kdeinit-:0 unix 2 [ ACC ] STREAM LISTENING 19225 /tmp/ksocket-root/klauncherghOg8b.slave-socket unix 2 [ ACC ] STREAM LISTENING 19303 /tmp/.ICE-unix/1597 unix 2 [ ACC ] STREAM LISTENING 19199 /tmp/.ICE-unix/1509 unix 2 [ ACC ] STREAM LISTENING 18807 /dev/gpmctl unix 2 [ ACC ] STREAM LISTENING 19279 /tmp/mcop-root/WhiteHat-05f9-3cea9f69
Are we sure it's port 11? We're now beginning to have doubts about our skills in this area. Maybe it is something to do with the only network interface being a loopback, and wtfu4 is clever enough to discover this? Ok, bring out nmap, the tried and trusted portscanner that always reveals the problem. We'll scan the loopback interface on ports 1-20 using a full connect scan and we'll know exactly where the wtfu4 binary is listening, won't we?
[root@WhiteHat root]# nmap -sT -p 1-20 -O -P0 127.0.0.1 Starting nmap V. 2.54BETA22 ( www.insecure.org/nmap/ ) Warning: OS detection will be MUCH less reliable because we did not find at least 1 open and 1 closed TCP port All 20 scanned ports on localhost.localdomain (127.0.0.1) are: closed No exact OS matches for host (test conditions non-ideal). TCP/IP fingerprint: SInfo(V=2.54BETA22%P=i686-pc-linux-gnu%D=5/22%Time=3CEAE67C%O=-1%C=1) T5(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) T6(Resp=Y%DF=Y%W=0%ACK=O%Flags=R%Ops=) T7(Resp=Y%DF=Y%W=0%ACK=S++%Flags=AR%Ops=) PU(Resp=Y%DF=N%TOS=C0%IPLEN=164%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E) Nmap run completed -- 1 IP address (1 host up) scanned in 4 seconds
Ok, we are missing something, back to the drawing board. After a quick Google analysis we altered the command line to the netstat command to include '--programs'. This will tells us the PID's of the programs within the port data. Back to the strace output we went and opened up the man page for the socket system call this time. We wanted to know if the "0xb" we had seen in the output from strace was in fact the socket number this process was attempting to bind to. It was, or more accurately it was the protocol number that the socket was using, but we also looked more closely at the other parameters, and there it was SOCK_RAW, how could we have missed that?
[root@WhiteHat root]# netstat -a -p | grep -i raw raw 0 0 *:nvp *:* 7 2271/[mingetty]
Now, that's interesting and something else we hadn't expected. The process was changing its name to [mingetty] (interestingly this was one of the strings we had already identified in wtfu4), it must have been after one of the fork()'s. But there we had our prize, a renamed process listening on a RAW tcp/ip socket - port 11. What now? After such a promising start we were stuck. It was at this point that self-doubt began to get the better of us, but we struggled on. If the wtfu4 binary is listening to raw sockets on port 11, then we have to connect with raw sockets to port 11. Simple, we'll write a small client to connect and send it a string of rubbish and invoke a response. So, that's what we did, and carried out an strace again... It responds by calling something called "oldselect" - means nothing even to google - and returns back to the recv loop.
Considering ourselves well and truly humbled we concluded this section of the analysis, resigned to the fact that someone else was going to win this challenge and we would have to come back stronger for the next one.
You must take care not to make mistakes. But when they happen, learn from them. Use your mistakes as a springboard into new areas of discovery; accidents can hold the key to innovation. | |
P. T. Sudo |