1

Code injection is the process of injecting some malicious code into a legitimate binary.

A lot of code injection projects that I have seen involve finding a large area of null bytes (\x00) in the .text section of the binary in order to inject shellcode. Then the address of the entry point is changed in order to point to the injected shellcode. The injected shell code will then point to the beginning of the original code after finishing.

Sometimes there are not enough contiguous \x00 bytes available in the .text section in order to perform this action. As a result, the project will not be able to perform code injection.

Why don't they just increase the size of the ".text" section segment? Is it because of security reasons?

2

Binary modification is a complex subject and requires some understanding of the system's application binary interface (ABI), process layout in virtual memory, the runtime environment, and the compilation process. Background information will be discussed here in order to provide context for the answer to the question.

1. Code Injection vs. Static Binary Modification (Patching)

From the question:

Code injection into binary is the process of injecting some malicious code into a legitimate binary.

A lot of code injection projects that I have seen will find a large \x00 into .text section in order to inject their shellcode. They then change the address of the entry point in order to point to the injected shellcode. The injected shell code will then point to the beginning of the original code after finishing.

Code injection usually refers to code included in input entered at program runtime, which differs from the process described above, in which a binary is statically modified prior to program execution in order to alter its runtime behavior.

From MITRE's Common Weakness Enumeration (CWE) entry regarding code injection (emphasis mine):

Description Summary

The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.

Extended Description

When software allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the software. Such an alteration could lead to arbitrary code execution.

Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.

One implication here is that code injection is a technique employed at program runtime in order to alter program behavior. A second implication is that code injection includes executable code in input whose origin is external to the binary itself, and code injection does not alter the structure of the binary.

Altering the structure of an executable binary prior to execution can be referred to as static binary instrumentation, but is more commonly referred to as patching.

From PEBIL: Efficient Static Binary Instrumentation for Linux (emphasis mine):

There are two main approaches to binary instrumentation: static and dynamic. Static binary instrumentation inserts additional code and data into an executable and generates a persistent modified executable whereas the dynamic instrumentation inserts additional code and data during execution without making any permanent modifications to the executable.

The distinction between runtime process modification and static modification is important, because their technical implications are quite different.

2. Sections, Segments, Linking and Relocation

From the question:

Sometimes, there is not enough available \x00 bytes into the .text section in order to perform this action. So, the project will not be able to perform that injection.

Why don't they just increase the size of the ".text" section segment?

There is an important distinction between sections and segments in ELF binaries:

  • Sections hold the bulk of object file information for the linking view: instructions, data, symbol table, relocation information, and so on1

Sections and their associated information are used by ld to perform relocation and symbol resolution.

  • segments and their associated information are used by the program loader in the kernel to map portions of the binary file on disk into virtual memory at the correct offsets

Here is a diagram from the System V ABI contrasting sections with segments: ELF Object File Format

Object files participate in program linking (building a program) and program execution (running a program). For convenience and efficiency, the object file format provides parallel views of a file’s contents, reflecting the differing needs of these activities.1

Why does this matter? The address ranges of the a program's sections, including the .text section, as well as the address range of the .text segment, are both determined by the linker which is invoked on the object file at after preprocessing, compilation and assembly. From Advanced C and C++ Compiling page 26:

• The ultimate intention behind the idea of building the program is that the sections obtained by compiling individual source files be combined (tiled) together into the single binary executable file. Such binary file would contain the sections of the same type (.text, .data, .bss, . . . ) obtained by tiling together the sections from the individual files. Figuratively speaking, an object file can be viewed as a simple tile waiting to find its place in the giant mosaic of the process memory map.

• The inner structure of the object file does not, however, suggest where the individual sections will ultimately reside in the program memory map. For that reason, the address ranges of each section in each of the object files is tentatively set to start from a zero value.

The actual address range at which a section from an object file will ultimately reside in the program map will be determined in the subsequent stages (linking) of program building process.

