企业安全建设—网络镜像流量分析的一些方法与思路
elknot 企业安全 15262浏览 · 2017-12-13 08:15

author:elknot@360corpsec

广告位招租


0x00 概述

这篇稿子其实被约了很长时间了,因为一直没时间去写,最近由于年底了,需要整理一些东西出来,所以现在就先写到这里了。
由于本人本科学的网络工程,所以对网络协议这一块的了解还算略懂,网络协议分析其实在网络安全领域里面算是一个比较古老的技术了,原理其实就是我们大学计算机网络基础课里面学的。但是实际知道企业里面,尤其是网络设施复杂,数据量较大的IDC网络,协议分析是比较痛苦的,一方面是性能问题,另一方面是储存和可视化问题。
首先先来点科普,网络镜像流量分析需要完成的几项工作:

  1. 端口镜像
  2. 数据包捕获和抓取
  3. 数据包分析
  4. 会话还原和重组
  5. 应用层协议分析
  6. 可视化展示

其实这个实验环境是可以在家模拟的,搞一个Mikrotik RouterBoard然后做端口镜像倒到数据包采集板里面,之后去分析就OK了,这里就需要简单的来说一下网络方面的知识了。(基础知识扎实的可以跳过)
由于我们现在的网络协议栈走的都是TCP/IP协议栈,按照TCP/IP协议栈的规范,TCP/IP协议栈的模型是四层模型(主机接口层、网络层、传输层和应用层,有些地方因为教材版本的差异主机接口层会被描述为数据链路层、传输层会被描述为运输层,但是实际不影响其功能的描述)。

  1. 主机接口层层是物理传输通道,可使用多种传输介质传输,可建立在任何物理传输网上。比如光纤、双绞线等
  2. 网络层:其主要功能是要完成网络中主机间“分组”(Packet)的传输。
  3. 传输层:其主要任务是向上一层提供可靠的端到端(End-to-End)服务,确保“报文”无差错、有序、不丢失、无重复地传输。它向高层屏蔽了下层数据通信的细节,是计算机通信体系结构中最关键的一层。
  4. 应用层:应用层确定进程间通信的性质,以满足用户的需要。

他们的数据存放方式依次为:(比特流、帧)、分组、会话、应用。

科普时间结束,回归正题,企业内网由于众所周知的原因吧,所以本文上所有的代码全部基于实验代码(包捕获方法采用libpcap,包解析方法采用libnids),与实际生产环境(包捕获方法推荐DNA模式的PF_RING,包分析的方法建议采用nDPI)要区分开,针对IDC的流量其实我们可以去设计这么一个架构来解决问题:

这个图其实大致的流程就是:用户在访问互联网业务的时候,流量经过入口路由,然后将其做镜像,使其可以直接访问业务系统,同时还能被我们分析。首先分析的时候由于数据包还是比特流的状态,所以我们需要将比特通过数据包采集器来还原成能用的东西,之后将数据移交至数据包分析服务器,对其进行深度分析,将分析结果存放至数据库,然后就是检索、规则匹配、可视化、etc一大堆常规的操作,如果有接口的话,可以尝试在DashBoard区域放置和IDC集群相关ACL的操作,直接将恶意的IP拉黑。


0x01 数据包的捕获

这里抓包我们的实验代码使用libpcap来编写,但是生产环境中应使用PF_RING,如果有更高的需求请移步Intel DPDK或者是腾讯的F-Stack。
libpcap是一个网络数据包捕获函数库,功能非常强大,Linux下著名的tcpdump就是以它为基础的。
其实libpcap使用的方法还是比较简单的,上一个简单的samplecode。

#include <pcap.h>
#include <stdio.h>
int main()
{
     char errBuf[PCAP_ERRBUF_SIZE], * device;
     device = pcap_lookupdev(errBuf);
     if(device)
     {
        printf("success: device: %s\n", device);
     }
     else
     {
        printf("error: %s\n", errBuf);
     }
     return 0;
}

使用gcc编译,需要添加编译选项:

gcc -g -W all -o test test.c -lpcap

上面这段代码的就是libpcap打开网络设备的方式,实际上我们在使用libpcap的时候要经过4个步骤:打开网络设备、设置过滤规则、捕获数据、关闭设备。
libpcap里面有几个比较重要的API:

