• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 使用 Snort 和 PHP 構建一個小型網絡防御系統

    發表于:2007-05-25來源:作者:點擊數: 標簽:
    碩士研究生, 四川大學計算機學院 2005 年 7 月 本文在 Linux 環境下,利用 Snort 和 Iptables 構建了一個小型 網絡 防御系統,由PHP 頁面提供了一個遠程管理工具,并給出關鍵程序的實現和說明。 引言 Snort 是目前十分流行的輕型 入侵檢測 系統。但是目前人

    碩士研究生, 四川大學計算機學院
    2005 年 7 月
    本文在 Linux 環境下,利用 Snort 和 Iptables 構建了一個小型網絡防御系統,由PHP 頁面提供了一個遠程管理工具,并給出關鍵程序的實現和說明。

    引言
    Snort 是目前十分流行的輕型入侵檢測系統。但是目前人們對Snort檢測結果的處理大都停留在記錄日志或簡單通知網絡管理員,由管理員進行審計再決定網絡防御策略的階段。Snort的檢測結果并沒有及時地用來抵御網絡入侵。本文通過為Snort的報警輸出模塊提供一個服務監聽程序的辦法,及時獲取Snort的報警信息并對其進行解析,根據解析結果向iptables添加相應的防火墻規則,達到實時阻止網絡攻擊的目的。服務程序同時監聽來自PHP的管理請求,并進行相應的操作。由于服務程序獨立于Snort而運行,故不會影響到Snort的運行效率。管理員可以通過PHP頁面對服務程序阻塞的主機進行監控和管理,如查看當前阻塞的主機IP地址、事件發生時間、阻塞時間、阻塞原因,修改阻塞時間等。

    總體思想
    Snort的輸出plugin為我們提供了豐富的報警輸出方式:輸出到文件,syslog,數據庫,Unix域Socket等。其中當Snort的報警輸出到Unix域Socket時,輸出模塊相當于一個報警的客戶端,Snort的用戶可以通過編寫服務器端代碼,獲取Snort的報警輸出消息,并根據這些消息采取相應的對策。本文基于這種思想,并利用Linux下自帶的防火墻Iptables,構建了一個網絡防御系統,系統總體結構如下:


    服務處理程序是該系統的核心部分,包括報警輸出處理子程序(AO_Handler)和PHP請求處理子程序(Web_Handler)兩個部分。報警輸出處理子程序主要負責接收Snort報警,解析報警信息,并通過向iptables加規則的方式對攻擊主機進行阻塞;而PHP請求處理子程序主要負責與PHP管理頁面通信,并根據頁面的請求做出相應的處理。

    服務程序中維護了一個規則相關信息表,這個表以隊列(報警隊列和阻塞隊列)的數據結構進行組織,其中報警隊列中記錄的是經Snort報警輸出解析得到的攻擊主機信息(即報警結點,由AO_Handler生成), 而阻塞隊列中記錄了正在被阻塞的攻擊主機信息(即阻塞結點)。如果報警結點不在阻塞隊列中,程序將對這個結點所代表的主機進行阻塞操作,并把該結點加入到阻塞隊列中。對每個阻塞操作所對應的阻塞結點設定一個有效期,當阻塞時間超過這個有效期時,程序將該阻塞結點從阻塞隊列中刪除,并刪除iptables中對應的阻塞規則,對該阻塞主機放行。采用兩個列隊來維護報警和阻塞信息可以防止程序在多次收到有關同一個主機地址的報警時,添加重復的阻塞規則而為規則管理和取消阻塞操作時所帶來的混亂。另外阻塞信息的維護使得程序可以為終端用戶提供當前被系統自動阻塞的主機的所有信息:報警時間,阻塞時間,阻塞原因等,為用戶了解和管理這些信息提供了接口。

    具體實現

    服務程序實現方式
    服務程序需要同時處理三種事件:監聽Snort報警輸出、維護規則相關信息表(即報警隊列和阻塞隊列)、處理來自PHP頁面的請求。因此,服務程序采用多線程并發模式。由主線程監聽來自Snort的報警數據,解析該數據、創建報警結點并將其加入報警隊列,另外主線程還執行服務程序的初始化工作;由兩個線程來專門維護規則相關信息表,一個線程(AlertHandler)用于檢測報警隊列,當其中有符合要求的報警結點時,將其加入阻塞隊列,執行阻塞操作,另一個(BlockHanlder)用于監測阻塞隊列,將其中到達阻塞有效期的阻塞節點刪除;對于來自PHP頁面的請求,服務程序采用建立一個線程池的辦法,對PHP請求做出相應操作,如:列表阻塞主機,刪除某阻塞主機,修改阻塞有效時間等。

    主線程和規則維護線程構成了報警輸出處理子程序, 而PHP請求處理線程構成了PHP請求處理子程序。由于這些線程都涉及到對規則相關信息表的讀寫操作,因此,需要一定的同步機制來保證操作的正確性。本文中的程序采用了建立互斥鎖和條件變量的辦法來達到這個目的。程序主要數據結構默認情況下,Snort的報警套接字以數據報方式向path為/var/log/snort_alert的Unix域套接字發送報警數據,報警數據被封裝在一個Alertpkt的結構體中(在Snort源碼包的Spo_alert_unixsock.h中定義),Alertpkt的定義如下:


    clearcase/" target="_blank" >cccccc" border="1">
    
                typedef struct _Alertpkt
                {
                u_int8_t alertmsg[ALERTMSG_LENGTH]; /* 報警消息 */
                struct pcap_pkthdr pkth;
                u_int32_t dlthdr;       /* datalink header offset. (ethernet, etc.. ) */
                u_int32_t nethdr;       /* network header offset. (ip etc...) */
                u_int32_t transhdr;     /* transport header offset (tcp/udp/icmp ..) */
                u_int32_t data;
                u_int32_t val;  /*指出有效的字段*/
                ……
                Event event; /* 報警事件的相關信息 */
                } Alertpkt;
                

    報警/阻塞結點HNInfo、報警隊列和阻塞隊列的數據結構定義如下:


    
                typedef struct _HNInfo
                {
                int event_id; // 結點ID
                char blockIP[16]; //阻塞主機的IP地址
                time_t timestamp; // 阻塞開始時間
                int blocktime; // 主機阻塞時間
                char msg[500]; // 阻塞原因
                char desIP[16]; // 引起報警的包的目的地址
                char protocol[5]; // tcp, udp or icmp
                uint16_t srcPort; // 源端口
                uint16_t desPort; // 目的端口
                struct _HNInfo* next; // 指向下一個結點
                } HNInfo; // 隊列里的結點結構
                typedef struct  _HNQueue
                {
                struct _HNInfo  *head, *tail;
                int count;
                } AlertQ; // 報警隊列
                typedef struct AlertQ BlockQ; // 阻塞隊列
                

    報警輸出處理子程序
    報警輸出子程序(AO_Handler)由主線程和規則信息表維護線程組成。AO_Handler在/var/log/snort_alert上建立監聽套接字與Snort的報警輸出模塊通信,它的處理流程如下:

    1) 接收Snort的報警輸出,根據Alertpkt的內容,解析出引發報警的源地址,報警內容,報警事件ID號等必要信息,并將其連同報警發生的時間、需要阻塞的時間等信息并形成報警結點放入報警隊列;

    2) 檢測報警隊列,當其不為空時,取出報警結點并與阻塞隊列中的結點進行比較,如果報警的主機地址已出現在阻塞隊列中,說明該主機已經被阻塞,只需修改阻塞隊列中該主機的報警時間戳,使其與現在的報警結點的時間戳相等;如果報警主機未出現,則將報警結點加入阻塞隊列,并執行阻塞操作。

    3) 檢測阻塞隊列,當發現主機的阻塞時間已到時,從阻塞隊列中刪除該主機結點,且取消對該主機的阻塞。報警輸出處理子程序的主要代碼和相關全局變量定義如下:


    
                int sockfd; // 監聽Snort的報警消息
                int btime = (1 << 29) - 1; // 默認的阻塞時間
                struct _HNQueue AQ; // 報警隊列
                struct _HNQueue BQ; // 阻塞隊列
                // 線程同步所需mutex和條件變量
                pthread_mutex_t AQ_mutex = PTHREAD_MUTEX_INITIALIZER;
                pthread_mutex_t BQ_mutex = PTHREAD_MUTEX_INITIALIZER;
                pthread_cond_t AQ_cond = PTHREAD_COND_INITIALIZER;
                pthread_cond_t BQ_cond = PTHREAD_COND_INITIALIZER;
                pthread_cond_t BQ_timedcond = PTHREAD_COND_INITIALIZER;
                pthread_t mtid; // 記錄主線程ID
                Int main (int argc,char** argv)
                {
                struct sockaddr_un snortaddr;
                struct sockaddr_un bogus;
                Alertpkt alert;
                Packet *p = NULL;
                int recv;
                socklen_t len = sizeof (struct sockaddr_un);
                HNInfo *cur, *node, *pivot;
                pthread_t tidA, tidB;
                if(argc == 2 && !strcmp(argv[1],"-D"))
                daemon_init(); // 以后臺方式運行
                //程序初始化工作,包括AQ、BQ的初始化,建立php請求監聽端口和建立php請求處理線程池。
                goInit();
                // 捕獲用戶終止或kill消息并進行清理操作
                signal(SIGINT, sig_int);
                signal(SIGTERM, sig_term);
                // 建立維護報警隊列和阻塞隊列的兩個線程
                pthread_create(&tidA, NULL, alertHandler, NULL);
                pthread_create(&tidB, NULL, blockHandler, NULL);
                mtid = pthread_self(); // 記錄主線程ID
                // 使用UDP套接字接收Snort的報警消息
                if ((sockfd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
                {
                perror ("socket");
                exit (1);
                }
                unlink(UNSOCK_FILE);
                bzero (&snortaddr, sizeof (snortaddr));
                snortaddr.sun_family = AF_UNIX; // 設置為Unix域套接字
                strcpy (snortaddr.sun_path, UNSOCK_FILE); // 將path設為UNSOCK_FILE
                if (bind (sockfd, (struct sockaddr *) &snortaddr, sizeof (snortaddr)) < 0)
                {
                perror ("bind");
                exit (1);
                }
                // 接收Snort的報警數據
                while ((recv = recvfrom (sockfd, (void *) &alert, sizeof (alert),
                0, (struct sockaddr *) &bogus, &len)) > 0)
                {
                p = ParsePacket(&alert); // 解析報警信息
                if(p != NULL)
                {
                node = MakeNode(&alert, p);  // 建立報警信息結點
                if(node != NULL)
                {
                PushQueue(node); // 將報警信息加入報警隊列
                }
                free(p);
                p = NULL;
                }
                }
                exitClean(); // 清理工作
                return 0;
                }

    主線程接收到報警數據并解析后,由MakeNode函數生成報警信息并由PushQueue函數加入報警隊列:


    
                HNInfo * MakeNode(Alertpkt* alert, Packet *p)
                {
                分配新結點node;
                strcpy(node->blockIP , (char*) inet_ntoa (p->iph->ip_src)); // 解析源IP地址
                strcpy(node->desIP, (char*) inet_ntoa(p->iph->ip_dst)); // 解析目的IP地址
                node->timestamp = time(NULL); // 結點生成時間
                node->blocktime = btime; // 阻塞時間
                node->event_id = alert->event.event_id; // 事件ID號
                strcpy(node->msg, alert->alertmsg); // 報警原因
                // 解析報警協議及端口號
                if (!(alert->val & NOPACKET_STRUCT))
                if (p->iph && (p->tcph || p->udph || p->icmph))
                {
                switch (p->iph->ip_proto)
                {
                case IPPROTO_TCP:
                strcpy(node->protocol, "TCP");
                node->srcPort = ntohs (p->tcph->th_sport);
                node->desPort = ntohs (p->tcph->th_dport);
                break;
                case IPPROTO_UDP:
                strcpy(node->protocol, "UDP");
                node->srcPort = ntohs (p->udph->uh_sport);
                node->desPort = ntohs (p->udph->uh_dport);
                break;
                case IPPROTO_ICMP:
                strcpy(node->protocol, "ICMP");
                break;
                }
                }
                return node;
                }
                void PushQueue(HNInfo* node)
                {
                pthread_mutex_lock(&AQ_mutex);
                加入報警信息結點;
                // 通知報警信息處理線程,有新結點加入
                pthread_cond_signal(&AQ_cond);
                pthread_mutex_unlock(&AQ_mutex);
                }
                

    AlertHandler檢測報警隊列AQ,當隊列中有結點時,則將其取出,與BQ中的結點比較,若BQ中沒有相應主機的結點,則在BQ中添加該結點,否則只是修改BQ中相應結點的時間戳和報警事件原因。檢測時使用條件變量,當AQ不為空時,再進行操作,否則程序進入睡眠,這比輪詢的方式節省CPU資源。


    
                void *alertHandler(void * vptr)
                {
                HNInfo* cur, *pivot, *tmp;
                char cmd[200];
                while(1)
                {
                pthread_mutex_lock(&AQ_mutex);
                // 直到AQ不為空,線程被喚醒
                while(AQ.count == 0)
                pthread_cond_wait(&AQ_cond, &AQ_mutex);
                cur = AQ.head;
                while(cur != NULL)
                {
                // 檢驗BQ中是否含相同主機結點
                pthread_mutex_lock(&BQ_mutex);
                if(BQ.count) // BQ不為空
                {
                cur = PopQueue(&AQ);
                if(找到相應結點)修改BQ中對應結點的值并釋放cur;
                else{// 結點未找到,將其添加入BQ并添加防火墻規則
                結點加入BQ;
                snprintf(cmd,sizeof(cmd),"/sbin/iptables -I INPUT
                -s %s -j DROP",cur->blockIP);
                system(cmd); // 添加防火墻規則
                }
                }
                // BQ為空,只需添加結點、防火墻規則,并通知阻塞隊列維護線程,BQ中有新結點產生
                else
                {
                結點加入BQ并添加防火墻規則 ;
                //通知阻塞隊列維護線程
                pthread_cond_signal(&BQ_cond);
                }
                pthread_mutex_unlock(&BQ_mutex);
                cur = AQ.head;
                }
                pthread_mutex_unlock(&AQ_mutex);
                }
                }
                // blockHandler檢測阻塞主機隊列,一旦某個主機阻塞時間已到,
                則從阻塞隊列中刪除,并// 將對應的阻塞主機放行
                void *blockHandler(void * vptr)
                {
                HNInfo* pivot, *cur;
                char cmd[200];
                time_t min, tmp;
                while(1)
                {
                pthread_mutex_lock(&BQ_mutex);
                while(BQ.count == 0) // 若BQ為空,線程休眠
                pthread_cond_wait(&BQ_cond, &BQ_mutex);
                pivot = BQ.head;
                cur = BQ.head;
                while(cur != NULL)
                {
                tmp = cur->timestamp + cur->blocktime ;
                if(cur == BQ.head)min = tmp;
                else min =  min > tmp ? tmp : min;
                if(tmp <= time(NULL)) 時間到刪除結點及防火墻規則;
                else
                {
                pivot = cur;
                cur = cur->next;
                }
                }
                // 當肯定沒有主機阻塞時間到時,線程休眠。
                struct timespec ts;
                ts.tv_sec = min;
                ts.tv_nsec = 0;
                pthread_cond_timedwait(&BQ_timedcond, &BQ_mutex, &ts);
                pthread_mutex_unlock(&BQ_mutex);
                }
                }
                

    PHP請求處理子程序
    用戶通過PHP頁面對阻塞的主機進行管理操作,主要的操作有:

    1) 實時查看阻塞主機信息;

    2) 修改某阻塞主機的阻塞時間;

    3) 將某主機從阻塞列表中刪除。

    此時,PHP服務器端程序需要和服務程序交互,由于交互過程比較簡單,故采用問答方式,通信協議如下:

    Client: command[&event_id[&blocktime]]
    Server: cont&event_id&blockip&timestamp&blocktime&msg&desIP&protocol&srcport&desport#
    或over&result#

    客戶端發出的消息分為查詢,刪除,修改三種情況,對應command值為query、del、mod,當command為del時,需要填入event_id的值,而當command為mod時,需要加入event_id和blocktime的值,各個字段之間以&分隔。

    服務器端的回應消息主要分兩種,一種針對query,一種針對del和mod。對del和mod,只是簡單返回執行結果,即over&result#,result為ok或err;而對query,需要返回所有阻塞主機的信息,每條信息放在一個'cont'開頭的串中,各主機信息以'#'分隔,而各字段信息以'&'分隔。當所有的信息傳送完畢時,發送一個over&result#消息。

    PHP請求處理子程序主要代碼如下:


    
                int listenfd; // 處理PHP請求的監聽端口
                pthread_t THREADPOOL[50]; // 處理php請求的線程池
                pthread_mutex_t WEB_mutex = PTHREAD_MUTEX_INITIALIZER;
                webHandler線程處理來自php的請求,并對阻塞隊列進行相應操作。
                void* webHandler(void *vptr)
                {
                int connfd; // 與php通信端口
                …
                while(1)
                {
                pthread_mutex_lock(&WEB_mutex);
                // 監聽來自php的請求并建立通信連接
                connfd = accept(listenfd,  &cliaddr, &clilen);
                pthread_mutex_unlock(&WEB_mutex);
                memset(buf, 0, sizeof(buf));
                if((n = read(connfd, buf, sizeof(buf))) == 0)continue; // 讀取php請求
                if(strlen(buf) == 0)continue;
                解析客戶端請求;
                if(查詢BQ狀態)
                {
                pthread_mutex_lock(&BQ_mutex);
                讀取并發送BQ信息;
                pthread_mutex_unlock(&BQ_mutex);
                }
                else if(刪除或修改BQ中某主機信息)
                {
                pthread_mutex_lock(&BQ_mutex);
                刪除或修改主機信息和防火墻規則,并返回執行結果;
                pthread_mutex_unlock(&BQ_mutex);
                }
                else 發送出錯消息;
                close(connfd);
                }
                }
                

    Php管理頁面使用socket向服務程序發送請求,并顯示相應結果。 PHP管理頁面的查詢和修改代碼如下:


    
                function DoQuery(&$errno)
                {
                $servSocket = OpenAlertSock(); // 打開通信端口
                $buf = "query";
                socket_write($servSocket, $buf, strlen($buf));
                while ($out = socket_read($servSocket, 2048))
                {
                $result = $result.$out;
                }
                socket_close($servSocket);
                解析阻塞主機信息,并將其存入數組返回。
                }
                function DoModify($cmd)
                {
                $servSocket = OpenAlertSock();
                socket_write($servSocket, $cmd, strlen($cmd)); // 發送刪除或修改請求
                while ($out = socket_read($servSocket, 2048))
                {
                $result = $result.$out;
                }
                socket_close($servSocket);
                解析操作結果并返回。
                }
                

    結束語
    本文利用Snort和php構建了一個小型的網絡防御系統,由于采用了獨立的服務程序,防御系統對Snort和iptables的運行效率影響很小。

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>