0xGame2023 Crypto WP
题目在这里→题目地址
第一周:
觅码:
考点:
- python编程
- 编码原理
碎碎念:这题的本意是想让大家熟悉一下python的语法,了解信息是如何编码成为一串数字,并进行一系列数学运算操作的,中文flag属实是有点抽象。这道题应该算是本周的签到题,但是解出的人数不太符合预期,在这里给大伙道歉了。该给出的函数都给出了,那么都配置好环境了,接下来的题目应该会顺利多了。
一些小坑:中文编码,因为c1,c2,c3,c4之间可能会有一些比特是连着的构成中文字符,所以单解出部分密文是无法直接decode的。考虑到这点,每串字符里面都有一些英文字符,告诉做题的师傅解出来的是正确的。
小知识:在python中的几个常用的数字表示类型有:
- 十六进制(0x)开头数字
- 八进制(0o)开头数字
- 二进制(0b)开头数字
- 十进制无特殊开头
在python中,用(b’)做开头的表示bytes类型的数据,一个bytes大小为8bit。
几个常用的函数解释:
1 | from gmpy2 import iroot #引入函数库,用来开根用的,gmpy2里面有很多比较好用的密码算法可以使用,推荐使用。 |
有了上述这些工具基本就能解题了,题目给了三个数字,一个base64编码后的比特流,直接将相应的数字按照python识别数字的办法还原回去,然后long_to_bytes就完了。(不学会编码,接下来学习密码的路咋走嘛?)
exp:
1 | from gmpy2 import iroot |
RSA:
考点:
- RSA的基本概念
- 欧拉函数定义
- 逆元的运算与定义
- 基本分解模数的工具应用
碎碎念:出的是有点杂,基本入门需要掌握的知识都含括在里面了,对初次接触密码领域的新朋友有点小坑。我们可以通过这题知道:逆元可以代替除法在有限域(某个模数下的式子)中进行运算,同时逆元不是百分百存在的(必须与模数互质)。所以题目中的逆元不能直接求出,这是这题的一个小坑。
虽然这个坑不太符合预期,大伙都能直接除以解出来就是了,就当做下降难度了,,,
思路:RSA概念网上已经很多了,应该也不是特别难懂,比较令新人烦恼的可能是(mod)这个概念,就是取余这个操作,慢慢适应就好了。这道题用yafu.exe,还是用factordb网站,或者是自己随便写写就能分解模数,拿到欧拉函数直接解逆元了,得到flag了。
exp:
1 | from Crypto.Util.number import * |
Take my bag
考点:
- 逆元的运用
- 超递增数列
- 加密算法至数学式子的推导
碎碎念:这题主要是背包密码,这里给出数学公式
m=i_{n}i_{n-1}i_{n-2}······i_2i_1(i_k\in{1,0})\ 加密公式:\sum{3^{i_n}}*w=c(modn)
解密逻辑:w已经给出,那么就很自然的可以考虑到用逆元做除法化简式子得到:
c*w{-1}=\sum{3{m_{i_n}}}ww{-1}=\sum{3{m_{i}}}(modn)
接下来通过尝试我们可以知道:
n>\sum{3^n}\ 且有3{n+1}>\sum{3n}>3^{n-1}
那么问题就很简单了,可能只需要对贪心算法有一点点了解,就可以直接写脚本了(如果对你来说贪心算法可能一时半会有点难以理解,或是没接触编程,不要紧,权当学习编程思想也是不错的选择。)
exp:
1 | from Crypto.Util.number import * |
CBC:
考点:
- 对称加密中的分组模式
- 密钥爆破
碎碎念:关于分组模式存在的原因,还望大家自行通过搜索引擎获取,一般的加密算法都是通过了世人的长久考验而留下来的,要想通过分析并攻破是及其困难的事情,但是由于分组模式不同而存在的某些缺陷却是可以利用的,在进行更深一步的探索之前,我想通过基本的概念题,让大家理解这个分组是怎么操作的、并有哪些好处和缺陷。关于解密脚本的编写入门,可能对新人不是那么友好,但是通过观察流程图可以发现,很多操作只要看懂了是可以硬抄的,就没啥太大的难度了,那么这题给出两种解法。
exp1:
可以观察得到,密钥空间并不是很大,可以通过穷举爆破的办法一个个尝试得到,接下来写出基本的CBC解密脚本就可以。
1 | from Crypto.Util.number import * |
exp2:
因为已知明文、密钥固定的特点,这里利用了CBC分组模式的特点可以直接逆推出密钥,在这里给出这种解法,目的是让新师傅了解一下利用已知明文解密的这种思想。
1 | iv = b'11111111' |
猜谜:
考点:
- 已知明文攻击
- base64编码算法
碎碎念:考虑到难度,当天还是放出了魔改base64解码函数,通过编码可以将不可打印字符转换成可打印字符(A-Z\a-z\0-9\+),以便于在网络传输中显示,随便写的算法就看个乐呵就行。重点是已知明文攻击这部分:如果我们知道了部分明文的情况下,可以通过一定的推导得到部分密钥的信息、甚至是密钥,这在密码学中是一个重要的攻击思想。
在这里我们可以知道,一般的正常加解密算法是难以攻破的,如果我们能在现实中通过侧信道攻击,获取了某些关键的信息呢?
exp:
1 | from Crypto.Util.number import * |
维吉尼亚密码
这道古典密码题很简单,有不少师傅甚至直接猜都能猜得出密钥是啥(Game),在目前的CTF赛事中古典密码的题已经很少了。这种传统的加密技术中,就算猜不到密钥是啥,通过统计某些密文和密钥的规律基本都能还原信息。WP就不想写了,,
废话:
确实不可否认的是,第一周我弄得题不是很简单,基本都要沾点python编程,对想入门密码、或者是想尝试CTF的哥们不友好。
但是核心思路都非常简单,而且脚本的编写也不会太复杂(要么可以直接抄题目给的部分代码,要么就是想一下就出来了),因为我并不太想看到新人能够在第一周疯狂上分,然后到后面遇到比较复杂的题就开始放弃了,试着适应可能比较好。
秉持这个态度出题(思路唯一),,相信经过第一周的师傅对密码这个方向有一个初步的认识。那么既然坚持下来了,就开始试着去破译一些好玩的算法吧。
第二周:
中间的那个人
考点:
- DH算法
- 简单的爆破
DH算法具体细节和作用,望新师傅们利用搜索引擎了解一下,这里不细谈。这道题比较简单,有些时候面对未知密钥的时候,需要考虑爆破求解,那么这时候就要进行一定的计算量估计,这题不管是求A还是B都是可以出的来的,两者的位数比32位低,也没啥限制,还是可以求的。
exp:
1 | from Crypto.Util.number import * |
那么简单的DLP可以通过爆破去求解,但是遇到更加困难的离散对数问题的时候,我们就必须开始考虑是否存在更优的算法去求解这个问题了。
What’s CRT?
考点:
- 中国剩余定理
- 对逆元的理解运用
- 解方程
小彩蛋:公钥260792700是一个QQ号码,因为出题人的Q号拿来做公钥不怎么合适就用小号来玩了。
这题可能是我弄得有点复杂了,因为考虑到第一周逆向题里面大家已经做过解方程的题了,就不直接放出q,p了(本意就是想直接送出q,p,q_,p_的)。
思路:首先解方程
q+p=gift\ q*p=N
用高中知识还是直接用函数库解都可以,我选择用sagemath自带的函数:
1 | mygift=[15925416640901708561793293991573474917595642805739825596593339102414328214313430010166125066639132916608736569443045051644173933089503934675628814467277922, 18342424676996843423829480445042578097182127446865571536445030052846412665700132683433441858073625594933132038175200824257774638419166516796318527302903098] |
拿到因子之后考虑直接求私钥解题,但是可以发现求不出来,原因是:
gcd(e,phi)==4
在上周的题中,我们已经知道只有在e,phi互质的时候才能求出逆元,所以考虑退而求次,求解另外一个逆元:
令e’=\frac{e}{4},求d’ \equiv e’^{-1}(mod\phi(n))\ 得c^{d’} \equiv m^{ed’} \equiv m^{4\frac{e}{4}d’} \equiv m^{4} (modn)\ 但是发现:m{4}>n,m{4}=c^{d’}+kn\ 我们并不能直接开根
此时就需要利用中国剩余定理去将模数进行变换,用来求解以下情况:
\begin{align} \left{ \begin{aligned} x=a_1(modm_1)\ x=a_2(modm_2)\ x=a_3(modm_3)\ x=a_4(modm_4)\ \end{aligned} \right. \end{align}
证明:
那么知道公式的情况下,直接按照公式去构造就可以了,需要注意的是不同模数之间必须互质。
exp:
1 | import gmpy2 |
题后总结,基本解法需要使用到的函数、数学知识在上周都了解过了,剩下的就是一个中国剩余定理的考点,望周知。
以上是预期的解法,下面放出一些非预期解,用SageMath自带的有限域开方的函数nthroot_mod(),也是可以的,还有师傅说能直接phi//4后直接求逆元进行求解也是可以,总之解法蛮多。
有限域开方(同理AMM算法一样是可以的):
1 | from sympy.ntheory.residue_ntheory import nthroot_mod |
在某些特殊情况下(多半是出题人的为难),e和phi是不互素的,
那么当pow(m,gcd(e,phi),n)<n时,我们可以采用直接开方的办法,
当pow(m,gcd(e,phi),n)时,一般我们采用有限域开方、中国剩余定理换模,有些时候有限域开方的办法不同,或者是条件更加特殊(可以参照NCTF2019的题),就需要在这个基础上去思考更多的问题,感兴趣的师傅可以了解一下。
EzRSA
考点:
- 费马小定理
- 光滑数分解
- 连分数分解
hint已经给出所有考点,题目的交互脚本也给了,本意就是上点送分题的,,
因为交互也不算太复杂,直接连上去之后把数据拖到本地解就可以了,详细的概念和知识点这里就不拓展了,在这里,都有详细的介绍。
exp:
1 | #数据从服务器上面拖下来解就好,这里就只做分解,不做解密了 |
EzLFSR
考点:
- 矩阵的理解应用
- 线性反馈移位寄存器
- SageMath的应用(直接解方程不用也行,这里还是推荐使用)
这题是出题人从网上摁抄的,可能直接搜索都能搜得出原题,重点是想让大家了解异或和按位与这两个操作其实就是mod2下的加法和乘法,以及矩阵的构造(如果是大一新师傅的话,这个时间点应该也学到了矩阵怎么构了)。
从网上搜索就能知道LFSR的概念,这个就是反复的:按照状态位计算,生成新的状态位,直接解了这个128元方程组就好,结合矩阵的知识,用逆矩阵去求解题设的增广矩阵就好了。
exp:
1 | #SageMath |
Fault!Fault!
考点:
- 数学推导、数据处理
- 远程交互脚本的编写
关于推导的过程和原理直接看这篇文章就好,如果没推过的话直接百度“RSA Fault”都能出的来这篇文章,所以我觉得理解这部分应该不会太复杂。
脚本编写部分:首先因为服务器有时间限制、交互要验证等一系列的原因,一般不推荐写好脚本了再去和服务器交互、调试脚本,容易浪费时间、搞自己心态,我的建议是在自己本地上部署一遍题目,或者是模拟题目的条件再调试数据,看看符不符合自己的预期、设定。
其次就是在这种时间限定要拿很多数据的情况下,就不推荐一边拖数据一边做数据处理了,影响速度,建议是写好数据处理的脚本之后,直接从服务器拖下我们需要的1024组数据,本地运算一遍交上去就行,方便故障排查、也不会浪费很多时间。
exp:
1 | #part1 拖数据: |
小思考:虽然时间是有一定的限制,但这道题的私钥是固定的,所以我们可以多次交互拿数据推导出这个私钥。
但是如果这个私钥不是固定的呢?我们只有一次交互的机会,多次交互拿数据推导出这个私钥这个思路恐怕不太行,不管怎么优化可能时间都是不太够的,那么我们要如何处理这种情况?哈哈,我们第四周再见。
第三周:
ECC
考点:
- ECC概念
- SageMath应用
ECC相关概念可以上网查查看,阿贝尔群下运算,具体概念这里不放了。
我们设:r是加密方的生成随机数\ k是私钥、K是公钥(K=kG)\ 加密:C_1 = M+rK = M+rkG\ 同时告诉解密方:C_2 = rG\ 解密:M = C_1-rkG = C_1-kC_2
SageMath自带DLP问题求解的函数,直接用就行,要注意的是,加密的时候信息一般要编码到曲线上面,但是这题并没有这样做,就导致了C1,C2都不是在曲线上的点,但这个不要紧,照着题目逆向求出来就行了
1 | #sagemath |
LLL-FirstBlood
直接规约就可以得到结果
详细的原理这里看
怕误人子弟,这里就不详细地写出更多概念,原理了。
LLL算法的核心是施密特约化,输入一组向量基,得到一组约化基。
使得我们可以通过一定的构造,去规约出某个向量(一组数字),那么在这道题中,因为A是正交矩阵,经过LLL算法之后就被“约掉”了,所以我们可以直接得到题设的结果。
exp:
1 | from Crypto.Util.number import * |
LLL-SecondBlood
先推公式:
题设:Am+noise=c(modp)\ 展开:Am+noise=c+kp\ 构造:Am-c+k*p=-noise\ 可以发现左边大部分是已知参数,右边是较小的未知质数
那么我们可以构造这样一个矩阵:
直接对右边这个构造的矩阵进行规约就好,得到的结果就是下面的矩阵,详细原理在上边。
exp:
1 | from Crypto.Util.number import * |
以上是正解
下面是邪道速通:
因为未知量的位数都比较小,直接考虑使用多元Coppersmith,这里解释一下多元Coppersmith的思想和作用:
我们想要在有限域中解方程,可以通过展开和一定的方式换到整数域上,把问题变成简单的解方程问题(利用牛顿迭代法),然后再利用LLL算法去对构造的数学式子进行求解。
总而言之就是,实现有限域求根的算法(前提是这个根必须要比模数小很多)。因为求根的时候用到了LLL算法去规约基向量,得到的结果也是一组基(性质更好的),这种问题就被称之为SVP(最短向量)问题。
同理的还有CVP,HNP这些,在将来的格密码学习中会经常打交道,这里就不误人子弟了。
1 | import itertools |
Matrix
考研真题:大一新师傅的练习册上就有解法的。
已知A^{secret}=C\ 通过相似矩阵得:A=P^{-1}BP\ 那么问题就变成:A{secret}=(P{-1}BP)^{secret}=C\ 对中间的式子展开得到:P{-1}*B{secret}*P\ 其中B是对角矩阵,问题就变成了求对角元素上面的离散对数问题\ 那么之后参照上周的解法就可以了。
exp:
1 |
|
Overflow:
签名用的是ElGamal算法,这里这样放出这种题,理由如下:
- 了解一下ElGamal算法的特点
- 适应代码审计(有些时候题目代码很长,很容易让人感到害怕)
签名的时候没有对消息做校验,那么导致了我们的签名可以溢出被模数消掉,直接看WP吧,摆烂了。
核心的考点其实不难,只要细心就好了。
exp:
1 |
|
第四周:
Normal ECC:
考点:
- 椭圆曲线
- DLP
- SmartAttack
脚本一把梭就可以了,因为题目生成的模数也比较难找,抽象,直接就抄了别人的参数了,根据题目的Hint很快就就可以搜出这个方法来,本周的送分题。拓展阅读
exp:
1 | from hashlib import md5 |
Drange Leak:
考点
- Coppersmith的利用
- 阅读论文的能力
- 基本数学推导
这题其实比较简单的,看着论文长篇大论的蛮哈人,但其实我们知道一篇论文的顺序是:
- 引论(这篇论文的背景,解决了什么问题)
- 引理(用了哪些关键的定理、这些定理的内容是什么)
- 推导过程和结论(基本上追求速通的话就看这部分就可以)
- 实验数据
- 总结,引用文章……
大致上都是这样,所以这里直接快进到中间,这里可以发现:
d = Md_1+d_0\ ed = 1 (modphi)\ 展开:ed=1+kphi\ 再展:e*(Md_1+d_0)-1-kphi=0\ 展:e*(Md_1+d_0)-1-k[N-(q+p-1)]=0\ 到这一步就是题目的关键了
这里引入一个Coppersmith定理:用来求解有限域的小根问题。(给定一个模数下的数学式子,求解其中的较小未知数。)
在论文中可以看到这个Coppersmith实现的一定过程。具体核心思想在上周的WP中已经给出,所以这里直接拿来用:只要找对了关系式,找到较小的根,直接small_roots就完了。
那么在这里,我们先粗略地估算一下位数(不需要太准确,我们只要知道较小的未知数是否存在就行):
式子:e*(Md_1+d_0)-1-k[N-(q+p-1)]=0\ e:2048位,N:2048位,k:1024位左右可能\ 利用已知信息构造有限域式子:\ ed_0 - 1 - k[N-(q+p-1)]=0(modMe)\ 令q+p-1 =z\ 得ed_0 - 1 - k*(N-z)=0(modM*e)\ d_0:70位,k<1024位,z:512位\ 之后利用small_roots函数去解未知数就可以
其实论文看不看都行,经验熟练了直接拿手推就好。
这里有一点小坑的地方在于,能够常规small_roots函数能求解的位数比较低,我卡的参数也比较极限,有的师傅构造出来了,但是解不出来,原因可能有几个:
- bounds:这个求解的界卡太准不一定好,卡个差不多就行了,太大、过小都不一定求得出来
- m:格基规约的维数?总之越大求解的小根就越准确,但是算法的速度也会越慢。
- d:多项式的次数。
经验之谈:找对办法的情况下,估算好位数之后就可以开始梭哈了。
exp:
1 | import itertools |
LLL-ThirdBlood
考点:
- 格基的理解
- DSA算法
- 签名伪造
详细的文章在这里 一类基于各种DSA的HNP问题求解
其实仔细搜索一下hint的内容,网上应该也有大量的文章,通过大量地搜索发现:阴差阳错之下甚至发现和20年的学长出过的赛题撞了demo。(一周内共有十位师傅,包括一位校内新生做出来了,跪了)
part1:拖取数据
k的位数这里没有卡太死(甚至导致了非预期解),直接拖四组就行,求稳多拖几组的结果都一样的。
1 | #part1:拖取数据 |
part2:求解私钥
仔细想想这一步,其实就是上周LLLSecond的拓展,原理是一个差不多的(甚至构造也能一样?)
以下是论文的构造解:
1 | from Crypto.Util.number import inverse |
以下是学弟的非预期解:
1 | q = |
嗯,确确实实的非预期了,而且和题目说的一样,和上周的预期构造是相同,我怎么能如此的粗心大意?
part3:伪造签名
1 | from Crypto.Util.number import * |
Orac1e
第一周就学过的,CBC分组模式大致是什么流程。
详细文章这里看(看我看我),下面的代码就不用看了,有点啰嗦的。
介绍视频这里看视频在这里。
这里简单阐述一下:因为CBC分组模式存在的原因,我们可以把解密的处理分为以下流程
- 密文分组
- 密文组解密->得到明文组
- 合并明文组
- 去填充
- 检验是否合法
我们将中间解密的这块算法视作一个“黑盒”,
其中因为CBC分组解密流程的原因:密文->黑盒->上组密文->明文,
又因为必须检验填充是否合法的原因:
当且仅当去填充位上的数字==去填充位数,解密才能成功
那么按照第一周的CBC思想,通过已知明文去猜解密钥(中间经过黑盒的向量),这道题就结束了。
1 | from pwn import * |