pcap_lookupdev():函数用于查找网络设备,返回可被 pcap_open_live() 函数调用的网络设备名指针。
pcap_lookupnet():函数获得指定网络设备的网络号和掩码。
pcap_open_live(): 函数用于打开网络设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
pcap_compile(): 函数用于将用户制定的过滤策略编译到过滤程序中。
pcap_setfilter():函数用于设置过滤器。
pcap_loop():函数 pcap_dispatch() 函数用于捕获数据包,捕获后还可以进行处理,此外 pcap_next() 和 pcap_next_ex() 两个函数也可以用来捕获数据包。
pcap_close():函数用于关闭网络设备,释放资源。

我们用一段代码来熟悉一下这些API:

#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
    char errBuf[PCAP_ERRBUF_SIZE], * devStr;

    /* get a device */
    devStr = pcap_lookupdev(errBuf);

    if(devStr)
    {
        printf("success: device: %s\n", devStr);
    }
    else
    {
        printf("error: %s\n", errBuf);
        exit(1);
    }
    /* open a device, wait until a packet arrives */
    pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
     if(!device)
     {
        printf("error: pcap_open_live(): %s\n", errBuf);
        exit(1);
    }
    /* wait a packet to arrive */
    struct pcap_pkthdr packet;
    const u_char * pktStr = pcap_next(device, &packet);

    if(!pktStr)
    {
        printf("did not capture a packet!\n");
        exit(1);
    }
    printf("Packet length: %d\n", packet.len);
    printf("Number of bytes: %d\n", packet.caplen);
    printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec)); 
    pcap_close(device);
    return 0;
}

这个其实就是一个抓包的简单实现,实际上我们会一直抓包,而不是抓一个,同时要把数据包里面的内容进行输出,所以我们这里就需要循环捕获数据包,这时候就需要编写以下的代码来完成操作。

#include <stdio.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdlib.h>

#define BUFSIZE 1514

struct ether_header
{
    unsigned char ether_dhost[6];   //目的mac
    unsigned char ether_shost[6];   //源mac
    unsigned short ether_type;      //以太网类型
};

/*******************************回调函数************************************/
void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)
{
    unsigned char *mac_string;              //
    struct ether_header *ethernet_protocol;
    unsigned short ethernet_type;           //以太网类型
    printf("----------------------------------------------------\n");
    printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //转换时间
    ethernet_protocol = (struct ether_header *)packet_content;

    mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac地址
    printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));
    mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac
    printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));

    ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网的类型
    printf("Ethernet type is :%04x\n",ethernet_type);
    switch(ethernet_type)
    {
        case 0x0800:printf("The network layer is IP protocol\n");break;//ip
        case 0x0806:printf("The network layer is ARP protocol\n");break;//arp
        case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp
        default:break;
    }
    usleep(800*1000);
}

int main(int argc, char *argv[])
{
    char error_content[100];    //出错信息
    pcap_t * pcap_handle;
    unsigned char *mac_string;              
    unsigned short ethernet_type;           //以太网类型
    char *net_interface = NULL;                 //接口名字
    struct pcap_pkthdr protocol_header;
    struct ether_header *ethernet_protocol;

    //获取网络接口
    net_interface = pcap_lookupdev(error_content);
    if(NULL == net_interface)
    {
        perror("pcap_lookupdev");
        exit(-1);
    }

    pcap_handle = pcap_open_live(net_interface,BUFSIZE,1,0,error_content);//打开网络接口

    if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)
    {
        perror("pcap_loop");
    }

    pcap_close(pcap_handle);
    return 0;
}

其实libpcap本身还支持BPF过滤器语法,由于我们这里不是一个讲编程和网络的地方,所以我们这里就不在详细阐述了,上面的代码来自于http://blog.csdn.net/tennysonsky/article/details/44811899 ,大家可以去这里看详细的libpcap的使用。


0x02 数据包分析

