0xB6 入口流量分析平台搭建與流量分析那些事兒
文章中由於是samplecode,所以我會使用libnids+libpcap的方式去對數據包進行分析,這種方法由於libpcap抓包有瓶頸,所以實際生產環境中建議還是用nDPI+PF_RING的套路去做,原因你懂得我就不說了,另外由於應用層協議眾多,所以我們只拿SMTP/POP3協議進行測試,其他的應用層協議基本上差不多。
author:elknot@360corpsec
廣告位招租
0x00 概述
這篇稿子其實被約了很長時間了,因為一直沒時間去寫,最近由於年底了,需要整理一些東西出來,所以現在就先寫到這裡了。
由於本人本科學的網路工程,所以對網路協議這一塊的了解還算略懂,網路協議分析其實在網路安全領域裡面算是一個比較古老的技術了,原理其實就是我們大學計算機網路基礎課裡面學的。但是實際知道企業裡面,尤其是網路設施複雜,數據量較大的IDC網路,協議分析是比較痛苦的,一方面是性能問題,另一方面是儲存和可視化問題。
首先先來點科普,網路鏡像流量分析需要完成的幾項工作:- 埠鏡像
- 數據包捕獲和抓取
- 數據包分析
- 會話還原和重組
- 應用層協議分析
- 可視化展示
其實這個實驗環境是可以在家模擬的,搞一個Mikrotik RouterBoard然後做埠鏡像倒到數據包採集板裡面,之後去分析就OK了,這裡就需要簡單的來說一下網路方面的知識了。(基礎知識紮實的可以跳過)
由於我們現在的網路協議棧走的都是TCP/IP協議棧,按照TCP/IP協議棧的規範,TCP/IP協議棧的模型是四層模型(主機介面層、網路層、傳輸層和應用層,有些地方因為教材版本的差異主機介面層會被描述為數據鏈路層、傳輸層會被描述為運輸層,但是實際不影響其功能的描述)。- 主機介面層層是物理傳輸通道,可使用多種傳輸介質傳輸,可建立在任何物理傳輸網上。比如光纖、雙絞線等
- 網路層:其主要功能是要完成網路中主機間「分組」(Packet)的傳輸。
- 傳輸層:其主要任務是向上一層提供可靠的端到端(End-to-End)服務,確保「報文」無差錯、有序、不丟失、無重複地傳輸。它向高層屏蔽了下層數據通信的細節,是計算機通信體系結構中最關鍵的一層。
- 應用層:應用層確定進程間通信的性質,以滿足用戶的需要。
他們的數據存放方式依次為:(比特流、幀)、分組、會話、應用。
科普時間結束,回歸正題,企業內網由於眾所周知的原因吧,所以本文上所有的代碼全部基於實驗代碼(包捕獲方法採用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>n#include <stdio.h>nint main()n{n char errBuf[PCAP_ERRBUF_SIZE], * device;n device = pcap_lookupdev(errBuf);n if(device)n {n printf("success: device: %sn", device);n }n elsen {n printf("error: %sn", errBuf);n }n return 0;n}n
使用gcc編譯,需要添加編譯選項:
gcc -g -W all -o test test.c -lpcapn
上面這段代碼的就是libpcap打開網路設備的方式,實際上我們在使用libpcap的時候要經過4個步驟:打開網路設備、設置過濾規則、捕獲數據、關閉設備。
libpcap裡面有幾個比較重要的API:pcap_lookupdev():函數用於查找網路設備,返回可被 pcap_open_live() 函數調用的網路設備名指針。npcap_lookupnet():函數獲得指定網路設備的網路號和掩碼。npcap_open_live(): 函數用於打開網路設備,並且返回用於捕獲網路數據包的數據包捕獲描述字。對於此網路設備的操作都要基於此網路設備描述字。npcap_compile(): 函數用於將用戶制定的過濾策略編譯到過濾程序中。npcap_setfilter():函數用於設置過濾器。npcap_loop():函數 pcap_dispatch() 函數用於捕獲數據包,捕獲後還可以進行處理,此外 pcap_next() 和 pcap_next_ex() 兩個函數也可以用來捕獲數據包。npcap_close():函數用於關閉網路設備,釋放資源。n
我們用一段代碼來熟悉一下這些API:
#include <pcap.h>n#include <time.h>n#include <stdlib.h>n#include <stdio.h>nnint main()n{n char errBuf[PCAP_ERRBUF_SIZE], * devStr;nn /* get a device */n devStr = pcap_lookupdev(errBuf);nn if(devStr)n {n printf("success: device: %sn", devStr);n }n elsen {n printf("error: %sn", errBuf);n exit(1);n }n /* open a device, wait until a packet arrives */n pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);n if(!device)n {n printf("error: pcap_open_live(): %sn", errBuf);n exit(1);n }n /* wait a packet to arrive */n struct pcap_pkthdr packet;n const u_char * pktStr = pcap_next(device, &packet);nn if(!pktStr)n {n printf("did not capture a packet!n");n exit(1);n }n printf("Packet length: %dn", packet.len);n printf("Number of bytes: %dn", packet.caplen);n printf("Recieved time: %sn", ctime((const time_t *)&packet.ts.tv_sec)); n pcap_close(device);n return 0;n}n
這個其實就是一個抓包的簡單實現,實際上我們會一直抓包,而不是抓一個,同時要把數據包裡面的內容進行輸出,所以我們這裡就需要循環捕獲數據包,這時候就需要編寫以下的代碼來完成操作。
#include <stdio.h>n#include <pcap.h>n#include <arpa/inet.h>n#include <time.h>n#include <stdlib.h>nn#define BUFSIZE 1514nnstruct ether_headern{n unsigned char ether_dhost[6]; //目的macn unsigned char ether_shost[6]; //源macn unsigned short ether_type; //乙太網類型n};nn/*******************************回調函數************************************/nvoid ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)n{n unsigned char *mac_string; //n struct ether_header *ethernet_protocol;n unsigned short ethernet_type; //乙太網類型n printf("----------------------------------------------------n");n printf("%sn", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //轉換時間n ethernet_protocol = (struct ether_header *)packet_content;nn mac_string = (unsigned char *)ethernet_protocol->ether_shost;//獲取源mac地址n printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02xn",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));n mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//獲取目的macn printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02xn",*(mac_string+0),*(mac_string+1),*(mac_string+2),*(mac_string+3),*(mac_string+4),*(mac_string+5));nn ethernet_type = ntohs(ethernet_protocol->ether_type);//獲得乙太網的類型n printf("Ethernet type is :%04xn",ethernet_type);n switch(ethernet_type)n {n case 0x0800:printf("The network layer is IP protocoln");break;//ipn case 0x0806:printf("The network layer is ARP protocoln");break;//arpn case 0x0835:printf("The network layer is RARP protocoln");break;//rarpn default:break;n }n usleep(800*1000);n}nnint main(int argc, char *argv[])n{n char error_content[100]; //出錯信息n pcap_t * pcap_handle;n unsigned char *mac_string; n unsigned short ethernet_type; //乙太網類型n char *net_interface = NULL; //介面名字n struct pcap_pkthdr protocol_header;n struct ether_header *ethernet_protocol;nn //獲取網路介面n net_interface = pcap_lookupdev(error_content);n if(NULL == net_interface)n {n perror("pcap_lookupdev");n exit(-1);n }nn pcap_handle = pcap_open_live(net_interface,BUFSIZE,1,0,error_content);//打開網路介面nn if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)n {n perror("pcap_loop");n }nn pcap_close(pcap_handle);n return 0;n}n
其實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>n#include <sys/socket.h>n#include <netinet/in.h>n#include <netinet/in_systm.h>n#include <arpa/inet.h>n#include <string.h>n#include <stdio.h>n#include "nids.h"nn#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x))nn// struct tuple4 包含TCP連接的地址和埠號n// 下面的輔助函數生成一個類似10.0.0.1,1024,10.0.0.2,23的字元串nnchar *adres (struct tuple4 addr)n{n static char buf[256];n strcpy (buf, int_ntoa (addr.saddr));n sprintf (buf + strlen (buf), ",%i,", addr.source);n strcat (buf, int_ntoa (addr.daddr));n sprintf (buf + strlen (buf), ",%i", addr.dest);n return buf;n}nnvoid tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)n{n char buf[1024];n strcpy (buf, adres (a_tcp->addr)); // we put conn params into bufn if (a_tcp->nids_state == NIDS_JUST_EST)n {n // 由a_tcp描述的連接已經建立n // 這裡我們決定是否希望跟蹤這個流n // 例子條件: if (a_tcp->addr.dest!=23) return;n // 在本程序中我們跟蹤所有的流所以。。。。。:n a_tcp->client.collect++; // 我們需要客戶端接收到的數據.......n a_tcp->server.collect++; // 我們需要伺服器接收到的數據.......n a_tcp->server.collect_urg++; // 我們需要伺服器接收到的緊急數據.......nn#ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENTnn a_tcp->client.collect_urg++; // 如果我們不增加這個值,當緊急數據到達n // 時我們不會被通知。n#endifn fprintf (stderr, "%s establishedn", buf);n return;n }n if (a_tcp->nids_state == NIDS_CLOSE)n {n // 連接已經正常結束n fprintf (stderr, "%s closingn", buf);n return;n }n if (a_tcp->nids_state == NIDS_RESET)n {n // 連接已經通過RST關閉。n fprintf (stderr, "%s resetn", buf);n return;n }nn if (a_tcp->nids_state == NIDS_DATA)n {n // 新的數據已經到達,必須判斷其數據流向n // 判斷其是否緊急數據n struct half_stream *hlf;nn if (a_tcp->server.count_new_urg)n {n // 緊急數據的新位元組已經到達n strcat(buf,"(urgent->)");n buf[strlen(buf)+1]=0;n buf[strlen(buf)]=a_tcp->server.urgdata;n write(1,buf,strlen(buf));n return;n }nn // 我們不必必須檢查是否客戶端的緊急數據已經到達n // 因為我們沒有增加a_tcp->client.collect_urg的值n // 因此,我們還有一些正常的數據關心n if (a_tcp->client.count_new)n {n //客戶端的新數據n hlf = &a_tcp->client; // 現在我們將處理hlf變數n // 這個變數指向客戶端一邊的連接。n strcat (buf, "(<-)"); // 數據的符號方向n }n elsen {n hlf = &a_tcp->server; // 類似的n strcat (buf, "(->)");n }n fprintf(stderr,"%s",buf); // 我們列印連接參數(saddr, daddr, sport, dport)n // 和數據流向(-> or <-)nn write(2,hlf->data,hlf->count_new); // 我們列印最新到達的數據n }n return ;n}nnint main ()n{n // 這裡我們可以改變Libnids的params,例如 nids_params.n_hosts=256;n if (!nids_init ())n {n fprintf(stderr,"%sn",nids_errbuf);n exit(1);n }n nids_register_tcp (tcp_callback);n nids_run ();n return 0;n}n
0x02 應用層協議分析
這裡我們就完成了一個簡單的TCP會話重組,但是實際上我們需要分析應用層中的協議,所以我這裡放兩個samplecode用來分析smtp協議和pop3協議。
samplecode1:#include <stdio.h>n#include<stdlib.h>n#include"nids.h"n#include"string.h"n#include <ctype.h>n#include<arpa/inet.h>nchar ascii_string[10000];nchar *char_to_ascii(char ch)n{n char *string;n ascii_string[0]=0;n string = ascii_string;n if(isgraph(ch))n *string++=ch;n else if(ch== )n *string++=ch;n else if(ch==n||ch==r)n *string++=ch;n elsen *string++=.;n *string=0;n return ascii_string;n}nn//+++++++++++++++++++++++++++++++++++++++++++++++++n// 下面分析pop3協議的回調函數n//+++++++++++++++++++++++++++++++++++++++++++++++++nvoid pop3_protocol_callback(struct tcp_stream *pop3_connection,void **arg)n{n int i;n char address_string[1024];n char content[65535];n struct tuple4 ip_and_port;n ip_and_port=pop3_connection->addr;n strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);n strcat(address_string,"<------------>");n strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);n strcat(address_string,"n");n switch (pop3_connection->nids_state)n {n case NIDS_JUST_EST:/*POP3客戶端和伺服器建立連接*/n if(pop3_connection->addr.dest==110)n {n pop3_connection->client.collect++;/*pop3客戶端接收數據*/n pop3_connection->server.collect++;/*pop3伺服器接收數據*/n pop3_connection->client.collect_urg++;/*pop3客戶端接收緊急數據*/n pop3_connection->server.collect_urg++;/*pop3伺服器接收緊急數據*/n printf("%spop3客戶端和伺服器建立連接n",address_string);n }n case NIDS_CLOSE:/*POP3協議連接正常關閉*/n printf("---------------------------------------------------n");n printf("%sPOP3協議連接正常關閉n",address_string);n return;n case NIDS_RESET:/*POP3協議連接被RESET關閉*/n printf("---------------------------------------------------n");n printf("%sPOP3協議連接被RESET關閉n",address_string);n return;n case NIDS_DATA:/*POP3協議有新的數據到達*/n {n char status_code[5];n struct half_stream *hlf;n if(pop3_connection->server.count_new_urg)/*POP3伺服器收到新的緊急數據*/n {n printf("---------------------------------------------------n");n strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);n strcat(address_string,"urgent---->");n strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);n strcat(address_string,n);n address_string[strlen(address_string)+1]=0;n address_string[strlen(address_string)];n pop3_connection->server.urgdata;n printf("%s",address_string);n return;n }n if(pop3_connection->client.count_new_urg)/*POP3客戶端收到新的緊急數據*/n {n printf("---------------------------------------------------n");n strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);n strcat(address_string,"<------urgent");n strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);n strcat(address_string,n);n address_string[strlen(address_string)+1]=0;n address_string[strlen(address_string)];n pop3_connection->server.urgdata;n printf("%s",address_string);n return;n }n if(pop3_connection->client.count_new)/*POP3客戶端收到新的數據*/n {n hlf=&pop3_connection->client;n strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);n strcat(address_string,"<------count_new");n strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);n strcat(address_string,n);n printf("---------------------------------------------------n");n printf("%sn",address_string);n memcpy(content,hlf->data,hlf->count_new);n content[hlf->count_new]=0;n if(strstr(strncpy(status_code,content,4),"+OK"))n printf("操作成功n");n if(strstr(strncpy(status_code,content,4),"-ERR"))n printf("操作失敗n");n for(i=0;i<hlf->count_new;i++)n {n printf("%s",char_to_ascii(content[i]));n }n printf("n");n if(strstr(content,"nr.nr"))n printf("數據傳輸結束n");n }n else/*POP3伺服器收到新的數據*/n {n hlf=&pop3_connection->server;n strcpy(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.saddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.source);n strcat(address_string,"count_new------>");n strcat(address_string,inet_ntoa(*((struct in_addr*)&(ip_and_port.daddr))));n sprintf(address_string+strlen(address_string),":%i",ip_and_port.dest);n strcat(address_string,n);n printf("---------------------------------------------------n");n printf("%sn",address_string);n memcpy(content,hlf->data,hlf->count_new);n content[hlf->count_new]=0;n if(strstr(content,"USER"))n printf("郵件用戶名為:n");n if(strstr(content,"PASS"))n printf("用戶密碼為:n");n if(strstr(content,"STAT"))n printf("返回統計資料:n");n if(strstr(content,"RETR"))n printf("獲取郵件:n");n if(strstr(content,"DELE"))n printf("刪除郵件:n");n if(strstr(content,"QUIT"))n printf("退出連接:n");n for(i=0;i<hlf->count_new;i++)n {n printf("%s",char_to_ascii(content[i]));n }n printf("n");n }n }nn default:n break;n }n return;n}nint main(void)n{n if(nids_init())/*Libnids初始化*/n {n printf("%sn",nids_errbuf);n return -1;n }n nids_register_tcp(pop3_protocol_callback);/*註冊分析Telnet協議的回調函數*/n nids_run();/*進入循環捕獲數據包的狀態*/n}n
samplecode2:
#include <stdio.h>n#include<stdlib.h>n#include"nids.h"n#include"string.h"n#include <ctype.h>n#include<arpa/inet.h>nchar ascii_string[10000];nchar *char_to_ascii(char ch)n{n char *string;n ascii_string[0] = 0;n string = ascii_string;n if (isgraph(ch))n *string++ = ch;n else if (ch == )n *string++ = ch;n else if (ch == n || ch == r)n *string++ = ch;n elsen *string++ = .;n *string = 0;n return ascii_string;n}n/*n=======================================================================================================================n下面是分析SMTP協議的回調函數n=======================================================================================================================n */nvoid smtp_protocol_callback(struct tcp_stream *smtp_connection, void **arg)n{n int i;n char address_string[1024];n char content[65535];n char content_urgent[65535];n struct tuple4 ip_and_port = smtp_connection->addr;n strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));n sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);n strcat(address_string, " <---> ");n strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));n sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);n strcat(address_string, "n");n switch (smtp_connection->nids_state)n {n case NIDS_JUST_EST:n if (smtp_connection->addr.dest == 25)n {n /* SMTP客戶端和SMTP伺服器端建立連接 */n smtp_connection->client.collect++;n /* SMTP客戶端接收數據 */n smtp_connection->server.collect++;n /* SMTP伺服器接收數據 */n smtp_connection->server.collect_urg++;n /* SMTP伺服器接收緊急數據 */n smtp_connection->client.collect_urg++;n /* SMTP客戶端接收緊急數據 */n printf("%sSMTP發送方與SMTP接收方建立連接n", address_string);n }n return ;n case NIDS_CLOSE:n /* SMTP客戶端與SMTP伺服器連接正常關閉 */n printf("--------------------------------n");n printf("%sSMTP發送方與SMTP接收方連接正常關閉n", address_string);n return ;n case NIDS_RESET:n /* SMTP客戶端與SMTP伺服器連接被RST關閉 */n printf("--------------------------------n");n printf("%sSMTP發送方與SMTP接收方連接被REST關閉n", address_string);n return ;n case NIDS_DATA:n {n /* SMTP協議接收到新的數據 */n char status_code[4];n struct half_stream *hlf;n if (smtp_connection->server.count_new_urg)n {n /* SMTP伺服器接收到新的緊急數據 */n printf("--------------------------------n");n strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));n sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);n strcat(address_string, " urgent---> ");n strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));n sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);n strcat(address_string, "n");n address_string[strlen(address_string) + 1] = 0;n address_string[strlen(address_string)] = smtp_connection->server.urgdata;n printf("%s", address_string);n return ;n }n if (smtp_connection->client.count_new_urg)n {n /* SMTP客戶端接收到新的緊急數據 */n printf("--------------------------------n");n strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));n sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);n strcat(address_string, " <--- urgent ");n strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));n sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);n strcat(address_string, "n");n address_string[strlen(address_string) + 1] = 0;n address_string[strlen(address_string)] = smtp_connection->client.urgdata;n printf("%s", address_string);n return ;n }n if (smtp_connection->client.count_new)n {n /* SMTP客戶端接收到新的數據 */n hlf = &smtp_connection->client;n strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));n sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);n strcat(address_string, " <--- ");n strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));n sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);n strcat(address_string, "n");n printf("--------------------------------n");n printf("%s", address_string);n memcpy(content, hlf->data, hlf->count_new);n content[hlf->count_new] = 0;n if (strstr(strncpy(status_code, content, 3), "221"))n printf("連接中止n");n if (strstr(strncpy(status_code, content, 3), "250"))n printf("操作成功n");n if (strstr(strncpy(status_code, content, 3), "220"))n printf("表示服務就緒n");n if (strstr(strncpy(status_code, content, 3), "354"))n printf("開始郵件輸入,以"."結束n");n if (strstr(strncpy(status_code, content, 3), "334"))n printf("伺服器響應驗證n");n if (strstr(strncpy(status_code, content, 3), "235"))n printf("認證成功可以發送郵件了n");n for (i = 0; i < hlf->count_new; i++)n {n printf("%s", char_to_ascii(content[i]));n }n printf("n");n }n elsen {n /* SMTP伺服器接收到新的數據 */n hlf = &smtp_connection->server;n strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));n sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);n strcat(address_string, " ---> ");n strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));n sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);n strcat(address_string, "n");n printf("--------------------------------n");n printf("%s", address_string);n memcpy(content, hlf->data, hlf->count_new);n content[hlf->count_new] = 0;n if (strstr(content, "EHLO"))n printf("HELLO命令n");n if (strstr(content, "QUIT"))n printf("退出連接n");n if (strstr(content, "DATA"))n printf("開始傳輸數據n");n if (strstr(content, "MAIL FROM"))n printf("發送方郵件地址為n");n if (strstr(content, "RCPT TO"))n printf("接收方郵件地址為n");n if (strstr(content, "AUTH"))n printf("請求認證n");n if (strstr(content, "LOGIN"))n printf("認證機製為LOGINn");n for (i = 0; i < hlf->count_new; i++)n {n printf("%s", char_to_ascii(content[i]));n }n printf("n");n if (strstr(content, "n."))n printf("數據傳輸結束n");n }n }n default:n break;n }n return ;n}n/*n=======================================================================================================================n主函數n=======================================================================================================================n */nvoid main()n{n if (!nids_init())n /* Libnids初始化 */n {n printf("%sn", nids_errbuf);n exit(1);n }n nids_register_tcp(smtp_protocol_callback);n /* 註冊分析TCP協議的回調函數 */n nids_run();n /* 進入循環捕獲數據包狀態 */n}n
到這裡的話,基本上我們對協議分析就有了個了解。
0x03 數據DPI分析
DPI(Deep Packet Inspection)是一種基於數據包的深度檢測技術,針對不同的網路應用層載荷(例如HTTP、DNS等)進行深度檢測,通過對報文的有效載荷檢測決定其合法性。
其實安全方面使用DPI主要還是為了做流量方面的分析與取證,從而發現攻擊行為和異常行為。通常情況下,我們可以再出口流量抓一些流量樣本過來使用wireshark分析就可以確定攻擊與否,但是這個的話人力成本較大,如果我們有了DPI,就可以迅速的獲得數據包內的信息,這樣的話可以減少發現攻擊的時間。因為這種所有周知的原因,規則什麼的不太好說,可以私下交流。0x04 總結
這可能是今年這個系列的最後一篇文章了,下一篇文章打算寫一個基於中間件模板的蜜罐平台的設計,估計看時間得到明年了,到時候再說吧
推薦閱讀:
※11月安卓系統漏洞小結:31個不得不修復的漏洞
※CVE-2017-8715分析:利用PS模塊清單文件繞過微軟安全補丁
※安全認證代理的透明接入
※黑產拿下萬台IoT設備許可權,用來群發垃圾郵件
※使用超聲波「無聲」劫持語音助理系統:以Siri、Google Now為例