昨天白虎组的比赛刚刚结束,题目难度还是一如既往的难以言尽,本次分享一下解题思路和分析过程,从细小之处抽丝剥茧拿到flag。
MISC(杂项)
misc1
题目描述:
某5G研发单位接到5G系统瘫痪的告警,研究人员将其收发的流量进行监控,发现异常的报文,请对其分析。找到全部异常报文,将恶意报文中攻击者构造的teid按时间先后顺序进行拼接,找到全部由攻击者触发的异常报文进行拼接,所得出的拼接结果即为最终nag,例如,若按时间先后顺序排列的恶意报文中攻击者构造的teid“分别为0001和0002,则最终的fag值为'wdfag{00010002}~。
题目描述分析关键点:teid(这个是肯定要找到的);时间先后顺序,需要拼接且按照样例猜测是两个teid。
解题过程:
Wireshark打开UDF.cap,第一时间一眼很明显就能看到GTP(TCP)协议,不懂就GPT问,结果直接看到了参数包含了teid,这不就直接找到tied在哪了嘛。接下来就是过滤GTP协议的流量看了一下还挺多的,所以这里需要用到tshark进行提取,先选中过滤一下完了之后可以准确看到参数为gtp.teid,这个后面提取需要用到用tsahrk进行提取和分析,打开csv,可以看到这两条异常数据很明显,直接转成10进制,按照顺序进行拼接就是flag了。
tshark.exe -r UPF.cap -T fields -e gtp.teid > ted.csv
这里就是进制转换之后的结果,拼接后就是flag了。Misc2
题目描述:某通信运营商遭受到了攻击,但始终无法发现攻击过程,请你帮忙分析流量包,发现其中存在的问题。
题目分析关键点:这题没发现题目给啥提示,但是附件里面有一个encrypted.py的代码,所以结合题目说的无法发现攻击过程,猜测应该是流量加密了。关键就在这个encrypted.py的解密。
解题过程:
还是先打开GTP.cap流量包看看吧,发现全是GTP协议,而且数量也不多追踪一下数据流,查看原始数据,确实是一串儿加密的,放gpt也不知道啥加密方式,流量分析就只能到这儿了。转战看看encrypted.py脚本里面的加密方式,丢给了gpt进行了分析,发现是使用了ECB(电子密码本)模式的AES加密。
以下是加密脚本注释,方便大家直接看懂:
# 导入必要的模块
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes # 导入Cipher类以及AES算法和ECB模式
from cryptography.hazmat.backends import default_backend # 导入默认的后端
import struct # 导入struct模块,用于处理Python值与C结构体之间的转换
# 定义一个填充函数,用于将明文填充到AES块大小的倍数(AES块大小为16字节)
def pad(text):
# 使用空格字符将文本填充到长度是16的倍数
while len(text) % 16 != 0:
text += ' '
return text
# 定义一个加密函数,接受密钥和明文作为输入
def encrypt(key, plaintext):
# 将整数密钥转换为4字节的二进制数据,然后填充到16字节
key_bytes = struct.pack('>I', key) # '>I'表示无符号大端整数(4字节)
key_bytes = key_bytes.ljust(16, b'\0') # 使用空字节填充到16字节
# 创建一个Cipher对象,使用AES算法、ECB模式和默认后端
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
# 创建一个加密器对象
encryptor = cipher.encryptor()
# 对明文进行填充,然后编码为二进制数据
padded_plaintext = pad(plaintext).encode()
# 使用加密器对填充后的明文进行加密
# update方法用于处理输入数据,可能返回部分密文;finalize方法用于完成加密并返回剩余的密文
encrypted = encryptor.update(padded_plaintext) + encryptor.finalize()
# 返回加密后的密文
return encrypted
# 主程序入口
if __name__ == "__main__":
# 定义密钥和明文
key = 1 # 注意:这里使用整数1作为密钥,这在实践中是不安全的,因为密钥太短且缺乏随机性
msg = "123" # 明文消息
# 调用加密函数并打印加密后的密文(以二进制形式表示)
print(encrypt(key, msg))
现在知道了加密方式以及密文,这里用的也是GTP协议,所以就猜测了密钥是teid,最后gpt辅助编写了以下解密脚本:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import struct
def unpad(text):
# 移除末尾的空格填充
return text.rstrip(b' ')
def decrypt(key, ciphertext):
# 将整数密钥转换为4字节的二进制数据,然后填充到16字节
key_bytes = struct.pack('>I', key).ljust(16, b'\0')
# 创建一个Cipher对象,使用AES算法、ECB模式和默认后端
cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
# 创建一个解密器对象
decryptor = cipher.decryptor()
# 使用解密器对密文进行解密
decrypted = decryptor.update(ciphertext) + decryptor.finalize()
# 移除空格填充
decrypted = unpad(decrypted)
# 将解密后的二进制数据解码为字符串(如果原始明文是文本的话)
# 注意:这里假设明文是UTF-8编码的文本,如果不是,请替换为正确的编码
try:
plaintext = decrypted.decode('utf-8')
except UnicodeDecodeError:
# 如果解密后的数据不是有效的UTF-8编码文本,则可能返回原始二进制数据
# 或者根据需要处理错误(例如,记录日志、返回None等)
plaintext = None # 或者 return decrypted 直接返回二进制数据
return plaintext
if __name__ == "__main__":
key = 475070864
ciphertext = bytes.fromhex(
'4ff7909b1d1e3e1ef33dd958adf1f4fb25306274720f807c4252beaaa1fe31ad867ec46c1f48fa734de206574d3189f1')
print(decrypt(key, ciphertext))
Misc3
题目描述:某单位内网遭受攻击,请对提取到的网络流量数据进行分析。
题目分析:没有可用信息
解题过程:
先wireshark打开流量,追踪流看看,发现了攻击语句。一看是伪协议,多看两个包看看规律,发现全是伪协议,尝试着把这些伪协议进行提取看看
tshark.exe -r UPF.cap -T fields -e urlencoded-form.value > value.csv
剩下的大家自己看吧,原理相似:https://www.ctfiot.com/142082.html
Misc4
题目描述:一天,某单位收到一个奇怪的文件,文件中的内容让人觉得存在安全隐患,于是你迅速联系我,邀请我同进行对文件进行分析。
题目分析:这道题没看出来啥
解题过程:
一共三个文件,打开第一个看看发现有zip和png,这不就妥妥文件分离嘛,binwalk梭哈,两个压缩包,一个图片有半个flag,另一个压缩包有密码看到这种情况,就两种可能:1、密码(试了不对) 2、掩码爆破直接爆破掩码,密码出来了解密后又是一张图片图片的解法都是网上的老一套,先binwalk、formost啥的都试试,结果又分离出一张图片遇到图片没啥好思路,不知道就所有解法用工具都淦一遍,乱拳打死老师傅。最后爆破宽高解出来了。最后两段flag凑一凑就完整了
Crypt(密码学)
Crypt01
题目描述:小明是一名数学专业的学生,某一号师给他出了一道题,并在题目中隐藏了秘密,你能帮他解开题目吗?
解题过程:
实力不允许我解出这么高端的题目。
Crypt02
题目描述:小明说"我有量了计算机,你有吗?",你决定借他所谓的"量子计算机,"进行安全测试。
题目分析:只能从附件里面看出是rsa加密,pubkey.txt是解密需要用到的东西,另外一个应该是要解密的文件
解题过程:
分析源代码,可以看出是加密一个名为flag.txt的文件。它首先生成了两个大素数p和q,然后计算它们的乘积N作为模数,接着使用常见的公钥指数e = 65537。最后,它将公钥(e和N)保存到pubkey.txt文件中,并将加密后的消息保存到cipher.txt文件中。
Tip:密码学的题目主要就跟大家在学校做数学题目一样。
整理一下已知条件:pubkey文件中已经有了e和N,加密文件是cipher.txt
未知条件:p、q
# 导入必要的库函数
from Crypto.Util.number import getPrime, isPrime # 用于生成和检查素数
# 定义密钥的位数
nbits = 2048 # 总位数,用于生成p和q
gbits = 1000 # 中间素数的位数,用于生成p和q的特殊形式
# 生成一个中间素数g
g = getPrime(int(gbits))
# 生成第一个大素数p
while True:
# 生成一个较小的素数a
a = getPrime(int(nbits * 0.5) - gbits)
# 根据特殊形式计算p
p = 2 * g * a + 1
# 检查p是否为素数
if isPrime(p):
break # 如果是,则跳出循环
# 生成第二个大素数q,过程与生成p类似,但要确保q != p
while True:
b = getPrime(int(nbits * 0.5) - gbits) # 生成另一个较小的素数b
q = 2 * g * b + 1 # 根据特殊形式计算q
# 检查q是否为素数且q != p
if p != q and isPrime(q):
break # 如果是,则跳出循环
# 计算模数N,它是p和q的乘积
N = p * q
# 定义公钥指数e
e = 65537 # 常用的公钥指数值
# 定义字符串与整数之间的转换函数
def str2int(s):
# 将字符串编码为latin-1,然后转换为十六进制字符串,最后转换为整数
return int(s.encode('latin-1').hex(), 16)
def int2str(i):
# 将整数转换为十六进制字符串,确保长度为偶数,然后解码为latin-1字符串
tmp = hex(i)[2:] # 去除'0x'前缀
if len(tmp) % 2 == 1:
tmp = '0' + tmp # 如果长度为奇数,则在前面补0
return bytes.fromhex(tmp).decode('latin-1') # 转换为字节并解码为字符串
# 保存公钥到文件
with open('pubkey.txt', 'w') as f:
f.write(str(e) + '\n') # 写入公钥指数e
f.write(str(N) + '\n') # 写入模数N
# 读取flag.txt文件并加密其内容
with open('flag.txt') as f:
plain = str2int(f.read()) # 将文件内容转换为整数
# 使用RSA加密算法加密明文
c = pow(plain, e, N) # 计算密文c = plain^e % N
# 保存加密后的密文到文件
with open('cipher.txt', 'wb') as f:
# 将密文转换为字符串并编码为latin-1,然后写入文件
f.write(int2str(c).encode('latin-1'))
RSA加密特点就是N=p*q,解密要素就是得找到p和q两个因素。
不太懂RSA加密核心思想的可以参考这篇文章:
https://jayxv.github.io/2019/11/11/%E5%AF%86%E7%A0%81%E5%AD%A6%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8B%E6%B5%85%E6%9E%90Pollard's%20rho%20algorithm%E5%8F%8A%E5%85%B6%E5%BA%94%E7%94%A8/
所以目前的首要目标是的想办法解出p和q,
p,q有了,pub文件中给65537
最后根据这三个,rsa解出txt,脚本是上面这个wdflag{0b239acd8d741d855dfd1045258a5a6c2b188698690e54afa7a3dce08b62b942}