刚刚只进行了数据包捕获的模块,但是实际上还会进行分析详细的数据包和流量,所以这时候我们需要用到另外一个库,另一个库叫做libnids,也是一个比较常见的库。 libnids的英文意思是 Network Intrusion Detect System library,即网络入侵监测系统函数库。它是在前面介绍的两种C函数接口库libnet和libpcap的基础上开发的,封装了开发NIDS所需的许 多通用型函数。linids提供的接口函数监视流经本地的所有网络通信,检查数据包等。除此之外,还具有重组TCP数据段、处理IP分片包和监测TCP端 口扫描的功能。利用libnids接口函数库,NIDS开发者不需要再编写底层的网络处理代码,只需专注于NIDS本身功能的实现即可。
在使用libnids对数据包进行重组和会话还原的时候,IP分片重组和TCP会话重组是必须得知道的。首先先来说IP分片,在libnids中,我们需要定义void ip_frag_func(struct ip * a_packet)作为回调函数,在调用nids_init()函数初始化后,使用nids的函数进行注册nids_register_ip_frag(ip_frag_func),这样回调函数ip_frag_func会在适当的时候由libnids调用,参数a_packet指针将指向接收到的数据报。
我们通过一段代码来说明一下libnids如何使用:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include "nids.h"

#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x))

// struct tuple4 包含TCP连接的地址和端口号
// 下面的辅助函数生成一个类似10.0.0.1,1024,10.0.0.2,23的字符串

char *adres (struct tuple4 addr)
{
    static char buf[256];
    strcpy (buf, int_ntoa (addr.saddr));
    sprintf (buf + strlen (buf), ",%i,", addr.source);
    strcat (buf, int_ntoa (addr.daddr));
    sprintf (buf + strlen (buf), ",%i", addr.dest);
    return buf;
}

void tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)
{
    char buf[1024];
    strcpy (buf, adres (a_tcp->addr)); // we put conn params into buf
    if (a_tcp->nids_state == NIDS_JUST_EST)
    {
        // 由a_tcp描述的连接已经建立
        // 这里我们决定是否希望跟踪这个流
        // 例子条件: if (a_tcp->addr.dest!=23) return;
        // 在本程序中我们跟踪所有的流所以。。。。。:
        a_tcp->client.collect++; // 我们需要客户端接收到的数据.......
        a_tcp->server.collect++; // 我们需要服务器接收到的数据.......
        a_tcp->server.collect_urg++; // 我们需要服务器接收到的紧急数据.......

#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT

        a_tcp->client.collect_urg++; // 如果我们不增加这个值,当紧急数据到达
        // 时我们不会被通知。
#endif
        fprintf (stderr, "%s establishedn", buf);
        return;
    }
    if (a_tcp->nids_state == NIDS_CLOSE)
    {
        // 连接已经正常结束
        fprintf (stderr, "%s closingn", buf);
        return;
    }
    if (a_tcp->nids_state == NIDS_RESET)
    {
        // 连接已经通过RST关闭。
        fprintf (stderr, "%s resetn", buf);
        return;
    }

    if (a_tcp->nids_state == NIDS_DATA)
    {
        // 新的数据已经到达,必须判断其数据流向
        // 判断其是否紧急数据
        struct half_stream *hlf;

        if (a_tcp->server.count_new_urg)
        {
            // 紧急数据的新字节已经到达
            strcat(buf,"(urgent->)");
            buf[strlen(buf)+1]=0;
            buf[strlen(buf)]=a_tcp->server.urgdata;
            write(1,buf,strlen(buf));
            return;
        }

        // 我们不必必须检查是否客户端的紧急数据已经到达
        // 因为我们没有增加a_tcp->client.collect_urg的值
        // 因此,我们还有一些正常的数据关心
        if (a_tcp->client.count_new)
        {
            //客户端的新数据
            hlf = &a_tcp->client; // 现在我们将处理hlf变量
            // 这个变量指向客户端一边的连接。
            strcat (buf, "(<-)"); // 数据的符号方向
        }
        else
        {
            hlf = &a_tcp->server; // 类似的
            strcat (buf, "(->)");
        }
        fprintf(stderr,"%s",buf); // 我们打印连接参数(saddr, daddr, sport, dport)
        // 和数据流向(-> or <-)

        write(2,hlf->data,hlf->count_new); // 我们打印最新到达的数据
    }
    return ;
}

int main ()
{
    // 这里我们可以改变Libnids的params,例如 nids_params.n_hosts=256;
    if (!nids_init ())
    {
        fprintf(stderr,"%sn",nids_errbuf);
        exit(1);
    }
    nids_register_tcp (tcp_callback);
    nids_run ();
    return 0;
}

