GTP 全称为GPRS Tunneling Protocol即GPRS隧道协议。GTP(GPRS隧道协议)用于建立核心通信网络GSN(GPRS服务节点)之间的通道。snort提供的GTP解码和预处理器提供通过GTP解决对这些网络的入侵企图的方法。它还使检测新攻击更容易。
GTP解码器提取GTP PDU内的有效载荷;GTP预处理器检查所有信令消息,并提供关键字以供进一步检查。
启用和配置解码器后,解码器将剥离GTP头并解析底层IP/TCP/UDP封装的数据包。因此,所有规则和检测工作就像没有GTP头一样。
例子:
大部分数据包时以下格式:
IP -> UDP -> GTP -> IP -> TCP -> HTTP
如果您有一个标准HTTP规则"alert tcp any any -> ?any $HTTP_PORTS (msg: "Test HTTP"; flow:to_server,established; content:"SOMETHINGEVIL"; http_uri;? ?.... sid:X; rev:Y;)",如果你配置了GTP decoder(config enable_gtp),它可以对被gtp协议包含内部的http内容发出报警。
下面我们以源码的形式来分析解码过程,关于预处理器在另一篇博客中讲解。
其中关于解析的原理可以参考之前写过的博客snort 源码分析之模式匹配引擎。根据这篇博客我们知道:
config enable_gtp
的解析过程。
ParseConfigFile=》snort_conf_keywords[i].parse_func(sc, p, args)=》ParseConfig=》config_opts[i].parse_func(sc, opts)=》ConfigEnableGTPDecoding 通过这一系列的调用,gtp decoder的配置就算是解析完成了。
void ConfigEnableGTPDecoding(SnortConfig *sc, char *args)
{
PortObject *portObject;
int numberOfPorts = 0;
if (sc == NULL)
return;
DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Enabling GTP decoding\n"););
sc->enable_gtp = 1;//设置启用标志
/*Set the ports*/
//解析端口配置 在snort.conf中配置了portvar GTP_PORTS [2123,2152,3386],所以会成功
portObject = PortVarTableFind( sc->targeted_policies[getParserPolicy(sc)]->portVarTable, "GTP_PORTS");
if (portObject)
{
sc->gtp_ports = PortObjectCharPortArray(sc->gtp_ports,portObject, &numberOfPorts);//65536大小的数组
}
if (!sc->gtp_ports || (0 == numberOfPorts))
{
/*No ports defined, use default GTP ports*/
sc->gtp_ports = (char *)SnortAlloc(UINT16_MAX);
sc->gtp_ports[GTP_U_PORT] = 1;
sc->gtp_ports[GTP_U_PORT_V0] = 1;
}
}
?之前的数据包,上层协议是udp 所以在snort中的DecodeUDP函数中调用了DecodeGTP
void DecodeUDP(const uint8_t * pkt, const uint32_t len, Packet * p)
{
.......
if (ScGTPDecoding() &&/*为true*/
(ScIsGTPPort(p->sp)||ScIsGTPPort(p->dp)))/*端口配置 2152 所以为true*/
{
if ( !p->frag_flag )//没有分段
DecodeGTP(pkt + sizeof(UDPHdr), len - sizeof(UDPHdr), p);//调用gtp解码函数
}
}
ScGTPDecoding:
static inline int ScGTPDecoding(void)
{
return snort_conf->enable_gtp;//在解析配置的时候,设置为1
}
//--------------------------------------------------------------------
// decode.c::GTP
//--------------------------------------------------------------------
/* Function: DecodeGTP(uint8_t *, uint32_t, Packet *)
*
* GTP (GPRS Tunneling Protocol) is layered over UDP.
* Decode these (if present) and go to DecodeIPv6/DecodeIP.
*
*/
void DecodeGTP(const uint8_t *pkt, uint32_t len, Packet *p)
{
uint32_t header_len;
uint8_t next_hdr_type;
uint8_t version;
uint8_t ip_ver;
GTPHdr *hdr;
DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Start GTP decoding.\n"););
hdr = (GTPHdr *) pkt;
if (p->GTPencapsulated)
{
DecoderAlertEncapsulated(p, DECODE_GTP_MULTIPLE_ENCAPSULATION,
DECODE_GTP_MULTIPLE_ENCAPSULATION_STR,
pkt, len);
return;
}
else
{
p->GTPencapsulated = 1;
}
/*Check the length*/
if (len < GTP_MIN_LEN)//检查数据包长度
return;
/* We only care about PDU*/
if ( hdr->type != 255)
return;
/*Check whether this is GTP or GTP', Exit if GTP'*/
if (!(hdr->flag & 0x10))
return;
/*The first 3 bits are version number*/
version = (hdr->flag & 0xE0) >> 5;
switch (version)//检查版本
{
case 0: /*GTP v0 0版本*/
DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "GTP v0 packets.\n"););
header_len = GTP_V0_HEADER_LEN;//数据包头固定长度
/*Check header fields*/
if (len < header_len)
{
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
p->proto_bits |= PROTO_BIT__GTP;
/*Check the length field. */
if (len != ((unsigned int)ntohs(hdr->length) + header_len))
{
DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Calculated length %d != %d in header.\n",
len - header_len, ntohs(hdr->length)););
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
break;
case 1: /*GTP v1 1版本*/
DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "GTP v1 packets.\n"););
/*Check the length based on optional fields and extension header*/
if (hdr->flag & 0x07)
{
header_len = GTP_V1_HEADER_LEN;//1版本的头长度
/*Check optional fields*/
if (len < header_len)
{
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
next_hdr_type = *(pkt + header_len - 1);//下一个扩展属性的类型
/*Check extension headers*/
while (next_hdr_type)//循环解码扩展属性,然后更新header_len 的值
{
uint16_t ext_hdr_len;
/*check length before reading data*/
if (len < header_len + 4)
{
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
ext_hdr_len = *(pkt + header_len);
if (!ext_hdr_len)
{
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
/*Extension header length is a unit of 4 octets*/
header_len += ext_hdr_len * 4;
/*check length before reading data*/
if (len < header_len)
{
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
next_hdr_type = *(pkt + header_len - 1);
}
}
else
header_len = GTP_MIN_LEN;
p->proto_bits |= PROTO_BIT__GTP;
/*Check the length field. */
if (len != ((unsigned int)ntohs(hdr->length) + GTP_MIN_LEN))
{
DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Calculated length %d != %d in header.\n",
len - GTP_MIN_LEN, ntohs(hdr->length)););
DecoderEvent(p, EVARGS(GTP_BAD_LEN), 1, 1);
return;
}
break;
default:
DEBUG_WRAP(DebugMessage(DEBUG_DECODE, "Unknown protocol version.\n"););
return;
}
PushLayer(PROTO_GTP, p, pkt, header_len);//添加gtp头这一层,保存了这一层的相关信息,比如开始地址、长度、协议信息
if ( ScTunnelBypassEnabled(TUNNEL_GTP) )
Active_SetTunnelBypass();
len -= header_len;//去除gtp头长度,所以如果看起gtp解码,则可以顺利去除这一层,然后重新解码ip层和传输层
if (len > 0)//如果还有数据,继续处理
{
ip_ver = *(pkt+header_len) & 0xF0;//前面的截图,可以看出,内部的数据接下来为ip层
if (ip_ver == 0x40)//如果ipv4,调用DecodeIP进行解码
DecodeIP(pkt+header_len, len, p);
else if (ip_ver == 0x60)
DecodeIPV6(pkt+header_len, len, p);
p->packet_flags &= ~PKT_UNSURE_ENCAP;
}
}
大致的流程就这些了。
|