freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

第四届网鼎杯白虎组解密思路总结
2024-11-07 15:59:35
所属地 四川省

昨天白虎组的比赛刚刚结束,题目难度还是一如既往的难以言尽,本次分享一下解题思路和分析过程,从细小之处抽丝剥茧拿到flag。

MISC(杂项)

misc1

题目描述:

某5G研发单位接到5G系统瘫痪的告警,研究人员将其收发的流量进行监控,发现异常的报文,请对其分析。找到全部异常报文,将恶意报文中攻击者构造的teid按时间先后顺序进行拼接,找到全部由攻击者触发的异常报文进行拼接,所得出的拼接结果即为最终nag,例如,若按时间先后顺序排列的恶意报文中攻击者构造的teid“分别为0001​和0002​,则最终的fag值为'wdfag{00010002}~。

题目描述分析关键点:teid(这个是肯定要找到的);时间先后顺序,需要拼接且按照样例猜测是两个teid。

解题过程:

Wireshark打开UDF.cap,第一时间一眼很明显就能看到GTP(TCP)协议,不懂就GPT问,结果直接看到了参数包含了teid,这不就直接找到tied在哪了嘛。1730966056_672c722804496e0532916.png!small?17309660553841730966062_672c722e84e2f2551b503.png!small?1730966061897​接下来就是过滤GTP协议的流量1730966070_672c72361555f357f4a55.png!small?1730966069405​看了一下还挺多的,所以这里需要用到tshark进行提取,先选中过滤一下1730966076_672c723ca9af1d52f3612.png!small?1730966076144​完了之后可以准确看到参数为gtp.teid,这个后面提取需要用到1730966085_672c724548844454a8206.png!small?1730966084694用tsahrk进行提取和分析,打开csv,可以看到这两条异常数据很明显,直接转成10进制,按照顺序进行拼接就是flag了。

tshark.exe -r UPF.cap -T fields -e gtp.teid > ted.csv

1730966093_672c724d76cd6a472ba91.png!small?1730966092732​这里就是进制转换之后的结果,拼接后就是flag了。1730966100_672c72547dd68a03b4f2c.png!small?1730966099907​Misc2

题目描述:某通信运营商遭受到了攻击,但始终无法发现攻击过程,请你帮忙分析流量包,发现其中存在的问题。

题目分析关键点:这题没发现题目给啥提示,但是附件里面有一个encrypted.py的代码,所以结合题目说的无法发现攻击过程,猜测应该是流量加密了。关键就在这个encrypted.py的解密。

解题过程:

还是先打开GTP.cap流量包看看吧,发现全是GTP协议,而且数量也不多1730966111_672c725f8563936c32c09.png!small?1730966111348​追踪一下数据流,查看原始数据,确实是一串儿加密的,放gpt也不知道啥加密方式,流量分析就只能到这儿了。1730966117_672c72659b0b28998b251.png!small?1730966117058​转战看看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))

1730966845_672c753d8466366cc4225.png!small?1730966844924​Misc3

题目描述:某单位内网遭受攻击,请对提取到的网络流量数据进行分析。

题目分析:没有可用信息

解题过程:

先wireshark打开流量,追踪流看看,发现了攻击语句。​1730966482_672c73d285d78a6e18f3c.png!small?1730966482148​一看是伪协议,多看两个包看看规律,发现全是伪协议,尝试着把这些伪协议进行提取看看

tshark.exe -r UPF.cap -T fields -e urlencoded-form.value > value.csv

1730966490_672c73daaf1a0d7233d05.png!small?1730966490080​剩下的大家自己看吧,原理相似:https://www.ctfiot.com/142082.html

Misc4

题目描述:一天,某单位收到一个奇怪的文件,文件中的内容让人觉得存在安全隐患,于是你迅速联系我,邀请我同进行对文件进行分析。

题目分析:这道题没看出来啥

解题过程:

一共三个文件,打开第一个看看1730966497_672c73e109097b196f883.png!small?1730966496445​发现有zip和png,这不就妥妥文件分离嘛,binwalk梭哈,两个压缩包,一个图片有半个flag,另一个压缩包有密码1730966504_672c73e83934c2b40555b.png!small?1730966503564​看到这种情况,就两种可能:1、密码(试了不对) 2、掩码爆破1730966510_672c73ee169b14715bce4.png!small?1730966509222​直接爆破掩码,密码出来了1730966515_672c73f3c098f889a09be.png!small?1730966515068​解密后又是一张图片1730966521_672c73f98fd91aadf711a.png!small?1730966521014​图片的解法都是网上的老一套,先binwalk、formost啥的都试试,结果又分离出一张图片1730966529_672c74011f3fffc0f3ec7.png!small?1730966528243​遇到图片没啥好思路,不知道就所有解法用工具都淦一遍,乱拳打死老师傅。最后爆破宽高解出来了。1730966534_672c7406346df0f3435a3.png!small?1730966533544​最后两段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}​

# 漏洞 # 网络安全 # web安全 # CTF # 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录