9月份有念头复盘,11月中旬开始施工,到12月中旬结束,拖了有点久。
关键词:copper,two_squares(),discrete_log(),拓展欧几里得,md5拓展长度攻击,矩阵,分组密码,LSB Oracle Attack,MSB Oracle Attack
BaseCTF
复现前提:要是比赛中没有打开看的题目,先快速把所有题目(题目多就分块看)都看一遍,写下感觉可以的解法,然后看wp复现,刷题。
week1(没来的及打)
十七倍
- 题目
1 |
|
- 分析
乘法逆元,17模256,用gmpy2.invert()
或者Crypto.Util.number.inverse()
- exp
1 | from gmpy2 import * |
- flag
1 | BaseCTF{yoUr_CrYpt0_1earNinG_5tarTs_n0w} |
========================================================================================
babypack
- 题目
1 | from Crypto.Util.number import * |
考点:超递增背包
解析
a数列每次添加都是大过前面所有数之和,
所以从开始(即最大的数)开始遍历 i,与c作比较,如果 c 大于 i,那么说明这一次c加了i,对应m这一位二进制就是1,反之就是0。
- EXP
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{2c4b0c15-3bee-4e4a-be6e-0f21e44bd4c9} |
========================================================================================
babyrsa
1 | from Crypto.Util.number import * |
考点:欧拉函数
解析
其中有n=getPrime(1024)
,素数的欧拉函数是它减一(因为它与小于它所有的数都互为素数),得到phi就可以求d,并不用求p和q。接下来就和正常解rsa就行
- exp
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{7d7c90ae-1127-4170-9e0d-d796efcd305b} |
========================================================================================
ez_math
【感觉】:矩阵,不会
【task.py】
1 | import numpy as np |
【解析】
百度了一下——“三角矩阵的行列式就是其对角线上元素的乘积”
结合[BaseCTF2024]sudopacman个人Writeup
三角形行列式_百度百科 (baidu.com)是一种特殊的行列式,包括上三角形行列式和下三角形行列式,亦称上三角行列式和下三角行列式,统称三角形行列式。,每个行列式都可以只运用行或者列的性质化为一个与其相等的上 (下)三角形行列式,上 (或下)三角形行列式都等于它们主对角线上元素的乘积 [1]。)
python:@运算符(numpy库中的运算符)-CSDN博客食用
randomArray()
函数生成了两个
1 | 对角线为1 上半部分由随机数`getPrime(128)`填充的上三角矩阵 |
然后进行矩阵乘法 @,对这个符号不熟可以看第三篇文章
那么按照前文铺垫,B的行列式就是对角线乘积,也就是1
那么$MAT = A$
已给出A矩阵 (对于为什么会是这个后面会说)
的分块矩阵
后者的行列式为$a\cdot d-b \cdot c$ ,其中$a\cdot d$就为point1,$b\cdot c$就为point2,那么A的行列式为
即$MAT$的行列式整除(point1-point2)
即可得到flag
对于为什么A矩阵的形式,本人原因,并未接触到矩阵的系统学习,在网上搜了个大概,在这里说一下自己的理解一下,多多包含
——因为相当于先把含有flag的矩阵垫高了变成3x3方形矩阵(下图一),并把含有abcd的矩阵拉低变成3x3的方形矩阵(下图二),然后再相加,所以得到上图A矩阵的样子。
【EXP】
1 | # sagemath |
flag
1 | BaseCTF{7E9328AF-784C-8AF5-AC10-D6A8FC0977A8} |
========================================================================================
mid_math
【感觉】:先复现上面baby_math再说看看复现的怎么样
学成归来
套上面那个EXP就行
【flag】
1 | BaseCTF{E439646E-1768-18B3-DC4B-483C40C5340C} |
========================================================================================
ez_rsa
【感觉】:给了no_phi,括号展开减去n就有了p+q,放到phi公式即可算出
【task.py】
1 | from Crypto.Util.number import * |
【解析】
从no_phi中提取$p+q$ ,$phi = (p-1)\cdot (q-1)=n-(p+q)+1$
【EXP】
1 | # 创物者:善嵯峨 |
【flag】
1 | BaseCTF{it_1s_ez!!} |
========================================================================================
helloCrypto
【感觉】:AES,给了key和密文,拿去给AI写脚本,或者自己用pycryptodome库来做
【EXP】
1 | from Crypto.Util.number import long_to_bytes |
【flag】
1 | BaseCTF{b80bf679-1869-4fde-b3f9-d51b872d31fb} |
========================================================================================
你会算md5吗
【想法】:逐字节爆破md5,然后比较,或许可以先建一个字典,然后取值。
【题目】
1 | import hashlib |
【解析】
分析脚本,for i in flag:
逐个字母计算md5,那么很好反推
【EXP】
1 | import hashlib |
让ai生成一个脚本的时候犯了个错误,应该是md5值做字典的键,然后对应字母做值,这样后续查找才方便找。
然后给的字母范围也错了,用string.printable
就行了
【flag】
1 | BaseCTF{a4bf43a5-3ff9-4764-bda2-af8ee4db9a8a} |
========================================================================================
week2
basic
- 题目
1 | from Crypto.Util.number import * |
- 解析
给ai分析一下这代码,得知是有四种加密方式,每次随机用一种方式来加密随机生成的字符串,告诉你加密方式和密文,让你输入解密后的明文,重复一百遍后便出flag,这题旨在考查使用pwntools库来连接,和处理字符串和字节串(用.decode或者截取然后用ord()转字符)。有耐心的老哥也可以写一个代码,用人工充当pwntools,一边python解密,一边ncat终端连接交互。
- exp
人工组 手搓版(用来一遍遍调试,看看解密方式和输入到终端的字符格式是否正确)
1 | for i in range(101): |
机械组 pwntools
1 | from pwn import * |
【flag】
1 | BaseCTF{1732e054-e172-4d94-b5cf-3fb7bb022091} |
========================================================================================
two_squares
- 题目
1 | from Crypto.Util.number import * |
- 分析
小整数快速分解为平方和的函数
10版本
分解为四个平方和
1 | four_squares_pyx() |
分解为三个平方和
1 | three_squares_pyx() |
分解为二个平方和
1 | two_squares_pyx() |
判断一个数是否为两个数平方和
1 | [x for x in range(30) if is_sum_of_two_squares_pyx(x)] |
- exp
1 | #sagemath 9.3 |
【flag】
1 | BaseCTF{0760becd-cefaab0b094d} |
========================================================================================
铜匠
- 题目
1 | from Crypto.Util.number import getPrime, bytes_to_long |
- 解析
在查small_root()参数要求时,找到了原题鹤城杯2021,babyrsa
文章RSA中coppersmith定理的应用条件_sagemath coppersmith-CSDN博客
测试一下在pq均为1024位下,未知量需要控制在多少一下才能使得small_root()有解
1 | from Crypto.Util.number import getPrime |
- exp
1 | # sagemath 9.3 |
【flag】
1 | BaseCTF{7074ddc3e006810688241196414e49e2} |
========================================================================================
random_primes
- 题目
1 | from Crypto.Util.number import * |
- 分析
给了flag长度45,写个脚本看flag位数(二进制)
1 | from Crypto.Util.number import * |
是359位,每个素数都是128位,拿三个素数就有128*3=384位,远大于359位。所以可以拿这三个素数来模整体,看看rsa解出来,记得把n也给换了
- EXP
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{7cf4eedb-8b2d-406f-83bf-b2cb70882832} |
========================================================================================
mid_math2
【想法】:已经给了n的三个因子,就是MAT第一行的那三个
1 | import numpy as np |
【解析】
想法有点天真,可以自己验证一下,MAT的数与A是不同的,MAT每一位数明显比A要大。
这里有$|MAT|=|A|$ ,那么可以用LLL算法去找更好的基。
MAT = C @ A 是线性变换
LLL算法是线性变换
我也找不到MAT = C @ A矩阵乘法 和 LLL算法之间有什么联系,除了都是线性变换
为什么能通过LLL算法找到MAT的最佳的基就是A矩阵
LLL算法进行的是 线性变换,它通过对原始基进行正交化和短化(即通过格的 Gram-Schmidt 正交化或其他类似步骤),但不改变格的体积。因此,虽然
A
是一个经过优化的基,它的行列式与原始的基是相同的。
https://jia.je/kb/cryptography/lll.html#_1
【EXP】
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{8E2BD73F-9C10-F813-2CA4-B4B2DED4E961} |
========================================================================================
try_to_factor
【想法】:原来生成的$gift_0$应该位数比N大,因为$gift_0$里面有最多6个偶数因子,最少有5个偶数因子,那么先还原再看看能否分解N
【题目】
1 | from Crypto.Util.number import * |
【解析】
从其中gift的构造可以看出,其因子至少有5个2(素数减一)
先乘上2^5,然后套脚本
如果分解不出那就再乘上2
【EXP】
1 | from math import gcd |
【flag】
1 | BaseCTF{ed4bff90-d1f4-4f0f-a3bd-999c54d9eeb7} |
========================================================================================
week3
exgcd
【想法】:给了两个e,加密同一个明文,用共模攻击
【题目】
1 | from Crypto.Util.number import * |
【解析】
观察代码,给了两个e,下面来推一下攻击过程
已知:
由贝祖定理
若a,b是整数,且$\gcd(a, b)=d$,那么对于任意整数x,y,$a\cdot x+b\cdot y$都是d的倍数;且一定存在唯一的整数x,y,使$a\cdot x_0+b\cdot y_0=d$
带入c1和c2
用拓展欧几里得算法来求s1和s2,即
下面推导一下拓展欧几里得算法是如何根据e1,e2来求得s1与s2的
拓展欧几里得算法
了解拓展欧几里得算法之前,先讲一下欧几里得算法
已知有整数a,b(不妨设a>b),$g=\gcd(a,b)$
有
($k_1$为任意整数,$r_1$为a%b的余数)
两边同时除以整数g,得
因为g是a,b的最大公因数($g|a$ 且 $g|b$),即$\frac{a}{g}$,$\frac{b}{g}$都是整数
根据上式,整数之间的加减乘运算,那么$\frac{r_1}{g}$也是整数,即g 也是$r_1$的因子,而且$a = k_1\cdot b+r_1\to$ ($a>b>r_1$),g是a,b的最大公因数,那么g也是$r_1$的最大公因数。
欲求a,b的最大公因数,即求b与$r_1$的最大公因数。($a>b>r_1$,数值减小)
继续,$b=k_2\cdot r_1+r_2$,同理g是$r_2$的最大公因数 ($r_2=b\%r_1$)
此时,我们列出我们得到的这两个式子看看
那么等式右边第一项(原来b的位置)的数会越来越小,逐渐靠近0,到0就是这个递归的边界($b`=0 $)。
最终
递归:递归的本质是归约,把一个困难的问题转化为另一个简单的问题,且这个简单问题和前者是同一个问题,那么我们称为归约。
递归流水线:
而因为0除以任何数都为一个整数0,那么所有数都是0的因子($\frac{0}{k}=0\to $$k|0$)。
则$\gcd(r_n,0)=r_n$,$r_n$与0的最大公因数就是$r_n$本身。
现在我们来看一下拓展欧几里得算法
续上
有$g=r_n$ ,$r_{n-1}>r_n$
且$r_{i+2}$%$r_{i+1}=r_i$
$\Bigg\lceil{r_{n-1}=r_{n-3}\%r_{n-2}}\\r_{n-2}=r_{n-4}\%r_{n-3}$
则有
且有
则
代码实现为
1 | def exgcd(a, b): |
搞定了拓展欧几里得算法,
求得s1与s2
如果e1与e2得最大公因数是1,那么我们就可以直接得到m
实践下来会发现$\gcd(e1,e2)$为3,那么因为剩下的指数很小,爆破一下k就可以出m(小明文攻击)
【EXP】
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{feb7e1ae-a8f7-4fc4-8d6d-945a45cc3f6d} |
========================================================================================
ez_log
【想法】:爆破,2**7-1的范围也不是很大
【题目】
1 | from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b, getPrime |
【解析】
解法一:
爆破x,然后AES解密
解法二(预期解):
离散对数求解
【EXP】
1 | from Crypto.Util.number import bytes_to_long , long_to_bytes , inverse |
离散对数求解
1 | # sagemath |
【flag】
1 | BaseCTF{BF3DCONZ-67FE-ENZU-385S-CSNI13B2} |
========================================================================================
wiener
【想法】:第一次见到这种给小数的wiener
【题目】
1 | from Crypto.Util.number import * |
【解析】
wiener解决连分数
连分数定理
其中也用到了辗转相除法的思想
- 定义
当a和b满足
时,则$\frac{a}{b}$是x的一个连分数近似。
- 例子
把 $\frac{1314}{520}$ 进行连分数展开,形式就是对分子分母做辗转相除。
所以 $\frac{1314}{520}$ 展开连分数为
- 运用
可以从上面一个例子看出,
我们通过连分数展开可以轻松知道每一层的分子与分母,如果对无理数或者两个很大的数字分数进行连分数展开,那么我们会得到很多很多层分子分母,此时就可以只拿较多的层数,然后计算成一个分数,得到其的一个近似值
还是 $\frac{1314}{520}$ ,我们从连分数展开的第四层断开,即把7后面的分式约等于0
有
即 $\frac{1314}{520}$ 的近似值为 $\frac{38}{15}$
这么,我们可以从一个小数中拿到其近似值的分子与分母,也可以从一个分数中提取分子与分母
【EXP】
1 | # sagemath |
【flag】
1 | BaseCTF{9431ee53-5d5c-4b0b-956f-1eafff6c9e87} |
========================================================================================
没有n啊
【想法】:给了有关n的leak,那么先解出n,再解c
【题目】
1 | from Crypto.Util.number import * |
【解析】
虽然给了d,但这是解密m的密钥d1,并不是解密n的密钥d2,所以我们还得单独计算d2
先去 factordb.com 分解c,从而计算c的phi,根据
知道c是合数,那么其欧拉函数是每个因子减一后相乘。
得到d2,后RSA解密得到n-c
因为n>c,在给n
RSA
加密时,mod c抹去了n部分信息,那么我们在解密时也要加k倍的c来还原n
再正常的解RSA就好了。
【EXP】
1 | from Crypto.Util.number import long_to_bytes |
【flag】
1 | BaseCTF{2eefa8ef-ba57-4bed-9230-d280ac2273d8} |
========================================================================================
没有n啊_pro
【想法】:这次没有给n的leak,那么就看看能不能通过ed方程解
【题目】
1 | from Crypto.Util.number import * |
【解析】
给了c,d,e
有
解法一:
对①改造一下
又有②$d<\phi(n)$,那么$e>k$
从而知道了k的范围 0~e ,相当于我们已经知道了k
四个参数中,有两个已知数,一个可以爆破,那么我们可以求另一个的可能集合[phi1, phi2…… phi n]
再用这个集合去分解得到p和q,然后正常解RSA
【EXP】
1 | # sagemath time_speed 31.9s |
【flag】
1 | BaseCTF{3e226a94-babb27696416} |
========================================================================================
week4
extendmd5
【想法】:不输入然后直接返回给的md5
【题目】
1 | from Crypto.Util.number import * |
【解析】
md5扩展攻击
参考浅谈HASH长度拓展攻击 - Yunen的博客 - 博客园 (cnblogs.com)
题目给了一串由want生成的md5值A1,然后允许我们输入一串字符,添加在want变量后面,然后服务器再计算MD5值A2,我们要自己输入这个MD5值A3,然后服务器比较验证两个MD5(A2==A3)是否相同。
1 | def handle(self): |
MD5
参考【CRYPTO】哈希长度拓展攻击 | 狼组安全团队公开知识库 (wgpsec.org)
- 填充方法(补足长度成一个分组,方便hash)
图片摘自:哈希长度拓展攻击(Hash Length Extension Attacks)
当hash函数拿到需要被hash的字符串后,先将其字节长度对64取模。如果余数正好等于56,那么就在该字符串最后添加上8个字节的长度描述符(具体用bit表示)。如果不等于56,就先对字符串进行长度填充,填充时规定第一个字节为hex(80),其余字节均用hex(00)填充,填充至余数为56以后,增加8个字节的长度描述符(该长度描述符为需要被hash的字符串的长度,不是填充之后整个字符串的长度)。
若余数超过56,那么还得再用填充字符\x80\x00……来填充成新的一块,到下一块的第56位停止到加上明文长度的十六进制表达
- 长度描述符的填入方式
注意MD5运算的时候是小端序存储,假设我们最后8个字节的值为0000001234567890那么存储在MD5运算时的存储方式为9078563412000000,每两个字节构成一个十六进制并且按照逆序的顺序存储。
例如
计算aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbb
60个a和3个b
- 转一下hex
- 填充
那么填充后是下图,填充了57(即64*2-63-8)个填充符(一个\x80
和56个\x00
)
- 加上长度描述符(8位十六进制)
63的十六进制是\x3f,采用小端序存储
- 计算过程
参考浅谈HASH长度拓展攻击 - Yunen的博客 - 博客园 (cnblogs.com)
一开始有个初始VI
1 | A=0x67452301 |
这串初始向量的值是固定的,作为与第一块数据运算的原始向量。
当这串向量与第一块数据块运算之后,得到了一串新的向量值,这串新的向量值接着与第二块数据块参加运算,直到最后一块数据块。
VI与md5值相转换的过程
“ “ “而最后的MD5值就是这最后的向量串经过如下转换的结果。
如向量串:
1 | A=0xab45bc01 |
先两两为一组进行组合,得到如下数据:
1 | ab 45 bc 01 |
再进行高低位互换,得到如下数据:
1 | 01 bc 45 ab |
最终拼接得到MD5值:01bc45ab53bb646afe8aba23627a8446
。 ” ” ”
那么MD5值也可以逆推到VI。
攻击方法
已知前几块的md5,那么我们可以利用这个md5构造一个新的初始VI,然后与我们构造的“填充字块”+B去计算md5
当然还要跳过省略前面的分组
例子
还是上面那个,我们现在只知道有63个字符,那么已经超过了一个块,那么我们自己帮它填充并加上长度描述符,再在后面添加任意字符,使得这个任意字符单独在一个新的分组中,不干扰前面的MD5计算。
但这里还是有个问题(我也不太清楚):md5函数能自己识别我填充的字符和长度描述符吗?它会不会把我自己加的这些也当成了前面的明文来计算一个新的md5吗。
流程图
这题还需要爆破明文长度
【EXP】
脚本来源:naby
1 | from pwn import * |
脚本解析
1 | # 定义MD5所需的常量 |
8到74行都是md5实现加上一点点魔改(skip变量来实现跳到最后一个块前)
我们只能添加尾巴,并不知道前面到底有多少字符,当然也不知道有多少块,进行过多少次md5计算才得到最后给我们的这串md5值。那自然就忽略了前面的块。
1 | rem = remote("gz.imxbt.cn", 端口号) |
77行远程连接
1 | want_md5 = rem.recv(32).decode() # 接收原始数据的md5 |
80行到88行
接收md5值并转成VI
1 | # 爆破长度 |
90行到101行
爆破最后一个分组的明文长度(模64的情况下)
1 | # 计算填充数据后的有几个分组,需要跳过计算 |
用给的VI来计算我们的 任意字符(这里是出题人师傅的名字)
的md5值
【flag】
动态flag
可以去BaseCTF2024新生赛 - Review::CTF (imxbt.cn) 复现
题外话
这题我的想法可以执行,给个空输入,然后再输入回它给的md5值,结果也是返回“correct!”,属于非预期了
========================================================================================
hard_math
【想法】:还是矩阵,$|W|=|V|=|hint|\cdot |k|$ k是$\begin{bmatrix}0&b&c\\0&0&f\\0&0&0\end{bmatrix}$
然后格基归约,因为矩阵第一行位数与其他位相差甚大
【题目】
1 | import numpy as np |
【解析】
问ai的历史
智谱清言.html F5刷新后立刻取消刷新来查看
- 解法一
看了官方WP
首先是对W进行LLL格基归约,然后把第一行做V的第一行
后面通过计算行列式得到V其他行
得到完整V后
通过W = U * V
来求U
但我并不懂为什么明明给了W,对m乘上U V 的逆矩阵就可以排除r的干扰
- 解法二
来源:【BaseCTF 2024】 高校联合新生赛 Crypto (week1-7)_basectf新生赛-CSDN博客
构造了格 (4X6)
来看看U的位数矩阵(即里面的数都是代表该位置的位数) 由上三角矩阵乘下三角矩阵而得
V的位数矩阵
那么W的 ($W=V\cdot U$)
mat_flag = matrix([flag1, flag2, flag3])
,
m = mat_flag * W + r
r的位数矩阵
那么m的矩阵抽象表示为 (1x3)
“””依然是Ax+e=B
这题给了个迷惑项hint,V的9个变量给了6个,不过这题拿W和m构造格的话用不着。”””
停滞一周,先留下问题吧,等我后面彻底搞懂格与格基归约是什么的时候再来解答自己的问题。
构造格是为了格基归约找到更好的基,从而在里面找到自己想要的数。
那么用什么标准来判断一个基是否比另一个基更好?
格基归约背后的原理是什么?
【EXP】
官方解
1 | # sagemath |
解法二
1 | # sagemath |
【flag】
1 | BaseCTF{A99C2980-9F32-5BDC-26E9-C0551E3997A8} |
========================================================================================
rabin
【想法】:两次rabin
【题目】
1 | from Crypto.Util.number import * |
【解析】
套模板,rabin核心是二次剩余和中国剩余定理,关于rabin网上有很多师傅都写过,这里不多赘述。
【EXP】
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{01c28b9c-7924-4c04-b71d-1cca15342618} |
========================================================================================
哎呀数据丢失了
【想法】:读pri.pem读不出来,应该是损毁了,尝试按照格式手动修复一下
【题目】
task.py
1 | from Crypto.Util.number import * |
out
1 | 鸇㦷뫂�ㅪ쬬︗첷㜞御ﵥ�趹쥃´䓻晞 젉Ǩ䶞낆跥뜔杢럪gﭑꐐꭹ횼ᇦ䥬ヘᥩㆀ付脎扝鎲鎦婟唢鵌䈴免ꜽ祖蚜獫ƾ藊죫洚㟋ᱧ |
pri.pem
1 | -----BEGIN RSA PRIVATE KEY----- |
【解析】
参考文章:(PKCS1) RSA 公私钥 pem 文件解析 - 知乎 (zhihu.com)
ASN.1 - 维基百科,自由的百科全书 (wikipedia.org)
有意思,学了私钥证书格式,之前都是只会用Crypto库来解读证书,现在自己也能读懂一些了。
我们拿到一个损毁的私钥证书文件,打开来看能知道,星号占据了大部分地方
1 | -----BEGIN RSA PRIVATE KEY----- |
按照文章,讲证书里面内容出去标识头-----BEGIN RSA PRIVATE KEY-----
和尾-----END RSA PRIVATE KEY-----
然后将里面内容进行 base64解码+转hex
1 | 30 82 02 5c 02 01 00 02 81 81 00 bd 27 84 84 12 2a ef 9a 69 ec 64 72 90 21 9d ed 06 ed d2 b7 61 17 21 b3 26 85 0b 2f 50 60 da ee d7 69 43 56 66 7c 47 9c a9 cc b6 96 9f 4f be 6d c7 fa 67 59 ac a2 1d 8a 96 a8 81 a8 e4 a0 21 77 32 75 7e 64 9d 50 31 91 51 1f a9 6d a4 2e d1 da 2f a3 bc 8c 9c 65 fb d9 c0 dd 6f 43 03 59 ac 45 e4 55 d3 2c 5b 0e a2 9d 21 e6 47 ff 80 e5 0a bc bb 80 f7 6a db 67 00 7a 04 e8 5d ba eb 4c 8f 1d 02 03 01 00 01 02 81 80 22 65 e3 55 59 30 71 ae 35 01 06 2b 47 46 b5 bf 7a f9 18 ce bc 5b 46 87 9b c3 aa 0b 0a a4 f2 6b 68 c4 fd b7 e2 9f 4b 2e 94 3a 64 21 f4 0a be 68 9c 6b 4f 0c 21 b6 c1 84 88 6d 50 56 f4 6c a2 69 08 54 0e c0 7b 82 ad 47 e6 67 97 1a 01 fa c6 16 2e 93 a7 fc 61 ae d5 66 0f 82 6a eb a3 4d 78 ac cd 18 fc 59 e7 92 17 01 f1 0f f5 1d 52 88 37 06 b8 64 28 7c fd b3 4e 30 9c 93 82 9d 29 d8 67 c9 02 41 00 c6 e0 |
对于转出来的十六进制,解读:
02
为标识符,后面紧跟长度,长度后面就是数据内容
02
就是分隔符,后面跟的是长度,长度后面是内容。
如图
- 从第一行
02 81 81
后面开始到02 03
前面都是n(十六进制表示中) 02 03
~02 81 80
:e02 81 80
~02 41
:d02 41
~02 41
:p02 41
~02 40
:q02 40
~02 40
:dp 即 $d \mod {(p-1)}$0240
~0241
:dq 即 $d \mod{(q-1)}$0241
~ 结束:$q^{-1}\mod p$ #这个还不清楚有什么用
在这题中即使缺失了,但也关键的给出了e,n,d这三个数,其中就有解密要素n和d。
那么读十六进制给n,d就可以正常RSA解密就行了
【EXP】
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{57e6c35a-2768-49f9-8aca-47a3abcff1c7} |
或许可以写一个自动点的脚本,自动根据关键词识别
详细
将关键词(02 81 81
等等)放在一个数组a[]中,
读取私钥文件,去头和尾
然后调用a[]识别,然后赋值给对应的元素
(默认给e,n,d,p,q等等赋个初始值-1)
========================================================================================
Fin
PDP
【想法】:我去里面有RSA签名,AES, sha256,我的目标好像是找这玩意b'BaseCTF{' + flag_md5.hexdigest().encode() + b'}'
那么看看有什么困难。人还挺好,给了一个链接作为hint,等等,怎么是个论文??
【题目】
1 | from Crypto.Util.number import * |
【解析】
论文给出了面对一个大文件时,客户端对服务端是否有其原始数据的快速校验方法。
还挺有意思的,有点像做零知识证明
详细请看:S-PDP/阅读笔记.md at main · naby1/S-PDP (github.com)
【EXP】
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{d198bce7a2f06ed24d3b043bddbd7512} |
========================================================================================
の歪来
【想法】:n是素数,然后e是偶数,与$\phi(n)$不互素,先解$m^{gcd(e,\phi(n))}$,再有限域开根号
【题目】
task.py
1 | from Crypto.Util.number import * |
output.txt
1 | flag = b'BaseCTF{}' |
【解析】
实践一下,$\gcd(e,\phi(n))$ =e,是很大的数,感觉用sagemath 有限域开根不行。
那么就用AMM算法来开根
【EXP】
1 | # 创物者:善嵯峨 |
【flag】
1 | BaseCTF{e_d0u_hu1_4_w@i_lal} |
========================================================================================
猜猜看
【想法】:也是关于矩阵,问ai是给我们一个矩阵,然后我们输入一个矩阵,但关系还不清楚,再看看。
拿一个45x45的任意01填充的矩阵T,与m每个字节分开得到的1x45矩阵相乘,将结果y给我们;
我们还可以自己构造一个矩阵mat给服务器,让它拿这个mat与T相乘,将结果res返回。
那么就可以得到T,进而解密m
【题目】
1 | from Crypto.Util.number import * |
【解析】
自己构造mat
根据
1 | length=len(m) |
x 是一维矩阵,里面包含二进制m的信息,假设列数是a,格式是1xa
T是长宽都为a的的正交矩阵
那么$x\cdot T = y$
y就是1x$a$的一维矩阵
服务器会把y给我们
那么我们也知道了a
1 | mat=np.array(user_input) |
我们可以构造一个
1xa且只有一位是1,其余为0的一维矩阵
就可以拿到T每层的数值
有了y和T
关于为什么y在点乘的右边而不是左边,笔者暂时没有找到解答
得到x后
因为“y/T”会出现小数,还需要x = np.round(x)
进行四舍五入
最后二进制转十进制转字符得flag
【EXP】
1 | import numpy as np |
【flag】
1 | BaseCTF{edae9bdc-e251-4b92-a493-a2addd2974fa} |
========================================================================================
老涩批了
【想法】:lsb?复现平台上的题目好像是docker配置文件,附件在lspl\src中
【题目】
1 | from Crypto.Util.number import * |
【解析】
参考:RSA#Parity-Oracle-Attack | Lazzaro (lazzzaro.github.io)
3个模式
而模式一是我们来操作LSB Oracle Attack(Least Significant Bit Oracle Attack )的地方。
不断地 c1乘上$2^e$,发送,看返回值,假设这次是i
- 如果是
b'0'
,那么$m<\frac{n}{2^i}$,即上限R是原来上限+下限的一半 - 如果是
b'1'
,那么$2^i\cdot m>n$,即下限L是原来上限+下限的一半
通过这个攻击方式,我们可以拿到一个被约束的整数
因为二分法,并且在这题中所有参数都在整数域中,那么经过有限次二分法后就必定能找到一个整数m
而模式二是MSB Oracle Attack(Most Significant Bit Oracle Attack )
不断发送$c2\cdot {2^e}^i$,看返回值,如果它返回的不是预定的填充头\x00
,那么就有$m2\cdot 2^i>2^{kbit}$
由二分法得,此时$m2\cdot \frac{2^i}{2}<2^{kbit}$
有了上限和下限,那么找x,$x = \frac{L+R}{2}$
关于EXP中flag2
flag2 = (2**(kbits-8)//L)
为什么要多减一个8,因为原本还会有个\x00在末尾
【EXP】
1 | from Crypto.Util.number import * |
【flag】
1 | BaseCTF{1b639591-1ce1-462d-8afc-b1fedfc2b85f} |
========================================================================================
ECB是不安全的
【想法】:没有附件,只好nc试试。有点像是对AES的oracle-attack
【题目】
1 | 没有附件,纯试 |
【解析】
先了解一下ECB
16个字符为一组,且ECB分组模式块之间互不影响如下
那么可以利用这个特性,填充15个任意字符’0’,让flag漏出头一位,得到此时的密文c1,
然后我们再自己在15个填充字符’0’后爆破一位i,当密文与c1相同时,那么此时爆破的i为明文(即‘B’);
接下来填充14位’0’,让flag漏出第二位,得到此时的密文c2,
然后在14个‘0’+已知flag片段 ‘B’后爆破第二位, 当密文与c1相同时,那么此时爆破的i为明文 (即‘a’);
接下来同理。
如果flag超出16位,那么在前面预留出足够空间就行(即填充31或47(16的倍数减一)……个填充字符)
用已知的flag头BaseCTF
手动爆破体现这个过程
总结来说就是
步骤一:先放一位,然后得到c1
步骤二:接着自己尝试填上一个字母,补全这一块,使得加密后的密文与c1相同,加到已知flag片段中
步骤三:然后再放更多位,得到$c_i$,尝试在已知flag片段后面填上一个字母,加密得到密文与$c_i$比较,相同的明文加到已知flag片段中
重复步骤三
【EXP】
1 | import string |
【flag】
1 | BaseCTF{37d03b65-5c3d-45fd-9260-37496cad8cfe} |
复盘结束感想
学到挺多东西的——copper,矩阵(三角矩阵,行列式,LLL运用),md5拓展长度攻击和分组密码
途中写了几篇笔记(欧几里得算法等等)
接下来搞完期末考就开始 0XGAME的复盘!