CTF密码学实战:RSA加密流量分析与解密完全指南

admin 2026-01-23 13:36:57 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详解CTF中RSA加密流量分析与解密的实战流程。作者通过Wireshark提取TCP流,识别Base64编码结构,利用小模数RSA的脆弱性分解素数并计算私钥,最终解密数据验证签名以获取flag。内容涵盖流量分析、RSA数学原理及脚本编写,强调密钥长度对安全的重要性,适合密码学初学者学习。 综合评分: 100 文章分类: CTF,漏洞分析,数据安全,实战经验,安全工具


cover_image

CTF密码学实战:RSA加密流量分析与解密完全指南

原创

破镜安全 破镜安全

破镜安全

2026年1月23日 08:00 四川

CTF密码学实战:RSA加密流量分析与解密完全指南

前言

在CTF竞赛中,密码学题目往往结合多个知识点,既考察对加密算法的理解,也考验实际的分析能力。本文将通过ISCC 2017的一道真实题目,完整展示如何从网络流量包中提取加密数据,分析加密方式,最终通过RSA密码学知识破解并获取flag。

本文适合密码学初学者阅读,会详细解释每一步操作的原理和目的。

题目背景

拿到题目后,我们得到一个文件:Basic-06.pcapng。从文件扩展名可以看出,这是一个网络数据包捕获文件,通常由Wireshark或tcpdump等工具生成。

题目名称为”说我作弊需要证据”,这个略带挑衅的名字暗示我们需要从流量中找到”证据”——也就是隐藏在网络通信中的秘密信息。

第一步:流量文件初步分析

1.1 为什么要分析流量文件?

在现实场景中,很多敏感信息会通过网络传输,即使使用了加密,但如果加密方式存在漏洞或密钥强度不够,仍然可能被破解。这道题模拟的就是这样一个场景:攻击者截获了一段加密通信,需要尝试解密获取明文。

1.2 使用Wireshark/tshark分析

首先使用tshark(Wireshark的命令行版本)查看网络流量的基本信息:

tshark -r Basic-06.pcapng -q -z conv,tcp

执行后得到TCP会话统计信息:

TCP Conversations
192.168.0.13:31337 <-> 192.168.0.37:31338
&nbsp; 发送: 150包 10KB
&nbsp; 接收: 152包 28KB
&nbsp; 总计: 302包 38KB

分析结果:

  • 存在一个TCP连接,从192.168.0.13的31337端口到192.168.0.37的31338端口
  • 通信持续约16.5秒
  • 数据传输方向主要是192.168.0.13向192.168.0.37发送数据

这说明192.168.0.13是数据发送方(我们称为Alice),192.168.0.37是接收方(我们称为Bob)。

1.3 提取TCP流数据

接下来提取完整的TCP流内容:

tshark -r Basic-06.pcapng -q -z follow,tcp,ascii,0 > tcp_stream.txt

这个命令会把TCP会话中的所有数据按顺序提取出来,保存为文本文件。

打开tcp_stream.txt后,我们看到大量类似这样的内容:

121
U0VRID0gMTM7IERBVEEgPSAweD...
117
U0VRID0gMDsgREFUQSA9IDB4N...

每段数据前面有一个数字(表示数据长度),然后是一串由大小写字母、数字、加号和等号组成的字符串。

第二步:识别编码方式

2.1 Base64编码特征识别

看到这些字符串,有经验的安全研究人员会立即联想到Base64编码。Base64是一种常见的二进制到文本的编码方案,特征非常明显:

Base64编码特征:

  1. 字符集:A-Z, a-z, 0-9, +, /(共64个字符)
  2. 填充字符:使用=或==作为结尾(使长度成为4的倍数)
  3. 不含特殊字符(除了+、/、=)
  4. 编码后长度约为原始数据的4/3倍

2.2 Base64解码

让我们尝试解码第一行数据:

import&nbsp;base64

data =&nbsp;"U0VRID0gMTM7IERBVEEgPSAweD..."
decoded = base64.b64decode(data)
print(decoded.decode('utf-8'))

解码结果:

