The ELF Virus Writing HOWTO
Prev |  | Next

# 7. 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/redhat-linux-i386/entry_point/segments

    -rwxr-xr-x    1 alba     anonymou   524060 Jun 30 00:06 sh_infected

    Elf file type is EXEC (Executable file)
    Entry point 0x80c1280
    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 0x7a273 0x7a273 R E 0x1000
      LOAD           0x07a280 0x080c2280 0x080c2280 0x057e0 0x09bd0 RW  0x1000
      DYNAMIC        0x07f980 0x080c7980 0x080c7980 0x000e0 0x000e0 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.got .rel.bss .rel.plt .init .plt .text .fini .rodata
       03     .data .eh_frame .ctors .dtors .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 - sh.file_size = 524060 - 519964 = 4096 = 0x1000

> infected.`LOAD`[1].`Filesiz` - sh.`LOAD`[1].`Filesiz` = 0x7a273 - 0x79273 = 0x1000

> infected.`LOAD`[2].`Offset` - sh.`LOAD`[2].`Offset` = 0x7a280 - 0x79280 = 0x1000

> infected.`DYNAMIC`.`Offset` - sh.`DYNAMIC`.`Offset` = 0x7f980 - 0x7e980 = 0x1000

## 7.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/redhat-linux-i386/entry_point/scan

    tmp/redhat-linux-i386/one_step_closer/e1i1/sh_infected virtaddr=0x080c2280 dist=0x0000000d
       2 files;    1 detected; min=0x0000000d; max=0x0000100d

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

## 7.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 = 0x8059380 - 0x8048000 = 0x11380 = 70528 bytes.

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

> entry_point_ofs = 0x80c1280 - 0x8048000 = 0x79280 = 496256 bytes.

> end_of_LOAD1 = 0x8048000 + 0x7a273 = 0x80c2273

> entry_point_distance_to_end = 0x80c2273 - 0x80c1280 = 0xff3 = 4083

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/redhat-linux-i386/entry_point/sections

    -rwxr-xr-x    1 root     root       519964 Jul  9  2001 /bin/bash
    There are 25 section headers, starting at offset 0x7eb34:

    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 002994 04   A  4   0  4
      [ 4] .dynsym           DYNSYM          0804aabc 002abc 0065c0 10   A  5   1  4
      [ 5] .dynstr           STRTAB          0805107c 00907c 0067db 00   A  0   0  1
      [ 6] .gnu.version      VERSYM          08057858 00f858 000cb8 02   A  4   0  2
      [ 7] .gnu.version_r    VERNEED         08058510 010510 000080 00   A  5   2  4
      [ 8] .rel.got          REL             08058590 010590 000008 08   A  4  14  4
      [ 9] .rel.bss          REL             08058598 010598 000050 08   A  4  17  4
      [10] .rel.plt          REL             080585e8 0105e8 000478 08   A  4   c  4
      [11] .init             PROGBITS        08058a60 010a60 000018 00  AX  0   0  4
      [12] .plt              PROGBITS        08058a78 010a78 000900 04  AX  0   0  4
      [13] .text             PROGBITS        08059380 011380 053cb0 00  AX  0   0 16
      [14] .fini             PROGBITS        080ad030 065030 00001e 00  AX  0   0  4
      [15] .rodata           PROGBITS        080ad060 065060 014213 00   A  0   0 32
      [16] .data             PROGBITS        080c2280 079280 0054a0 00  WA  0   0 32
      [17] .eh_frame         PROGBITS        080c7720 07e720 000004 00  WA  0   0  4
      [18] .ctors            PROGBITS        080c7724 07e724 000008 00  WA  0   0  4
      [19] .dtors            PROGBITS        080c772c 07e72c 000008 00  WA  0   0  4
      [20] .got              PROGBITS        080c7734 07e734 00024c 04  WA  0   0  4
      [21] .dynamic          DYNAMIC         080c7980 07e980 0000e0 08  WA  5   0  4
      [22] .sbss             PROGBITS        080c7a60 07ea60 000000 00   W  0   0  1
      [23] .bss              NOBITS          080c7a60 07ea60 0043f0 00  WA  0   0 32
      [24] .shstrtab         STRTAB          00000000 07ea60 0000d4 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, `0x8059380`, equals the entry point as reported by option `-l` in Bashful glance.

## 7.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 $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/redhat-linux-i386/scanner/entry_point_big

    1475 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/redhat-linux-i386/scanner/entry_point_small

      35 files;    5 detected

Prev | Home | Next
One step closer to the edge |  | The entry point