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

Wed 03 October 2007 by JB

Le package JavaVirtualMachine.jar

Cette archive jar contient le coeur du cheval de Troie. Via des classes Java, il charge une bibliothèque dynamique en mémoire, et l'utilise pour réaliser certaines de ses actions.

Avant l'ajout de Twain.class comme nous venons de le voir, l'archive contient :

>> unzip JavaVirtualMachine.jar
Archive:  JavaVirtualMachine.jar
creating: META-INF/
inflating: META-INF/MANIFEST.MF        inflating: JPEGEncoder.class
inflating: AttackTCP.class             inflating: LoggerThread.class
inflating: AttackUDP.class             inflating: MailSender.class
inflating: AutoController.class        inflating: MesajGonderici.class
inflating: AutoOrderServer.class       inflating: NativeKaynaklar.class
inflating: BridgeWay.class             inflating: ProcThread.class
inflating: Decyrptor.class             inflating: ProtectorClass.class
inflating: Encriptor.class             inflating: RegistryAPI.class
inflating: Encyrptor.class             inflating: URLDownloader.class
inflating: IPChangeListener.class      inflating: Win32.class
inflating: JarUpdate.class             inflating: WinEnum.class
inflating: JKeyMailThread.class

Cette archive est lancé en tant que processus à part entière. Nous l'analysons donc à partir de son point de départ, la fonction main() de la classe RegistryAPI.

Fonctionnement du reverse shell KBD

Au lancement, RegistryAPI charge le fichier de paramètres Twain.class créé lors de l'infection, décode les paramètres et enregistre le texte en clair dans un nouveau fichier system_conf.dat. En cas d'erreur, des paramètres par défaut sont chargés. L'URL standard est kadir.cgimarket.com `` et le port ``3232.

Alarm et Port contiennent les paramètres du client de l'attaquant sur lequel la machine infectée se connecte. Si la valeur du paramètre AutoControl est true, le client se connecte à l'adresse qui est dans le paramètre URL, sur le port 80 en TCP et télécharge un fichier. Le contenu est de ce fichier est l'IP de l'attaquant, en charge de la collecte des mots de passe.

Fournir cette adresse en paramètre au trojan permet de ne pas être attaché à une adresse fixe. Comme nous avons vu que plusieurs adresses étaient utilisées, et qu'elles appartiennent toutes à un ISP turque, on peut penser que cela permet au pirate de continuer à conserver le contrôle de ses machines compromises, même lorsque son ISP lui attribue une nouvelle adresse.

Bref, le trojan se connecte vers son serveur maître, selon le principe du reverse shell. En effet, si le système est sur un réseau local protégé derrière un firewall, cela n'empêchera pas la connexion vers l'extérieur (en général, seuls les flux entrants sont bloqués), surtout quand il s'agit du port TCP/80 destiné au trafic HTTP : rares sont les endroits où se trafic est interdit.

À la lecture de la bannière du shell, 361 1.6 Version of KBD, on ne peut s'empêcher de se demander à quoi correspond le 'D' dans KBD, supposant que KB sont les initiales du concepteur, Kadir Basol.

Les commandes disponibles dans le reverse shell de la classe RegistryAPI sont :

  • Commandes de manipulation de fichiers : suppression de fichiers (DELE) et de dossiers (RMDIR), création de dossiers (MKDIR), renommage de fichiers (RENAME), changement de dossier (UPWARD et CHANDIR), compression (CMPS) et décompression (UNCMPS), listage (LIST) et recherche (FIND) de fichiers.
  • Envoi et réception de fichiers distants sur l'ordinateur client (UPLOAD et DOWNLOAD) ou depuis une URL (URLDOWN).
  • Récupération d'informations sur le système :: nom de l'OS, de l'utilisateur, langue, dossier utilisateur, variable $PATH (GETSYS), liste des lecteurs (GETROOTS), version du serveur (KBD_VERSION), chemin du dossier Windows (WINDIR).
  • Commandes variées :: envoi de captures d'écran (SCREEN), ouverture d'une boÎte de messages (MSGBOX), exécution de fichiers (EXEC) et suppression du dossier %WINDIR% (FINISH).

Une backdoor dans le trojan !

Lors du lancement de RegistryAPI, une autre classe est instanciée, AutoOrderServer. Cette classe ouvre une nouvelle backdoor, exploitable non pas par l'attaquant mais par le concepteur du cheval de Troie. L'ordinateur infecté ouvre une connexion TCP vers kadir.cgimarket.com sur le port 6531, très probablement un serveur contrôlé par le concepteur du cheval de Troie.