SEQ = 13; DATA = 0x3b04b26a0adada2f67326bb0c5d6L; SIG = 0x2e5ab24f9dc21df406a87de0b3b4L;

太好了!解码成功,并且得到了结构化的数据。

2.3 数据结构分析

仔细观察解码后的内容,可以发现三个关键字段:

  1. SEQ(Sequence):序列号,值为整数(如13)
  2. DATA:以0x开头的十六进制大整数,末尾有L(Python 2中长整型标记)
  3. SIG(Signature):同样是0x开头的十六进制大整数

这种结构强烈暗示:

  • SEQ用于标识数据包顺序
  • DATA可能是加密后的密文
  • SIG可能是数字签名

第三步:批量提取和解析数据

3.1 编写提取脚本

我们需要从TCP流文件中提取所有base64数据并解码:

import&nbsp;base64
import&nbsp;re

# 读取TCP流文件
with&nbsp;open('tcp_stream.txt',&nbsp;'r')&nbsp;as&nbsp;f:
&nbsp; &nbsp; content = f.read()

# 使用正则表达式提取所有base64行
base64_lines = re.findall(r'^[A-Za-z0-9+/=]+$', content, re.MULTILINE)

print(f"找到&nbsp;{len(base64_lines)}&nbsp;行base64数据")

运行结果显示找到了298行base64数据。

3.2 解析数据包

对每一行进行解码和字段提取:

packets = []

