5. A closer look

 

It looks worse than you can imagine!

I can imagine some pretty bad things!

That's why I said *worse*!

 Terry Pratchett, Moving Pictures

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

Command: src/entry_point/segments.sh
#!/bin/sh
cd ${TMP}/one_step_closer/e1i1 \
&& ls -l sh_infected \
&& ${READELF} -l sh_infected

Output: out/i386-redhat-linux/entry_point/segments
-rwxrwxr-x    1 alba     alba       545192 Aug 16 00:13 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

5.1. First scan

The big output of the scanner from Turn the pages contains the executable from last chapter. But for clarity we repeat the exercise.

Command: src/entry_point/scan_dist.sh
#!/bin/sh
echo	"/bin/bash
	${TMP}/one_step_closer/e1i1/sh_infected" \
| src/scanner/dist.pl

Output: out/i386-redhat-linux/entry_point/scan
tmp/i386-redhat-linux/one_step_closer/e1i1/sh_infected virtaddr=0x080c7420 dist=0x0000000c
   2 files;    1 detected; min=0x0000000c; max=0x0000100c

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

5.2. Looking around

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

entry_point_ofs = 0x8059440 - 0x8048000 = 0x11440 = 70720 bytes.

The infected copy moved that to less than 4096 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 is another easy vulnerability to scanners. But the bad news ain't over. readelf(1) features another option, -S.

Command: src/entry_point/sections.sh
#!/bin/sh
ls -l /bin/bash
${READELF} -S /bin/bash

Output: out/i386-redhat-linux/entry_point/sections
-rwxr-xr-x    1 root     root       541096 Apr 12 18:09 /bin/bash
There are 23 section headers, starting at offset 0x83e10:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048128 000128 002a20 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          0804ab48 002b48 0067f0 10   A  5   1  4
  [ 5] .dynstr           STRTAB          08051338 009338 006538 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          08057870 00f870 000cfe 02   A  4   0  2
  [ 7] .gnu.version_r    VERNEED         08058570 010570 000070 00   A  5   2  4
  [ 8] .rel.dyn          REL             080585e0 0105e0 000058 08   A  4   0  4
  [ 9] .rel.plt          REL             08058638 010638 0004a0 08   A  4   b  4
  [10] .init             PROGBITS        08058ad8 010ad8 000018 00  AX  0   0  4
  [11] .plt              PROGBITS        08058af0 010af0 000950 04  AX  0   0  4
  [12] .text             PROGBITS        08059440 011440 058680 00  AX  0   0 16
  [13] .fini             PROGBITS        080b1ac0 069ac0 00001e 00  AX  0   0  4
  [14] .rodata           PROGBITS        080b1ae0 069ae0 014934 00   A  0   0 32
  [15] .data             PROGBITS        080c7420 07e420 0055e8 00  WA  0   0 32
  [16] .eh_frame         PROGBITS        080cca08 083a08 000004 00  WA  0   0  4
  [17] .dynamic          DYNAMIC         080cca0c 083a0c 0000d8 08  WA  5   0  4
  [18] .ctors            PROGBITS        080ccae4 083ae4 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        080ccaec 083aec 000008 00  WA  0   0  4
  [20] .got              PROGBITS        080ccaf4 083af4 000260 04  WA  0   0  4
  [21] .bss              NOBITS          080ccd60 083d60 004190 00  WA  0   0 32
  [22] .shstrtab         STRTAB          00000000 083d60 0000b0 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

The most interesting entry is .text. The start of the section, 0x8059440, equals the entry point as reported by option -l in Bashful glance.

5.3. Second scan

The following scanner compares the start of section .text with the entry point. It's structure is similar to the script from Turn the pages.

Source: src/scanner/entry_point.pl
#!/usr/bin/perl -w
use strict;

my $readelf = $ENV{'READELF'}
|| die "Environment variable READELF undefined.";

my $min = 0xFFFFFFFF;
my $max = 0;
my $detected = 0;
LOOP: while(my $filename = <>)
{
  chomp $filename; $filename =~ s/^\s*//;
  next LOOP if ( ! -e $filename );
  open(ELF, "$readelf -Sl $filename 2>&1 |") || die "$1 ($filename)";

  my $entry_point;
  my $start_of_text;
  while(my $line = <ELF>)
  {
    chomp $line;
    if ($line =~ m/^Entry point 0x([0-9A-Fa-f]+)/)
    {
      $entry_point = hex($1);
    }
    elsif ($line =~ m/^\s*\[[\s\d]+\]\s+.text\s+PROGBITS\s+([0-9A-Fa-f]+)/)
    {
      $start_of_text = hex($1);
    }
  }
  close ELF;
  if (!defined($entry_point))
  {
    printf "%-46s has no entry point.\n", $filename;
  }
  elsif ($entry_point != $start_of_text)
  {
    $detected++;
    printf "%-46s ep=0x%08x sot=0x%08x\n",
      $filename, $entry_point, $start_of_text;
  }
}
printf "%4d files; %4d detected\n", $., $detected;

Once again we first test with typical places like /bin. Everything clean.

Output: out/i386-redhat-linux/scanner/entry_point_big
1543 files;    0 detected

And then with all infected executables. Only a few are detected. Which means there is cure against this vulnerability (just read on).

Output: out/i386-redhat-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/which_infected ep=0x0804b2c0 sot=0x08048b30
one_step_closer/e1i1/sh_infected ep=0x080c6420 sot=0x08059440
one_step_closer/e1i1/strip_sh_infected ep=0x080c6420 sot=0x08059440
  35 files;    5 detected