0x02 应用层协议分析

这里我们就完成了一个简单的TCP会话重组,但是实际上我们需要分析应用层中的协议,所以我这里放两个samplecode用来分析smtp协议和pop3协议。
samplecode1:

#include <stdio.h>
#include<stdlib.h>
#include"nids.h"
#include"string.h"
#include <ctype.h>
#include<arpa/inet.h>
char ascii_string[10000];
char *char_to_ascii(char ch)
{
    char *string;
    ascii_string[0]=0;
    string = ascii_string;
    if(isgraph(ch))
    *string++=ch;
    else if(ch==' ')
        *string++=ch;
    else if(ch=='\n'||ch=='\r')
        *string++=ch;
    else
        *string++='.';
    *string=0;
    return ascii_string;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++
//    下面分析pop3协议的回调函数
//+++++++++++++++++++++++++++++++++++++++++++++++++
void pop3_protocol_callback(struct tcp_stream *pop3_connection,void **arg)
{
    int i;
    char address_string[1024];
    char content[65535];
    struct tuple4 ip_and_port;
    ip_and_port=pop3_connection->addr;
    strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));
    sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
    strcat(address_string,"<------------>");
    strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));
    sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
    strcat(address_string,"\n");
    switch (pop3_connection->nids_state)
    {
    case NIDS_JUST_EST:/*POP3客户端和服务器建立连接*/
        if(pop3_connection->addr.dest==110)
        {
            pop3_connection->client.collect++;/*pop3客户端接收数据*/
            pop3_connection->server.collect++;/*pop3服务器接收数据*/
            pop3_connection->client.collect_urg++;/*pop3客户端接收紧急数据*/
            pop3_connection->server.collect_urg++;/*pop3服务器接收紧急数据*/
            printf("%spop3客户端和服务器建立连接\n",address_string);
        }
     case NIDS_CLOSE:/*POP3协议连接正常关闭*/
        printf("---------------------------------------------------\n");
        printf("%sPOP3协议连接正常关闭\n",address_string);
        return;
     case NIDS_RESET:/*POP3协议连接被RESET关闭*/
        printf("---------------------------------------------------\n");
        printf("%sPOP3协议连接被RESET关闭\n",address_string);
        return;
     case NIDS_DATA:/*POP3协议有新的数据到达*/
        {
            char status_code[5];
            struct half_stream *hlf;
            if(pop3_connection->server.count_new_urg)/*POP3服务器收到新的紧急数据*/
            {
                printf("---------------------------------------------------\n");
                strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
                strcat(address_string,"urgent---->");
                strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
                strcat(address_string,'\n');
                address_string[strlen(address_string)+1]=0;
                address_string[strlen(address_string)];
                pop3_connection->server.urgdata;
                printf("%s",address_string);
                return;
            }
            if(pop3_connection->client.count_new_urg)/*POP3客户端收到新的紧急数据*/
            {
                printf("---------------------------------------------------\n");
                strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
                strcat(address_string,"<------urgent");
                strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
                strcat(address_string,'\n');
                address_string[strlen(address_string)+1]=0;
                address_string[strlen(address_string)];
                pop3_connection->server.urgdata;
                printf("%s",address_string);
                return;
            }
            if(pop3_connection->client.count_new)/*POP3客户端收到新的数据*/
            {
                hlf=&pop3_connection->client;
                strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
                strcat(address_string,"<------count_new");
                strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
                strcat(address_string,'\n');
                printf("---------------------------------------------------\n");
                printf("%s\n",address_string);
                memcpy(content,hlf->data,hlf->count_new);
                content[hlf->count_new]='\0';
                if(strstr(strncpy(status_code,content,4),"+OK"))
                printf("操作成功\n");
                if(strstr(strncpy(status_code,content,4),"-ERR"))
                printf("操作失败\n");
                for(i=0;i<hlf->count_new;i++)
                {
                    printf("%s",char_to_ascii(content[i]));
                }
                printf("\n");
                if(strstr(content,"\n\r.\n\r"))
                    printf("数据传输结束\n");
            }
            else/*POP3服务器收到新的数据*/
            {
                hlf=&pop3_connection->server;
                strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);
                strcat(address_string,"count_new------>");
                strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));
                sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);
                strcat(address_string,'\n');
                printf("---------------------------------------------------\n");
                printf("%s\n",address_string);
                memcpy(content,hlf->data,hlf->count_new);
                content[hlf->count_new]='\0';
                if(strstr(content,"USER"))
                    printf("邮件用户名为:\n");
                if(strstr(content,"PASS"))
                    printf("用户密码为:\n");
                if(strstr(content,"STAT"))
                    printf("返回统计资料:\n");
                if(strstr(content,"RETR"))
                    printf("获取邮件:\n");
                if(strstr(content,"DELE"))
                    printf("删除邮件:\n");
                if(strstr(content,"QUIT"))
                    printf("退出连接:\n");
                for(i=0;i<hlf->count_new;i++)
                {
                    printf("%s",char_to_ascii(content[i]));
                }
                printf("\n");
            }
        }

    default:
        break;
    }
    return;
}
int main(void)
{
    if(nids_init())/*Libnids初始化*/
    {
        printf("%s\n",nids_errbuf);
        return -1;
    }
    nids_register_tcp(pop3_protocol_callback);/*注册分析Telnet协议的回调函数*/
    nids_run();/*进入循环捕获数据包的状态*/
}