• In the process of tiling object files’ sections into the resultant program memory map, the only truly important parameter is the length of its sections, or to say it more precisely, its address range.

Page 27: Linking sections together into final binary's .text section

It should also be noted that an executable ELF binary's text segment consists of multiple sections in addition to linked .text sections:

sections:

$ readelf -SW /bin/ls
There are 29 section headers, starting at offset 0x1e738:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000400238 000238 00001c 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            0000000000400254 000254 000020 00   A  0   0  4
  [ 3] .note.gnu.build-id NOTE            0000000000400274 000274 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        0000000000400298 000298 0000c0 00   A  5   0  8
  [ 5] .dynsym           DYNSYM          0000000000400358 000358 000cd8 18   A  6   1  8
  [ 6] .dynstr           STRTAB          0000000000401030 001030 0005dc 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          000000000040160c 00160c 000112 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         0000000000401720 001720 000070 00   A  6   1  8
  [ 9] .rela.dyn         RELA            0000000000401790 001790 0000a8 18   A  5   0  8
  [10] .rela.plt         RELA            0000000000401838 001838 000a80 18  AI  5  24  8
  [11] .init             PROGBITS        00000000004022b8 0022b8 00001a 00  AX  0   0  4
  [12] .plt              PROGBITS        00000000004022e0 0022e0 000710 10  AX  0   0 16
  [13] .plt.got          PROGBITS        00000000004029f0 0029f0 000008 00  AX  0   0  8
  [14] .text             PROGBITS        0000000000402a00 002a00 011289 00  AX  0   0 16
  [15] .fini             PROGBITS        0000000000413c8c 013c8c 000009 00  AX  0   0  4
  [16] .rodata           PROGBITS        0000000000413ca0 013ca0 0069b4 00   A  0   0 32
  [17] .eh_frame_hdr     PROGBITS        000000000041a654 01a654 00080c 00   A  0   0  4
  [18] .eh_frame         PROGBITS        000000000041ae60 01ae60 002c84 00   A  0   0  8
  [19] .init_array       INIT_ARRAY      000000000061de00 01de00 000008 00  WA  0   0  8
  [20] .fini_array       FINI_ARRAY      000000000061de08 01de08 000008 00  WA  0   0  8
  [21] .jcr              PROGBITS        000000000061de10 01de10 000008 00  WA  0   0  8
  [22] .dynamic          DYNAMIC         000000000061de18 01de18 0001e0 10  WA  6   0  8
  [23] .got              PROGBITS        000000000061dff8 01dff8 000008 08  WA  0   0  8
  [24] .got.plt          PROGBITS        000000000061e000 01e000 000398 08  WA  0   0  8
  [25] .data             PROGBITS        000000000061e3a0 01e3a0 000260 00  WA  0   0 32
  [26] .bss              NOBITS          000000000061e600 01e600 000d68 00  WA  0   0 32
  [27] .gnu_debuglink    PROGBITS        0000000000000000 01e600 000034 00      0   0  1
  [28] .shstrtab         STRTAB          0000000000000000 01e634 000102 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

segments and section-to-segment mappings:

$ readelf -l /bin/ls

Elf file type is EXEC (Executable file)
Entry point 0x4049a0
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x000000000001dae4 0x000000000001dae4  R E    200000
  LOAD           0x000000000001de00 0x000000000061de00 0x000000000061de00
                 0x0000000000000800 0x0000000000001568  RW     200000
  DYNAMIC        0x000000000001de18 0x000000000061de18 0x000000000061de18
                 0x00000000000001e0 0x00000000000001e0  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x000000000001a654 0x000000000041a654 0x000000000041a654
                 0x000000000000080c 0x000000000000080c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x000000000001de00 0x000000000061de00 0x000000000061de00
                 0x0000000000000200 0x0000000000000200  R      1

 Section to Segment mapping:
  Segment Sections...
   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 .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .jcr .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 .jcr .dynamic .got 

