R0 CREW

I need help with injection to ET_DYN ELF64 file

I am trying to inject simple payload to an elf64 et_dyn file. I write the payload to the end of the text segment and increase file offsets of next segments and sections by the size of the payload. When the resulted file is executed, segfault happens. Gdb tells the following: “During startup program terminated with signal SIGSEGV, Segmentation fault”. However when i strip infected file, everything starts to work,

Also i found a way to inject elf files with objcopy. You can dump the .fini section (the last section in the text segment), write payload to its end, and then update section with objcopy. But objcopy just increase .fini section’s and text segment’s sizes in corresponding section and program headers and overwrite content that follows .fini section with its new content.

So, I wanna know how to inject code to the et_dyn elf64 executable without content overwriting and my mistakes.

All project files

et_dyn_infection.zip (12.7 KB)

C code to inject payload to the et_dyn file

typedef struct {
    Elf_Ehdr *ehdr;
    Elf_Phdr *phdr;
    Elf_Shdr *shdr;
    size_t   size; // size of entire elf file
    void     *mem; // mem with elf content
} elf_t;

elf_t* elf_open(const char *filename) {
    elf_t *elf = NULL;
    int fd = -1;

    fd = open(filename, O_RDONLY);
    if(fd < 0)
        goto error;

    struct stat f_info;
    if(fstat(fd, &f_info) < 0)
        goto error;

    elf = malloc(sizeof(elf_t));
    if(!elf)
        goto error;

    elf->mem = malloc(f_info.st_size);
    if(!elf->mem)
        goto error;

    if(read(fd, elf->mem, f_info.st_size) != f_info.st_size)
        goto error;
    close(fd);

    elf->ehdr = (Elf_Ehdr*)elf->mem;
    elf->phdr = (Elf_Phdr*)(elf->mem + elf->ehdr->e_phoff);
    elf->shdr = (Elf_Shdr*)(elf->mem + elf->ehdr->e_shoff);
    elf->size = f_info.st_size;

    return elf;
error:
    perror("elf_open");
    if(fd >= 0) close(fd);
    if(elf) {
        if(elf->mem) free(elf->mem);
        free(elf);
    }
    return NULL;
}

bool elf_write(elf_t *elf, const char *filename) {
    int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0755);
    if(fd < 0 || write(fd, elf->mem, elf->size) != elf->size) {
        perror("elf_write");
        return false;
    }
    return true;
}

void elf_close(elf_t *elf) {
    free(elf->mem);
    free(elf);
}

Elf_Phdr* elf_segment_by_section(elf_t *elf, Elf_Shdr *shdr) {
    for(int i = 0; i < elf->ehdr->e_phnum; i++) {
        Elf_Phdr *phdr = &elf->phdr[i];
        if(shdr->sh_offset >= phdr->p_offset &&
           shdr->sh_offset < phdr->p_offset + phdr->p_filesz)
            return phdr;
    }
    return NULL;
}

Elf_Shdr* elf_section_by_name(elf_t *elf, const char *section) {
    char *strtab = elf->mem + elf->shdr[elf->ehdr->e_shstrndx].sh_offset;
    for(int i = 0; i < elf->ehdr->e_shnum; i++) {
        Elf_Shdr *shdr = &elf->shdr[i];
        char *name = &strtab[shdr->sh_name];
        if(!strcmp(name, section))
            return shdr;
    }
    return NULL;
}

bool elf_resize_section(elf_t *elf, Elf_Shdr *shdr, size_t new_section_size) {
    Elf_Phdr *phdr = elf_segment_by_section(elf, shdr);
    if(!phdr) {
        fputs("Failed to find segment by section\n", stderr);
        return false;
    }

    long int delta_size = new_section_size - shdr->sh_size;

    size_t before_section;
    size_t new_elf_size = elf->size + new_section_size - shdr->sh_size;
    size_t old_section_size = shdr->sh_size;
    if(delta_size < 0)
        before_section = shdr->sh_offset + new_section_size;
    else
        before_section = shdr->sh_offset + shdr->sh_size;

    void *new_mem = malloc(new_elf_size);
    if(!new_mem) {
        perror("elf_resize_section");
        return false;
    }

    elf->ehdr->e_shoff += delta_size;
    phdr->p_filesz += delta_size;
    phdr->p_memsz += delta_size;
    shdr->sh_size = new_section_size;

    // move segments
    for(int i = 0; i < elf->ehdr->e_phnum; i++) {
        if(elf->phdr[i].p_offset > phdr->p_offset)
            elf->phdr[i].p_offset += delta_size;
    }

    // move sections
    for(int i = 0; i < elf->ehdr->e_shnum; i++) {
        if(elf->shdr[i].sh_offset > shdr->sh_offset)
            elf->shdr[i].sh_offset += delta_size;
    }

    void *new_mem_cur = new_mem, *old_mem_cur = elf->mem;
    memcpy(new_mem_cur, old_mem_cur, before_section);
    new_mem_cur += shdr->sh_offset + new_section_size;
    old_mem_cur += shdr->sh_offset + old_section_size;
    memcpy(new_mem_cur, old_mem_cur, elf->size - (old_mem_cur - elf->mem));

    free(elf->mem);

    elf->ehdr = (Elf_Ehdr*)new_mem;
    elf->phdr = (Elf_Phdr*)(new_mem + elf->ehdr->e_phoff);
    elf->shdr = (Elf_Shdr*)(new_mem + elf->ehdr->e_shoff);
    elf->mem = new_mem;
    elf->size = new_elf_size;
    return true;
}
static char shellcode[] = "\x56\x57\x52\x6a\xa\x48\xb8\x66\x75\x63\x6b\x20\x79\x6f\x75\x50\x48\xc7\xc0\x1\x0\x0\x0\x48\x89\xc7\x48\x89\xe6\x48\xc7\xc2\x9\x0\x0\x0\xf\x5\x48\x83\xc4\x10\x5a\x5f\x5e\xe9\x39\xfe\xff\xff";

int main() {
    int res = -1;
    elf_t *elf = elf_open("test1");
    if(!elf)
        goto cleanup;

    Elf_Shdr *text_shdr = elf_section_by_name(elf, ".fini");
    if(!text_shdr) {
        fputs("Failed to find .text section\n", stderr);
        goto cleanup;
    }
    size_t old_section_size = text_shdr->sh_size;

    if(!elf_resize_section(elf, text_shdr, text_shdr->sh_size + sizeof(shellcode) - 1))
        goto cleanup;

    text_shdr = elf_section_by_name(elf, ".fini");
    if(!text_shdr) {
        fputs("Failed to find .text section\n", stderr);
        goto cleanup;
    }

    memcpy(elf->mem + text_shdr->sh_offset + old_section_size,
           shellcode,
           sizeof(shellcode)-1);

    if(!elf_write(elf, "infected"))
        goto cleanup;

    res = 0;
cleanup:
    if(elf) elf_close(elf);
    return res;
}