samplecode2:

#include <stdio.h>
#include<stdlib.h>
#include"nids.h"
#include"string.h"
#include <ctype.h>
#include<arpa/inet.h>
char ascii_string[10000];
char *char_to_ascii(char ch)
{
    char *string;
    ascii_string[0] = 0;
    string = ascii_string;
    if (isgraph(ch))
        *string++ = ch;
    else if (ch == ' ')
        *string++ = ch;
    else if (ch == '\n' || ch == '\r')
        *string++ = ch;
    else
        *string++ = '.';
    *string = 0;
    return ascii_string;
}
/*
=======================================================================================================================
下面是分析SMTP协议的回调函数
=======================================================================================================================
 */
void smtp_protocol_callback(struct tcp_stream *smtp_connection, void **arg)
{
    int i;
    char address_string[1024];
    char content[65535];
    char content_urgent[65535];
    struct tuple4 ip_and_port = smtp_connection->addr;
    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
    strcat(address_string, " <---> ");
    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
    strcat(address_string, "\n");
    switch (smtp_connection->nids_state)
    {
        case NIDS_JUST_EST:
            if (smtp_connection->addr.dest == 25)
            {
                /* SMTP客户端和SMTP服务器端建立连接 */
                smtp_connection->client.collect++;
                /* SMTP客户端接收数据 */
                smtp_connection->server.collect++;
                /* SMTP服务器接收数据 */
                smtp_connection->server.collect_urg++;
                /* SMTP服务器接收紧急数据 */
                smtp_connection->client.collect_urg++;
                /* SMTP客户端接收紧急数据 */
                printf("%sSMTP发送方与SMTP接收方建立连接\n", address_string);
            }
            return ;
        case NIDS_CLOSE:
            /* SMTP客户端与SMTP服务器连接正常关闭 */
            printf("--------------------------------\n");
            printf("%sSMTP发送方与SMTP接收方连接正常关闭\n", address_string);
            return ;
        case NIDS_RESET:
            /* SMTP客户端与SMTP服务器连接被RST关闭 */
            printf("--------------------------------\n");
            printf("%sSMTP发送方与SMTP接收方连接被REST关闭\n", address_string);
            return ;
        case NIDS_DATA:
            {
                /* SMTP协议接收到新的数据 */
                char status_code[4];
                struct half_stream *hlf;
                if (smtp_connection->server.count_new_urg)
                {
                    /* SMTP服务器接收到新的紧急数据 */
                    printf("--------------------------------\n");
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
                    strcat(address_string, " urgent---> ");
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
                    strcat(address_string, "\n");
                    address_string[strlen(address_string) + 1] = 0;
                    address_string[strlen(address_string)] = smtp_connection->server.urgdata;
                    printf("%s", address_string);
                    return ;
                }
                if (smtp_connection->client.count_new_urg)
                {
                    /* SMTP客户端接收到新的紧急数据 */
                    printf("--------------------------------\n");
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
                    strcat(address_string, " <--- urgent ");
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
                    sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
                    strcat(address_string, "\n");
                    address_string[strlen(address_string) + 1] = 0;
                    address_string[strlen(address_string)] = smtp_connection->client.urgdata;
                    printf("%s", address_string);
                    return ;
                }
                if (smtp_connection->client.count_new)
                {
                    /* SMTP客户端接收到新的数据 */
                    hlf = &smtp_connection->client;
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
                    strcat(address_string, " <--- ");
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
                    strcat(address_string, "\n");
                    printf("--------------------------------\n");
                    printf("%s", address_string);
                    memcpy(content, hlf->data, hlf->count_new);
                    content[hlf->count_new] = '\0';
                    if (strstr(strncpy(status_code, content, 3), "221"))
                        printf("连接中止\n");
                    if (strstr(strncpy(status_code, content, 3), "250"))
                        printf("操作成功\n");
                    if (strstr(strncpy(status_code, content, 3), "220"))
                        printf("表示服务就绪\n");
                    if (strstr(strncpy(status_code, content, 3), "354"))
                        printf("开始邮件输入,以\".\"结束\n");
                    if (strstr(strncpy(status_code, content, 3), "334"))
                        printf("服务器响应验证\n");
                    if (strstr(strncpy(status_code, content, 3), "235"))
                        printf("认证成功可以发送邮件了\n");
                    for (i = 0; i < hlf->count_new; i++)
                    {
                        printf("%s", char_to_ascii(content[i]));
                    }
                    printf("\n");
                }
                else
                {
                    /* SMTP服务器接收到新的数据 */
                    hlf = &smtp_connection->server;
                    strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
                    strcat(address_string, " ---> ");
                    strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
                    sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
                    strcat(address_string, "\n");
                    printf("--------------------------------\n");
                    printf("%s", address_string);
                    memcpy(content, hlf->data, hlf->count_new);
                    content[hlf->count_new] = '\0';
                    if (strstr(content, "EHLO"))
                        printf("HELLO命令\n");
                    if (strstr(content, "QUIT"))
                        printf("退出连接\n");
                    if (strstr(content, "DATA"))
                        printf("开始传输数据\n");
                    if (strstr(content, "MAIL FROM"))
                        printf("发送方邮件地址为\n");
                    if (strstr(content, "RCPT TO"))
                        printf("接收方邮件地址为\n");
                    if (strstr(content, "AUTH"))
                        printf("请求认证\n");
                    if (strstr(content, "LOGIN"))
                        printf("认证机制为LOGIN\n");
                    for (i = 0; i < hlf->count_new; i++)
                    {
                        printf("%s", char_to_ascii(content[i]));
                    }
                    printf("\n");
                    if (strstr(content, "\n."))
                        printf("数据传输结束\n");
                }
            }
        default:
            break;
    }
    return ;
}
/*
=======================================================================================================================
主函数
=======================================================================================================================
 */