The third segment (segment 02) is of type LOAD, has execute permissions, and corresponds to the process text segment in virtual memory. Here, this segment consists not only of the .text section but also several others, some of which consist of data rather than instructions, such as the .rodata section.

In light of the information of sections and segments shown above, it is important to note the at linker controls the following:

  • section definitions

    The most frequently used statement in the SECTIONS command is the section definition, which specifies the properties of an output section: its location, alignment, contents, fill pattern, and target memory region. Most of these specifications are optional

  • section attributes
  • section placement

    In a section definition, you can specify the contents of an output section by listing particular input files, by listing particular input-file sections, or by a combination of the two. You can also place arbitrary data in the section, and define symbols relative to the beginning of the section.

  • memory layout

    The linker's default configuration permits allocation of all available memory. You can override this configuration by using the MEMORY command. The MEMORY command describes the location and size of blocks of memory in the target. By using it carefully, you can describe which memory regions may be used by the linker, and which memory regions it must avoid. The linker does not shuffle sections to fit into the available regions, but does move the requested sections into the correct regions and issue errors when the regions become too full. A command file may contain at most one use of the MEMORY command; however, you can define as many blocks of memory within it as you wish.

Looking at a sample output from a link map shows in a concrete manner that it is the linker that controls program memory layout through the assignment of memory addresses to sections and symbols:

.plt.got
 *(.plt.got)

.text           0x0000000008048074        0xa
 *(.text.unlikely .text.*_unlikely .text.unlikely.*)
 *(.text.exit .text.exit.*)
 *(.text.startup .text.startup.*)
 *(.text.hot .text.hot.*)
 *(.text .stub .text.* .gnu.linkonce.t.*)
 .text          0x0000000008048074        0xa test.o
                0x0000000008048074                main
 *(.gnu.warning)

.fini
 *(SORT(.fini))
                [!provide]                        PROVIDE (__etext, .)
                [!provide]                        PROVIDE (_etext, .)
                [!provide]                        PROVIDE (etext, .)    
.rodata
 *(.rodata .rodata.* .gnu.linkonce.r.*)

.rodata1
 *(.rodata1)

.eh_frame_hdr
 *(.eh_frame_hdr)
 *(.eh_frame_entry .eh_frame_entry.*)

.eh_frame       0x0000000008048080       0x38
 *(.eh_frame)
 .eh_frame      0x0000000008048080       0x38 test.o
 *(.eh_frame.*)

3. Direct Answer to Question

Why don't they just increase the size of the ".text" section segment? Is it because of security reasons?

The address range of the .text section and the layout of the text segment in memory are determined by the linker at binary build time. In order for any changes to these to be made, access to the source code of the binary is required, since the binary would have to be rebuilt (compiled -> assembled -> linked) with a new memory layout, which is controlled by the linker.

Patching is performed if rebuilding the binary (which implies access to the source code) cannot be done.


1. SYSTEM V APPLICATION BINARY INTERFACE Edition 4.1

  • 1
    That is an amazing answer. You are spot on with how difficult parsing and understanding executable structures is. Increasing the size of .data might well impact the offset of the sections right after it. Resizing one section, would require recalculating the offsets of the other sections, and the header as well. – Nik Roby Apr 23 '17 at 22:41
  • Thank you for your complete answer, as it provides also a lot of corrections about mistakes that I made. Yes, I was talking about static binary injection, and I understand better now. – Duke Nukem Apr 24 '17 at 19:03
  • @DukeNukem You are welcome. Remember, the term injection implies insertion of code at program runtime. I know that you mean static binary instrumentation/modification, I just want to make sure you understand. Are you able to provide links to the projects you mention in your question? I am interested in that kind of thing – julian Apr 24 '17 at 21:04
0

If I am reading your question correctly, it all depends on the code of the binary, not the size of the .text section. Have you read how a buffer overflow works? The reason for the padding is to get to a point that the code will "Return" or execute your desired (shell)code or address. Expanding the .text section would not do anything. It's all about putting the proper input guards in place so that the malicious injection/ user input will not allow for this type of attack.

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.