一、引言 Libnids是一个用于网络入侵检测应用的专业编程接口。利用Libnids所提供的基本框架和基本功能,可以方便快速地构建基于网络的入侵检测系统,实现网络协议分析、网络数据的嗅探以及重现等多种应用。比如,利用Libnids可以实现基于TCP SYN类型的端口扫描攻击[2]。 Libnids提供的主要功能有:网络数据包的捕获、IP碎片重组、TCP数据流重组、端口扫描攻击检测以及异常数据包检查等。其中,Libnids的数据包捕获功能是通过其对Libpcap工具包中的网络数据包捕获、过滤功能的直接调用来实现的。因此,利用Libnids可以非常容易地实现网络数据包的捕获以及对所捕获的数据进行灵活多样的过滤处理,并完成对所捕获的数据的分析。 所谓网络嗅探,也称网络监视,是指通过捕获、分析、监测网络数据包,从而实现察看网络内容的目的。网络嗅探技术各式各样,也一直为各种类型的人员以各种不同的目的所使用。对于网络管理员来说,可以利用网络嗅探来掌握网络的运行状况、检测网络系统出现的故障、攻击等;对于执法人员来说,可以用嗅探技术来还原、重现网络数据从而获取某些违法活动的计划、内容和证据等;而对于非法攻击者来说,则可以用其来进行密码、帐号、重要文件等敏感信息的获取。 本文主要讨论如何利用Libnids的TCP数据流重组功能来实现对电子邮件内容、包括其用户名和密码的重现和还原。 二、电子邮件应用与POP3协议 电子邮件属于Internet 技术中的应用之一。电子邮件功能主要是依靠工作在应用层的与电子邮件应用相关的协议来实现的。POP3协议是电子邮件应用的标准协议之一,适用于C/S模式的脱机模型的电子邮件服务[6]。其工作机制为:允许用户在客户端从服务器上把邮件存储到本地主机上,同时删除保存在邮件服务器上的邮件;而在服务器端,POP3 服务器则遵循POP3协议来接收用户所发送的电子邮件。 大多数现有的POP3客户机在与服务器进行信息交互的时候,都是采用ASCII明文的方式来发送用户名和口令,因此,如果能够捕获到POP3协议的数据包,然后对其进行重组,就不但可以获取电子邮件的内容,还可以获取用户名、口令等电子邮件的信息。 而Libnids工具包则提供了TCP数据流的重组功能,并可以在此基础上进一步实现对应用层的各种协议数据包的分析[1]。因此,通过捕获网络数据包,利用Libnids的TCP数据流重组功能对POP3协议数据包进行重组和分析,就能实现对电子邮件的还原和重现。 三、系统设计 要实现对POP3协议中所传输的电子邮件内容的还原和重现,首先需要捕获POP3协议数据包。Libnids是通过直接调用Libpcap开发包总的数据包捕获函数来实现数据捕获的。Libpcap开发包的捕获功能非常强大,并且还提供了灵活多样的数据过滤机制,使得开发者可以根据自己的需要捕获过滤自己感兴趣的信息。为了提高准确率和效率,在数据包捕获时应该合理地设计并设置相应的过滤规则。Linpcap提供了多种过滤机制,比如,基于协议类型的过滤、基于数据包大小的过滤、基于源或目的地址的过滤、基于端口的过滤等等。在本应用中,可以采用如下的过滤处理措施: 1.如果想要重现所有可能的电子邮件的内容,则可以基于协议类型进行数据的过滤,将非POP3的协议数据包丢弃。 2.如果需要重现某一特定源或目的地址的邮件内容,则可以将源或目的地址的信息纳入过滤规则的设置中。 图1 利用Libnids进行开发的基本流程 在捕获了POP3的协议数据包后,就可以在此基础上利用TCP数据流的重组。Libnids的TCP数据流重组功能不但可以方便地显示任何基于TCP协议的应用层协议。在此,我们只需要POP3的协议数据。为此,现要对Libnids进行初始化,然后利用回调函数,实现对TCP连接和TCP连接状态以及TCP协议所传输的数据进行分析,接下来是进入循环捕获数据包的状态,最后,是将所获取的数据包进行重组以实现邮件内容的重现。其流程如图1所示。 图2 POP3协议分析和邮件内容重现流程 利用不同的回调函数可以实现不同的功能,要实现对POP3协议的分析,必须调用POP3协议分析的回调函数: Void pop3_callback(struct tcp_stream * pop3_connection,void ** arg) 图1中“分析POP3协议,实现邮件内容重现”的详细过程如图2所示。 在上述过程中有一个需要引起重视的关键问题,那就是回调函数所接收的TCP数据是存放在half_stream的缓存中的,系统对此缓存的使用规则是一旦回调函数返回,则马上回收该缓存,一旦被回收,则此缓存中所存放的数据也就不存在了。因此,应该对half_stream缓存中的数据进行立即读取操作。如果要想延时再读取half_stream缓存中的内容,则需要在程序中作相应的处理,以便half_stream将内容一直缓存到其容量满的时候再一次性读取。 按照上述方法完成相关系统的开发调试后,即可进行编译运行了。其实例代码如下。 四、实例代码解析 #include "nids.h" /*包含Libnids头文件。*/ char ascii_string[10000];/*用于存放ASCII明文邮件内容的全局变量。*/ char *char_to_ascii(char ch)/*读取并分析所获取的ASCII字符串内容。*/ { /*初始化*/ char *string; ascii_string[0] = 0; string = ascii_string; if (isgraph(ch))/* 判断该字符是否是除空格之外的可打印字符(ascii码33-126之间的字符)*/ *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];/*用于存放邮件正文的数组*/ char content_urgent[65535];/*用于存放紧急数据正文的数组*/ /*获取POP3连接的地址和端口信息*/ struct tuple4 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: if (pop3_connection->addr.dest == 110) { /*POP3客户端和POP3服务器端建立连接 */ pop3_connection->client.collect++; /* POP3客户端接收数据 */ pop3_connection->server.collect++; /* POP3服务器接收数据 */ pop3_connection->server.collect_urg++; /* POP3服务器接收紧急数据 */ pop3_connection->client.collect_urg++; /* POP3客户端接收紧急数据 */ printf("%sPOP3客户端与POP3服务器建立连接\n", address_string); } return ; case NIDS_CLOSE: /* POP3客户端与POP3服务器端连接正常关闭 */ printf("--------------------------------\n"); printf("%sPOP3客户端与POP3服务器连接正常关闭\n", address_string); return ; case NIDS_RESET: /* POP3客户端与POP3服务器端连接被RST关闭 */ printf("--------------------------------\n"); printf("%sPOP3客户端与POP3服务器连接被REST关闭\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-> client.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, " <--- "); 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, 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, " ---> "); 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, "USER")) printf("邮件用户名为\n");/*输出用户名*/ if (strstr(content, "PASS")) printf("用户密码为\n");/*输出用户密码*/ if (strstr(content, "STAT")) printf("返回统计资料\n");/*输出统计信息*/ if (strstr(content, "LIST")) 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 ; } /*主函数*/ void main() { if (!nids_init()) /* Libnids初始化 */ { printf("出现错误:%s\n", nids_errbuf); exit(1); } nids_register_tcp(pop3_protocol_callback); /* 注册分析POP3协议的回调函数 */ nids_run(); /* 进入循环捕获数据包的状态 */ } 五、环境设置 由于Libnids需要适用Libpcap和Libnet开发工具包中的功能和函数,因此,在安装Libnids开发包之前必须先安装Libpcap和Libnet开发包。以上工具分别由Linux平台和Windows平台的版本。下面简单介绍在Linux平台上的安装方法。 1.安装Libpcap开发包,运行如下命令: tar –xzvf libpcap-0.8.3.tar.gz ./configure make make install 2.安装Libnet开发包,运行如下命令: tar –xzvf libnetp-1.1.2.1.tar.gz ./configure make make install 3.安装Libnids开发包,运行如下命令: tar –xzvf libnids -1.20.tar.gz ./configure make make install 六、结语 本文利用Libnids开发包所提供的TCP数据流重组功能实现基于POP3协议的电子邮件内容的重现。其基本原理、框架、流程和方法也同样适用于如SMTP、ESMTP协议或应用层其它协议的网络数据的分析和重现,具有通用性。 |