FTP Account Weak Password Detection By Dynamic port scan And Socket Connection Try

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

时间: 2024-10-18 23:18:29

FTP Account Weak Password Detection By Dynamic port scan And Socket Connection Try的相关文章

SQLSERVER Account Weak Password Detection Automatic By System API

catalog 1. DB暴力破解方式 2. DB弱密码入侵向量 3. SQLAPI++ 4. C++ ADO 5. C++ ODBC 6. tiodbc - TinyODBC C++ Wrapper for ODBC API 7. 基于API调用的自动化检测 1. DB暴力破解方式 0x1: 利用存储过程 核心思想,就是存储帐号密码的master.dbo.sysxlogins表和密码比较存储过程pwdcompare alter proc p_GetPassword2 @username sys

Linux System Account SSH Weak Password Detection Automatic By System API

catalog 1. Linux弱口令攻击向量 2. Linux登录验证步骤 3. PAM 4. 弱口令风险基线检查 1. Linux弱口令攻击向量 0x1: SSH密码暴力破解 hydra -l root -P /root/passwdCracker/password.lst -t 16 -vV -e ns 112.124.51.10 ssh 对于Linux系统来说,从外部破解系统密码的途径只有SSH这一条路,攻击者必须借助网络进行密码猜解尝试 0x2: Linux SSH空口令帐号 1. 通

Windows System Account、Linux System Account SSH Weak Password Detection Automatic By System API

catalog 0. 引言 1. windows系统账户弱密码检测 2. windows弱密码检测遇到的问题 3. linux系统账户弱密码检测 0. 引言 windows.linux密码暴力破解.身份认证.密码策略加固的相关知识,请参阅另外两篇文章 http://www.cnblogs.com/LittleHann/p/3662161.html http://www.cnblogs.com/LittleHann/p/4515498.html 今天我们来讨论一下如何在客户端通过系统API实现弱密

C# 消息队列-MSMQ

MQ是一种消息中间件技术,所以它能够支持多种类型的语言开发,同时也是跨平台的通信机制,也就是说MQ支持将信息转化为XML或者JSon等类型的数据存储到消息队列中,然后可以使用不同的语言来处理消息队列中的消息,这样就很容易的做到了信息的通信,同时也为信息的通信起到了缓冲的作用,经常会在金融项目中使用这种通信机制. 1 MQ安装 打开控制面板-"添加/删除程序" – "添加/删除 windows组件"步骤安装MSMQ. MSMQ可以安装为工作组模式或域模式.如果安装程序

阿里云服务器cent0S安装web环境PHP+MySql+Nginx,给nginx添加网站

下载一键安装包: http://market.aliyun.com/product/12-121590002-cmgj000262.html?spm=5176.7150518.1996836753.5.ZoE32o 在服务器安装rz命令 yum install lrzsz rz    选择要上传文件sh.zip 安装解压命令: yum install unzip    #本机已经安装了.所以这里不安装 解压 unzip -x sh.zip    #解压后脚本的权限不够 chmod -R 755

ZooKeeper-3.3.4集群安装配置(转载)

ZooKeeper是一个分布式开源框架,提供了协调分布式应用的基本服务,它向外部应用暴露一组通用服务——分布式同步(Distributed Synchronization).命名服务(Naming Service).集群维护(Group Maintenance)等,简化分布式应用协调及其管理的难度,提供高性能的分布式服务.ZooKeeper本身可以以Standalone模式安装运行,不过它的长处在于通过分布式ZooKeeper集群(一个Leader,多个Follower),基于一定的策略来保证Z

node基础14:连接数据库

1.连接数据库 node本身没有连接数据库的功能,需要安装一些中间库来支持.本节,我讲学习连接mysql. 首先需要安装mysql,需要去mysql官网下载mysql,并安装.也需要安装一个mysql可视化管理工具,这个也可以在mysql官网下载. 第一步: 去官网下载好mysql,并且安装.如果是mac os 系统,必须记下localhost服务器的初始密码(截图),安装好了之后,新建一个database,这时候提醒你,需要更改初始密码.更改初始密码之后,你才可以进行mysql指令的操作. 第

Redis+Django(Session,Cookie、Cache)的用户系统

转自 http://www.cnblogs.com/BeginMan/p/3890761.html 一.Django authentication django authentication 提供了一个便利的user api接口,无论在py中 request.user,参见 Request and response objects .还是模板中的 {{user}} 都能随时随地使用,如果从web开发角度来看,其实无非就是cookie与session的运用. 在项目首页,在登陆和注销状态下分别输出

Java利用 ganymed-ssh2-build.jar来上传文件到linux以及下载linux文件以及执行linux shell命令

package api; import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.IOException;import java.io.InputStream;import org.apache.log4j.*;import ch.ethz.ssh2.Connection;import ch.ethz.ssh2.SCPClient;import ch.ethz.ssh2.Session;impor