网络安全之tcp阻截引擎
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络安全之tcp阻截引擎 相关的知识,希望对你有一定的参考价值。
1、tcp协议的缺陷
任何协议都有缺陷,tcp协议同样如此,tcp协议的三次握手是有一个不能轻易发现的缺陷,这个缺陷容易引起旁路中断,引起中间人攻击,可以让中间人篡改数据包,Man-in-the-MiddleAttack,我们先看下图,看懂了,就可以继续了。
实际上,这个缺陷就是在服务器和客户机真正建立连接之前,我们可以发送RST包,先告诉服务器断开,然后我们模拟服务器给客户端发包,直接串入到他们的网络中间。
在TCP层,tcp的意思是传输控制协议,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 对于我们日常的分析有用的就是前面的五个字段,SYN,建立连接,FIN包的意思是finish 关闭连接,ACK是回答响应,而RST表示我异常中断,链接重置,PSH才是真正的数据传送。对于我们来说,关键的截断就在于RST 和 FIN,我们选取RST包。
2 、抓包
2.1 抓包方式1 交换机镜像
这种方式是直接从交换机镜像端口复制数据,可以抓该交换机口路径下的所有包,缺点是不能抓所有包。在网络安全中,为了方便对一个或多个网络接口的流量进行分析(如IDS 入侵检测、网络分析仪等),可以通过配置交换机或路由器来把一个或多个端口(VLAN)的数据转发到某一个端口,这就是端口镜像,来实现对网络的监听。
2.2 抓包方式2 网桥
在路由器和交换机之间串入网桥,抓所有包,这种方式是可以抓所有包,缺点是流量更大。通常的网桥做法,可以使用两个网卡,在两个网卡之间既可以用旁路方式,也可以使用串入方式,在传输层这一层,既可以抓取tcp包,也可以抓取udp包,这取决于需求。
2.3 抓包方式3 网关
网关方式是比较安全的方式,一般是纯软件旁路方式抓包。旁路方式抓包是不能阻截udp的,除非我们动用arp 协议,在网络中下毒arp,让所有主机误认为我们发放病毒的机器是网关,这种方式不可取,本身具有arp防火墙的主机是不会受干扰的。
为了方便演示,显然我们制作一个网关,在网关上旁路抓包是比较安全的,下面是建立RAW SOCKET,抓取所有流经网关的原始数据包,然后一层一层往下走,代码其实是适应各种环境的,无论交换机镜像,还是旁路抓包都是这么写,网桥的方式不一样,可以通过linux内核模块流经处理的方式发送到用户态去分析,这里暂时不讨论这种方式。一下是旁路方式抓包。
if((m_helperSocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==SOCKET_ERROR)
{
return 0;
}
if(setsockopt(m_helperSocket, IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval))==SOCKET_ERROR)
{
return 0;
}
if((m_sniffSocket = socket(AF_INET, SOCK_RAW, IPPROTO_IP))==SOCKET_ERROR)
{
return 0;
}
PIP_ADAPTER_INFO pAdapterInfo = m_pAdapterInfo;
u_long in = 0;
do
{
if (strcmp (in_szSourceDevice, pAdapterInfo->AdapterName ) == 0)
{
break;
}
in++;
pAdapterInfo = pAdapterInfo->Next; // Progress through
}
while(pAdapterInfo);
struct sockaddr_in src;
memset(&src, 0, sizeof(src));
src.sin_addr.S_un.S_addr = inet_addr (pAdapterInfo->IpAddressList.IpAddress.String);
src.sin_family = AF_INET;
src.sin_port = 0;
if (bind(m_sniffSocket,(struct sockaddr *)&src,sizeof(src)) == SOCKET_ERROR)
{
return 0;
}
int j=1;
if (WSAIoctl(m_sniffSocket, SIO_RCVALL, &j, sizeof(j), 0, 0, &in,0, 0) == SOCKET_ERROR)
{
return 0;
}
3、建立线程,查询流经数据包
一个最大的IP包为64K,因为一个包是由两个字节来表示的,建立64K数据包缓存,下面只是样例写法,实际上需要建立缓存链表,逐次分析每个包。
3.1 抓取IP
int ThreadHandler(void * in_pParam)
{
// libnet_t *LibnetHandle;
//char *DestAddr = "192.168.1.178";
// char *SourAddr = "192.168.1.1";
//LibnetHandle = libnet_init(LIBNET_LINK, g_DevName, NULL);
// if (LibnetHandle == NULL)
// {
//
// return -1;
// }
// unsigned int DestAddrNumber = libnet_name2addr4(LibnetHandle, DestAddr, LIBNET_RESOLVE);
// unsigned int SourAddrNumber = libnet_name2addr4(LibnetHandle, SourAddr, LIBNET_RESOLVE);
cprocess* _this = (cprocess*)in_pParam;
int res = 0;
char *pkt_data = (char *)malloc(65536); //64K的缓存!
char m_pLogString[256];
Packet p;
if (pkt_data == NULL)
{
return 0;
}
SetEvent(_this->m_hThrdReadyEvent);
// Capture packet
string strName;
do
{
//#define GET_IP(x,buf) sprintf(buf,"%d,%d,%d,%d", x&0xFF, (x>>8)&0xFF, (x>>16)&0xFF, (x>>24)&0xFF)
res = recvfrom(_this->m_sniffSocket,pkt_data,65536,0,0,0); //
if(res > 0)
{
ZeroMemory(&p, sizeof (Packet));
unsigned int Number = _this->DecodeIP((u_int8_t*)pkt_data, res, &p);
if (p.banned == 1)
{
_this->FilterHttpRequestXP(&p);
char ip_string_src[17];
char ip_string_dst[17];
memcpy (ip_string_src, inet_ntoa(p.iph->ip_src), 17);
memcpy (ip_string_dst, inet_ntoa(p.iph->ip_dst), 17);
sprintf (m_pLogString,
"关键词 \\'%s\\' 被侦测从 %s 到 %s. 暗影联盟日志",
p.matched, ip_string_src, ip_string_dst);
_this->m_pFilterLog->AddLog(m_pLogString);
}
}
}
while (res > 0);
free(pkt_data);
return 1;
}
3.2 解析IP包
unsigned int DecodeIP(u_int8_t * pkt, const u_int32_t len, Packet * p)
{
u_int32_t ip_len; /* length from the start of the ip hdr to the pkt end ip包长度*/
u_int32_t hlen; /* ip header length ip包头长度 一个IP包如tcp包即使只有一个字节的应用层数据也是要大于40字节的 */
/* lay the IP struct over the raw data */
p->iph = (IPHdr *) pkt;
/* 小于一个IP包头的长度立刻退出 */
if(len < IP_HEADER_LEN)
{
p->iph = NULL;
return -1;
}
if(IP_VER(p->iph) != 4)
{
p->iph = NULL;
return -1;
}
/* set the IP datagram length */
ip_len = ntohs(p->iph->ip_len);
/* set the IP header length */
hlen = IP_HLEN(p->iph) << 2;
/* header length sanity check */
if(hlen < IP_HEADER_LEN)
{
p->iph = NULL;
return -1;
}
//抓包后分析的包长度
if (ip_len > len)
{
ip_len = len;
}
if(ip_len < hlen)
{
p->iph = NULL;
return -1;
}
/* test for IP options */
p->ip_options_len = hlen - IP_HEADER_LEN;
if(p->ip_options_len > 0)
{
p->ip_options_data = pkt + IP_HEADER_LEN;
}
/* set the remaining packet length */
ip_len -= hlen;
/* check for fragmented packets */
p->frag_offset = ntohs(p->iph->ip_off);
/*
* get the values of the reserved, more
* fragments and don't fragment flags
*/
p->rf = (u_int8_t)((p->frag_offset & 0x8000) >> 15);
p->df = (u_int8_t)((p->frag_offset & 0x4000) >> 14);
p->mf = (u_int8_t)((p->frag_offset & 0x2000) >> 13);
/* mask off the high bits in the fragment offset field */
p->frag_offset &= 0x1FFF;
if(p->frag_offset || p->mf)
{
/* set the packet fragment flag */
p->frag_flag = 1;
}
/* if this packet isn't a fragment 如果不是一个分片代码*/
if(!(p->frag_flag))
{
if(p->iph->ip_src.S_un.S_addr == g_SelfIPAddress)
//iphdr *pip = (iphdr *) pkt;
{
//这个包是从本地发出去的ip包
g_Direction = 0;
}
else
{
if(p->iph->ip_dst.S_un.S_addr == g_SelfIPAddress)
{
//这个包是个进来的ip包
g_Direction = 1;
}
else g_Direction = -1;//内网地址
}
//2013-02-01 注释,做家长行为管理,不进行协议分析
if(g_Direction >=0)
{
//unsigned int t = time(NULL);
//return Analyze_Protocol(pip,t,g_Direction);
}
//2009-04-7 先进行协议分析,以下屏蔽
//2013-02-01 又重新打开,做家长行为管理
/* Decode only TCP headers */
if (p->iph->ip_proto == IPPROTO_TCP)
{
DecodeTCP(pkt + hlen, ip_len, p);
}
}
else
{
/* set the payload pointer and payload size */
p->data = pkt + hlen;
p->dsize = (u_short) ip_len;
return -1;
}
return -1;
}
3.3 解析tcp
///解析tcp包,pkt传进来的是ip包
void DecodeTCP(u_int8_t * pkt, const u_int32_t len, Packet * p)
{
u_int32_t hlen; /* TCP header length */
if(len < 20)
{
p->tcph = NULL;
return;
}
/* lay TCP on top of the data cause there is enough of it! */
p->tcph = (TCPHdr *) pkt;
/* multiply the payload offset value by 4 */
hlen = TCP_OFFSET(p->tcph) << 2;
if(hlen < 20)
{
p->tcph = NULL;
return;
}
if(hlen > len)
{
p->tcph = NULL;
return;
}
/* if options are present, decode them */
p->tcp_options_len = hlen - 20;
if(p->tcp_options_len > 0)
{
p->tcp_options_data = pkt + 20;
}
/* set the data pointer and size */
p->data = (u_int8_t *) (pkt + hlen);
if(hlen < len)
{
p->dsize = (u_short)(len - hlen);
}
else
{
p->dsize = 0;
}
if (p->tcph->th_flags & TH_ACK && p->tcph->th_flags & TH_PSH)
{
if(p->tcph->th_dport != htons(80) &&
p->tcph->th_dport != htons(443) )
return ;
DecodeHTTP(p->data, p->dsize, p);
}
return;
}
上面的的流程就是一层一层往下解析,我们还要做的就是建立数据特征向量,分析数据,保存数据,同样可以完成数据回放。
4 、包分析和阻断流程
5、IP 校验算法
在发送IP包的过程中,需要用到校验算法
unsigned short CalcIPSum(unsigned short * w, int blen)
{
unsigned int cksum;
/* IP must be >= 20 bytes */
cksum = w[0];
cksum += w[1];
cksum += w[2];
cksum += w[3];
cksum += w[4];
cksum += w[5];
cksum += w[6];
cksum += w[7];
cksum += w[8];
cksum += w[9];
blen -= 20;
w += 10;
while( blen ) /* IP-hdr must be an integral number of 4 byte words */
{
cksum += w[0];
cksum += w[1];
w += 2;
blen -= 4;
}
cksum = (cksum >> 16) + (cksum & 0x0000ffff);
cksum += (cksum >> 16);
return (unsigned short) (~cksum);
}
6、制作界面
制作一个简单的界面,从网卡上抓包,这里其实做实验可以从本地做,如果做好了,可以做一个网关,局域网中的所有网络主机填写网关地址为本网关,就可以从网卡上以原始数据包方式抓取所有包。例如linux和windows server系列是可以抓包和发送原始数据包的,但是windows7 和 10 这种系列没有权限发送原始数据包,只能抓取,ok,原理已经清楚了,我们一步一步建立这样的界面和程序。
下一节我们继续探讨网络中抓到包以后如何把包解析出来。
以上是关于网络安全之tcp阻截引擎 的主要内容,如果未能解决你的问题,请参考以下文章