for&nbsp;line&nbsp;in&nbsp;base64_lines:
&nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# Base64解码
&nbsp; &nbsp; &nbsp; &nbsp; decoded = base64.b64decode(line).decode('utf-8', errors='ignore')

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 提取SEQ字段
&nbsp; &nbsp; &nbsp; &nbsp; seq_match = re.search(r'SEQ\s*=\s*(\d+)', decoded)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 提取DATA字段
&nbsp; &nbsp; &nbsp; &nbsp; data_match = re.search(r'DATA\s*=\s*(0x[0-9a-fA-F]+)', decoded)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 提取SIG字段
&nbsp; &nbsp; &nbsp; &nbsp; sig_match = re.search(r'SIG\s*=\s*(0x[0-9a-fA-F]+)', decoded)

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;seq_match&nbsp;and&nbsp;data_match&nbsp;and&nbsp;sig_match:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; packets.append({
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'seq': int(seq_match.group(1)),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'data': data_match.group(1),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'sig': sig_match.group(1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp;&nbsp;except:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass

print(f"成功解析&nbsp;{len(packets)}&nbsp;个有效数据包")

运行结果:成功解析了148个有效数据包。

3.3 数据包统计分析

对解析出的数据包进行统计:

# 统计SEQ分布
seq_set = set([p['seq']&nbsp;for&nbsp;p&nbsp;in&nbsp;packets])
print(f"SEQ范围:&nbsp;{min(seq_set)}&nbsp;-&nbsp;{max(seq_set)}")
print(f"唯一SEQ数量:&nbsp;{len(seq_set)}")

结果显示:

  • SEQ范围:0到33
  • 共34个不同的序列号
  • 总共148个数据包,说明有些SEQ有多个数据包(重复发送或干扰数据)

这个发现很重要:最终的消息可能只有34个字符,而148个包中大部分是重复或干扰数据。

第四步:识别加密算法

4.1 从数据特征判断

观察DATA和SIG字段:

  • 都是非常大的整数(100位左右)
  • 每个包的DATA和SIG都不同
  • SEQ相同的包,DATA和SIG也不同

这些特征强烈暗示使用了RSA加密

RSA的特点:

  1. 处理大整数(通常几百到几千位)
  2. 密文长度与明文相关,但相近明文会产生完全不同的密文
  3. 常与数字签名一起使用

4.2 RSA加密通信模型

在RSA加密通信中,典型的流程是:

发送方(Alice) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;接收方(Bob)
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | 生成消息M &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | 用Alice私钥签名 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | SIG = M^d_alice mod n_alice
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | 用Bob公钥加密 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | DATA = M^e_bob mod n_bob |
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; |----[DATA, SIG]---------->|
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| 用Bob私钥解密
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| M = DATA^d_bob mod n_bob
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| 用Alice公钥验证签名
&nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| M ?= SIG^e_alice mod n_alice

这样设计的目的:

  • 加密(DATA):保证数据机密性,只有Bob能解密
  • 签名(SIG):保证数据真实性,证明确实来自Alice且未被篡改

第五步:RSA密码学基础

在尝试破解之前,我们需要理解RSA的数学原理。

5.1 RSA算法核心概念

密钥生成:

  1. 选择两个大素数p和q
  2. 计算n = p × q(RSA模数)
  3. 计算φ(n) = (p-1) × (q-1)(欧拉函数)
  4. 选择公钥指数e(常用65537,即0x10001)
  5. 计算私钥指数d,满足:e × d ≡ 1 (mod φ(n))

加密解密:

  • 加密:C = M^e mod n
  • 解密:M = C^d mod n

数学原理:根据欧拉定理,当gcd(M, n) = 1时:

M^φ(n) ≡ 1 (mod n)

因此:

C^d = (M^e)^d = M^(ed) = M^(1+k×φ(n)) = M × (M^φ(n))^k ≡ M × 1^k ≡ M (mod n)

5.2 RSA安全性基础

RSA的安全性依赖于:

  • 大整数分解难题:已知n,很难分解出p和q
  • 如果n足够大(如2048位),分解几乎不可能

但是,如果n太小(如100位),就可以被分解!

5.3 模逆运算

计算私钥d需要求e在模φ(n)下的逆元,使用扩展欧几里得算法:

def&nbsp;iterative_egcd(a, b):
&nbsp; &nbsp;&nbsp;"""扩展欧几里得算法
&nbsp; &nbsp; 返回 (gcd, x, y) 使得 a*x + b*y = gcd(a,b)
&nbsp; &nbsp; """
&nbsp; &nbsp; x, y, u, v =&nbsp;0,&nbsp;1,&nbsp;1,&nbsp;0
&nbsp; &nbsp;&nbsp;while&nbsp;a !=&nbsp;0:
&nbsp; &nbsp; &nbsp; &nbsp; q, r = b // a, b % a &nbsp;# q是商,r是余数
&nbsp; &nbsp; &nbsp; &nbsp; m, n = x - u * q, y - v * q
&nbsp; &nbsp; &nbsp; &nbsp; b, a, x, y, u, v = a, r, u, v, m, n
&nbsp; &nbsp;&nbsp;return&nbsp;b, x, y

def&nbsp;modinv(a, m):
&nbsp; &nbsp;&nbsp;"""计算a在模m下的逆元
&nbsp; &nbsp; 即找到x使得 a*x ≡ 1 (mod m)
&nbsp; &nbsp; """
&nbsp; &nbsp; g, x, y = iterative_egcd(a, m)
&nbsp; &nbsp;&nbsp;if&nbsp;g !=&nbsp;1:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None&nbsp;&nbsp;# 不存在逆元
&nbsp; &nbsp;&nbsp;return&nbsp;x % m

算法原理:扩展欧几里得算法在计算gcd(a,b)的同时,找到x和y使得ax + by = gcd(a,b)。当gcd(a,m) = 1时,ax + my = 1,两边对m取模得到ax ≡ 1 (mod m),因此x就是a的模逆。

第六步:获取RSA参数

6.1 观察密文大小

我们先看看DATA字段的大小:

# 取第一个数据包的DATA
data_hex =&nbsp;"0x3b04b26a0adada2f67326bb0c5d6"
data_int = int(data_hex,&nbsp;16)
print(f"DATA整数值:&nbsp;{data_int}")
print(f"位数:&nbsp;{len(bin(data_int)) -&nbsp;2}&nbsp;bits")

发现密文大约是100位,这意味着RSA模数n也大约是100位。

关键发现:100位的RSA模数太小了,可以被分解!

6.2 分解RSA模数

对于小的RSA模数,可以使用在线工具(如factordb.com)或Yafu等工具分解。

在CTF题目中,通常会提供或能够找到分解后的素数。经过分析和查找,我们得到:

Bob的RSA参数(用于解密DATA):

B_p =&nbsp;49662237675630289
B_q =&nbsp;62515288803124247
B_n = B_p * B_q
# n = 3104649130901425335933838103517383

验证:

print(B_n ==&nbsp;3104649130901425335933838103517383) &nbsp;# True

Alice的RSA参数(用于验证签名):

A_p =&nbsp;38456719616722997
A_q =&nbsp;44106885765559411
A_n = A_p * A_q
# n = 1696206139052948924304948333474767

6.3 为什么需要两组参数?

在实际的RSA通信中:

  • Bob有自己的密钥对(n_bob, e_bob, d_bob),用于接收加密消息
  • Alice有自己的密钥对(n_alice, e_alice, d_alice),用于生成数字签名

因此:

  • DATA是用Bob的公钥加密的,需要Bob的私钥解密
  • SIG是用Alice的私钥签名的,需要Alice的公钥验证

6.4 公钥指数e

RSA中的公钥指数e通常使用标准值:

e =&nbsp;0x10001&nbsp;&nbsp;# 十六进制,等于十进制的65537

为什么选择65537?

  1. 是素数,保证与φ(n)互质
  2. 二进制表示为10000000000000001,只有两个1
  3. 模幂运算时可以优化,计算速度快
  4. 经过长期实践验证,是安全且高效的选择

第七步:计算私钥

7.1 计算欧拉函数

有了p和q,可以计算欧拉函数:

B_phi = (B_p -&nbsp;1) * (B_q -&nbsp;1)
print(f"φ(n) =&nbsp;{B_phi}")
# φ(n) = 3104649130901425223756311624762848

欧拉函数的意义:φ(n)表示小于n且与n互质的正整数的个数。对于n = p×q(p、q为素数),φ(n) = (p-1)(q-1)。

7.2 计算私钥d

使用模逆运算计算私钥:

d = modinv(e, B_phi)
print(f"私钥 d =&nbsp;{d}")
# d = 1427000713644866747260499795119265

7.3 验证私钥正确性

验证e和d是否满足关系:

verification = (e * d) % B_phi
print(f"(e × d) mod φ(n) =&nbsp;{verification}")
# 应该等于1

如果结果是1,说明私钥计算正确!

为什么要满足这个关系?因为在解密时:

M = C^d = (M^e)^d = M^(ed) mod n

要使M^(ed) ≡ M (mod n),根据欧拉定理,需要ed ≡ 1 (mod φ(n))。

第八步:解密数据

8.1 解密原理

现在我们有了:

  • Bob的私钥d
  • Bob的模数n
  • 148个加密的数据包

解密公式:

M = DATA^d mod n

使用Python的内置pow函数,支持模幂运算:

plaintext = pow(ciphertext, d, n)

8.2 签名验证原理

同时需要验证签名,确保数据未被篡改:

M_verify = SIG^e mod n_alice

如果M == M_verify,说明:

  1. 签名确实是Alice用私钥生成的
  2. 数据在传输过程中没有被修改

数字签名的数学原理:Alice生成签名时:SIG = M^d_alice mod n_alice Bob验证时:SIG^e_alice = (M^d_alice)^e_alice = M^(d×e) ≡ M (mod n_alice)

8.3 编写解密代码

A_n =&nbsp;1696206139052948924304948333474767
e =&nbsp;0x10001

decrypted_messages = []

for&nbsp;packet&nbsp;in&nbsp;packets:
&nbsp; &nbsp;&nbsp;# 将十六进制字符串转为整数
&nbsp; &nbsp; data_ciphertext = int(packet['data'],&nbsp;16)
&nbsp; &nbsp; sig_value = int(packet['sig'],&nbsp;16)

&nbsp; &nbsp;&nbsp;# 使用Bob私钥解密DATA
&nbsp; &nbsp; plaintext = pow(data_ciphertext, d, B_n)

&nbsp; &nbsp;&nbsp;# 使用Alice公钥验证签名
&nbsp; &nbsp; sig_verified = pow(sig_value, e, A_n)

&nbsp; &nbsp;&nbsp;# 只保留签名验证通过的数据
&nbsp; &nbsp;&nbsp;if&nbsp;plaintext == sig_verified:
&nbsp; &nbsp; &nbsp; &nbsp; decrypted_messages.append({
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'seq': packet['seq'],
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'value': plaintext,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'char': chr(plaintext)&nbsp;if&nbsp;plaintext <&nbsp;256&nbsp;else&nbsp;'?'
&nbsp; &nbsp; &nbsp; &nbsp; })

print(f"成功解密并验证&nbsp;{len(decrypted_messages)}&nbsp;个字符")

8.4 解密结果分析

运行后发现:

  • 总数据包:148个
  • 验证通过:33个
  • 验证通过率:22.3%

这说明148个包中有很多是干扰数据或重复数据,只有33个包含有效信息。

查看解密出的字符:

# 按SEQ排序
decrypted_messages.sort(key=lambda&nbsp;x: x['seq'])

# 查看前10个字符
for&nbsp;msg&nbsp;in&nbsp;decrypted_messages[:10]:
&nbsp; &nbsp; print(f"SEQ&nbsp;{msg['seq']:2d}: ASCII&nbsp;{msg['value']:3d}&nbsp;= '{msg['char']}'")

输出:

SEQ &nbsp;0: ASCII 102 = 'f'
SEQ &nbsp;1: ASCII 108 = 'l'
SEQ &nbsp;2: ASCII &nbsp;97 = 'a'
SEQ &nbsp;3: ASCII 103 = 'g'
SEQ &nbsp;4: ASCII 123 = '{'
SEQ &nbsp;5: ASCII 110 = 'n'
SEQ &nbsp;6: ASCII &nbsp;48 = '0'
SEQ &nbsp;7: ASCII 116 = 't'
SEQ &nbsp;8: ASCII 104 = 'h'
SEQ &nbsp;9: ASCII &nbsp;49 = '1'

看起来像是flag的开头!

第九步:组装最终flag

9.1 按序组装

将所有解密的字符按SEQ顺序拼接:

flag =&nbsp;''.join([msg['char']&nbsp;for&nbsp;msg&nbsp;in&nbsp;decrypted_messages])
print(f"Flag:&nbsp;{flag}")

9.2 最终结果

flag{n0th1ng_t0_533_h3r3_m0v3_0n}

成功!这就是题目的flag。

Flag含义:“nothing to see here move on” – 这里没什么可看的,继续前进吧

这是一个常见的CTF幽默风格的flag,暗示题目已经解决。

完整解题脚本

将所有步骤整合成完整的脚本:

# coding=utf-8
"""
RSA流量分析解密脚本
"""
import&nbsp;base64
import&nbsp;re

def&nbsp;iterative_egcd(a, b):
&nbsp; &nbsp;&nbsp;"""扩展欧几里得算法"""
&nbsp; &nbsp; x, y, u, v =&nbsp;0,&nbsp;1,&nbsp;1,&nbsp;0
&nbsp; &nbsp;&nbsp;while&nbsp;a !=&nbsp;0:
&nbsp; &nbsp; &nbsp; &nbsp; q, r = b // a, b % a
&nbsp; &nbsp; &nbsp; &nbsp; m, n = x - u * q, y - v * q
&nbsp; &nbsp; &nbsp; &nbsp; b, a, x, y, u, v = a, r, u, v, m, n
&nbsp; &nbsp;&nbsp;return&nbsp;b, x, y

def&nbsp;modinv(a, m):
&nbsp; &nbsp;&nbsp;"""计算模逆"""
&nbsp; &nbsp; g, x, y = iterative_egcd(a, m)
&nbsp; &nbsp;&nbsp;if&nbsp;g !=&nbsp;1:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;None
&nbsp; &nbsp;&nbsp;return&nbsp;x % m

def&nbsp;main():
&nbsp; &nbsp; print("="&nbsp;*&nbsp;60)
&nbsp; &nbsp; print("RSA流量分析解密")
&nbsp; &nbsp; print("="&nbsp;*&nbsp;60)

&nbsp; &nbsp;&nbsp;# 步骤1: 读取并解析TCP流
&nbsp; &nbsp;&nbsp;with&nbsp;open('tcp_stream.txt',&nbsp;'r')&nbsp;as&nbsp;f:
&nbsp; &nbsp; &nbsp; &nbsp; content = f.read()

&nbsp; &nbsp;&nbsp;# 步骤2: 提取base64数据
&nbsp; &nbsp; base64_lines = re.findall(r'^[A-Za-z0-9+/=]+$', content, re.MULTILINE)
&nbsp; &nbsp; print(f"\n[*] 找到&nbsp;{len(base64_lines)}&nbsp;行base64数据")

&nbsp; &nbsp;&nbsp;# 步骤3: 解码并解析
&nbsp; &nbsp; packets = []
&nbsp; &nbsp;&nbsp;for&nbsp;line&nbsp;in&nbsp;base64_lines:
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decoded = base64.b64decode(line).decode('utf-8', errors='ignore')
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; seq_match = re.search(r'SEQ\s*=\s*(\d+)', decoded)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data_match = re.search(r'DATA\s*=\s*(0x[0-9a-fA-F]+)', decoded)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sig_match = re.search(r'SIG\s*=\s*(0x[0-9a-fA-F]+)', decoded)

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;seq_match&nbsp;and&nbsp;data_match&nbsp;and&nbsp;sig_match:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; packets.append({
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'seq': int(seq_match.group(1)),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'data': data_match.group(1),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'sig': sig_match.group(1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass

&nbsp; &nbsp; print(f"[*] 解析出&nbsp;{len(packets)}&nbsp;个有效数据包")

&nbsp; &nbsp;&nbsp;# 步骤4: 设置RSA参数
&nbsp; &nbsp; B_p =&nbsp;49662237675630289
&nbsp; &nbsp; B_q =&nbsp;62515288803124247
&nbsp; &nbsp; B_n =&nbsp;3104649130901425335933838103517383
&nbsp; &nbsp; B_phi = (B_p -&nbsp;1) * (B_q -&nbsp;1)

&nbsp; &nbsp; A_n =&nbsp;1696206139052948924304948333474767
&nbsp; &nbsp; e =&nbsp;0x10001

&nbsp; &nbsp;&nbsp;# 步骤5: 计算私钥
&nbsp; &nbsp; d = modinv(e, B_phi)
&nbsp; &nbsp; print(f"[*] 计算私钥: d =&nbsp;{d}")

&nbsp; &nbsp;&nbsp;# 步骤6: 解密并验证
&nbsp; &nbsp; decrypted = []
&nbsp; &nbsp;&nbsp;for&nbsp;packet&nbsp;in&nbsp;packets:
&nbsp; &nbsp; &nbsp; &nbsp; data_c = int(packet['data'],&nbsp;16)
&nbsp; &nbsp; &nbsp; &nbsp; data_m = pow(data_c, d, B_n)

&nbsp; &nbsp; &nbsp; &nbsp; sig_c = int(packet['sig'],&nbsp;16)
&nbsp; &nbsp; &nbsp; &nbsp; sig_m = pow(sig_c, e, A_n)

&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;data_m == sig_m:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; decrypted.append({
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'seq': packet['seq'],
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'char': chr(data_m)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })

&nbsp; &nbsp; print(f"[*] 成功解密&nbsp;{len(decrypted)}&nbsp;个字符")

&nbsp; &nbsp;&nbsp;# 步骤7: 组装flag
&nbsp; &nbsp; decrypted.sort(key=lambda&nbsp;x: x['seq'])
&nbsp; &nbsp; flag =&nbsp;''.join([x['char']&nbsp;for&nbsp;x&nbsp;in&nbsp;decrypted])

&nbsp; &nbsp; print("\n"&nbsp;+&nbsp;"="&nbsp;*&nbsp;60)
&nbsp; &nbsp; print(f"Flag:&nbsp;{flag}")
&nbsp; &nbsp; print("="&nbsp;*&nbsp;60)

if&nbsp;__name__ ==&nbsp;'__main__':
&nbsp; &nbsp; main()

技术总结

核心知识点

通过这道题,我们学习了以下核心技术:

1. 网络流量分析

  • 使用Wireshark/tshark提取TCP流数据
  • 识别和分析网络通信模式
  • 从数据包中提取有用信息

2. 编码识别与解码

  • Base64编码的特征识别
  • 使用Python进行批量解码
  • 正则表达式提取结构化数据

3. RSA密码学

  • RSA加密解密的数学原理
  • 欧拉定理和模运算
  • 公钥加密与私钥解密的对应关系

4. 数字签名

  • 数字签名的生成与验证原理
  • 签名如何保证数据完整性和真实性
  • 加密与签名的组合使用

5. 密码分析

  • 小模数RSA的脆弱性
  • 大整数分解攻击
  • 如何计算RSA私钥

6. Python密码学编程

  • 扩展欧几里得算法实现
  • 模逆运算
  • 大整数的模幂运算

关键技术细节

为什么可以破解这个RSA?

  1. RSA模数只有约100位,太小
  2. 使用工具可以分解出p和q
  3. 有了p和q就能计算φ(n)
  4. 从φ(n)和e可以计算出d
  5. 有了私钥d就能解密所有数据

为什么需要签名验证?

  1. 148个数据包中混有干扰数据
  2. 签名验证可以过滤出真实数据
  3. 确保数据来自正确的发送方
  4. 确保数据在传输中未被篡改

为什么SEQ会重复?

  1. 可能是网络重传机制
  2. 可能是故意添加的干扰数据
  3. 通过签名验证可以识别有效数据
  4. 每个SEQ只需要保留一个有效字符

安全启示

这道题给我们的安全启示:

1. 密钥长度至关重要

  • 100位RSA模数极不安全
  • 实际应用中应使用至少2048位
  • 更高安全需求应使用4096位

2. 流量加密不是万能的

  • 即使使用加密,弱密钥仍可能被破解
  • 需要结合多种安全措施
  • 定期更新密钥和算法

3. 数字签名的重要性

  • 签名能够验证数据来源
  • 能够检测数据是否被篡改
  • 加密和签名应配合使用

4. 安全实践建议

  • 使用标准的密码库(如OpenSSL)
  • 不要自己实现密码算法
  • 保持密码学知识更新
  • 定期进行安全审计

进阶学习建议

如果你对密码学感兴趣,建议按以下路线深入学习:

基础数学:

  • 数论基础(素数、同余、模运算)
  • 群论、环论基础
  • 欧拉定理、费马小定理
  • 中国剩余定理

密码学算法:

  • 对称加密(AES、DES、ChaCha20)
  • 非对称加密(RSA、ECC、ElGamal)
  • 哈希函数(SHA系列、MD5)
  • 消息认证码(HMAC)

密码分析:

  • RSA攻击方法(小指数攻击、共模攻击、侧信道攻击)
  • 分组密码分析(差分分析、线性分析)
  • 哈希碰撞攻击
  • 时序攻击和能量分析

实践工具:

  • Python密码学库(pycryptodome、cryptography)
  • SageMath数学计算系统
  • OpenSSL命令行工具
  • RSATool、yafu等专用工具

推荐资源:

  • 书籍:《应用密码学》(Bruce Schneier)
  • 在线课程:Coursera的密码学课程(斯坦福大学)
  • CTF平台:CryptoHack、cryptopals
  • 论文:经典密码学论文阅读

结语

这道题目综合考察了网络流量分析、编码识别、RSA密码学、数字签名等多个知识点,是一道非常优秀的CTF密码学题目。通过实际动手解决这类问题,我们不仅能够理解密码学理论,更能够培养实际的安全分析能力。

密码学是信息安全的基石,理解其原理对于从事安全工作至关重要。希望通过本文的详细讲解,能够帮助初学者建立起对RSA加密、数字签名和密码分析的基本认识。

在实际工作中,我们要始终记住:安全不是一个点,而是一个系统。即使使用了加密技术,如果密钥管理不当、算法选择错误或参数设置不合理,仍然会导致安全问题。只有深入理解密码学原理,才能在实践中正确使用密码技术,保护信息安全。

继续学习,不断实践,你也能够成为密码学和信息安全领域的专家。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:破镜安全 破镜安全 破镜安全《CTF密码学实战:RSA加密流量分析与解密完全指南》

评论:0   参与:  0