4. Segment padding infection (i)

 

I am extremely surprised and pleased. I'm surprised because as far as I am concerned I have always done what I wanted to do and followed my own way. Really, the honour has as much to do with Paddington as myself.

 Michael Bond, creator of "Paddington Bear", on receiving an OBE

This infection method got a lot of press under the name Remote shell trojan (RST) (i). It is based on a pecularity described in the platform specific part, Segment padding infection. On i386 its future is bleak. It seems that the gap is beeing actively closed. On Slackware 8.1 and Red Hat 8.0 /bin/bash is not vulnerable anymore. On other platforms the gap is much larger and will probably remain.

Read Segment padding infection before you continue and see there for the results of the code following below.

Table 1. Platform specific defaults of ELF

PlatformAddress sizeOffsetBase addressAlignment_SC_PAGESIZEByte order
alpha640x000000000x1200000000x100000x2000L
i386320x000000000x080480000x10000x1000L
sparc320x000000000x000100000x100000x1000M

_SC_PAGESIZE is a hardware constant and nothing compiler vendors choose. On the other hand column "Alignment" varies from challengingly tiny to exceedingly large. A quote from the ELF specification: [4]

[…] executable and shared object files must have segment images whose file offsets and virtual addresses are congruent, modulo the page size. Virtual addresses and file offsets for the SYSTEM V architecture segments are congruent modulo 4 KB (0x1000) or larger powers of 2. Because 4 KB is the maximum page size, the files will be suitable for paging regardless of physical page size. […]

This means that for every segment the last three digits of Offset equal the last three digits of VirtAddr in every healthy output of readelf and objdump. So unless we change VirtAddr as well - which means enormous trouble like relocation of every access to a global variable - we are stuck with allocating memory in chunks of _SC_PAGESIZE. By the way, that value is returned by sysconf(3).

Source: src/segment_padding/sysconf.c
#include <stdio.h>
#include <unistd.h>

#define QUERY(n)	{ #n, n }

struct Query
{
  const char* name;
  int key;
} Query[] =
{
  QUERY(_SC_CLK_TCK),
  QUERY(_SC_VERSION),
  QUERY(_SC_PAGESIZE),
#ifdef __linux__
  QUERY(_SC_PHYS_PAGES),
  QUERY(_SC_AVPHYS_PAGES),
#endif 
  { 0, 0 }
};

int main()
{
  const struct Query* q = Query;
  for(; q->name != 0; q++)
    printf("%s=%ld\n", q->name, sysconf(q->key));
  return 0;
}

Obviously the output is platform dependent. See Segment padding infection if you are curious. Anyway, using free space resulting from alignment is problematic on i386 but comfortable on the other platforms. Scan segment padding is used to verify the existence of this gap. The output of that is again at Segment padding infection.

4.1. The plan

This setup has a few problems.

4.2. target_new_entry_addr

4.3. target_patch_phdr

4.4. target_patch_shdr

4.5. target_copy_and_infect