第四届“网鼎杯”白虎组官方资格赛-MISC02
考点
GTP信令协议、AES加解密
解题步骤
1、流量分析
在 C&C 通信流量包中,我们可以去观察 GTP 的 Echo Request/Response 请求格式。经过分析发现,teid 信元最有可能充当加密秘钥,而需要解密的加密内容则位于流量包的尾部 Raw 部分。
2、aes-ebc解密
已知有 aes-ebc 加密函数,现在需要依据它来编写相应的解密函数,以便对上述提到的处于流量包尾部 Raw 部分的加密内容进行解密操作,从而进一步分析和处理相关通信数据。
from scapy.all import IP, UDP, sniff, Raw, send, rdpcap
from scapy.contrib.gtp import GTP_U_Header
import struct
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
# 对文本进行填充,使其长度为16的倍数,填充字符为空格
def pad(text):
while len(text) % 16!= 0:
text += " "
return text
# 使用给定的密钥对密文进行解密的函数
def decrypt(key, ciphertext):
"""
使用AES-ECB模式对密文进行解密
参数:
key: 加密密钥,在代码中按大端序('>I')打包并处理成合适长度
ciphertext: 待解密的字节类型密文
返回:
解密后的文本,去除末尾空格后返回
"""
# 将密钥按大端序('>I')格式打包成字节串,然后填充到16字节长度(不足则补'\0')
key_bytes = struct.pack('>I', key)
key_bytes = key_bytes.ljust(16, b'\0')
# 创建AES加密器,使用ECB模式和默认后端
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
decryptor = cipher.decryptor()
# 对密文进行解密,先更新解密器状态,再完成最终的解密操作
decrypted = decryptor.update(ciphertext) + decryptor.finalize()
# 将解密后的字节串转换为字符串,并去除末尾的空格后返回
return decrypted.decode().strip()
3、读取密钥
使用scapy库,读取请求的teid为密钥,raw部分为密文内容。
from scapy.all import IP, UDP, sniff, Raw, send, rdpcap
from scapy.contrib.gtp import GTP_U_Header
import struct
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
# 对文本进行填充,使其长度为16的倍数,填充字符为空格
def pad(text):
while len(text) % 16!= 0:
text += " "
return text
# 使用给定的密钥对密文进行解密的函数
def decrypt(key, ciphertext):
"""
使用AES-ECB模式对密文进行解密
参数:
key: 加密密钥,在代码中按大端序('>I')打包并处理成合适长度
ciphertext: 待解密的字节类型密文
返回:
解密后的文本,去除末尾空格后返回
"""
# 将密钥按大端序('>I')格式打包成字节串,然后填充到16字节长度(不足则补'\0')
key_bytes = struct.pack('>I', key)
key_bytes = key_bytes.ljust(16, b'\0')
# 创建AES加密器,使用ECB模式和默认后端
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
decryptor = cipher.decryptor()
# 对密文进行解密,先更新解密器状态,再完成最终的解密操作
decrypted = decryptor.update(ciphertext) + decryptor.finalize()
# 将解密后的字节串转换为字符串,并去除末尾的空格后返回
return decrypted.decode().strip()
if __name__ == "__main__":
# 读取指定的数据包捕获文件(pcap格式)
cap = rdpcap("GTP.cap")
# 遍历捕获文件中的每一个数据包
for packet_index in range(len(cap)):
packet = cap[packet_index]
# 获取IP层数据,如果不存在IP层则跳过当前循环
ip_layer = packet.getlayer(IP)
if ip_layer is None:
continue
# 获取IP层中的第三个协议层(此处假设为GTP相关层,代码中命名为packet3,可能需要根据实际情况确认准确性)
packet3 = ip_layer[2]
# 获取可能作为密钥的teid值
key = packet3.teid
# 检查数据包中是否存在Raw层(可能包含加密数据),不存在则跳过当前循环
if Raw not in packet:
continue
# 获取Raw层的字节数据作为待解密的密文
ciphertext = bytes(packet[Raw])
# 调用解密函数进行解密
res = decrypt(key, ciphertext)
# 获取GTP类型字段(此处假设packet3中有gtp_type字段,同样需根据实际情况确认)
gtp_type = packet3.gtp_type
# 打印GTP类型和解密后的结果
print(gtp_type, res)
4、得到flag
最终对cap流量包中每个请求进行解密操作后得到完整的C&C通信明文流量,其中包含flag。