La lecture du code de la classe AutoOrderServer nous permet d'obtenir la liste des commandes :

  • PO crée un relais TCP entre le client et une autre machine ;
  • AT lance une attaque TCP sur un port et une IP spécifiée, certainement pour contribuer à un déni de service distribué. Parmi les options de cette commande, on peut spécifier le message à envoyer, le nombre de threads à lancer, le temps total d'envoi des requêtes, et le temps de pause entre l'envoi de deux paquets. Le même type d'attaque est également codé dans le cheval de Troie pour des attaques UDP, mais il n'est pas utilisé ;
  • UP télécharge un fichier depuis une URL spécifiée sur l'ordinateur infecté ;
  • EX exécute une commande ;
  • SS coupe toutes les connexions au niveau du relais TCP.

La commande destinée à créer un proxy TCP et celle pour le DoS sont particulièrement intéressantes. On peut penser que le but du développeur n'était pas seulement de créer un cheval de Troie, mais de le diffuser afin d'obtenir un certain nombre de machines à sa disposition, infectées par d'autres personnes, afin d'en faire ce qu'il veut.

La DLL JDukeNative

Certaines procédures du cheval de Troie n'ont pas été codées en Java, mais en C++. Elles sont regroupées dans une DLL. Ces procédures ne peuvent être appelées que sous Windows. Le cheval de Troie installe la DLL JDukeNative.dll dans le dossier système de Windows. L'interface Java pour appeler la bibliothèque se trouve dans la classe NativeKaynaklar.

La DLL exporte quinze fonctions, mais seules deux peuvent être appelées par le cheval de Troie. En fait, il est dérivé d'un autre trojan, KBD Devastator. Les fonctions qui n'intéressent pas l'utilisateur du trojan ont été laissées dans la DLL mais il n'y a aucune appel à ces fonctions.

Les appels à la DLL sont faits avec JNI (Java Native Interface). Le nom des fonctions réellement exportées par la DLL est préfixé par Java_NativeKaynaklar_, et deux arguments supplémentaires sont fournis, l'un pour l'environnemt d'exécution Java (ce qui permet d'appeler certaines fonctions Java depuis la DLL), l'autre étant une référence sur la classe appelante. À titre d'exemple, le prototype C++ de la première fonction est :

Java_NativeKaynaklar_SetStartKey(JNIEnv env, jclass c, jstring s, jstring s1)

La bibliothèque est compilée avec Visual C++ avec des symboles de debug. Aucune partie du code n'est chiffrée ou obfusquée, et les fonctions exportées ont des noms très explicites.

On peut dégager plusieurs types de fonctions d'après de leur usage pour l'attaquant. Sur les 15 fonctions exportées, les deux réellement appelées sont SetStartKey et getPasswordList. Étudions leur fonctionnement.

La fonction SetStartKey

La fonction SetStartKey est très courte et facilement compréhensible.

; int __stdcall Java_NativeKaynaklar_SetStartKey(
JNIEnv *env,jclass unused,jstring s1,jstring s2)
env             = dword ptr  4
unused          = dword ptr  8
s1              = dword ptr  0Ch
s2              = dword ptr  10h
mov     ecx, [esp+s1]
push    esi
mov     esi, [esp+4+env]
mov     eax, [esi]
push    edi
push    0
push    ecx
push    esi
call    dword ptr [eax+GetStringUTFChars]
mov     edx, [esi]           ; const char *cStr1 =
;   env->GetStringUTFChars(s1,0);
mov     edi, eax
mov     eax, [esp+8+s2]
push    0
push    eax
push    esi
call    dword ptr [edx+GetStringUTFChars]
mov     ecx, eax             ; const char *cStr2 =
;    env->GetStringUTFChars(s2,0);
lea     esi, [ecx+1]
lea     ecx, [ecx+0]
inline_strlen:
mov     dl, [ecx]            ; DWORD cbStr1 = strlen(cStr2);
inc     ecx
test    dl, dl
jnz     short inline_strlen
sub     ecx, esi
inc     ecx
push    ecx             ; cbData
push    eax             ; lpData
push    REG_SZ          ; dwType
push    0               ; Reserved
push    edi             ; lpValueName
push    HKEY_LOCAL_MACHINE ; hKey
call    ds:RegSetValueExA ; RegSetValueEx(HKEY_LOCAL_MACHINE,
;   cStr1,NULL,REG_SZ,cStr2,cbStr2);
pop     edi
pop     esi
retn    10h
void __stdcall Java_NativeKaynaklar_SetStartKey(
struct JNIEnv_ *, class _jclass *, class _jstring *, class _jstring *) endp

Cette fonction crée une entrée dans la base de registres (HKLM) de type chaîne de caractères (REG_SZ). C'est cette fonction qui est appelée par Function.class pour infecter la machine. L'emplacement est spécifié dans s1 et la valeur dans s2. En Java, les chaînes de caractères sont au format UTF, tandis que la DLL travaille avec des caractères ANSI : la conversion se fait via JNI avec la méthode GetStringUTFChars.

