4

I am currently working on an ELF-injector and my approach is standard: find code cave (long enough sequence of 0's), rewrite it with the instructions I want to execute and then jump back to the start of the original program to execute it as it normally would.

To actually execute code in the code cave I tried two different approaches, both of which result in sigsegv.

First one was changing entry point to the start of the code cave. The second one was "stealing" some of the first instructions from the original code and write jump to my code cave there and then after executing my injected code I would first execute stolen instructions and then jump to the instruction after the last stolen one in the original program.

I am also changing the access flags for the section, in which code cave resides.

Here are some screenshots of debugging the program in gdb:

Instructions at entry point - 0x555555556156 is the address of code cave

Instructions in the code cave - executing stolen ones and jumping back

Executing the code

And here are the flags for the section the code cave is in:

[19] 0x555555556058->0x555555556160 at 0x00002058: .eh_frame ALLOC LOAD READONLY CODE HAS_CONTENTS

This is the Valgrind output.

enter image description here

So is there a way to actually allow the execution of code in this section.

I also thought about adding new section to the binary and writing my code there. If someone had experience in doing so, I would appreciate the info.

EDIT: I think I know what my mistake is - I set the executable flag for the section, but the segment it's in isn't executable. But it also seems like the code cave I found doesn't belong to any section, since the start of the code cave is actually the end of one section and the end of the code cave is the start of another section. And there are no other sections in between.

EDIT 2: I changed the code cave to the one, that is in .fini section, which belongs to executable segment. However I am still confused about the empty space between sections (and segments).

Here are the screenshots of the readelf output

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 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
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003c8  000003c8
       00000000000000a8  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000470  00000470
       0000000000000082  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           00000000000004f2  000004f2
       000000000000000e  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000500  00000500
       0000000000000020  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             0000000000000520  00000520
       00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             00000000000005e0  000005e0
       0000000000000018  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000020  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001040  00001040
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001050  00001050
       0000000000000010  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         0000000000001060  00001060
       0000000000000185  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         00000000000011e8  000011e8
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       0000000000000012  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         0000000000002014  00002014
       0000000000000044  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002058  00002058
       0000000000000108  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003db8  00002db8
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003dc0  00002dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003dc8  00002dc8
       00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fb8  00002fb8
       0000000000000048  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000010  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004010  00003010
       0000000000000008  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003010
       000000000000002a  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003040
       0000000000000618  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00003658
       0000000000000202  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  0000385a
       000000000000011a  0000000000000000           0     0     1


Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R E    0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R E    0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000000005f8 0x00000000000005f8  R E    0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x00000000000001f5 0x00000000000001f5  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x0000000000000160 0x0000000000000160  R E    0x1000
  LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000258 0x0000000000000260  RWE    0x1000
  DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc8
                 0x00000000000001f0 0x00000000000001f0  RWE    0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R E    0x8
  NOTE           0x0000000000000358 0x0000000000000358 0x0000000000000358
                 0x0000000000000044 0x0000000000000044  R E    0x4
  GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x0000000000000338
                 0x0000000000000020 0x0000000000000020  R E    0x8
  GNU_EH_FRAME   0x0000000000002014 0x0000000000002014 0x0000000000002014
                 0x0000000000000044 0x0000000000000044  R E    0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    0x10
  GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db8
                 0x0000000000000248 0x0000000000000248  R E    0x1

 Section to Segment mapping:
  Segment Sections...
   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 


As can be seen, section .fini starts at 11e8 and has the size of d. The next section - .rodata starts at 2000. Does that mean that the space between 11e8 + d and 2000 does not belong to any section? That segment also ends at 11F5, which is the end of last section belonging to it - .fini.

EDIT 3: managed to solve the problem - had to choose the section, that belongs to the executable segment. Still a bit confused about the sizes of sections.

Actually managed to inject the code into the binary, however, I got the instructions from an assembly, which only has .text section:

.text
.globl _start
_start:
  #save the base pointer
  pushq %rbp
  pushq %rbx
  mov %rsp,%rbp

  #write syscall = 1
  movq $1, %rax
  #print to stdout
  movq $1, %rdi
  #9 character long string
  movq $9, %rdx

  # push "INJECTED\n" to the stack  
  movq $0x0a, %rcx
  pushq %rcx
  movq $0x44455443454a4e49, %rcx
  pushq %rcx

  movq %rsp, %rsi

  syscall

  #remove the string
  pop %rcx
  pop %rcx

  movq $0, %rax
  movq $0, %rdi
  movq $0, %rdx

  
  pop %rbx
  pop %rbp
  ret

It prints "INJECTED" before the original program execution. While this kind of payload works, what are other ideas of implementing better and actually usable injector? Maybe with the possibility of calling libc functions or some other library functions, that are linked by our victim binary? Because it seems like getting actual instruction from the code, we would like to inject, is kind of pain in the ass.

3
  • 1
    Thanks. Could you replace images by code snippets so it’s easier to read and copy/paste?
    – Igor Skochinsky
    Nov 28 '20 at 21:17
  • @IgorSkochinsky done. Also wrote another EDIT regarding my progress. Maybe you will have some ideas on how to implement a better injector? I would appreciate the help ! Nov 28 '20 at 22:03
  • 1
    added answer on bounds. It may be better to make a separate question about using libc functions.
    – Igor Skochinsky
    Nov 29 '20 at 14:43
3

When executing ELF files, the OS loader does not care about sections but only segments (aka program headers). You need to ensure your code belongs to an executable segment.

As can be seen, section .fini starts at 11e8 and has the size of d. The next section - .rodata starts at 2000. Does that mean that the space between 11e8 + d and 2000 does not belong to any section? That segment also ends at 11F5, which is the end of last section belonging to it - .fini.

Yes, the space between 11F5 and 2000 is a kind of "no man's land". In theory, the bytes there would not be present in the memory. However, in practice, the memory protections and mappings work on a page granularity (0x1000) so these bytes are mapped into memory and made executable so your patch works. To make everything "legit" it may be better to extend the segment length so it extends up to 2000.

4
  • Yes, thank you. Correct me if I'm wrong, but looking at readelf output, section .fini starts at 11e8 and has the size of d. The next section starts at 2000. Does that actually mean that the space from 11e8 + d to 2000 does not belong to any section? Nov 28 '20 at 13:24
  • @NazarPasternak sorry I don’t see your screen from here ;) anyway, you should be looking at segments, not sections.
    – Igor Skochinsky
    Nov 28 '20 at 13:25
  • @NazarPasternak just add all relevant info to the question (segments too)
    – Igor Skochinsky
    Nov 28 '20 at 13:27
  • that's right, but to figure out in which segment my code is going to reside, I need to know in which section it currently is :( Nov 28 '20 at 13:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.