CGI扫描器的原理和实现过程
作者: .abu. |
有很多网站为了安全起见,在WEB Server前面架了防火墙,或者做了TCP/IP过滤,对外只开放TCP 80 端口。从入侵者角度来看,要入侵那么从80上跑的CGI入手是比较可行的,当然也可以用别的办法,例如旁敲侧击,呵呵。从网管角度来看,一是要保证CGI的安全性,另外网络的整体安全性也是很重要的。针对基于80端口入侵、防范而出的CGI扫描器数不胜数,但基本上原理都一样。 CGI扫描器原理说起来其实非常简单,可以用四句话来概括:<1>连接目标WEB SERVER;<2>发送一个特殊的请求;<3>接收目标服务器返回数据;<4>根据返回数据判断目标服务器是否有此CGI漏洞。 当管理的服务器达到一定数量的时候,手工检测自己的服务器是否存在各种各样的CGI漏洞,那就太消耗时间和精力了,所以一个网管手上有个比较好用的CGI漏洞扫描器还是必要的。OK!今天我们就自己来动手用C写一个简单的CGI扫描器,帮助自己在日常工作中检测服务器:)) 源代码如下,很多地方我都加了注释,别嫌我烦哦:))编译好的程序可以从http://eyas.3322.net/program/cgicheck.exe下载。 /************************************************************************* Module:CGICheck.cpp Author:ey4s<ey4s@21cn.com> Date:2001/5/16 说明:这是一个Console下多线程,带有进度显示的CGI扫描器的模板,更改一下szSign和SendBuff就可以扫描其他CGI漏洞,设置了连接、发送、接收超时,速度还可以哦。希望可以帮助到admins检测自己的服务器:)) *************************************************************************/ #include <stdio.h> #include <winsock2.h> #include <time.h> #define iPort 80//目标Web Server端口 #define szSign "500 13Server: Microsoft-IIS/5.0"//根据此标志来检查目标是否有漏洞 #pragma comment(lib,"ws2_32.lib") /////////////////////////////////////////////////////////////////////////// // //定义&初始化全局变量 // char *SendBuff="GET /NULL.printer",//发送的请求buff CurrentTarget[52]={0},//存放最后一个线程将扫描的目标 turn[4][2]={"-","\","","/"};//显示进度时的字符 int SendBuffLen=strlen(SendBuff),//发送的buff长度 iConnTimeout,//TCP Connect TimeOut ii=0,//扫描进度 iTotal;//服务器总数 HANDLE hSemaphore=NULL,//信标内核对象句柄,用来控制线程数量 hStdout;//console标准输出句柄,做进度显示的时候用的 struct timeval timeout;//连接、发送和接收的超时值 DWORD SleepTime;//每个一个线程后等待的时间 /* SleepTime值根据用户输入的线程数量[ThreadNum]和TCP ConnectTimeOut[CONNTIMEO]来计算。确保在CONNTIMEO时间左右开 ThreadNum个线程。这样在CONNTIMEO时间后,所开的线程开始陆续超时退出,可以继续稳定的开线程,可以有效的保证同时有 ThreadNum个线程在运行。 */ /////////////////////////////////////////////////////////////////////////// void ShowError(char *);//显示出错信息函数,可以写完善一些,偶偷懒了:) BOOL ResetCursor(void);//重置光标位置,线程输出的时候调用的 DWORD WINAPI ShowProInfo(LPVOID);//显示进度信息 DWORD WINAPI scan(LPVOID);//扫描函数 void usage(char *);//帮助函数 /////////////////////////////////////////////////////////////////////////// int main(int argc,char **argv) { HANDLE hThread=NULL;//线程句柄 DWORD dwThreadID;//线程ID struct sockaddr_in sa; int i, MaxThread;//最大线程数量 WSADATA wsd; long PreviousCount; clock_t start,end;//程序运行的起始和结束时间 double duration; //检查用户输入参数 if(argc!=5) { usage(argv[0]); return 1; } //get target range int StartNet=inet_addr(argv[1]); int StopNet=inet_addr(argv[2]); int StartHost=ntohl(StartNet); int StopHost=ntohl(StopNet); //取得线程数量 MaxThread=atoi(argv[3]); //取得conn超时时间 iConnTimeout=atoi(argv[4]); //检查参数合法性 if((iConnTimeout>6) (iConnTimeout<2) (MaxThread<1) (MaxThread>500) (StopHost<StartHost)) { usage(argv[0]); return 1; } //计算时间 SleepTime=1000*iConnTimeout/MaxThread; //设置连接超时值 timeout.tv_sec = iConnTimeout; timeout.tv_usec =0; __try { //开始计时 start=clock(); //加载winsock库 if (WSAStartup(MAKEWORD(1,1), &wsd) != 0) { ShowError("WSAStartup"); __leave; } //创建信标内核对象句柄 hSemaphore=CreateSemaphore(NULL,MaxThread,MaxThread,NULL); if(hSemaphore==NULL) { ShowError("CreateSemaphore"); __leave; } //取得console标准输出句柄 hStdout=GetStdHandle(STD_OUTPUT_HANDLE); if(hStdout==INVALID_HANDLE_VALUE) { ShowError("GetStdHandle"); __leave; } //设置目标总数 iTotal=StopHost-StartHost; //创建进度显示线程 hThread=CreateThread(NULL,0,ShowProInfo,NULL,0,&dwThreadID); if(hThread==NULL) { ShowError("1 CreateThread"); __leave; } //关闭句柄 CloseHandle(hThread); //循环创建扫描线程 for(i=StartHost;i<=StopHost;i++) { //等待信标内核对象通知 WaitForSingleObject(hSemaphore,INFINITE); //create thread to scan hThread=CreateThread(NULL,0,scan,(LPVOID)i,0,&dwThreadID); if(hThread==NULL) { ShowError("2 CreateThread"); break; } //进度自加1 ii++; //重设最后一个线程扫描的目标 sa.sin_addr.s_addr=htonl(i); strncpy(CurrentTarget,inet_ntoa(sa.sin_addr),sizeof(CurrentTarget)); //休息一会儿:)) Sleep(SleepTime); //关闭线程句柄 CloseHandle(hThread); } //等待所有线程结束 while(1) { WaitForSingleObject(hSemaphore,INFINITE); if(!ReleaseSemaphore(hSemaphore,1,&PreviousCount)) { ShowError("main() ReleaseSemaphore"); Sleep(5000); break; } if(PreviousCount==(MaxThread-1)) { printf("All done."); break; } Sleep(500); } }//end of try //搞定,清场,收工 __finally { //计时结束 end=clock(); //转换时间格式 duration = (double)(end - start) / CLOCKS_PER_SEC; //显示所用时间 printf("Complete.Scan %d targets use %2.1f seconds.Speed %0.3g/s",iTotal,duration,iTotal/duration); //关闭句柄 CloseHandle(hStdout); CloseHandle(hSemaphore); WSACleanup(); } return 0; } /////////////////////////////////////////////////////////////////////////// // //回显错误信息函数 // void ShowError(char *msg) { MessageBox(NULL,msg,"ERROR",0); //printf("%s failed:%d",GetLastError()); } ////////////////////////////////////////////////////////////////////////// // //重置光标位置函数,以便扫描线程输出结果 // BOOL ResetCursor() { CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; //取得当前光标位置 if(!GetConsoleScreenBufferInfo(hStdout,&ConsoleScreenBufferInfo)) { ShowError("GetConsoleScreenBufferInfo"); return FALSE; } //设置光标X坐标为0 ConsoleScreenBufferInfo.dwCursorPosition.X=0; //设置当前光标位置 SetConsoleCursorPosition(hStdout,ConsoleScreenBufferInfo.dwCursorPosition); return TRUE; } /////////////////////////////////////////////////////////////////////////// // //显示进度信息函数 // DWORD WINAPI ShowProInfo(LPVOID lp) { int j,k; CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; float m; for(j=0;ii<iTotal;j++) { //休息一会儿:))) Sleep(SleepTime); //取得当前光标位置 if(!GetConsoleScreenBufferInfo(hStdout,&ConsoleScreenBufferInfo)) { ShowError("GetConsoleScreenBufferInfo"); return 1; } //设置百分比进度显示的X坐标 ConsoleScreenBufferInfo.dwCursorPosition.X=0; //设置当前光标位置 SetConsoleCursorPosition(hStdout,ConsoleScreenBufferInfo.dwCursorPosition); //已经完成的百分比 m=(ii+1)*100.00/iTotal; //显示进度 if(ii==iTotal) { printf("******** 100%% Wait %d seconds to exit ******** ",iConnTimeout); break; } else { k=j%4; printf("%-15s %s [%d/%d] %s %%%0.3g",CurrentTarget,turn[k],ii,iTotal,turn[k],m); } }//end of for return 0; } /////////////////////////////////////////////////////////////////////////// // //扫描函数 // DWORD WINAPI scan(LPVOID lp) { int i=(int)lp,iErr; struct sockaddr_in server; SOCKET s=INVALID_SOCKET; char RecvBuff[1024]={0},*ptr; int RecvBuffLen=sizeof(RecvBuff); u_long ul=1;//初始化为为非0值 fd_set r,w; //create socket s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(s==INVALID_SOCKET) { printf("Create socket failed:%d",GetLastError()); ExitProcess(1); } //fill the addr struct server.sin_family=AF_INET; server.sin_port=htons(iPort); server.sin_addr.S_un.S_addr=htonl(i); __try { //设置socket为非锁定模式,ul为0值的话,那么soocket将被设置为锁定模式 iErr=ioctlsocket(s,FIONBIO,(unsigned long*)&ul); if(iErr==SOCKET_ERROR ) { ResetCursor(); ShowError("ioctlsocket"); ExitProcess(1); } //printf("%X ioctl ok.strat conn",i); //connect to target connect(s,(struct sockaddr *)&server,sizeof(server)); //printf("%X conn return,start select w",i); //设置select参数 FD_ZERO(&w); FD_SET(s, &w); //等待connect成功&socket可写 iErr=select(0, 0, &w, 0, &timeout); //printf("%X select w return %d",i,iErr); //等待返回后,socket仍不可写则退出 if((iErr==SOCKET_ERROR) (iErr==0)) { //printf("%X select return w err,exit",i); __leave; } //socket可写则继续 else { //send buff to target //printf("%X send",i); iErr=send(s,SendBuff,SendBuffLen,0); //printf("%X send return",i); if(iErr==SOCKET_ERROR) __leave; } //等待socket可读 FD_ZERO(&r); FD_SET(s, &r); //printf("%X start select r",i); iErr=select(0, &r, 0, 0, &timeout); //printf("%X select r return %d",i,iErr); if((iErr==SOCKET_ERROR) (iErr==0)) { //printf("%X select r err,exit",i); __leave; } else { //recv buff from target //printf("%X start recv",i); iErr=recv(s,RecvBuff,RecvBuffLen,0); //printf("%X recv ret",i); if(iErr==SOCKET_ERROR) __leave; } //verify buff ptr=strstr(RecvBuff,szSign); if(ptr!=NULL) { //线程输出前要先调用ResetCursor函数 ResetCursor(); //输出信息后务必加一个以上换行符号,输出前请别加换行符号,以免显示混乱 printf("[%-15s] has .printer mapped. ",inet_ntoa(server.sin_addr)); } } __finally { if(!ReleaseSemaphore(hSemaphore,1,NULL)) ShowError("thread ReleaseSemaphore failed"); closesocket(s); } return 0; } /////////////////////////////////////////////////////////////////////////// void usage(char *proname) { printf("%s v0.1 only can find IIS5 .Printer mapped" "Power by ey4s<ey4s@21cn.com> 2001.5.20" "http://www.patching.net" "Usage:%s <StartIP> <EndIP> <ThreadNum> <CONNTIMEO>" "Notice" " StartIP StopIP ==>Don‘t forgot StopIP must large than StartIP " " ThreadNum ==>Thread number,please input between 1-500" " CONNTIMEO ==>TCP connect timeout,please input between 2-6" "Example" " %s 192.168.0.0 192.168.255.255 200 2",proname,proname,proname); } 程序在VC++6.0上编译通过,在windows2000上运行良好:) |
Tags:
作者:佚名评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论