Rappelons que cette fonction est utilisée lors de l'infection de la machine pour assurer le lancement de JavaVirtualMachine au démarrage du système en créant la clé de registre appropriée

Intéressons nous maintenant à la seconde fonction utilisée par le trojan.

La fonction getPasswordList

Le deuxième export utilisé est la fonction getPasswordList. Le code désassemblé est trop long pour être commenté ici, mais un code C équivalent est donné en annexe. Le prototype de la procédure est le suivant :

JNIEXPORT void JNICALL Java_NativeKaynaklar_getPasswordList(JNIEnv *env, jclass, jstring programName)

La procédure lance d'abord programName puis attend 2 secondes que le programme se charge. Dans ce trojan, la commande ainsi exécutée est User_Info.exe, en charge de la récupération de mots de passe.

MessenPass affiche la liste des mots de passe récupérés dans un contrôle ListBox. Le contenu de la liste pourrait être lu directement dans la mémoire du processus par la procédure getPasswordList, sauf que getPasswordList ne sait pas à quelle adresse chercher. Pour y remédier, getPasswordList alloue une zone mémoire dans l'espace d'adressage du processus MessenPass, puis envoie le message LVM_GETITEMTEXT à la liste. Cela provoque l'écriture du contenu de la liste à l'adresse mémoire donnée en argument, celle allouée par getPasswordList. La procédure lit ensuite le texte dans la mémoire précédemment alloué, puisqu'il en connaît cette fois l'adresse.

À la fin de la fonction, on récupère une chaîne du genre :

MSN Messenger
sexyboy_du_13@hotmail.com
kikoooo
!@FUNCTION_ENDED@!

Cette fonction est appelée lors de la phase d'infection de la machine. La chaîne de caractères est envoyée à l'adresse contenue dans le paramètre Alarm de l'applet sur le port 80. La version ultérieure de MessenPass permet d'exécuter le programme en ligne de commande. Une telle utilisation aurait beaucoup simplifié le code de cette routine.

Autres fonctions (non utilisées) de la DLL

Les fonctions exportées qui peuvent être appelées via NativeKaynakalr sont données ci-après. L'analyse de leur code serait un peu fastidieuse, et leur nom est assez explicite pour supposer leur rôle:

public static native void setMasterSoundVolume(int i);
public static native void setWaveSoundVolume(int i);
public static native void Disconnect();
public static native void checkProtectExec(String s, String s1);
public static native void beepFreqency(int i);
public static native int  getStatus();
public static native void earthQuake();
public static native void resetKeyMapData();
public static native void getForegroundWindow(StringBuffer stringbuffer);
public static native boolean captureScreen(String s);
public static native void Dial(String s);
public static native boolean shutdownSystem();

Ces procédures, dans leur ensemble, fournissent un grand nombre d'informations sur la machine infectée, en plus des fonctionnalités de nuisance comme setMasterSoundVolume ou earthQuake (qui fait trembler l'affichage à l'écran en déplaçant les fenêtres très rapidement). L'attaquant peut retrouver facilement l'ensemble des mots de passe de l'utilisateur, faire des copies d'écran, enregistrer ce qui a été tapé au clavier, etc. Mais elles ne sont pas utilisées par le trojan. On peut supposer que les machines infectées servent essentiellement de serveur de relais où à lancer des attaques type DDoS, les fonctionnalités habituelles des trojans étant laissées de côté.

Conclusion

Ce cheval de Troie, même s'il montre que l'utilisateur ne maîtrise pas réellement l'informatique, s'avère néanmoins simple et efficace. Il est susceptible d'infecter des postes sur un réseau local, passant outre les filtrages des proxies et autre anti-virus. Ensuite, la machine passe sous le contrôle de l'attaquant.

Plus intéressant, la backdoor dans le cheval de Troie permet à un attaquant plus avisé d'utiliser le système compromis sans rien faire auparavant.

Cet exemple est révélateur de deux phénomènes. D'une part, il n'est pas nécessaire d'employer des approches compliquées pour compromettre des systèmes. En défense tout comme en attaque, la simplicité est une très bonne alliée. Mais surtout, cet exemple illustre bien le fossé qui existe entre les concepteurs de ces programmes, qu'on peut considérer comme compétents (en plus de malveillants), et les utilisateurs des dits programmes ayant des connaissances très limitées.

Contrairement à une idée reçue, il n'est donc plus besoin d'être un gourou pour mener à bien des offensives informatiques de grande ampleur, certes sans finesse ou subtilité, mais en l'occurrence, pour ces attaquants, seul compte le résultat.