2024第八届御网杯信息安全网络大赛线上WP详解(misc+cryoto)(详解-思路-脚本)
路baby 2024-09-30 17:07:04 阅读 79
芜湖~
首届御网杯线上和ISCC分开进行 但还是用的ISCC的页面差评 嘻嘻
又是玄乎的一天
以下是我自己的一些思路和解析 有什么问题或者建议随时都可以联系我
目录
附件
# Misc
##Notice
##编码转换
Brainfuck编码
jsfuck编码
Ook! 编码
##bluetooth
导出压缩包
第一种
第二种
第三种
注意
进行异或
整合脚本解析
##coding
#Crypto
##不小心
原题目脚本注释
##Justmath
原脚本注释
第一部分:使用SageMath解方程恢复部分flag
第二部分:使用RSA和Coppersmith的小根数攻击恢复剩余的flag
##BASE
总结套娃层
##easy_crypto1
求解E1
求解E2: 中国剩余定理
求P以及flag
完整解题代码
加油各位( •̀ ω •́ )y 期待与君再相逢
附件
通过网盘分享的文件:2024第八届御网杯线上题目附件
链接: https://pan.baidu.com/s/1Jq7anXVdjH5AfxQ_CujY3w 提取码: lulu
--来自百度网盘超级会员v5的分享
# Misc
##Notice
一年一道签到题 开启“玄幻”的一天
签到题不会太难 所以别多想直接打开
直接目视 哈哈哈
有隐藏字符(先考虑是否字体颜色隐藏,再想文档隐写其他的)
改颜色 出 好下一道
flag{HNCTF9090AS9nbg87600hn77hn88}
##编码转换
Brainfuck编码 jsfuck编码 OoK! 编码 这个几个编码是深受御网主办方喜爱
无论线上线下出现在大众视野已经不止一两次了 (不会无需难过,这东西纯靠积累)
在线网址
http://www.hiencode.com/jsfuck.html
https://www.splitbrain.org/services/ook
Brainfuck编码
Brainfuck编码
语言只有八种符号,所有的操作都由这八种符号 **(> < + - . , [ ])** 的组合来完成
<code>jsfuck编码
jsfuck编码
jother是javascript语言中利用少量字符构造精简的匿名函数方法对于字符串进行的编码方式。其中少量字符包括:"!"、"+"、"("、")"、"["、"]"、"{"、"}"。只用这些字符就能完成对任意字符串的编码。
、
<code>Ook! 编码
Ook! 编码
由Ook ! . ? 组成
组合得到
flag{ab71cda1b495e13b3f21f6fd50221978}
##bluetooth
流量分析的题
看见流量分析的题若没有明显特征 需要去观察协议分级
发现没有HTTP TCP之类明显的协议
直接进行关键字符搜索
发现有明显的压缩包 以及flag.txt文档
还有个key的密钥
将压缩包导出(文件时间够老的 嘻嘻)
导出压缩包
这里有人不知道如何把这两个文件导出 我简单说一下
第一种
将流量包文件改成zip之类的压缩包格式 进行解压分离提取
第二种
直接将流量包文件用相关命令进行分离
第三种
打开流量包找到存在关键字的原始数据 复制导入16进制文件 需改文头尾 得到压缩包
注意
这里有很多人得到压缩包 发现文件受损 打不开 这里你换一个解压软件就可以
得到密文和密钥
<code>flag.txt
10004583275926070044326083910251708233320797779355779208703097816305188140191914132269450797
Key
5216294695211820293806247029887026154798297270637676463374801674229881314620340407569315152
将10进制转16进制
在进行异或即可
10进制转16进制(这里可以使用在线,但线下就没有办法联网,所以要熟悉掌握脚本编写使用)
这个脚本大学python基础课的水平
def dec_to_hex(number):
# 使用内置函数hex进行转换,但hex会返回以'0x'开头的字符串
# 使用切片操作去除'0x'
hex_str = hex(number)[2:]
# 如果转换后的字符串长度是1(即小于16),为了符合常规表示习惯,我们在前面添加一个'0'
if len(hex_str) == 1:
hex_str = '0' + hex_str
return hex_str
# 示例
if __name__ == "__main__":
# 从用户获取输入
number = int(input("请输入一个非负整数: "))
# 转换并输出结果
hex_representation = dec_to_hex(number)
print(f"{number}的16进制表示为: {hex_representation}")
密文转
密钥转
<code>密文
4e94dcdb6de87e65d263419ec45aec93e8a2e1d386b31fb804e0f02366df44dbe86a8a7c462d
密钥
28f8bdbc16de4850e05579acf33c8aa08ac3d9e6e3822b8c3081c04700eb25b88a08eb457550
进行异或
# 定义加密后的密文和密钥的十六进制字符串
txt = "4e94dcdb6de87e65d263419ec45aec93e8a2e1d386b31fb804e0f02366df44dbe86a8a7c462d"
key = "28f8bdbc16de4850e05579acf33c8aa08ac3d9e6e3822b8c3081c04700eb25b88a08eb457550"
# 将十六进制字符串转换为字节对象
ciphertext_bytes = bytes.fromhex(txt)
key_bytes = bytes.fromhex(key)
# 使用逐字节异或进行解密
# 注意:这里假设密钥和密文长度相同,否则zip会截断到较短的长度
decrypted_bytes = bytes([c ^ k for c, k in zip(ciphertext_bytes, key_bytes)])
# 尝试将解密后的字节对象解码为字符串,忽略无法解码的字节
# 这里假设解密后的字节能够大部分解码为有效的UTF-8字符
print(decrypted_bytes.decode(errors="ignore"))code>
# 打印解密后的字符串(这行代码其实和上面的print是重复的,但保留以展示意图)
# print(decrypted_bytes.decode(errors="ignore"))code>
得到flag
flag{66526827ff3ba85e1444a0df4acbba93}
当然基础比较好的可以直接把脚本整合(如下 仅供参考)
整合脚本解析
# 定义两个非常大的整数,flag 和 key
flag = 10004583275926070044326083910251708233320797779355779208703097816305188140191914132269450797
key = 5216294695211820293806247029887026154798297270637676463374801674229881314620340407569315152
# 将 flag 和 key 转换为十六进制字符串,并去掉前缀 '0x'
flag_hex = hex(flag)[2:]
key_hex = hex(key)[2:]
# 确定 flag_hex 和 key_hex 中较长者的长度,以便进行零填充
max_length = max(len(flag_hex), len(key_hex))
# 使用零填充较短的十六进制字符串,使其与较长的字符串长度相同
flag_hex = flag_hex.zfill(max_length)
key_hex = key_hex.zfill(max_length)
# 将填充后的十六进制字符串转换为字节对象
flag_bytes = bytes.fromhex(flag_hex)
key_bytes = bytes.fromhex(key_hex)
# 对 flag_bytes 和 key_bytes 进行逐字节的异或操作
# 使用列表推导式和 zip 函数来遍历两个字节对象的对应位置,并对它们进行异或操作
flag_xor = bytes([f ^ k for f, k in zip(flag_bytes, key_bytes)])
# 打印异或操作的结果
# 注意:这个结果是一个字节对象,可能无法直接作为可读文本打印(除非它仅包含可打印字符)
# 你可以将其再次转换为十六进制字符串来查看
print(flag_xor.hex())
# 如果你想要查看原始的字节表示(作为十六进制字符串,但带有空格分隔),可以这样做:
print(' '.join(f'{byte:02x}' for byte in flag_xor))
##coding
936544a55314a7e4339545f47776a6e41315a7d41325743575655455b4478516a6537416
谜底就是flag,希望大家享受解码的过程。
一看题目就知道是经典套娃
直接cyberchef搜哈了(以后离线成熟,那还得了)
(经典工具还是可靠,打CTF不知道这个工具就如同西方不知道耶路撒冷)
得到flag
flag{HNCTFbs789024301143b9}
#Crypto
##不小心
题目
<code># coding:utf-8
# python 3.6
from flag import flag
import re
s = "fst3Sem8Wgnobcd9+++++uv2JKpUViFGHz0QRMyjkA7NaBC14wXYxh5OP/DEqrZIl6LT"
assert re.match(r'^DASCTF\{[a-f0-9]+\}$',flag) != None
def encode(inputs):
bin_str = []
for i in inputs:
x = str(bin(ord(i))).replace('0b', '')
bin_str.append('{:0>8}'.format(x))
outputs = ""
nums = 0
while bin_str:
temp_list = bin_str[:3]
if (len(temp_list) != 3):
nums = 3 - len(temp_list)
while len(temp_list) < 3:
temp_list += ['0' * 8]
temp_str = "".join(temp_list)
temp_str_list = []
for i in range(0, 4):
temp_str_list.append(int(temp_str[i * 6:(i + 1) * 6], 2))
if nums:
temp_str_list = temp_str_list[0:4 - nums]
for i in temp_str_list:
outputs += s[i]
bin_str = bin_str[3:]
outputs += nums * '='
return outputs
c = encode(flag)
print(c)
# +Se++h+mF5u0d++Oc++RbQJYbyuMb++0cYuQc+SwdmK0d+fwcYRYG+==
原题目脚本注释
这里我们来分析一下 题目脚本(我直接在脚本中给出注释)
# 编码设置和导入必要的库
# coding:utf-8 # 指定文件编码为utf-8
# python 3.6 # 注释说明该脚本应在Python 3.6环境下运行,但实际上这行对Python解释器没有实际作用
from flag import flag # 从flag模块中导入flag变量,假设flag模块中定义了flag变量,存储了CTF比赛的flag
import re # 导入正则表达式库
# 给定的字符串,用于编码过程中的映射
s = "fst3Sem8Wgnobcd9+++++uv2JKpUViFGHz0QRMyjkA7NaBC14wXYxh5OP/DEqrZIl6LT"
# 使用正则表达式断言flag的格式,确保flag以DASCTF{开头,后跟一串十六进制数字,以}结尾
assert re.match(r'^DASCTF\{[a-f0-9]+\}$', flag) != None
def encode(inputs):
"""
将输入的字符串(假设为flag)进行编码。
编码过程:
1. 将每个字符转换为二进制字符串,并填充至8位。
2. 每3个二进制字符串(共24位)合并,然后每6位一组转换为十进制数。
3. 使用s字符串中对应位置的字符替换这些十进制数。
4. 如果最后不足3个二进制字符串,则补零至3个,并相应地减少输出的字符数(用'='表示)。
"""
bin_str = [] # 存储每个字符的二进制表示(填充至8位)
for i in inputs:
x = str(bin(ord(i))).replace('0b', '') # 将字符转换为二进制字符串,并去除前缀'0b'
bin_str.append('{:0>8}'.format(x)) # 填充至8位
outputs = "" # 存储编码后的字符串
nums = 0 # 记录需要补零的二进制字符串数量
while bin_str:
temp_list = bin_str[:3] # 每次取3个二进制字符串
if len(temp_list) != 3: # 如果不足3个,则记录需要补零的数量
nums = 3 - len(temp_list)
# 注意:这里的补零逻辑实际上在后续处理中被忽略了,因为temp_list直接用于处理
# 理论上应该在这里补零,但代码中没有实现,可能是个遗漏
temp_str = "".join(temp_list) # 合并3个二进制字符串
temp_str_list = [] # 存储每6位二进制转换成的十进制数
for i in range(0, 4): # 每6位一组进行转换(因为24位/6位=4组)
temp_str_list.append(int(temp_str[i * 6:(i + 1) * 6], 2)) # 转换为十进制
if nums: # 如果需要补零,则减少输出的字符数
temp_str_list = temp_str_list[0:4 - nums]
for i in temp_str_list: # 使用s字符串中的字符替换这些十进制数
outputs += s[i]
bin_str = bin_str[3:] # 处理下一个3个二进制字符串
outputs += nums * '=' # 使用'='表示补零的数量(但这里的实现可能不完全准确,因为补零逻辑未完全实现)
return outputs
# 对flag进行编码并打印结果
c = encode(flag)
print(c) # 输出编码后的字符串
# +Se++h+mF5u0d++Oc++RbQJYbyuMb++0cYuQc+SwdmK0d+fwcYRYG+==
“+”有多个,就需要爆破一下
其实这里一个小漏洞
我们看他自己给的脚本中有DASCTF的字样 这足以证明这道题是之前的原题(这里说明主办方有点懒呀 无意冒犯 哈哈哈) (是2022河北银行CTF的原题 )
所以脚本直接照搬加以修改也可以(注释)
<code># coding:utf-8
# Python 3.6
# 自定义编码函数,将输入字符串转换为基于特定字符串 s 的编码
def encode(inputs):
bin_str = []
for i in inputs:
# 将字符转换为二进制字符串,并去掉前缀 '0b',然后填充至8位
x = '{:08b}'.format(ord(i))
bin_str.append(x)
outputs = ""
nums = 0
while bin_str:
# 每组处理3个二进制字符串(不足时用'0'填充至8位后的全0字符串代替)
temp_list = bin_str[:3]
if len(temp_list) != 3:
nums = 3 - len(temp_list)
while len(temp_list) < 3:
temp_list.append('0' * 8) # 直接添加8个0的字符串,而不是列表
temp_str = "".join(temp_list)
# 从每24位二进制中提取4个6位的部分,转换为十进制索引
temp_str_list = [int(temp_str[i * 6:(i + 1) * 6], 2) for i in range(4)]
if nums:
temp_str_list = temp_str_list[:4 - nums] # 如果不足3个二进制字符串,则截取前面的部分
# 使用索引从字符串 s 中选择字符,并构建输出
for i in temp_str_list:
outputs += "fst3Sem8Wgnobcd9+++++uv2JKpUViFGHz0QRMyjkA7NaBC14wXYxh5OP/DEqrZIl6LT"[i]
bin_str = bin_str[3:]
# 如果不是3的倍数,则添加 '=' 填充
outputs += '=' * nums
return outputs
# 模拟解码 Base64 的过程(实际上并未直接使用 Base64 解码,而是利用了自定义的 s 字符串)
s = "fst3Sem8Wgnobcd9+++++uv2JKpUViFGHz0QRMyjkA7NaBC14wXYxh5OP/DEqrZIl6LT"
h = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
c = '+Se++h+mF5u0d++Oc++RbQJYbyuMb++0cYuQc+SwdmK0d+fwcYRYG+'
m = ''
for i in c:
# 通过查找 c 中每个字符在 s 中的索引,用该索引从 Base64 字符集 h 中选择字符
m += h[s.index(i)]
print("模拟解码结果:", m)
# 递归函数,用于解密特定的 Base64 字符串
from base64 import b64decode
import string
ss = b'0123456789abcdef-_{}'
def aaa(idx, mm):
if idx >= 4:
try:
# 尝试 Base64 解码
t = b64decode(mm)
except:
return
# 检查解码后的每个字节是否在允许的字符集 ss 中
if all(v in ss for v in t):
print("解码成功:", t.decode()) # 解码后可能需要转为字符串输出
return
if mm[idx] == 'Q':
# 替换 'Q' 为 'QRSTU' 中的一个字符,并递归处理
for v in "QRSTU":
aaa(idx + 1, mm[:idx] + v + mm[idx + 1:])
else:
# 继续递归处理下一个字符
aaa(idx + 1, mm)
# 解密过程
m = 'QEFQQ1QGezc4YWQhMQEzZQcwOWZkZjEyYQVhYQQhYQVkZDYyZQMzfQ=='
m = m[8:] # 去掉前缀 'QEFQQ1Q',可能是为了减少搜索空间或符合特定条件
# 分块处理 Base64 字符串
for v in range(0, len(m), 4):
print('---------', v, m[v:v + 4])
aaa(0, m[v:v + 4])
# 预期输出: DASCTF{78ada113e709fdf12a5aa4aa5dd62e33}
# 注意:这个输出是预期值,实际输出取决于 m 的内容和递归函数的实现
得到FLAG
DASCTF{78ada113e709fdf12a5aa4aa5dd62e33}
##Justmath
题目一个函数图像 一个脚本
原脚本注释
<code># 导入必要的库
from gmpy2 import * # 导入GMPY2库,用于高精度算术
from Crypto.Util.number import * # 导入Crypto库的实用程序,用于数字加密和数学函数
from secret import FLAG,x,y # 假设这是一个包含FLAG(待解密的消息)、x和y(可能是某个函数的变量和表达式)的模块
import sympy # 导入SymPy库,用于符号数学
import pylab # 导入pylab,通常用于绘图,但在这个上下文中可能不太必要
# 定义calc函数,可能用于演示或测试符号求导和绘图
def calc():
y1 = sympy.diff(y,x) # 对y关于x求一阶导数
y2 = sympy.diff(y1,x) # 对y1(即y的一阶导数)关于x求二阶导数
v1,v2,v3 = [],[],[] # 初始化三个列表,用于存储计算结果
for i in FLAG: # 遍历FLAG中的每个字符(这里假设FLAG被转换为数字列表)
v1.append(y.subs('x',i)) # 将y中的x替换为FLAG中的当前数字,并将结果添加到v1
print('v1 =',v1[:34]) # 打印v1的前34个元素
for i in v1:
v2.append(y1.subs('x',i)) # 将y1中的x替换为v1中的当前值,并将结果添加到v2
print('v2 =',v2[:2]) # 打印v2的前两个元素
for i in v2:
v3.append(y2.subs('x',i)) # 将y2中的x替换为v2中的当前值,并将结果添加到v3
print('v3 =',v3[:2]) # 打印v3的前两个元素
pylab.plot(v2,v3) # 使用pylab绘制v2和v3的图形
pylab.show() # 显示图形
# 定义rsam函数,生成RSA密钥对并加密FLAG
def rsam():
m = bytes_to_long(FLAG) # 将FLAG(假设为字节串)转换为长整型数字
while True:
try:
p = getPrime(768) # 生成一个768位的质数p
q = getPrime(768) # 生成另一个768位的质数q
n = p*q # 计算n = p*q,用于RSA模
e = 3 # 使用小整数e=3作为公钥的一部分(在实际应用中,e应选择与phi(n)互质的大整数)
phi = (p-1)*(q-1) # 计算欧拉函数phi(n)
d = invert(e,phi) # 计算e关于phi的模逆d,用于私钥
c = pow(m,e,n) # 使用公钥(e,n)加密m
print('n =',n) # 打印公钥和密文的一部分
print('e =',e)
print('c =',c)
break # 跳出循环
except:
pass # 如果生成质数或计算过程中发生错误,则忽略并继续尝试
# 定义main函数,按顺序调用rsam和calc
def main():
rsam() # 调用rsam函数以生成RSA密钥对并加密FLAG
calc() # 调用calc函数以进行符号求导和绘图(与RSA加密无关)
if __name__ == '__main__':
main() # 如果此脚本作为主程序运行,则调用main函数
# 给出的RSA参数和计算结果
# ...(这里列出了n, e, c, v1, v2, v3的值,这些值用于验证或进一步分析)
n = 2260375559104345425590426977960386256287009777233277062625487017885931446911942921201492850167115455071935831283269948569220356763988762825230315520633702443866690239945242948370781975714325308306543337600783340792458991506685843729962897796956171467876531084194426101796617903015810156717396227079274786269217370618477266867389155551378798713259843750289765858717627925689021561352438080039804957145513478767641674644346609224034274906228784593435462413278410143
e = 3
c = 1683427726786225271109289808778075351906457081282891335272956455076290407290946927840180672315908981114229434899424882579823897506730018911375238394076293908946844135295984336122170362703361647325169444373502665686779049846717305377396296752361918921007897449738856962248716579014267597667341690453460130215215256776249910808564677407383996700090361822122676428069577517468851642648993930679875398568383201032360229083338487146673018350740571719960730053254352184
v1 = [3149069, 2271689, 2337632, 3068562, 67697, 2337632, 3068562, 67697, 2143547, 2543093, 1844472, 2206998, 67697, 1844472, 2686547, 2020317, 67697, 3149069, 2271689, 2081324, 67697, 2143547, 2543093, 1844472, 2206998, 67697, 2337632, 3068562, 67697, 2143547, 2543093, 1844472, 2206998, 3752378]
v2 = [59499825996845, 30963434563085]
v3 = [713997911962144, 371561214757024]
编写脚本
第一部分:使用SageMath解方程恢复部分flag
# 引入SageMath的solve函数用于解方程
from sage.symbolic.relation import solve
# 定义变量x
x = var('x')
# y_value列表包含了通过某种方式(可能是符号函数在特定x值下的结果)得到的值
y_value = [3149069, 2271689, 2337632, 3068562, 67697, 2337632, 3068562, 67697, 2143547, 2543093, 1844472, 2206998, 67697, 1844472, 2686547, 2020317, 67697, 3149069, 2271689, 2081324, 67697, 2143547, 2543093, 1844472, 2206998, 67697, 2337632, 3068562, 67697, 2143547, 2543093, 1844472, 2206998, 3752378] # 省略了中间的值以节省空间
# 初始化flag字符串
flag = ""
# 遍历y_value列表中的每个值
for i in y_value:
# 构造一个三次方程,使其等于y_value中的当前值
equation = 2 * x ^ 3 + 2 * x ^ 2 + 3 * x + 17 == i
# 解这个方程
solutions = solve(equation, x)
# 假设方程有三个解,取第三个解(注意:这里假设了解的顺序和存在性)
# 并将该解转换为字符后添加到flag字符串中
flag += chr(solutions[2].rhs())
# 打印恢复的flag部分
print(flag)
第二部分:使用RSA和Coppersmith的小根数攻击恢复剩余的flag
# 引入Crypto.Util.number中的函数
from Crypto.Util.number import *
# 已知flag的一部分(前缀)
flag = b"this is flag and the flag is flag{"
# RSA的公钥参数:模n和指数e
n = 2260375559104345425590426977960386256287009777233277062625487017885931446911942921201492850167115455071935831283269948569220356763988762825230315520633702443866690239945242948370781975714325308306543337600783340792458991506685843729962897796956171467876531084194426101796617903015810156717396227079274786269217370618477266867389155551378798713259843750289765858717627925689021561352438080039804957145513478767641674644346609224034274906228784593435462413278410143 # 省略了具体的大数值以节省空间
e = 3
c = 1683427726786225271109289808778075351906457081282891335272956455076290407290946927840180672315908981114229434899424882579823897506730018911375238394076293908946844135295984336122170362703361647325169444373502665686779049846717305377396296752361918921007897449738856962248716579014267597667341690453460130215215256776249910808564677407383996700090361822122676428069577517468851642648993930679875398568383201032360229083338487146673018350740571719960730053254352184 # 省略了具体的密文大数值以节省空间
# 定义模n的多项式环
R. < x > = PolynomialRing(Zmod(n))
# 尝试使用Coppersmith的小根数攻击来找到m(原始消息)
# 由于我们知道flag的一部分,我们可以尝试通过不断添加'\x00'(即0字节)来扩展这个前缀
# 然后我们尝试找到满足(m + x)^e - c = 0的小x值
for i in range(40): # 尝试不同的扩展长度
mhigh = bytes_to_long(flag + b"\x00" * 32 + b"}") # 构造一个可能的高位m值
f = (mhigh + x) ^ e - c # 构造多项式
# 使用small_roots方法尝试找到多项式的小根
# 参数X指定了搜索的界限,beta和epsilon影响搜索的精度和性能
res = f.small_roots(X=256 ^ i, beta=0.4, epsilon=0.05)
if res != []: # 如果找到了根
# 将找到的小根值加到mhigh上,得到可能的m值
m = mhigh + int(res[0])
# 将m值转换回字节串并打印
print(long_to_bytes(m))
得到flag
flag{0f5a1806d07f030767e113352727ea2d}
##BASE
题目
👄👋🐹🐸👂👱🐯🐢👚👊🐬👨👌👢👅🐿👊🐩👅👆👈👤🐧🐢👁👡🐯👰👂🐩👛👂👂👋👯🐺👋🐩👄👤👉🐾👄👭👃👋👀🐢👁👠👯🐻👀👌🐽👩👐👍👫🐾👇👥🐼👭👌👌👣🐺👊🐾🐬👘👌👢👁👄👌👍👉👅👄👠👫👦👊👌👅🐺👅👍🐬👙👘🐧👍👞👂🐪👍🐼👉👰👐🐭👏👊👚🐧👏🐨👦🐯👆👋🐰👬👂🐪👍👈👈👢👟👭👃🐩👍👀👁🐼👑👙👋👋👉🐺👅👋🐯🐨👇🐼👁👈👁👠🐬🐼👐👱🐽👁👃👱🐫👤👃👎👅👂👆🐿🐸👧👋🐨👀🐦👅🐧👪👤👍👡👄👢👉🐺🐬🐾👈👢👯👉👍🐼🐧🐪👙👣🐸👟👑🐧👞👡👃👌👀👣👉👎🐸👪👃🐼👟🐿👁👡👦👧👍👡🐼👮👈🐺👪🐦👇👱👛👃👁👣👑🐺👊🐾🐯👭👑👌👁👫👇👠👄🐢👄👠👫👦👎🐼👢🐯👈👢🐯👮👁👢👍👞👃👎👈👰👈👎🐯👭👑👠🐯🐯👁👤👄👮👚👋🐰👊👑👣👣👣👄👰👉🐽👃🐨🐰🐺👋🐽👀🐮👏👱👅🐼👂🐼🐨👑👇🐼👁👅👉👠🐨🐾👀👋👍👢👌👌👚👤👆👣🐧👥👈👎👫👡👇👱🐧👭👄👌🐬👘👀👋👛👨👊🐩🐹🐽👄🐺🐰🐨👅🐻👀🐧👈👡👌🐦👅👌👅👠👊🐿👀🐪👉👠👑🐾👌👋👐🐢👁👠🐨👡👊👡🐼👮👇👥🐽👰👇🐨👁👣👍🐼👛🐺👊🐿🐽🐻👇🐼👁👄👌👍👉👅👉🐨👢👮👄👢👄🐩👚🐼👉👦👛🐼👍👞👂🐪👍🐼👈👎🐯👯👄👍👟🐹👘🐩👄🐦👇👊🐯👯👋👣👦👟👈👢👟👯👈👱👯🐺👙👋🐨🐨👇👋👉🐺👅👋🐯🐨👈🐩👁👀👚👡👛🐾👁👢👚👧👉👊👯🐹👑👍🐨🐹👈👠🐰👅👂👊🐯🐦👌👤👐👭👍🐧👁👀👚🐺👍👱👈👤🐧🐢👀👱🐫👰👂🐩👐👧👍👋🐽🐾👘👣👮👠👉👎🐸👩👎👡👫🐺👄🐩🐯👣👏👡🐼👮👇👥🐽👰👇🐩🐫👩👄🐽👌👱👁🐼👌👭👏🐧👁👫👇👠👄🐢👉🐨👢👱👆👋👛🐻👐👢👢🐰👘🐼👍👞👂🐨🐼🐫👆👤👢👭👘👢👀🐯👁👤👄👮👚👊🐧🐫👁👢👈👠👈👢👟👮👃👱👅🐺👌👠👁👭👇👊👯🐧👏👌👅🐽👅🐽🐫👮👃👍👯🐽👐🐺👮👪👊🐼👚👤👆🐼👮🐦👄👋🐹🐸👂👱🐯🐢👚👊🐬👨👌👡👛👨👊🐩🐹🐽👃🐩👮🐨👐👤👈👰👂🐩👟👐👊👋🐼👩👌👋👛🐿👉👎🐸👩👌👋👞👪👈👎👚👦👘👡👉👝👎👡👮🐬👇🐨👁👤👎👎👌👱👁🐼👁👋👅👌👀👯👅👢👫👄👅🐩🐬👈👀👎👚👱👂👰👅👊👃🐼👍👞👂🐪👍🐼👆👰🐨👩👋👎🐹🐹👘🐩👁👧👘🐻🐯🐪👊👍👫👃👈👢👟👮👈👠👅🐿👎🐽👪👟👙👰🐰👪👐👊👯👑👄👊👫👉👋🐧🐯🐪👑👞🐴🐴
这个提示已经很明显了 直接写到的文件名 说白了还是套娃的编码
有工具就直接梭哈 没工具就一个一个试
<code>chgdchg5clctclcxclc5cdc9chcdcdc9chctchglcdc9chclclcdclcpchchchgdchg5clg5chg9clcpchclcdcdchg9chg5chctclc9chghclchchclclcpclchchghchg5clcpcdclchcpchclcdc9clcdcdc9chglchg5chg5chctcdc9clchclcdchclchgdchcpcdclchclcdc9chghclcdchg5chctclcpchctclclchgpclclchgdclcBchgdchcpchgdclclcdc9chglchcdclg5chglchg5clctchctclctclchchgdchglchg9clcpcdchchcpclclclcpcdc9chg9chcxchclcdghcdghcdghcdghcdghcdgh
中间需要过度一下base62 (别问工具为什么不能直接搜哈 要么格式 要么不完善 这就需要你去开发了)
提供在线网址
base编码解码-base64、base32、base16加密与解密 - 在线工具base编码解码工具能够将普通的文字转为base32、base16、base58、base91、base85、base64、base36、base62等编码字符,还能够将这些base编码加密的字符重新解码转换为正常文字.
https://www.wqtool.com/basecode
<code>4C4A575851324332474E32455356444C4A5A4B5645334B4A47524D544556544D4A563546453253324E4A4A47325453454C463545324D534A475647554F554C594C464C55324E435A4E4A574757544C4E4B5634465556324B48453D3D3D3D3D3D
然后继续梭哈
flag{HNCTFb8cee34cf4f4633b90d1ac8b9d2e1eb} 这题八成也是之前的原题
总结套娃层
1 base100-->2 base64-->3 base85(ASCII85)-->4 base91-->5 base62-->6 hex-->7 base32-->8 base64
##easy_crypto1
RSA才是密码的主导 能把所有攻击模式和理论掌握并可以熟练掌握编写脚本 芜湖起飞
这个题也是之前的原题 (攻防世界 crypto---simpleRSA wp)
题目
<code>#!/usr/bin/env python3.9
# -*- coding: utf-8 -*-
import gmpy2
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
from secret import FLAG, E1, E2, P, Q1, Q2
def next_prime(num: int) -> int:
num = num + 2 if num % 2 else num + 1
while not isPrime(num):
num += 2
return num
p = getPrime(1024)
q = next_prime(getPrime(16) * p + 38219)
n = p * q
c = pow(E1, 65537, n)
print(f'n = {n}')
print(f'c = {c}')
# n = 1605247600724752598798254639224215706171506359654961357324428027985787942008103766562745464838961569081446916113769517713344420113584254259000172572811154232107339480903672251992191997458469905064423618888336088652352540882576826988355783159237971043770132628344798937353150930071309347972804118952814447576207066147031238749098842662046825743988208813903138796789940911515825517078554074496474819128789835309636804325132602557092847746454786387067599510769382078521691609970320528531270474091713477040343897269903489441410062592732302402854035415438078656688806905350495825334584533345448091335565792091890185673190424063
# c = 751639057610677013264061431434189083017589908118307247217007533938435229431015858783222167911772848893015518607229280589985711010766459396989232072512314594917029375221335361209036112742388866873824163350886610514973038316512032459352053158417705406031466332440378871927174731975794579894912999936641163063898365134788537389162378185448090279397717831977803284480743612393591614284972981435749362255654561121758163485884075260156288337176713756471879489767416836868661153693157792733142765671887792303181376620864506386820826866340907593080654521498766421056474652652337037121881207188033108746890998208582406826010121861
assert E2.bit_length() == 69
ns = [getPrime(1024) * getPrime(1024) for _ in range(3)]
cs = [pow(E2, 89, n) for n in ns]
print(f'ns = {ns}')
print(f'cs = {cs}')
# ns = [15863230586500684911356384742123404120213699052018048588650392009927565369685497256344682150189923131009586323640507773706997704860898682946308031020361302334248895233255911348365179153799197341744863134926804603973507415697810440916305092395180382239729550833607847524005391137474497849077097574452115379368463540087172800902210822143687014813631366360652583216269138116785489485772437870528892032119729929607857459621078790511144060710035933887337208301078892163837203412081114510143406013892393607932596921308889058909544584619676380766485493114814753878272881866907210235681877689493671668534251778397658670518117, 14144098469438619358682652828507744381697293556670717685553585719665002440476256008471235313826051740009083510860714991201047915737216102220242621674841600987122005914542061963618272275986835928673920375768272390912778741502655909281390948606467847118377641357547931472588836726339758576038273820470879637555458446243401248151675266602656677360819563744765522495640821496694918515669243614141704744848980746101569785439728585144841655665959389460512628800782742764147773150430552859331269667626942993392101897661719871375721143240270211821269260950380944670195863016621594387236339317938305273510719419578308449465183, 27563822879593503938377821960427219022565215631856333510782568496016547757945464794632272818101891677705256471714805217606503652132995136255720639088424576003650628211271025648183600635145895528466199068640094470078526413324708028578289949241288828542143203769199399500669311878391255837977932634772778594526940501234736059441483897017015324765266787399950699732518347518591167932031031320265136158304460199654008895095274754918153773566824931440342525688741289235153882699461549523425169846266597156773535163599640189457171272058311480951820887261040891344076039474315985825984444520336790670313179493074014037981261]
# cs = [3833095607830862948079097323254872789586576953317671099752083261949616608759231291050566542764984974722790226120399722937104503590740358249900089784508490830379531632752169777949200718567033018577184658177019404903817920024468923715441355404672443007723525750768430895425376124679225715687382380114628103058312176343693900115638265002657622618744666247132114654135429040069316368839938881716554901593031901272992940200484460436193699175500376368456706998564064693820008778900344357745691652875500810447147088715289581351501876012044611990972521570253106671158207677490849249612002954497927762168699886110455354481924, 1502420121177211156091634258259634977709023894278792755694473756163084431123774101512866316989917922052023168401167212284219907272528117024670443698990238243030221117004372456475521502350404137469088570170885409265567084376069256924135270283335242133163303599239181417949980292944203204296598188175632723968779672994090788585343302473442389865459398142634104331743517384589200789331489394375604801951994831647339839112698394141328178967516636452592385248135340133712522135715943787590172334743893259621909532456281362868290556461907936774231166936915669816509378419892149164552548131776979706381641477878931403040942, 8992204063713908492214256291861339175525948946919629972908439132005643626148678347198381531633907182877152728077958345519083406637446972079387161726967295886447791613166577391233866583354793842121902234644830640050181130381996083089350911224037154798259291124104894554037604500881250119806371348673833105103600782286898276354573884788251542211434143476774391457587885772379990104835187104619922442613860682792470389490804228050671124495925536024571104944112397143299499508504917890140939438891891453283594000764399193028606955089853654071198909973555844004685149713774167524224100487937899126480545681565581673958854]
qq = getPrime(1024)
nn = P * qq
qqq = qq >> 460 << 460
print(f'nn = {nn}')
print(f'qqq = {qqq}')
# nn = 16851735797771199659625936797279158526379741298692339786049494329385618191510929735113284926125682522862667382938603116481087115598324232020838136618518964343752653000145611092980612556947954728339508416646035295651852840099205127587606898235203114875942637900167644300657599966420459187131027117268004042708998239798434578246497419547543598779697909298102358128788120332794123690714647499091326245022977970510468925837363300545900657420134894815246189043375619879915523611890538142257042753868665844692029124229028056547096764320547579965641276151760507921199827910445919017775913411823263307923216323527883262438117
# qqq = 121042531930820997492656296084544616958724191434895945419858099204426898711413526806300854553993738803031497438495403291406481997877273916883918253302909196533823945327277312672931819555344139777992801106437643790498379469530787985051569590331291422592393540391481519004782904598710037907420679190942964514816
assert len(FLAG) == 42
n1 = P * Q1
n2 = P * Q2
c1 = pow(bytes_to_long(FLAG), E1, n1)
c2 = pow(bytes_to_long(FLAG), E2, n2)
print(f'n1 = {n1}')
print(f'n2 = {n2}')
print(f'c1 = {c1}')
print(f'c2 = {c2}')
# n1 = 21655617838358037895534605162358784326495251462447218485102155997156394132443891540203860915433559917314267455046844360743623050975083617915806922096697304603878134295964650430393375225792781804726292460923708890722827436552209016368047420993613497196059326374616217655625810171080545267058266278112647715784756433895809757917070401895613168910166812566545593405362953487807840539425383123369842741821260523005208479361484891762714749721683834754601596796707669718084343845276793153649005628590896279281956588607062999398889314240295073524688108299345609307659091936270255367762936542565961639163236594456862919813549
# n2 = 24623016338698579967431781680200075706241014384066250660360949684385831604822817314457973559632215801205780786144608311361063622813017396858888436529116737754653067203843306015767091585697803364656624926853551997229897087731298797904208292585562517602132663331748784390752958757661484560335406769204491939879324079089140420467301773366050084810282369044622442784113688062220370531522036512803461607049619641336524486507388232280683726065679295742456158606213294533956580462863488082028563360006966912264908424680686577344549034033470952036766850596897062924137344079889301948258438680545785139118107899367307031396309
# c1 = 2615722342860373905833491925692465899705229373785773622118746270300793647098821993550686581418882518204094299812033719020077509270290007615866572202192731169538843513634106977827187688709725198643481375562114294032637211892276591506759075653224150064709644522873824736707734614347484224826380423111005274801291329132431269949575630918992520949095837680436317128676927389692790957195674310219740918585437793016218702207192925330821165126647260859644876583452851011163136097317885847756944279214149072452930036614703451352331567857453770020626414948005358547089607480508274005888648569717750523094342973767148059329557
# c2 = 6769301750070285366235237940904276375318319174100507184855293529277737253672792851212185236735819718282816927603167670154115730023644681563602020732801002035524276894497009910595468459369997765552682404281557968383413458466181053253824257764740656801662020120125474240770889092605770532420770257017137747744565202144183642972714927894809373657977142884508230107940618969817885214454558667008383628769508472963039551067432579488899853537410634175220583489733111861415444811663313479382343954977022383996370428051605169520337142916079300674356082855978456798812661535740008277913769809112114364617214398154457094899399
以下脚本仅供参考理解并没有代入实际值
参考原题解析
https://blog.csdn.net/luochen2436/article/details/128012748
求解E1
由<code>q = next_prime(getPrime(16) * p + 38219)可知,
q
为p*getPrime(16)+38219
的下一个素数,因此getPrime(16)
较小,所以我们可以在其取值范围为遍历出符合题目的值,从而解出q
。
from sympy import isprime, nextprime, isqrt, mod_inverse
# 假设 n, e, c 已经被定义
n = ... # RSA 模数
e = ... # RSA 公钥指数
c = ... # RSA 密文
for i in range(2 ** 15, 2 ** 16):
if isprime(i):
# 尝试找到一个可能的 q
# 注意:这里我们假设 iroot 返回一个元组,其中第一个元素是整数部分
# 如果 n // i 不是一个完全平方,这将导致问题
sqrt_n_div_i = isqrt(n // i)
if sqrt_n_div_i ** 2 == n // i: # 确保是完全平方
candidate_q = i * sqrt_n_div_i + 38219
if isprime(candidate_q) and n % candidate_q == 0:
q = candidate_q
p = n // q
phi = (p - 1) * (q - 1)
d = mod_inverse(e, phi) # 使用 mod_inverse 而不是 invert(假设这是自定义的)
E1 = pow(c, d, n)
print(f"Found q: {q}, p: {p}, phi: {phi}, d: {d}")
# 将 E1 转换为字节串(如果可能)
try:
print(f"Decrypted message: {long_to_bytes(E1)}")
except OverflowError:
print("Decrypted message is too large to convert directly to bytes.")
break
# 如果不是完全平方或不是质数,则继续循环
# 如果循环结束仍未找到 q,则打印错误消息
else:
print("Failed to find a valid factorization of n.")
求解E2: 中国剩余定理
简单处理一下,之后开e
次方即可。
from sympy.ntheory.modular import crt
import gmpy2
# 假设这些值已经正确设置
ns = [...] # 您的模数列表
cs = [...] # 对应的余数列表
# 使用中国剩余定理找到 m
m = crt(ns, cs)[0]
# 尝试找到 m 的 89 次根
root, remainder = gmpy2.iroot(m, 89)
# 检查余数是否为 0(如果是,root 就是 m 的 89 次根)
if remainder == 0:
print(f"m 的 89 次根是: {root}")
else:
print("m 不是 89 次幂的整数")
# 可选:验证根
if remainder == 0:
# 计算 root 的 89 次幂并检查是否等于 m
verification = pow(root, 89, gmpy2.mpz(1)) # 使用 gmpy2.mpz 以防 root 太大
if verification == m:
print("验证成功:计算得到的 89 次幂等于 m")
else:
print("验证失败:计算得到的 89 次幂不等于 m")
求P以及flag
第三部分代码本来是出题者想出p高位然后去求P的,但是在第四段代码中出了纰漏,n1 = P * Q1和n2 = P * Q2这两个n有共同的因子P,所以可以直接求他们的最大公约数得到P,即P=gmpy2.gcd(n1,n2),则分别可以求出Q1=n1//P和Q2=n2//P。
本题难点:计算E1和phi1,E2和phi2发现都等于35,不能够直接求私钥d了,而且求出来的m直接开35次方也得不到flag。
完整解题代码
# 导入必要的库
import gmpy2
from Crypto.Util.number import CRT_list, long_to_bytes
# 定义两个RSA公钥的模数
n1 = ... # 第一个RSA公钥的模数
n2 = ... # 第二个RSA公钥的模数
# 定义两个RSA公钥的密文和公共指数
c1 = ... # 第一个RSA公钥加密的密文
c2 = ... # 第二个RSA公钥加密的密文
E1 = ... # 第一个RSA公钥的公共指数
E2 = ... # 第二个RSA公钥的公共指数
# 计算两个公钥模数的最大公约数,假设存在公共的质数因子P
P = gmpy2.gcd(n1, n2)
# 计算剩余的质数因子Q2和Q1
Q2 = n2 // P
Q1 = n1 // P
# 使用中国剩余定理的变种来解密(但这里首先需要调整指数以正确应用CRT)
# 注意:这里假设了E1和E2都可以被35整除,这在现实情况中是不常见的
c = [
pow(c1, gmpy2.invert(E1 // 35, (P - 1) * (Q1 - 1)), n1), # 对c1使用调整后的指数进行解密,模n1
pow(c2, gmpy2.invert(E2 // 35, (P - 1) * (Q2 - 1)), n2) # 对c2使用调整后的指数进行解密,模n2
]
# 使用CRT组合解密后的结果
# 注意:这里假设了存在solve_crt函数,实际上可以使用Crypto.Util.number.CRT_list
c3 = c[0] * c[1] % P # 这一步可能不是必要的,取决于solve_crt的实现
c_combined = CRT_list([c[0] % Q1, c[1] % Q2, c[0] * c[1] % P], [Q1, Q2, P]) # 更标准的CRT使用方式
# 计算phi和n(实际上n = P * Q2或P * Q1,因为P是公共因子)
phi = (Q1 - 1) * (Q2 - 1)
n = P * Q2 # 或 P * Q1,这里选择Q2作为例子
# 注意:这里的d计算可能不正确,因为它直接对7取模逆,这在RSA中是不常见的
# 正确的d应该是对phi取模逆的e的逆元,但这里我们假设d已经以某种方式获得
# d = gmpy2.invert(7, phi) # 这行可能是错误的,仅用于示例
# 假设我们已经有了一个有效的c_m(这里用c_combined代替)和d
# 注意:实际上,由于上面的d计算可能是错误的,下面的解密也可能不会成功
# c_m = result % n # 这里应该是c_combined,因为result未定义
c_m = c_combined
# 假设d是正确的,进行解密
# m = pow(c_m, d, n) # 这行在d正确时是解密操作
# 但由于上面的d计算可能是错误的,我们直接跳到最后的假设部分
# 假设m的5次方根是flag(这在RSA解密中是不常见的,可能是特定题目的要求)
# 注意:这里使用gmpy2.iroot来找到m的5次方根
flag = gmpy2.iroot(c_combined, 5)[0] # 注意这里使用了c_combined而不是m
# 将flag转换为字节串并打印
print(long_to_bytes(flag))
这段代码是一个典型的RSA密码破解示例,其中利用了RSA加密系统的某些弱点(如公共指数与欧拉函数φ(n)不互质)来恢复明文。此外,它还使用了中国剩余定理(CRT)来组合在两个不同模数下的解。下面是对代码的逐步解析和解释:
导入必要的库:代码首先导入了
gmpy2
库用于大数运算,以及Crypto.Util.number
中的CRT_list
函数用于应用中国剩余定理。
定义n1, n2, c1, c2, E1, E2:这些变量分别代表两个RSA公钥的模数、密文以及公共指数。
计算p和q:由于n1和n2有共同因子p,所以通过计算
gmpy2.gcd(n1, n2)
找到p,然后用n1//p
得到q。这里假设n1和n2都是p和某个不同q的乘积,这在现实中很少见,但在这个例子中是有效的。
计算欧拉函数φ(n):欧拉函数φ(n) = (p-1)*(q-1),用于后续计算私钥d。
找到E1和φ(n)的最大公约数t,并计算私钥d:由于E1和φ(n)不互质,这允许我们找到一个非1的t,并用它来计算私钥d。这里使用了
gmpy2.invert(E1//t, phi)
来计算d,这是基于扩展欧几里得算法求逆元。
解密c1得到m:使用私钥d和模数n1来解密c1,得到中间值m。
构造多项式并求解:由于m是模p和模q下的解,我们构造多项式
x^t - m
,并在模p和模q的域内求解这个多项式。这里使用了SageMath的Zmod
和roots
函数来找到解。
应用中国剩余定理:对于模p和模q下的解,我们使用
CRT_list
函数结合中国剩余定理来找到原始的m值(在这个场景下是flag的字节表示)。
验证并打印flag:遍历所有可能的组合,应用中国剩余定理后检查得到的字节串是否包含"flag"字符串。如果是,则打印出flag。
flag{27dab675-9e9b-4c1f-99ab-dd9fe49c190a}
加油各位( •̀ ω •́ )y 期待与君再相逢
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。