void main()
{
    if (!nids_init())
     /* Libnids初始化 */
    {
        printf("%s\n", nids_errbuf);
        exit(1);
    }
    nids_register_tcp(smtp_protocol_callback);
    /* 注册分析TCP协议的回调函数 */
    nids_run();
    /* 进入循环捕获数据包状态 */
}

到这里的话,基本上我们对协议分析就有了个了解。


0x03 数据DPI分析

DPI(Deep Packet Inspection)是一种基于数据包的深度检测技术,针对不同的网络应用层载荷(例如HTTP、DNS等)进行深度检测,通过对报文的有效载荷检测决定其合法性。
其实安全方面使用DPI主要还是为了做流量方面的分析与取证,从而发现攻击行为和异常行为。
通常情况下,我们可以再出口流量抓一些流量样本过来使用wireshark分析就可以确定攻击与否,但是这个的话人力成本较大,如果我们有了DPI,就可以迅速的获得数据包内的信息,这样的话可以减少发现攻击的时间。
因为这种所有周知的原因,规则什么的不太好说,可以私下交流。

0x04 总结

这可能是今年这个系列的最后一篇文章了,下一篇文章打算写一个基于中间件模板的蜜罐平台的设计,估计看时间得到明年了,到时候再说吧

1 条评论
某人
表情
可输入 255