The joys of impurity (was: MOSDEF, InlineEgg) Oct 06 2003 09:05PM
Alexander E. Cuttergo (cuttergo gmx net)
I enclose the impurity-1.0 release. The README follows. I would like to hear
some comparisons with MOSDEF and InlineEgg; can you guys implement nmap in
your shellcode ;) ?

by Alexander E. Cuttergo <cuttergo (at) gmx (dot) net [email concealed]>

Impurity is a set of scripts which make it easy to produce a
shellcode ("first stage") which is able to download over the net an
executable ELF file ("second stage") and execute it without writing it to
the disk first. The first stage shellcode itself is almost constant (except
for one parameter, the executable length) and very short - 46 bytes. Using
this approach, one does not waste hours composing and debugging a complex
shellcode - just write a .c program, run impurity (which creates the second
stage ELF binary) and you are all set.
Impurity is mostly useful when exploiting a daemon which runs
chrooted and with dropped privileges; in such case one cannot simply execute
/bin/sh. Compare it with MOSDEF and InlineEgg.
Currently impurity is implemented for Linux/i386 only; however,
porting to other OS/architectures should be trivial, provided they use ELF

GPL v2

Theory of operation
In order to be able to execute an ELF binary easily, we will have to
compile it (more precisely, link it) in a special way.
1) In order to not be troubled by shared libraries, we will link the binary
2) An usual executable text segment is mapped at 0x8048000. In our case,
this area is occupied by the text segment of an overflown binary. We could
unmap() it, but this would make our shellcode unnecessarily large. The
solution - use a linker script which will place the text segment starting at
different address. We will use 0xbfff1000 - it is within the stack, so all
we need to allocate memory for our executable is to do
"movl $0xbfff1000, %esp". This also means we will need an executable stack -
this should not be a problem usually. Enhancing shellcode in a way which
allocates memory for our ELF by mmap(...,PROT_EXEC,...) is left as an
excercise for a reader.
Moreover, in order to consequently simplify our first stage shellcode,
command line arguments and environment are not set, dereferencing them will
cause a SIGSEGV most likely. It is easily fixable in the second stage itself
(see tracepath.c example).
3) In a normal executable, the text and data segments are in disjoint memory
areas, with different permissions. This again complicates the loading
process. To avoid it, we will link our ELF executable as impure executable
(ld -N).
4) Binaries linked statically with recent glibc are large. This is usually
not a problem - during the bruteforcing stage of an exploit, we do not need
to send the binary at all to determine whether the correct offset has been
found. Anyway, "diet libc" is recommended - small programs linked with diet
libc are usually in 2k-30k size range. The provided shellcode (bootcode.S)
uses an unsigned short to store the executable length, thus limiting the
ELF binary to 64k (bla bla excercise for a reader bla).
5) The first stage shellcode downloads the second stage ELF binary from file
descriptor 0. It should not be difficult to add code which utilizes "find
socket shellcode", or just creates appropriate socket itself. Excercise,
reader, make bucks by selling this enhancement, bla.

Having 1-5 in mind, executing an ELF is really simple - just read
appropriate number of bytes from fd 0 into memory starting at 0xbfff1000,
and then jump to 0xbfff1074, where the entry point should be. Execve()
demystified ;)

Provided code
bootcode.S - first stage shellcode
bootstrap.c - trivial stub which emits to stdout asm from bootcode.S,
adjusting file length u16 in its body
filesize.c - a helper which prints the file size to stdout
script-ld-impure-into-stack - a proper ld script
tracepath.c - a sample second stage. It is almost a verbatim copy of
tracepath.c from iputils; the only changes are a few lines to
prepare a command line and environment.
shelix.c - a sample program, vulnerable to a buffer overflow. It does
chroot(/var/empty) and setuid(12345), so we cannot break out of chroot nor
execute /bin/sh; also we have no writeable directory to download any
executable to. As an example, we use impurity to produce a second stage
based on tracepath, hoping to learn the DMZ layout. Yes, we can use nmap (or
linux_autorooter.0227) as the second stage easily.

Running a sample code
You have to install diet libc (http://www.fefe.de/dietlibc/) first.
1) Run "make"
2) Run "shelix" as root via [x]inetd or "nc -l -p shelix_port -e ./shelix"
3) "make" produces "ovdata" file, which contains buffer overflow payload and
the first stage shellcode, so:
$ (cat ovdata ; sleep 1; cat tracepath; cat) | nc localhost shelix_port
Shelix: got token length 2
Shelix: got token length 2
Shelix: got token length 2
Shelix: got token length 2
Shelix: got token length 2
Shelix: got token length 2
Shelix: got token length 2
Shelix: got token length 46
Second stage OK. Which ipaddr should I try ?
c.d.40.1 <- entered from stdin
1?: [LOCALHOST] pmtu 1500
1: x.y.131.57 2.127ms
2?: x.y.20.176
3?: a.b.40.5
4?: c.d.40.1
Resume: pmtu 1500 hops 4 back 4

Again, if you want to use a different second stage, all you need is to compile
its sources instead of tracepath.c . If attacking a different vulnerable
daemon, you will need to change the overflow payload creation ("ovdata"
target in Makefile), but no need to touch any assembly.

[ reply ]


Privacy Statement
Copyright 2010, SecurityFocus