At the 5th of September Qualys released a Security Warning regarding a Linux based virus. This virus was called the "Remote Shell Trojan" (RST) and it attacks Linux ELF binaries. It has replicating abilities: when run it will infect all binaries in /bin and the current working directory. Besides that it also spawns a process listening on UDP port 5503. When a properly crafted packet is received by this process it will connect back with a system shell.
Credit:
The information has been provided by kai takashi.
Very often viruses are not seen as a real security threat for UNIX. A virus can not infect binaries where the UserID it is running under has no write access to. Even under this situation, viruses can be a threat for UNIX based operating systems: Every time an infected binary is run, it will infect all binaries in the current working directory. It is not unthinkable that a user with increased privileges will later run a binary infected by the RST. In this way, the virus can transparently spread itself over the system. This is especially the case in production environments of in an environment where many users share files. This process will get into a rapid once the /bin binaries are infected. Every execution of normal system commands like 'ls' will infect all binaries in the current working directory. In spite of the theoretical immunity UNIX has, the situation described here is not unlikely to happen in many human situations. The backdoor process can give unprivileged people access to your system under the UserID the backdoor process is running. Attackers can attempt to get higher privileges on the system from there.
Origin:
RST was developed as a research project and intended only for internal use. The goal was to analyze how a non-privileged virus could affect a system running Linux in a normal work-environment. Things however did not go as they were intended to go. An infected binary accidentally leaked out from the research lab and came into the hands of so-called "scriptkiddies". They infected their own systems and other systems where they had access to. From this point, the virus seemed to spread in the wild.
The main concern now is that the spread of this virus is stopped and that all the infected hosts are cleaned as soon as possible. As of now the format of the specially crafted packet, send to the listening backdoor process is unknown to the public. However, this might eventually be reverse engineered in the future and RST can then be actively abused by other people.
Solution:
A set of utilities that can recursively detect and remove the virus from the system has been created. It also has the option to make binaries IMMUNE for future infection by the RST. It is especially recommend that multi-user systems make their system immune for the RST as the risks for these systems are much higher. Immunisation works by increasing the size of the text segment by 4096 bytes so that the "hole" between the text and data segments is gone. After this there is no space for the RST to add it self to the binary anymore.
The interface to these programs is simple and easy to use. The user can decide whether he wants to automatically detect and remove the RST on the system recursively or if he wants to apply the remover on a per binary base. In this mode, the administrator can also get an individual status report on whether this binary is infected, immune, or innocent.
Sample usage would be:
% perl Recurse.pl remove
For more information, regarding this read the included documentation.
Conclusion:
Again, it is strongly recommend that anybody running Linux run the detector to see if their system is infected. Even if they do not expect anything, they can always optionally immunize their system. This is the only way we can fight the further spread of this virus.
Code:
Recurse.pl
#!/usr/bin/perl
use strict;
sub RecursiveDeinfect($);
my $path_to_cleaner = "./kill";
my $options_to_cleaner = "";
my $verbose;
if($ARGV[0] eq "-v") { $verbose = shift; }
$_ = $ARGV[0];
if(/detect/) {
$options_to_cleaner = "1";
print "Recursively detecting trojan starting in: ", $ARGV[1] || "/", "\n";
} elsif(/remove/) {
$options_to_cleaner = "2";
print "Recursively removing trojan starting in: ", $ARGV[1] || "/", "\n";
} elsif(/immune/) {
$options_to_cleaner = "4";
print "Recursively making all binaries starting in: ", $ARGV[1] || "/", "immune\n";
} else {
print "usage: $0 [-v] mode [startdir]\n".
"where mode is one of the following:\n".
"\tdetect\t: Recursively detect trojan\n".
"\tremove\t: Recursively remove trojan if trojan is present.\n".
"\timmune\t: Recursively remove trojan if trojan is present.\n".
"\t \t Make all innocent binaries immune for future infection\n".
"The default startdir is /\n";
exit(0);
}
RecursiveDeinfect($ARGV[1] || "/");
sub RecursiveDeinfect($) {
my $startdir = shift;
my $filename;
my $ret;
return unless(opendir(my $DH, $startdir));
print "Checking $startdir for infected binaries..\n" if($verbose);
kill.c
/* RST trojan manipulator program
*
* this is an RST-remover code, it has the following features:
* 1. Detect RST trojans on the specified executable
* 2. Disable RST on the specified binary and during that process make
* the binary immune to further infection attempts
* 3. Make an innocent, not-infected binary immune to infection attempts
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <elf.h>
#include <errno.h>
#include <sys/stat.h>
/* Disable_trojan()
* disables a trojan in an infected binary, making the binary immune to
* any further infection attempts */
int Disable_trojan(FILE *fp)
{
unsigned int i, padding, poffset, psize, oldentry;
Elf32_Phdr textseg, dataseg;
Elf32_Ehdr ehdr;
// move to the beginning of the file
if (fseek(fp, 0, SEEK_SET) != 0) goto err;
// read ELF binary header
if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) goto err;
if (ehdr.e_type != ET_EXEC) goto err;
// find text segment and data segment headers
if (fseek(fp, ehdr.e_phoff, SEEK_SET) != 0) goto err;
for (i=0;i<ehdr.e_phnum;i++)
{
if (fread(&textseg, sizeof(textseg), 1, fp) != 1) goto err;
if (textseg.p_offset == 0) break;
}
if (fread(&dataseg, sizeof(dataseg), 1, fp) != 1) goto err;
if (textseg.p_offset != 0)
{
/* wtf?! no text segment ??? */
goto err;
}
// do calculations
padding = dataseg.p_vaddr - (textseg.p_vaddr + textseg.p_filesz);
if (padding >= 4096)
return 0; /* Sheww, binary is not infected */
psize = (textseg.p_vaddr + textseg.p_filesz) - ehdr.e_entry;
poffset = (textseg.p_offset + textseg.p_filesz) - psize;
if (psize != 4096)
return 0; /* Binary already cleaned */
// read original entry point, that according to my reverse engineering
// is stored on the parasite at offset 1
if (fseek(fp, poffset+1, SEEK_SET)!=0) goto err;
if (fread(&oldentry, 4, 1, fp) != 1) goto err;
// restore the binary's entry point to point to the real program again,
// avoiding the execution of the parasite code.
// this pernamently disables the parasite code and makes the binary immune
// to further infection attempts.
ehdr.e_entry = oldentry;
if (fseek(fp, 0, SEEK_SET) != 0) goto err;
if (fwrite(&ehdr, sizeof(ehdr), 1, fp) != 1) goto err;
return 1; /* all done */
err: ;
return -1;
}
/* DisplayStatus()
* display the infection status of the specified binary */
int DisplayStatus(FILE *fp)
{
unsigned int i, padding, poffset, psize, oldentry;
Elf32_Phdr textseg, dataseg;
Elf32_Ehdr ehdr;
// move to the beginning of the file
if (fseek(fp, 0, SEEK_SET) != 0) goto err1;
// read ELF binary header
if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) goto err1;
if (ehdr.e_type != ET_EXEC) goto err1;
// find text segment and data segment headers
if (fseek(fp, ehdr.e_phoff, SEEK_SET) != 0) goto err1;
for (i=0;i<ehdr.e_phnum;i++)
{
if (fread(&textseg, sizeof(textseg), 1, fp) != 1) goto err1;
if (textseg.p_offset == 0) break;
}
if (fread(&dataseg, sizeof(dataseg), 1, fp) != 1) goto err1;
if (textseg.p_offset != 0)
{
/* wtf?! no text segment ??? */
goto err1;
}
printf("Estimated parasite offset : %d\n",poffset);
printf("Binary infection status : ");
if (padding >= 4096)
printf("Not infected. not immune.\n");
else if (psize != 4096)
printf("Trojan disabled, immune.\n");
else
printf("Trojan is very much alive!\n");
fflush(stdout);
return 0;
err1: ;
return -1;
}
int movedata(FILE *fp, int src_offset, int dst_offset, const int size)
{
char data[size];
int sloc;
// save current location
sloc = ftell(fp);
// move to source offset and read data
if (fseek(fp, src_offset, SEEK_SET) != 0) return -1;
fread(&data,size,1,fp);
// write data to destination offset
if (fseek(fp, dst_offset, SEEK_SET) != 0) return -1;
fwrite(&data,size,1,fp);
// return to original position
fseek(fp,sloc,SEEK_SET);
return 0;
}
/* make_immune()
* turns an innocent binary into an uninfectable binary */
int make_immune(FILE *fp)
{
unsigned int i, padding, poffset, psize, oldentry;
Elf32_Phdr textseg, dataseg;
struct stat stat;
Elf32_Ehdr ehdr;
// get file status
if (fstat(fileno(fp), &stat)<0) return -1;
// move to the beginning of the file
if (fseek(fp, 0, SEEK_SET) != 0) return -1;
// read ELF binary header
if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) return -1;
if (ehdr.e_type != ET_EXEC) return -1;
// find text segment and data segment headers
if (fseek(fp, ehdr.e_phoff, SEEK_SET) != 0) return -1;
for (i=0;i<ehdr.e_phnum;i++)
{
if (fread(&textseg, sizeof(textseg), 1, fp) != 1) return -1;
if (textseg.p_offset == 0) break;
}
if (fread(&dataseg, sizeof(dataseg), 1, fp) != 1) return -1;
if (textseg.p_offset != 0)
{
/* wtf?! no text segment ??? */
return -1;
}
// update section header table
if (fseek(fp, ehdr.e_shoff, SEEK_SET) != 0) return -1;
for (i=0;i<ehdr.e_shnum;i++)
{
Elf32_Shdr section;
if (fread(§ion, sizeof(section), 1, fp) != 1) return -1;
if (section.sh_offset > poffset)
{
section.sh_offset += 4096;
if (fseek(fp, -1 * sizeof(section), SEEK_CUR) != 0) return -1;
if (fwrite(§ion,sizeof(section),1,fp) != 1) return -1;
}
}
// physically move data from (poffset->eof), 4096 bytes forward
if (movedata(fp, poffset, poffset+4096, stat.st_size - poffset) != 0)
return -1; /* Fuck! */
// all done!
return 1;
}
int main(int argc, char *argv[])
{
FILE *fp;
int op;
if (argc < 3)
{
printf("usage: %s <operation> <path>\n",argv[0]);
printf("Available operations:\n");
printf("1 display trojan status on the specified binary\n");
printf("2 remove the trojan from a specified binary\n");
printf("3 make an innocent binary immune\n");
printf("4 remove trojan/if innocent binary then make immune\n");
printf("5 check if trojan is currently running on the system\n");
printf("\n");
return -1;
}
op = atoi(argv[1]);
/* Run status check if operation 5 was selected */
if (op == 5)
{
struct flock lock;
int fd;
// try to open the trojan's lockfile
if ((fd = open("/tmp/982235016-gtkrc-429249277", O_RDWR)) < 0)
{
printf("Trojan lockfile does not exist.\n");
goto testok;
}
// try to lock the trojan's lockfile
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
if (fcntl(fd, F_SETLK, &lock) < 0)
{
perror("fcntl");
printf("ALERT! A Trojan remote access process is currently running!!\n");
return 0;
}
printf("Trojan process is not currently running however trojan traces were discovered.\n");
return 0;
testok: ;
printf("Trojan is not currently running on this system.\n");
return 0;
}
/* Otherwise, open target binary file */
if (!(fp = fopen(argv[2], "r+b")))
{
fprintf(stderr, "Unable to open %s for reading/writing (%s)\n",argv[2],strerror(errno));
return -1;
}
/* Implement the rest of the operations */
switch(op)
{
case 2: /* Remove trojan */
{
switch(Disable_trojan(fp))
{
case -1:
fprintf(stderr, "An error has occured (%s)\n",strerror(errno));
return -1;
break;
case 0:
printf("%s: trojan not present\n",argv[2]);
break;
case 1:
printf("%s: trojan disabled\n",argv[2]);
break;
}
}
break;
case 1: /* Display infection status */
DisplayStatus(fp);
break;
case 3: /* Make an innocent binary immune */
switch (make_immune(fp))
{
case -1:
fprintf(stderr, "An error has occured (%s)\n",strerror(errno));
return -1;
break;
case 0:
printf("%s: already immune.\n",argv[2]);
return 0;
break;
case 1:
printf("%s: binary is now immune.\n",argv[2]);
break;
}
break;
case 4: /* Remove trojan and make innocent binaries immune */
{
switch(Disable_trojan(fp))
{
case -1:
fprintf(stderr, "An error has occured (%s)\n",strerror(errno));
return -1;
break;
case 0:
if (make_immune(fp) == 1)
printf("%s: trojan not present. innocent binary turned immune.\n",argv[2]);
else
printf("%s: immune binary.\n",argv[2]);
break;
case 1:
printf("%s: trojan disabled\n",argv[2]);
break;
}
}
break;
default:
printf("Operation %d not implemented\n",op);
return -1;
break;
}
}
##########################################################
## Making the antivirus binary immune to RST infections ##
##########################################################
cp temp kill ; ./temp 4 kill ; rm -f temp
##########################################################
## All done, run 'perl Recurse.pl' for RST detection, ##
## and removal options. ##
## -- anonymous research team ##
##########################################################