Nicolas SURRIBAS

Développement / Réseau / Sécurité Informatique

Analyse du malware Podnuha.ql : quatrième partie

Rédigé par devloop - -

Suite (et fin) des épisode précédents...
Alors que le malware a récupéré des adresses de fonctions Windows dont il vient de décoder les noms, il entre dans une courte fonction que j'ai baptisé "time_and_beep" qui :
  • Prend une référence de temps avec GetTickCount
  • Attend 51 secondes en utilisant les fonctions d'événements comme on a pu déjà le voir
  • Prend une seconde référence de temps
  • Fait la soustraction de ces deux références

Si le résultat vaut 0 (ce qui est impossible à moins par exemple de hooker GetTickCount), le malware emet un Beep (quasi inaudible car d'une fréquence de 10 hertz sur 10 millisecondes) puis quitte.
Dans le cas contraire il rappelle GetTickCount, effectue des opérations dessus et stocke le résultat dans magic_int :

402151 ! GetTickCount_magic:             ;xref c4023d5
...... !   push        ebp
402152 !   mov         ebp, esp
402154 !   call        dword ptr [GetTickCount]
40215a !   and         eax, 17fffh
40215f !   shr         eax, 1
402161 !   add         eax, 523h
402166 !   mov         [magic_int], eax
40216b !   mov         eax, [magic_int]
402170 !   and         eax, 0ffffh
402175 !   mov         [magic_int], eax
40217a !   pop         ebp
40217b !   ret


Le rôle de cette fonction peut sembler étrange au premier coup d'oeil : on récupère un timestamp et on lui applique un masque le réduisant à une valeur de l'ordre de la minute (entre 1 et deux minutes).
En réalité, cette fonction agit comme un générateur de nombres pseudo-aléatoire (pour l'utilisation que l'auteur du malware en a, ça suffit amplement)

Il entre ensuite dans une fonction qui itére sur les noms de processus avec CreateToolhelp32Snapshot / Process32First / Process32Next. Il les passe en minuscule et les compare à "teatimer.exe". Si un exécutable de ce nom est en cours d'exécution il le termine avec la fonction Windows TerminateProcess.
Si on prend la calculatrice windows (calc.exe) et si on l'exécute après l'avoir renommée en teatimer.exe, on la voit effectivement se fermer lors de l'exécution du malware.

Décodage

On entre alors dans le vrai du malware avec la fonction que j'ai baptisé "decrypt_dll_and_load_it" qui effectue les opérations suivantes :
  • réserve de l'espace pour des chaines de caractères
  • appelle une autre fonction que j'ai nommé "decrypt_dll_file_content"
  • récupère le répertoire système de Windows avec GetSystemDirectoryA
  • décode les chaines de caractères "\???*.dll" et ".dll" selon la routine vue dans un précédent article
  • appelle une fonction "trouve_un_nom_de_fichier_dll"
  • détruit le fichier obtenu avec cette fonction avec DeleteFileA
  • lance une fonction "cree_un_fichier"
  • charge un fichier dll avec LoadLibraryA

Vous avez déjà une vision globale de l'objectif du malware. Etudions plus en détails le fonctionnement des fonctions précédemment citées.

decrypt_dll_file_content prend pour argument les même arguments que ceux que decrypt_dll_and_load_it a reçu, soit :
  • 408030h = une très longue chaine de caractères encodée située dans la section .data
  • 15A00h = ce qui s'avère être la longueur de cette mystérieuse chaine
  • 8220h = una variable servant comme vecteur d'initialisation pour déchiffrer la chaine de caractères.

Je n'entrerais pas dans les détails du code assembleur. Pour faire bref, on trouve une boucle qui traite octet par octet la chaine jusqu'à arriver à 15A00h.
L'opération de décodage se fait par un xor avec une variable qui subit différentes opérations mathématiques à chaque passage de la boucle (valeur initiale = 8220h)

J'ai préféré écrire un programme en C (plus simple à comprendre) qui reproduit le décryptage. Il prend comme argument un fichier avec les données codée et un nom de fichier ou écrire la version décodée :

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int data_42009c, data_420098;

int main(int argc,char *argv[])
{
  unsigned int i, x = 0x8220, len = 0x15a00;
  int fd_in, fd_out;
  char *str;

  if(argc!=3)
  {
    printf("Usage: %s <input_file> <output_file>\n", argv[0]);
    return 1;
  }
  fd_in=open(argv[1],O_RDONLY);
  if(fd_in<0)
  {
    perror("open");
    return 1;
  }

  fd_out = open(argv[2], O_WRONLY|O_TRUNC|O_CREAT,S_IRUSR|S_IWUSR);
  if(fd_out<0)
  {
    perror("open");
    return 1;
  }

  str = (char*)malloc(len+1);
  read(fd_in, str, len);

  data_42009c = 0x1d;
  data_420098 = 0x7d02;
  data_420098 += 0x15;

  for(i=0;i<len;i++)
  {
    x *= data_420098;
    x += data_42009c;
    x = (char)(x & 0x000000ff);
    str[i] = str[i] ^ x;
  }
  write(fd_out, str, len);

  free(str);
  close(fd_out);
  close(fd_in);
  return 0;
}


Il ne reste plus qu'à extraire la chaine de l'exécutable. J'ai d'abord cherché un plugin OllyDgb mais je me suis dit que c'était aussi simple avec dd.
dd if=malware.exe of=plop bs=1 count=88576 skip=32816


Une fois le fichier décrypté, on obtient une dll de 87Ko (md5: 1154378d77d4dd1eb83d40a3a0b6982f) que AVG détecté comme "Trojan horse BackDoor.Generic9.AYAH".
Un petit passage dans HTEditor pour voir les sections et on remarque aussitôt que la dll est compressée avec UPX.
Une fois décompressé, le fichier fait 202Ko (md5: 70e2780a0ecce1ec7755397381bb5606) et est détecté (toujours par AVG) comme "Trojan horse Agent.XMD" (étrange qu'ils ne soient pas connus sous le même nom...)

Recherche d'un nom de fichier

Mais reprenons le cours du programme (car à ce moment de l'exécution le fichier n'a normalement pas encore touché le disque).
Le malware récupère le répertoire système (sous XP, c'est C:\WINDOWS\System32) et le passe comme argument à "trouve_un_nom_de_fichier_dll" avec les chaines de caractères décodées (.dll et \???*.dll) ainsi que différentes zones mémoire (buffer de sortie).

trouve_un_nom_de_fichier_dll effectue une recherche sur les fichiers correspondant au masque "c:\windows\system32\???*.dll".
Il génère aussi deux valeurs d'un octet avec des appels à calcul_sur_magic_int (voir articles précédents) dont la valeur initiale a été préalablement créée par le générateur pseudo-aléatoire.
Il itère ainsi sur tous les noms de fichier dll qu'il trouve dans le répertoire system32 et s'arrête uniquement lorsque le compteur de boucle modulo (reste d'une division) l'une de ces deux valeurs est nul.
Quand la condition est rencontrée, il récupère le début du nom du fichier dll en cours (les caractères avant le premier point) et calcule la longueur de chaine obtenue.

Il recopie cette chaine en mémoire avec lstrcpyn en prenant soin de retirer le dernier caractère puis rajoute finalement l'extension .dll.

Pour conclure, si trouve_un_nom_de_fichier_dll s'arrête sur kernel32.dll, le résultat obtenu sera kernel3.dll. Il vérifie tout de même que le fichier n'existe pas (pour ne pas faire de bétises) mais appellera quand même DeleteFileA en sortant de la fonction.

Fonctionnalité de dropper

La fonction suivante, cree_un_fichier, va générer un fichier temporaire avec GetTempPathA / GetTempFileNameA. Le chemin du fichier sera de la forme :
C:\Documents and Settings\<username>\Local Settings\Temp\datXXXX.tmp
Le fichier est mappé en mémoire avec CreateFileMappingA / MapViewOfFile et le contenu du fichier dll décrypté (actuellement en mémoire) est recopiée à l'aide de la fonction de recopie obfusquée vu dans un précédent article.
Le fichier écrit dans le fichier temporaire est finalement déplacé avec MoveFileA dans system32 sous le nom dll qui a été calculé.
Quand on sort de cette fonction pour retourner à decrypt_dll_and_load_it, le fichier dll est chargé en mémoire avec LoadLibraryA.

Il est intéressant de noter que cette fonction contient une portion de code alternative par laquelle on ne passe pas mais dont l'objectif semble être de créer un fichier exe et d'appeller CreateProcess.
Le dropper a donc été créé pour déposer aussi bien des dll que des exécutables bien que la présente version ne contient qu'un fichier dll (il n'y a pas d'autres chaines de grande taille dans la section .data).

Fin de l'exécution

Pour terminer, le malware commande sa propre suppression en appelant MoveFileEx avec le flag MOVEFILE_DELAY_UNTIL_REBOOT. Ainsi Windows supprime lui-même le fichier au redémarrage du système.

Il appelle ensuite une fonction qui retourne invariablement la valeur 5018 (présence de tests qui donneront toujours le même résultat !?) et dormira (encore) ce délais en millisecondes. En cumulant tous ces délais d'attente on se rapprocherait presque d'une minute d'exécution.

Le malware quitte alors, il a atteint son objectif : déposer un fichier dll et le charger pour l'exécuter.
Je n'ai pas encore commencé l'analyse du dll qui doit receler plein de surprises (je ne vous promets rien).

Conclusion

Bien que le malware ne fasse pas grand chose et ne contenait pas de techniques anti-analyse avancées (il n'était pas packé, utilisait peu de junk-code...) il était néanmoins intéressant d'analyser son fonctionnement (contenu dissimulé, réécriture de code pour appeller un code de détection de débogueur, kill de logiciel de sécurité...)

Ca m'a aussi permis de découvrir quelques astuces assembleur comme la suite neg / sbb / inc.

En gros :
neg reg
sbb reg,reg
inc reg

correspond à
(reg==0)?reg=1;reg=0

et
neg reg
sbb reg,reg
neg reg

correspond à
(reg==0)?reg=0;reg=1


Un petit dropper sympathique à analyser :)

Classé dans : Non classé - Mots clés : malware

Les commentaires sont fermés.