catalog
1. FTP弱密码导致的入侵向量 2. FTP Listening端口动态扫描 3. 根据Banner判断目标端口是否为FTP服务 4. 基于Socket TCP FTP连接、FTP登录控制协议的自动化弱密码检测
1. FTP弱密码导致的入侵向量
1. 网站管理员常常把FTP的根目录配置为WWW(WEB目录),以此来方便网站的管理工作 2. 黑客猜解到FTP的弱密码之后,即可获取到WWW网站的敏感配置文件 3. 如果猜解到的账号具有目录/文件写权限,黑客可以向WWW网站目录下写入WEBSHELL文件 4. 在特定的FTP(例如serv-U),黑客在上了恶意程序/目录后,可以通过: quote site exec net user had had /add 添加用户 5. 如果目标FTP配置的匿名访问,则黑客可以直接匿名登录
0x1: IIS FTP加固方案
Relevant Link:
http://www.cnblogs.com/LittleHann/p/3988923.html
2. FTP Listening端口动态扫描
服务器上所开放的端口就是潜在的通信通道,也就是一个入侵通道。对目标计算机进行端口扫描,能得到许多有用的信息,进行端口扫描的方法很多,可以是手工进行扫描、也可以用端口扫描软件进行
扫描器通过选用远程TCP/IP不同的端口的服务,并记录目标给予的回答,通过这种方法可以搜集到很多关于目标主机的各种有用的信息,例如远程系统是否支持匿名登陆、是否存在可写的FTP目录、是否开放TELNET服务和HTTPD服务等
0x1: 常用端口扫描技术
1. TCP connect()扫描 这是最基本的TCP扫描,操作系统提供的connect()系统调用可以用来与每一个感兴趣的目标计算机的端口进行连接。如果端口处于侦听状态,那么connect()就能成功。否则,这个端口是不能用的,即没有提供服务 1) 这个技术的一个最大的优点是,你不需要任何权限。系统中的任何用户都有权利使用这个调用 2) 另一个好处就是速度,如果对每个目标端口以线性的方式,使用单独的connect()调用,那么将会花费相当长的时间,使用者可以通过同时打开多个套接字来加速扫描。使用非阻塞I/O允许你设置一个低的时间用尽周期,同时观察多个套接字 3) 但这种方法的缺点是很容易被察觉,并且被防火墙将扫描信息包过滤掉。目标计算机的logs文件会显示一连串的连接和连接出错消息,并且能很快使它关闭 2. TCP SYN扫描 这种技术通常认为是"半开放"扫描,这是因为扫描程序不必要打开一个完全的TCP连接。扫描程序发送的是一个SYN数据包,好象准备打开一个实际的连接并等待反应一样。一个SYN|ACK的返回信息表示端口处于侦听状态:返回RST表示端口没有处于侦听态。如果收到一个SYN|ACK,则扫描程序必须再发送一个RST信号,来关闭这个连接过程。这种扫描技术的优点在于一般不会在目标计算机上留下记录,但这种方法的缺点是 必须要有root权限才能建立自己的SYN数据包 3. TCP FIN扫描: SYN扫描虽然是"半开放"方式扫描,但在某些时候也不能完全隐藏扫描者的动作,防火墙和包过滤器会对管理员指定的端口进行监视,有的程序能检测到这些扫描。相反,FIN数据包在扫描过程中却不会遇到过多问题,这种扫描方法的思想是关闭的端口会用适当的RST来回复FIN数据包。另一方面,打开的端口会忽略对FIN数据包的回复。这种方法和系统的实现有一定的关系,有的系统不管端口是否打开都会回复RST,在这种情况下此种扫描就不适用了。另外这种扫描方法可以非常容易的区分服务器是运行Unix系统还是NT系统 4. IP段扫描 这种扫描方式并不是新技术,它并不是直接发送TCP探测数据包,而是将数据包分成两个较小的IP段。这样就将一个TCP头分成好几个数据包,从而过滤器就很难探测到。但必须小心: 一些程序在处理这些小数据包时会有些麻烦 5. TCP反向ident扫描: ident协议允许(rfc1413)看到通过TCP连接的任何进程的拥有者的用户名,即使这个连接不是由这个进程开始的。例如扫描者可以连接到http端口,然后用identd来发现服务器是否正在以root权限运行。这种方法只能在和目标端口建立了一个完整的TCP连接后才能看到 6. FTP返回攻击 FTP协议的一个有趣的特点是它支持代理(proxy)FTP连接,即入侵者可以从自己的计算机self.com和目标主机target.com的FTP server-PI(协议解释器)连接,建立一个控制通信连接。然后请求这个server-PI激活一个有效的server-DTP(数据传输进程)来给Internet上任何地方发送文件。对于一个User-DTP,尽管RFC明确地定义请求一个服务器发送文件到另一个服务器是可以的,但现在这个方法并不是非常有效。这个协议的缺点是"能用来发送不能跟踪的邮件和新闻,给许多服务器造成打击,用尽磁盘,企图越过防火墙" 7. UDP ICMP端口不能到达扫描 这种方法与上面几种方法的不同之处在于使用的是UDP协议,而非TCP/IP协议。由于UDP协议很简单,所以扫描变得相对比较困难。这是由于打开的端口对扫描探测并不发送确认信息,关闭的端口也并不需要发送一个错误数据包。幸运的是许多主机在向一个未打开的UDP端口发送数据包时,会返回一个ICMP_PORT_UNREACH错误,这样扫描者就能知道哪个端口是关闭的。UDP和ICMP错误都不保证能到达,因此这种扫描器必须还实现在一个包看上去是丢失的时候能重新传输。这种扫描方法是很慢的,因为RFC对ICMP错误消息的产生速率做了规定。同样这种扫描方法也需要具有root权限 8. UDP recvfrom()和write()扫描 当非root用户不能直接读到端口不能到达错误时,Linux能间接地在它们到达时通知用户。比如,对一个关闭的端口的第二个write()调用将失败。在非阻塞的UDP套接字上调用recvfrom()时,如果ICMP出错还没有到达时回返回EAGAIN-重试。如果ICMP到达时,返回ECONNREFUSED-连接被拒绝。这就是用来查看端口是否打开的技术
0x2: TCP connect()扫描
//ftp port scan #include <WinSock.h> #pragma comment(lib,"ws2_32.lib") #define PORTSCAN_MAX 9999 //dynamic scan ftp listening port int ftpPortScan() { //初始化Windows Sockets 动态库 WSADATA wsaData; int _port; if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "can‘t find WinSock dll!" << std::endl; return 0; } SOCKET sClient = NULL; SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; //scan localhost addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); for (_port = 79; _port < PORTSCAN_MAX; _port++) { //every tcp connect try, you need create a new socket if (sClient == NULL) { sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sClient == INVALID_SOCKET) { cout << "create client socket faild!" << endl; return 0; } } addrServ.sin_port = htons(_port); //连接服务器 if(connect(sClient, (sockaddr *)&addrServ, sizeof(sockaddr)) == SOCKET_ERROR) { //cout << "port " << _port << " is not open!" << endl; } else { std::cout << "port " << _port << " open" << endl; closesocket(sClient); sClient = NULL; } } //关闭套接字,释放资源 WSACleanup(); return 0; }
扫描速度较慢,如果不是扫描localhost本地环回IP的话,会产生系统/防火墙日志,采取多线程方式改进扫描速度
//ftp port scan #include <WinSock.h> #pragma comment(lib,"ws2_32.lib") #define PORTSCAN_MAX 9999 #define THREADCOUNT 60 int WINAPI ftpPortScan( LPVOID lpParameter ); //port number globally int PortNum=0; //CRITICAL var CRITICAL_SECTION cs; //dynamic scan ftp listening port int WINAPI ftpPortScan( LPVOID lpParameter ) { //create socket SOCKET TryConnect; while (1) { if (PortNum > PORTSCAN_MAX) { break; } //get into Critical region EnterCriticalSection(&cs); //get a task int tmpport = PortNum; PortNum++; //get outof Critical region LeaveCriticalSection(&cs); TryConnect = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == TryConnect) { std::cout << "create client socket faild!" << std::endl; return 0; } //try connect sockaddr_in addrMe = {0}; addrMe.sin_family = AF_INET; DWORD threadID = GetCurrentThreadId(); //PortNum is a Critical var, may be wrong, you need tmpport as a TLS var addrMe.sin_port = htons(tmpport); addrMe.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int retCon = connect(TryConnect, (sockaddr*)&addrMe, sizeof(sockaddr_in)); if (SOCKET_ERROR != retCon) { std::cout << "port " << tmpport << " open" << endl; closesocket(TryConnect); TryConnect = NULL; } } return 0; } else if(action == "--ftpPwdcheck") { //初始化套接字 WSADATA ws; ::WSAStartup(MAKEWORD(2,0),&ws); int i; //初始化临界区 InitializeCriticalSection(&cs); //多线程扫描 HANDLE hThread[THREADCOUNT]; for (i = 0; i <THREADCOUNT; i++) { hThread[i] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ftpPortScan,(LPVOID)0,0,NULL); } WaitForMultipleObjects(THREADCOUNT,hThread,true,INFINITE); //删除临界区 DeleteCriticalSection(&cs); return 0; }
0x3: TCP SYN扫描
// SYNScan.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <WINSOCK2.h> #include "SYNScan.h" #include "ws2tcpip.h" #include "mstcpip.h" #pragma comment(lib,"ws2_32.lib") #define Port_Src 2233 #define SEQ 2315 USHORT SYNScan::checksum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } int SYNScan::Scan(char* IP_Dest, int Port_Dst, u_int time_out, char* msg) { char msg_buf[100]; WSADATA wsa; SOCKET s; char buf[256]; char recv_buf[256]; struct sockaddr_in src_addr,dst_addr; int ret = WSAStartup(MAKEWORD(2,2), &wsa); s = socket(AF_INET , SOCK_RAW , IPPROTO_IP); int flag = 1; ret = setsockopt(s, IPPROTO_IP,IP_HDRINCL ,(char *)&flag, sizeof(flag)); //socket选项,手动定义IP头 if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: setopt:syn Error %d\r\n", Port_Dst,WSAGetLastError()); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&time_out, sizeof(time_out)); if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: 无法设置套接字的接受超时时间!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&time_out, sizeof(time_out)); if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: 无法设置套接字的发送超时时间!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } dst_addr.sin_family = AF_INET; dst_addr.sin_port = htons(Port_Dst); dst_addr.sin_addr.s_addr = inet_addr(IP_Dest); char host_name[100]; //取得主机地址 gethostname(host_name, sizeof(host_name)-1); LPHOSTENT host = gethostbyname(host_name); src_addr.sin_addr = *(in_addr *)host->h_addr; IPV4_HDR ip_hdr; //IP头设置 ip_hdr.ip_verlen = (4<<4 | sizeof(IPV4_HDR)/sizeof(unsigned long)); ip_hdr.ip_tos = 0; ip_hdr.ip_totallength = htons(sizeof(IPV4_HDR)+sizeof(TCP_HDR)); ip_hdr.ip_id = 1; ip_hdr.ip_offset = 0; ip_hdr.ip_ttl = 128; ip_hdr.ip_protocol = IPPROTO_TCP; ip_hdr.ip_checksum = 0; ip_hdr.ip_srcaddr = src_addr.sin_addr.s_addr; ip_hdr.ip_destaddr = inet_addr(IP_Dest); TCP_HDR tcp_hdr; //Tcp头设置 tcp_hdr.th_sport = htons(Port_Src); tcp_hdr.th_dport = htons(Port_Dst); tcp_hdr.th_seq = htonl(SEQ); tcp_hdr.th_ack = 0; tcp_hdr.th_lenres= (sizeof(TCP_HDR)/4<<4|0); tcp_hdr.th_flag = 2; // SYN = 2;ack = 0; means SYN scan tcp_hdr.th_win = htonl(512); tcp_hdr.th_checksum = 0; tcp_hdr.th_urp = 0; IP_PSD_HDR psd_hdr; //伪IP头设置 psd_hdr.src_addr = src_addr.sin_addr.s_addr; psd_hdr.dest_addr = inet_addr(IP_Dest); psd_hdr.mbz = 0; psd_hdr.protocol = IPPROTO_TCP; psd_hdr.tcplength = htons(sizeof(TCP_HDR)); memcpy(buf, &psd_hdr, sizeof(IP_PSD_HDR)); memcpy(buf + sizeof(IP_PSD_HDR), &tcp_hdr, sizeof(TCP_HDR)); tcp_hdr.th_checksum = checksum((u_short *)buf, sizeof(IP_PSD_HDR)+sizeof(TCP_HDR)); //计算Tcp头校验和 memcpy(buf, &ip_hdr, sizeof(IPV4_HDR)); memcpy(buf + sizeof(IPV4_HDR), &tcp_hdr, sizeof(TCP_HDR)); memset(buf + sizeof(IPV4_HDR) + sizeof(TCP_HDR), 0, 4); ip_hdr.ip_checksum = checksum((u_short *)buf, sizeof(IPV4_HDR)+sizeof(TCP_HDR)); ((IPV4_HDR *)&buf)->ip_checksum = ip_hdr.ip_checksum; //计算IP头校验和 ret = sendto(s, buf, sizeof(IPV4_HDR)+sizeof(TCP_HDR), 0, (struct sockaddr*) &dst_addr,sizeof(dst_addr)); //发送Syn包 if (ret == SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: Syn send error %d!\r\n",Port_Dst,WSAGetLastError()); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } int src_len = sizeof(src_addr); ret = recvfrom(s, (char *)&recv_buf, sizeof(recv_buf), 0, (SOCKADDR *)&src_addr, &src_len); //接受回复 if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: Syn Reply recv error %d!\r\n",Port_Dst,WSAGetLastError()); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } int recv_len = ret; //回复包分析 if (recv_len < sizeof(IPV4_HDR)){ sprintf(msg_buf,"Scan Port %d: Syn Reply IPHEADER error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } IPV4_HDR *ip_hdr_recv = (IPV4_HDR *)recv_buf; //回复报文IP头分析 int ip_hdr_recv_len = ip_hdr_recv->ip_verlen & 0x0F; ip_hdr_recv_len = (ip_hdr_recv_len) << 2; if (checksum((u_short *)ip_hdr_recv, ip_hdr_recv_len)) { sprintf(msg_buf,"Scan Port %d: Syn Reply checksum error!\r\n",Port_Dst); strcat(msg,msg_buf); WSACleanup(); return(-1); } if (recv_len < (ip_hdr_recv_len + sizeof(TCP_HDR))) { //校验和 sprintf(msg_buf,"Scan Port %d: Syn Reply TCPHEADER recv error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return(-1); } TCP_HDR *tcp_hdr_recv = (TCP_HDR *) (recv_buf + ip_hdr_recv_len); //回复报文TCP头分析 if ((tcp_hdr_recv->th_flag & 0x04) > 0){ sprintf(msg_buf,"Scan Port %d: Syn connect is rejected!\r\n",Port_Dst); strcat(msg,msg_buf); WSACleanup(); return(-1); } if ((tcp_hdr_recv->th_flag & 0x10) == 0){ //标志位 sprintf(msg_buf,"Scan Port %d: Syn reply ack error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return(-1); } if (ntohl(tcp_hdr_recv->th_ack) != (SEQ+1) ){ //是否对刚发出去的报文的ack sprintf(msg_buf,"Scan Port %d: Syn reply ack sequence error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return(-1); } sprintf(msg_buf,"Scan Port %d: Syn scan succeed!\r\n",Port_Dst); strcat(msg,msg_buf); WSACleanup(); return (0); }
0x4: TCP FIN扫描
// FinScan.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <WINSOCK2.h> #include "FinScan.h" #include "ws2tcpip.h" //#include "mstcpip.h" #pragma comment(lib,"ws2_32.lib") #define Port_Src 2233 #define SEQ 2315 USHORT FinScan::checksum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } int FinScan::Scan(char* IP_Dest, int Port_Dst, u_int time_out, char* msg) { char msg_buf[100]; WSADATA wsa; SOCKET s; char buf[256]; char recv_buf[256]; struct sockaddr_in src_addr,dst_addr; int ret = WSAStartup(MAKEWORD(2,2), &wsa); s = socket(AF_INET , SOCK_RAW , IPPROTO_IP); int flag = 1; ret = setsockopt(s, IPPROTO_IP,IP_HDRINCL ,(char *)&flag, sizeof(flag)); //socket选项,手动定义IP头 if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: setopt:syn Error %d\r\n", Port_Dst,WSAGetLastError()); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&time_out, sizeof(time_out)); if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: 无法设置套接字的接受超时时间!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&time_out, sizeof(time_out)); if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: 无法设置套接字的发送超时时间!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } dst_addr.sin_family = AF_INET; dst_addr.sin_port = htons(Port_Dst); dst_addr.sin_addr.s_addr = inet_addr(IP_Dest); char host_name[100]; //取得主机地址 gethostname(host_name, sizeof(host_name)-1); LPHOSTENT host = gethostbyname(host_name); src_addr.sin_addr = *(in_addr *)host->h_addr; IPV4_HDR ip_hdr; //IP头设置 ip_hdr.ip_verlen = (4<<4 | sizeof(IPV4_HDR)/sizeof(unsigned long)); ip_hdr.ip_tos = 0; ip_hdr.ip_totallength = htons(sizeof(IPV4_HDR)+sizeof(TCP_HDR)); ip_hdr.ip_id = 1; ip_hdr.ip_offset = 0; ip_hdr.ip_ttl = 128; ip_hdr.ip_protocol = IPPROTO_TCP; ip_hdr.ip_checksum = 0; ip_hdr.ip_srcaddr = src_addr.sin_addr.s_addr; ip_hdr.ip_destaddr = inet_addr(IP_Dest); TCP_HDR tcp_hdr; //TCP头设置 tcp_hdr.th_sport = htons(Port_Src); tcp_hdr.th_dport = htons(Port_Dst); tcp_hdr.th_seq = htonl(SEQ); tcp_hdr.th_ack = 0; tcp_hdr.th_lenres= (sizeof(TCP_HDR)/4<<4|0); tcp_hdr.th_flag = 1; // Fin = 2;ack = 0; means Fin scan tcp_hdr.th_win = htonl(512); tcp_hdr.th_checksum = 0; tcp_hdr.th_urp = 0; IP_PSD_HDR psd_hdr; //伪IP头设置 psd_hdr.src_addr = src_addr.sin_addr.s_addr; psd_hdr.dest_addr = inet_addr(IP_Dest); psd_hdr.mbz = 0; psd_hdr.protocol = IPPROTO_TCP; psd_hdr.tcplength = htons(sizeof(TCP_HDR)); memcpy(buf, &psd_hdr, sizeof(IP_PSD_HDR)); memcpy(buf + sizeof(IP_PSD_HDR), &tcp_hdr, sizeof(TCP_HDR)); tcp_hdr.th_checksum = checksum((u_short *)buf, sizeof(IP_PSD_HDR)+sizeof(TCP_HDR)); //计算TCP头校验和 memcpy(buf, &ip_hdr, sizeof(IPV4_HDR)); memcpy(buf + sizeof(IPV4_HDR), &tcp_hdr, sizeof(TCP_HDR)); memset(buf + sizeof(IPV4_HDR) + sizeof(TCP_HDR), 0, 4); ip_hdr.ip_checksum = checksum((u_short *)buf, sizeof(IPV4_HDR)+sizeof(TCP_HDR)); //计算IP头校验和 ((IPV4_HDR *)&buf)->ip_checksum = ip_hdr.ip_checksum; ret = sendto(s, buf, sizeof(IPV4_HDR)+sizeof(TCP_HDR), 0, (struct sockaddr*) &dst_addr,sizeof(dst_addr)); //发送Fin报文 if (ret == SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: Fin send error %d!\r\n",Port_Dst,WSAGetLastError()); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } int src_len = sizeof(src_addr); ret = recvfrom(s, (char *)&recv_buf, sizeof(recv_buf), 0, (SOCKADDR *)&src_addr, &src_len); //接受回复 if (ret==SOCKET_ERROR) { sprintf(msg_buf,"Scan Port %d: Fin Reply recv error %d!\r\n",Port_Dst,WSAGetLastError()); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } int recv_len = ret; //回复报文分析 if (recv_len < sizeof(IPV4_HDR)){ sprintf(msg_buf,"Scan Port %d: Syn Reply IPHEADER error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return (-1); } IPV4_HDR *ip_hdr_recv = (IPV4_HDR *)recv_buf; //回复报文IP头分析 int ip_hdr_recv_len = ip_hdr_recv->ip_verlen & 0x0F; ip_hdr_recv_len = (ip_hdr_recv_len) << 2; if (checksum((u_short *)ip_hdr_recv, ip_hdr_recv_len)) { sprintf(msg_buf,"Scan Port %d: Syn Reply checksum error!\r\n",Port_Dst); strcat(msg,msg_buf); WSACleanup(); return(-1); } if (recv_len < (ip_hdr_recv_len + sizeof(TCP_HDR))) { sprintf(msg_buf,"Scan Port %d: Syn Reply TCPHEADER recv error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return(-1); } TCP_HDR *tcp_hdr_recv = (TCP_HDR *) (recv_buf + ip_hdr_recv_len); //回复报文TCP头分析 if ((tcp_hdr_recv->th_flag & 0x04) > 0){ //是否RST包 sprintf(msg_buf,"Scan Port %d: Syn connect is rejected!\r\n",Port_Dst); strcat(msg,msg_buf); WSACleanup(); return(-1); } if ((tcp_hdr_recv->th_flag & 0x10) == 0){ //ack位检测 sprintf(msg_buf,"Scan Port %d: Syn reply ack error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return(-1); } if (ntohl(tcp_hdr_recv->th_ack) != (SEQ+1) ){ //是否对刚发的数据报的ack sprintf(msg_buf,"Scan Port %d: Syn reply ack sequence error!\r\n",Port_Dst); strcat(msg,msg_buf); closesocket(s); WSACleanup(); return(-1); } sprintf(msg_buf,"Scan Port %d: Syn scan succeed!\r\n",Port_Dst); strcat(msg,msg_buf); WSACleanup(); return (0); } int main() { char result[500]; result[0] = ‘\0‘; char* ip_dest = "192.168.1.5"; FinScan* test = new FinScan(); test->Scan(ip_dest,25,1000,result); std::cout<<result; return 0; }
0x5: UDP ICMP端口不能到达扫描
// ICMP_ECHO.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <iostream> #include <winsock2.h> #include "ICMP_ECHO.h" #pragma comment(lib,"ws2_32.lib") void ICMP_ECHO::Init_Header(char* buf) { ICMP_HDR *icmp = NULL; char* datapart = NULL; icmp = (ICMP_HDR*)buf; //初始化ICP头 icmp->icmp_type = 8; icmp->icmp_code = 0; icmp->icmp_id = GetCurrentProcessId(); icmp->icmp_checksum = 0; icmp->icmp_sequence = 0; icmp->icmp_timestamp=GetTickCount(); datapart = buf + sizeof(ICMP_HDR); memset(datapart, ‘E‘, 32); icmp->icmp_checksum = checksum((USHORT *)buf,sizeof(ICMP_HDR)+32); } USHORT ICMP_ECHO::checksum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); } int ICMP_ECHO::Ping(char* IP_dest,u_int time_out, char* msg) { WSADATA wsd; ICMP_HDR *icmp_send = NULL; ICMP_HDR *icmp_rev = NULL; SOCKADDR_STORAGE dest; struct sockaddr_in src_addr; SOCKET s; char send_buf[sizeof(ICMP_HDR)+32]; char recv_buf[sizeof(IPV4_HDR)+sizeof(ICMP_HDR)+32]; char tempMsg[50]; tempMsg[0] = ‘\0‘; if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { sprintf(tempMsg,"Ping %s: WSAStartup() failed: %d\r\n", IP_dest,WSAGetLastError()); strcat(msg,tempMsg); return -1; } s = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); //ICMP协议socket if (s==INVALID_SOCKET) { sprintf(tempMsg,"Ping %s:无法建立套结字 \r\n", IP_dest); strcat(msg,tempMsg); WSACleanup(); return (-1); } //设置套接字的超时时间 int ret_code = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&time_out, sizeof(time_out)); if (ret_code==SOCKET_ERROR) { sprintf(tempMsg,"Ping %s:ICMP设置接收超时时间失败 \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return (-1); } ret_code = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&time_out, sizeof(time_out)); if (ret_code==SOCKET_ERROR) { sprintf(tempMsg,"Ping %s:ICMP设置发送超时时间失败 \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return (-1); } ((SOCKADDR_IN *)&dest)->sin_family = AF_INET; //初始化目标地址 ((SOCKADDR_IN *)&dest)->sin_port = htons(0); ((SOCKADDR_IN *)&dest)->sin_addr.s_addr = inet_addr(IP_dest); Init_Header(send_buf); //初始化ICMP报文 ret_code = sendto(s,send_buf,sizeof(ICMP_HDR)+32,0,(SOCKADDR *)&dest,sizeof(dest)); //发送ICMP报文 if (ret_code==SOCKET_ERROR) { sprintf(tempMsg,"Ping %s:ICMP发送失败 \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } int src_len = sizeof(src_addr); ret_code = recvfrom(s, (char *)&recv_buf, sizeof(recv_buf), 0, (SOCKADDR *)&src_addr, &src_len); //接受回复 if (ret_code==SOCKET_ERROR) { sprintf(tempMsg,"Ping %s: 主机关机 \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } int recv_len = ret_code; //分析回复报文 if (recv_len < sizeof(IPV4_HDR)){ sprintf(tempMsg,"Ping %s: ICMP Reply ERROR! \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } IPV4_HDR *ip_hdr_recv = (IPV4_HDR *)recv_buf; //IP头分析 int ip_hdr_recv_len = ip_hdr_recv->ip_verlen & 0x0F; ip_hdr_recv_len = (ip_hdr_recv_len) << 2; if (checksum((u_short *)ip_hdr_recv, ip_hdr_recv_len)) { sprintf(tempMsg,"Ping %s: ICMP Reply IP header checksum is wrong! \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } if (recv_len < (ip_hdr_recv_len + sizeof(ICMP_HDR))) { sprintf(tempMsg,"Ping %s: ICMP Reply len 不正确! \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } ICMP_HDR *icmp_hdr_recv = (ICMP_HDR *) (recv_buf + ip_hdr_recv_len); //ICMP头分析 if (icmp_hdr_recv->icmp_type !=0 ){ //是否对ping回复 sprintf(tempMsg,"Ping %s: ICMP Reply type 不正确! \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } if (icmp_hdr_recv->icmp_id != (u_short)GetCurrentProcessId() ){ //是否对本进程回复 sprintf(tempMsg,"Ping %s: ICMP Reply 不是返回本进程的! \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return(-1); } sprintf(tempMsg,"Ping %s: 主机开机! \r\n", IP_dest); strcat(msg,tempMsg); closesocket(s); WSACleanup(); return 0; }
Relevant Link:
http://www.360doc.com/content/09/0429/22/130577_3317566.shtml http://coder.beitown.com/archives/814 http://blog.csdn.net/liujiayu2/article/details/41498563 http://help.aliyun.com/knowledge_detail/5974678.html http://read.pudn.com/downloads75/sourcecode/hack/scanner/278202/PortScan/PortScan/TCPConnect.cpp__.htm http://read.pudn.com/downloads75/sourcecode/hack/scanner/278202/PortScan/PortScan/SYNScan.cpp__.htm http://read.pudn.com/downloads75/sourcecode/hack/scanner/278202/PortScan/PortScan/FinScan.cpp__.htm http://read.pudn.com/downloads75/sourcecode/hack/scanner/278202/PortScan/PortScan/ICMP_ECHO.cpp__.htm
3. 根据Banner判断目标端口是否为FTP服务
对于FTP服务器这种有十几年历史的应用来说,它的碎片化非常严重,独立运行的 FTP、serv-U、filezila、IIS FTP种类繁多,不适合采取mysql那种通过db driver的方式进行模拟登录尝试,相反,所有的FTP服务器都会返回格式基本相同的Banner信息,根据Banner进行服务器识别相对可行
0x1: FTP协议工作机制
FTP协议采用C/S模式,其实现包括服务器端和客户端。服务器端内含
1. FTP协议控制模块: FTP协议控制模块用于和客户端进行控制指令的交互,用于完成传输协商 2. 数据传输模块: 数据传输模块,则用于和客户端进行实际数据的传输
FTP的服务器进程由两大部分组成
1. 主进程: 接受来自客户端的FTP连接请求,其工作流程为 1) 打开端口: 默认情况下是21(可配置) 2) 等待客户连接请求 3) 启动从属进程来处理客户进程发来的请求(主进程与从属进程的处理是并发进行的) 4) 从属进程对客户进程的请求处理完毕后即终止 5) 回到等待状态 2. 从属进程: 处理单个请求
FTP的两个连接
1. 控制连接: FTP客户发出的传送请求通过控制连接发送给服务器端的控制进程,控制连接不用来传送用户数据,它只传输少量字节的控制交互数据 2. 数据连接: 用来连接客户端和服务器端的数据传送进程
FTP的两种工作方式
1. Standard(Active 主动方式) PORT命令: 用于设定主动传输方式 1) FTP客户端首先和FTP Server的TCP 21端口建立连接 2) 通过这个通道发送命令,客户端需要接收数据的时候在这个通道上发送PORT命令 3) PORT命令包含客户端接收数据端口的信息 4) 在传送数据的时候,服务器端通过自己的TCP 20端口发送数据 5) FTP server必须和客户端建立一个新的连接用来传送数据 2. Passive(PASV 被动方式) PASV命令: 进入被动传输方式 1) 当FTP客户端发送PASV命令的时候,FTP server打开一个随机端口并且通知客户端在这个端口上传送数据请求 2) FTP server将通过这个端口进行数据的传送,这个时候FTP server不需要主动建立一个新的和客户端之间的连接 3) 此命令要求服务器DTP在指定的数据端口侦听,进入被动接收请求的状态,参数是主机和端口地址
0x2: GetIpStatistics function
The GetTcpTable function retrieves the IPv4 TCP connection table. 我们的程序既然运行在本机,完全不必要采取Network Package Scanner的思路,虽然本地换回网卡的效率较高,但整个端口遍历过程还是太消耗CPU了,我们可以调用操作系统提供的API直接获取到本地Listening的端口列表
/* 1. pTcpTable [out]: A pointer to a buffer that receives the TCP connection table as a MIB_TCPTABLE structure. 2. pdwSize [in, out]: 1) On input, specifies the size in bytes of the buffer pointed to by the pTcpTable parameter. 2) On output, if the buffer is not large enough to hold the returned connection table, the function sets this parameter equal to the required buffer size in bytes. On the Windows SDK released for Windows Vista and later, the data type for this parameter is changed to a PULONG which is equivalent to a PDWORD. 3. bOrder [in]: A Boolean value that specifies whether the TCP connection table should be sorted. If this parameter is TRUE, the table is sorted in the order of: 1) Local IP address 2) Local port 3) Remote IP address 4) Remote port */ DWORD WINAPI GetTcpTable( _Out_ PMIB_TCPTABLE pTcpTable, _Inout_ PDWORD pdwSize, _In_ BOOL bOrder );
Return value
1. If the function succeeds, the return value is NO_ERROR. 2. If the function fails, the return value is one of the following error codes. 1) ERROR_INSUFFICIENT_BUFFER: The buffer pointed to by the pTcpTable parameter is not large enough. The required size is returned in the DWORD variable pointed to by the pdwSize parameter. This error is also returned if the pTcpTable parameter is NULL. 2) ERROR_INVALID_PARAMETER: The pdwSize parameter is NULL, or GetTcpTable is unable to write to the memory pointed to by the pdwSize parameter. 3) ERROR_NOT_SUPPORTED: This function is not supported on the operating system in use on the local system. 4) Other: Use FormatMessage to obtain the message string for the returned error.
0x3: 技术方案
FTP服务器端和客户端在开始文件传输之前会有一个建立"控制连接"的过程,包括显示欢迎语、询问用户名密码、协商传输方式等等。在建立"控制连接"的过程完成之后,FTP 服务器端和客户端就会通过协商的端口来进行数据的传输
1. 调用GetTcpTable()获取本机的LISTENING状态的端口列表 2. 调用socket遍历这些端口列表 3. 和目标FTP Server建立连接之后,FTP Server会立刻通过控制端口(默认是21端口)返回一个欢迎标语,例如 1) 220 Microsoft FTP Service 4. 其中220、FTP这种标识字段都可以作为Banner识别标识,如果命中则说明当前端口为FTP Server端口 5. 主机上可能同时存在多个FTP Server
0x4: Code
//ftp port scan #include <WinSock.h> #include <windows.h> #include <iphlpapi.h> #pragma comment(lib,"ws2_32.lib") #pragma comment(lib, "iphlpapi.lib") #define PORTSCAN_MAX 9999 #define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) #define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) /* Note: could also use malloc() and free() */ //dynamic scan ftp listening port int ftpPortScan( std::vector<int>& portList ) { // Declare and initialize variables PMIB_TCPTABLE pTcpTable; DWORD dwSize = 0; DWORD dwRetVal = 0; int _port; int i; //initialize socket dynamic lib WSADATA wsaData; if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return 0; } SOCKET sClient = NULL; SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; //scan localhost addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); char recvBuf[50]; char sendBuf[50]; sprintf(sendBuf, "LIST\n"); CString csBanner; //initialize MIB_TCPTABLE strut pTcpTable = (MIB_TCPTABLE *) MALLOC(sizeof (MIB_TCPTABLE)); if (pTcpTable == NULL) { return 0; } dwSize = sizeof (MIB_TCPTABLE); // Make an initial call to GetTcpTable to get the necessary size into the dwSize variable if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, TRUE)) == ERROR_INSUFFICIENT_BUFFER) { FREE(pTcpTable); pTcpTable = (MIB_TCPTABLE *) MALLOC(dwSize); if (pTcpTable == NULL) { return 0; } } // Make a second call to GetTcpTable to get the actual data we require if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, TRUE)) == NO_ERROR) { //loop the port list for (i = 0; i < (int)pTcpTable->dwNumEntries; i++) { //we only foucus on LISTEN state if( pTcpTable->table[i].dwState == MIB_TCP_STATE_LISTEN) { if (sClient == NULL) { //every tcp connect try, you need create a new socket sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sClient == INVALID_SOCKET) { return 0; } //set recv timeout limit int nNetTimeout = 2000; //2秒 setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int)); } _port = ntohs((u_short)pTcpTable->table[i].dwLocalPort); //std::cout << "_port: " << _port << std::endl; addrServ.sin_port = htons(_port); //连接服务器 if(connect(sClient, (sockaddr *)&addrServ, sizeof(sockaddr)) == SOCKET_ERROR) { //connect faild } else { //check ftp banner //1. send a byte to get target response send(sClient, sendBuf, strlen(sendBuf) + 1, 0); //2. get response recv(sClient, recvBuf, 50,0); csBanner = (CString)recvBuf; csBanner.MakeLower(); //3. check banner characteristics if( csBanner.Find(_T("220")) != -1 || csBanner.Find(_T("ftp")) != -1 ) { portList.push_back(_port); } //close this connection try closesocket(sClient); sClient = NULL; memset(recvBuf, 0, sizeof(char)); } } } } else { FREE(pTcpTable); return 0; } if (pTcpTable != NULL) { FREE(pTcpTable); pTcpTable = NULL; } WSACleanup(); return 0; }
0x5: 需要注意的问题
1. 使用socket进行探测型握手交互,获取banner的时候,显式地向目标端口发送几个字符(包含换行符),可以加速激发目标端口返回response 2. 对于很多端口来说,处于对程序自身的保护,只有严格遵守协议的数据包才会得到响应,这影响到了我们获取banner的进度,因此,我们需要对recv设置超时机制(例如2s),如果目标端口超过2s了还没响应,则肯定不是ftp server,可以直接跳过
Relevant Link:
file:///C:/Users/zhenghan.zh/Downloads/A200809-59.pdf http://www.cplusplus.com/forum/unices/3678/ http://www.codeproject.com/Articles/4173/Windows-netstat-application http://stackoverflow.com/questions/15707035/windows-api-to-get-netstat-s-statistics https://msdn.microsoft.com/zh-cn/library/windows/desktop/aa366026(v=vs.85).aspx http://blog.csdn.net/newger/article/details/2459113 http://venglu.blog.51cto.com/2657190/1185216 http://blog.csdn.net/yztgx/article/details/250291 https://zh.wikipedia.org/wiki/Vector_(STL) https://software.intel.com/zh-cn/blogs/2011/08/10/c-vector
4. 基于Socket TCP FTP连接、FTP登录控制协议的自动化弱密码检测
0x1: 技术方案
对于客户端而言,连接服务器成功后需要发送用户名等验证信息到服务器进行验证登录
1. 构造验证信息: 在FTP中,验证信息是以命令字符串的形式发送到服务器的。首先,验证信息用户名 sprintf(sendBuf, "USER %s\r\n", "test"); 2. 如果目标端口是FTP Server在监听,则会返回: 331 User name okay, need password这种字符串 3. 继续构造密码: sprintf(send_buf,"PASS %s\r\n",password); 4. 如果用户使用匿名登录方式登录FTP服务器,则用户名使用默认的anonymous,密码可以是自己的邮箱。那么命令流为"USER"+anonymous+"PASS"+xxxx@163.com 5. 客户端在接收到服务器返回的信息以后,我们需要将返回的信息内容进行分析,通过判断返回码的不同判断当前帐号/密码组合是否正确
0x2: Code
//从返回信息中获取状态码 int getStateCode(char* buf) { int num; CString csBuf = (CString)buf; CString number; number = csBuf.Mid(0, 3).GetString(); num = _ttoi((TCHAR *)number.GetString()); return num; } //通过控制socket执行FTP命令 bool executeFTPCmd(SOCKET controlSocket, char* buf, int len, int stateCode) { char recvBuf[200]; send(controlSocket, buf, len, 0); memset(recvBuf, 0, sizeof(recvBuf)); recv(controlSocket, recvBuf, sizeof(recvBuf), 0); if(getStateCode(recvBuf) == stateCode) { return true; } else { return false; } } //dynamic scan ftp listening port int ftpPortScan( std::vector<int>& portList ) { // Declare and initialize variables PMIB_TCPTABLE pTcpTable; DWORD dwSize = 0; DWORD dwRetVal = 0; int _port; int i; //initialize socket dynamic lib WSADATA wsaData; if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return 0; } SOCKET sClient = NULL; SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; //scan localhost addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); char socketBuf[200]; CString csBanner; //initialize MIB_TCPTABLE strut pTcpTable = (MIB_TCPTABLE *) MALLOC(sizeof (MIB_TCPTABLE)); if (pTcpTable == NULL) { return 0; } dwSize = sizeof (MIB_TCPTABLE); // Make an initial call to GetTcpTable to get the necessary size into the dwSize variable if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, TRUE)) == ERROR_INSUFFICIENT_BUFFER) { FREE(pTcpTable); pTcpTable = (MIB_TCPTABLE *) MALLOC(dwSize); if (pTcpTable == NULL) { return 0; } } // Make a second call to GetTcpTable to get the actual data we require if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, TRUE)) == NO_ERROR) { //loop the port list for (i = 0; i < (int)pTcpTable->dwNumEntries; i++) { //we only foucus on LISTEN state if( pTcpTable->table[i].dwState == MIB_TCP_STATE_LISTEN) { if (sClient == NULL) { //every tcp connect try, you need create a new socket sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sClient == INVALID_SOCKET) { return 0; } //set recv timeout limit int nNetTimeout = 2000; //2秒 setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int)); } _port = ntohs((u_short)pTcpTable->table[i].dwLocalPort); addrServ.sin_port = htons(_port); //连接服务器 if(connect(sClient, (sockaddr *)&addrServ, sizeof(sockaddr)) == SOCKET_ERROR) { //connect faild } else { //1. get response(ftp server banner) memset(socketBuf, 0, sizeof(socketBuf)); recv(sClient, socketBuf, sizeof(socketBuf), 0); //2. check banner characteristics if(getStateCode(socketBuf) == 220) { //3. send USER to check if ftp memset(socketBuf, 0, sizeof(socketBuf)); sprintf(socketBuf, "USER %s\r\n", "test"); //4. if target is ftp, you will get: 331 User name okay, need password if( executeFTPCmd(sClient, socketBuf, strlen(socketBuf) + 1, 331) == true) { portList.push_back(_port); } } //close this connection try closesocket(sClient); sClient = NULL; } } } } else { FREE(pTcpTable); return 0; } if (pTcpTable != NULL) { FREE(pTcpTable); pTcpTable = NULL; } closesocket(sClient); sClient = NULL; WSACleanup(); return 0; } else if(action == "--ftpPwdcheck") { //json class JsonEasy JsonResult = JsonEasy(); JsonResult.jRoot["isvul"] = 0; int Userlength, Pwdlength; //initialize socket dynamic lib WSADATA wsaData; if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { return 0; } SOCKET sClient = NULL; SOCKADDR_IN addrServ; addrServ.sin_family = AF_INET; //scan localhost addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); char recvBuf[200]; char sendBufUser[100]; char sendBufPwd[100]; int _port; int i, j; int found = 0; int isAnonymous = 0; Userlength = sizeof(windowsFtpUser) / sizeof(windowsFtpUser[0]); Pwdlength = sizeof(windowsFtpPassword) / sizeof(windowsFtpPassword[0]); std::vector<int> portList; //get ftp listening port lists ftpPortScan(portList); for ( std::vector<int>::iterator it = portList.begin(); it != portList.end(); it++) { //initialize found = 0; isAnonymous = 0; //try to login ftp server if (sClient == NULL) { //every tcp connect try, you need create a new socket sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sClient == INVALID_SOCKET) { return 0; } //set recv timeout limit int nNetTimeout = 2000; //2秒 setsockopt(sClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int)); } _port = (*it); char ssPort[25]; _itoa(_port, ssPort, 10); addrServ.sin_port = htons(_port); //连接服务器 if(connect(sClient, (sockaddr *)&addrServ, sizeof(sockaddr)) == SOCKET_ERROR) { //connect faild } else { //1. ftp server say hello first memset(recvBuf, 0, sizeof(recvBuf)); recv(sClient, recvBuf, sizeof(recvBuf), 0); //anonymous check memset(sendBufUser, 0, sizeof(sendBufUser)); sprintf(sendBufUser, "USER %s\r\n", "anonymous"); memset(sendBufPwd, 0, sizeof(sendBufPwd)); sprintf(sendBufPwd, "PASS %s\r\n", "anonymous@qq.com"); if( executeFTPCmd(sClient, sendBufUser, strlen(sendBufUser), 331) == true ) { if( executeFTPCmd(sClient, sendBufPwd, strlen(sendBufPwd), 230) == true ) { found = 1; isAnonymous = 1; JsonResult.execinfoItemFTPpwd["port"] = ssPort; JsonResult.execinfoItemFTPpwd["is_anonymous"] = 1; JsonResult.execinfoItemFTPpwd["banner"] = recvBuf; JsonResult.execinfoItemFTPpwd["username"] = "anonymous"; JsonResult.execinfoItemFTPpwd["password"] = "anonymous@qq.com"; JsonResult.jRoot["isvul"] = 1; JsonResult.execinfoArrayObj.append(JsonResult.execinfoItemFTPpwd); std::wcout << "_port: " << _port << " anonymous login success!" << std::endl; } } //start to user/pwd crack test for(i = 0; i < Userlength; i++) { if(isAnonymous == 1) { break; } //2. set and send username memset(sendBufUser, 0, sizeof(sendBufUser)); sprintf(sendBufUser, "USER %s\r\n", windowsFtpUser[i]); for(j = 0; j < Pwdlength; j++ ) { //3. set and send password memset(sendBufPwd, 0, sizeof(sendBufPwd)); sprintf(sendBufPwd, "PASS %s\r\n", windowsFtpPassword[j]); //everytime need send USER:XXX fisrt if( executeFTPCmd(sClient, sendBufUser, strlen(sendBufUser), 331) == true ) { if( executeFTPCmd(sClient, sendBufPwd, strlen(sendBufPwd), 230) == true ) { found = 1; JsonResult.execinfoItemFTPpwd["port"] = ssPort; JsonResult.execinfoItemFTPpwd["is_anonymous"] = 0; JsonResult.execinfoItemFTPpwd["banner"] = recvBuf; JsonResult.execinfoItemFTPpwd["username"] = windowsFtpUser[i]; JsonResult.execinfoItemFTPpwd["password"] = windowsFtpPassword[j]; JsonResult.jRoot["isvul"] = 1; JsonResult.execinfoArrayObj.append(JsonResult.execinfoItemFTPpwd); std::wcout << "_port: " << _port << " " << windowsFtpUser[i] << " " << windowsFtpPassword[j] << " login success!" << std::endl; break; } else { //password wrong, restart from USER:XXX } } } } if(found == 0) { JsonResult.execinfoItemFTPpwd["port"] = ssPort; JsonResult.execinfoItemFTPpwd["is_anonymous"] = 0; JsonResult.execinfoItemFTPpwd["banner"] = recvBuf; JsonResult.execinfoItemFTPpwd["username"] = "Your Paaword Is Strong Enough"; JsonResult.execinfoItemFTPpwd["password"] = "Your Paaword Is Strong Enough"; JsonResult.execinfoArrayObj.append(JsonResult.execinfoItemFTPpwd); std::wcout << "_port: " << _port << " password is strong!" << std::endl; } } //close this connection try closesocket(sClient); sClient = NULL; } //all check is over, we can exit closesocket(sClient); sClient = NULL; WSACleanup(); //wrap the json JsonResult.jRoot["execinfo"] = JsonResult.execinfoArrayObj; JsonResult.jRoot["moduleid"] = ftpPwdcheck; JsonResult.jRoot["execresult"] = 1; //output finnal result std::string out = JsonResult.jRoot.toStyledString(); std::cout << out << std::endl; return 0; }
0x3: 遇到的问题
1. 在探测FTP弱密码的时候,意外发现了: VMware Authentication Daemon Version 1.0, ServerDaemonProtocol:SOAP, MKSDisplayProtocol:VNC VMware在912端口提供了类似FTP的服务,而且直接复用了操作系统的帐号密码体系,这可能导致黑客通过912端口爆破目标操作系统的系统密码 2. IP和端口必须采取网络序列 addrServ.sin_port = htons(_port); 3. 在适配FTP Server的时候,不同的FTP Server对指令集的支持都是不同的 4. FTP Server为了防止溢出,对Client传输的"命令行"大小作了限制,如果客户端发送的命令行过长,FTP Server会报500错误 5. FTP USER/PASS登录验证的过程需要socket send/recv交互,涉及到网络协议栈的大量操作,因此导致枚举速度较慢,可以考虑使用多线程并发处理来加快枚举速度,但这同时也会加剧对系统CPU的消耗 6. 在实际的攻防场景中,服务器运维人员往往采用了网络访问限制、边界网关保护等措施,我们通过本地猜解得到的存在弱密码、匿名登录的机器从外网来看却未必能访问,这对检测的精确度造成一定的影响
0x4: 兼容性测试
1. windows 7: 测试通过 2. windows server 2003: 测试通过 3. windows server 2008: 测试通过 4. windows server 2012: 测试通过 5. 非IIS FTP、IIS FTP测试通过
Relevant Link:
http://book.51cto.com/art/201007/215407.htm http://book.2cto.com/201212/10925.html http://dkw.com.cn/user/showfaqs.asp?newsid=142 http://bars.51.hk/help/zh-CN/help_174.html https://codeinsecurity.wordpress.com/2013/03/13/password-cracking-with-vmware-auth-daemon/ http://zhanyonhu.blog.163.com/blog/static/161860442010411220532/ https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx http://blog.csdn.net/cjc211322/article/details/43416179 http://www.ibm.com/developerworks/cn/linux/l-cn-socketftp/
Copyright (c) 2015 LittleHann All rights reserved