Crypto
SteinsGate
越过无数条世界线,我终于找到了你。
Padding Oracle + 差分优化
填充的方法取自:HITCON2023同文章(细心的朋友一定发现了这里题目脚本大部分直接抄HITCON2023的)
上次HITCON是用了模型1,看了官方的WP说,他推了一下发现三个模型都是有问题的,那我也试着去推一下看看,这题不就出来了吗?这题不就出来了吗?
那么这题有什么新意在里面呢?除了PaddingOracle这种古老的东西。
- 和HITCON2023的CareLess 一样,我提供了一个NCTF{的明文头作为提示,可以发现每组密文单独提出来都有办法提交到服务器去做解密,我们只需要猜测后面两个字符就可以直接按照PaddingOracle的思路去打,但是我们一定要猜256256吗?注意到填充的时候是会忽略到Y的最低位,所以我们如法炮制,猜256128组就可以。
- 接下来就是猜明文,很多师傅都是直接猜16*16的十六进制明文,一开始出这道题的时候我也想过会有这种办法,但是实际上这样猜最差情况要猜大概20-30分钟左右(复杂度我懒得估算了),有点没意思。仔细想想,我们再猜倒数第三位和第四位的时候,是要用到异或"有效明文"才能继续猜测,有没有一种可能有效明文我们不一定知道,但是这个异或值是可以知道的,那就是差分。
- 16*16的两个十六进制数差分值大概只有30组左右,猜那个复杂度一下就低很多了。所以预期解就如下所示:
1 | from pwn import * |
上题的时候没注意,把测试版丢上去了,结果出了一个BUG,我在用自己的脚本打的时候发现解不了,我以为是服务器问题,加上有师傅反馈本地通了,远程交互时间不够。出题时间太早,一时半会我也记不清细节了,还真以为这题废了。后来再测试的时候发现400秒完全是绰绰有余的:
随即把交互时间改回去了,唉明明是一个很巧妙的Padding,被暴力非预期了。。
FaultMilestone
经典的故障注入,里程碑式的攻击思路——差分。
参考文献:文献地址
本题为DES故障差分分析,其实关于DES差分的核心都差不多,
篇幅有限,这里就简单阐述如何攻击DES算法:
- 只要我们能够恢复某一轮的密钥,就能够倒推回256种可能的主密钥(轮密钥拓展时会丢失信息)
- 现在首要目标就是恢复某一轮的轮密钥,仔细观察DES的其中某一轮的加密结构,如图:
可以看到轮密钥加(第一个XOR)这步在S盒之前,那么我们的差分分析就应该应用在此处。
- E盒,P盒是线性可逆的,那么接下来要应用差分攻击就得拿到进入S盒的前后输入,之后就猜测轮密钥就可以。通过密文可以直接了当地拿到输入加密前的差分(注意右半部分直接移动到左半部分作为密文),那么现在问题自然而然地就落到了怎么找输出差分上面,也就是拿到(第二个XOR的输入值)。
- 观察题目故障(Fault)的发生位置是位于13轮加密前,只造成了 1 bit 的故障,就是因为这个才使得我们有机可乘拿到第二个XOR的差分输入,所以重点的分析就落在了这里——找到这个bit造成的影响。
这里我推荐一种直观的分析办法:现在我们的目标是拿到最后一轮的输入输出差分,那么既然题目是可以多次提供密文的,就说明上一轮的输出差分(作为下一轮的输入差分)会有某种固定的关系(具体原因篇幅有限难以解释),那么我们直接在本地把16轮中的最后一轮加密删去,反复测试一下密文右半部分的差分关系。
这时候就发现猫腻了——15轮输出的差分虽然不是固定的,但是确实可猜测的,有极大概率会落在10种可能中(出完题测试脚本就删了,这里就不放截图了,感兴趣的可以自己测试,这里直接给出结果)。
大致上可能的取值如下:
1 | diffs = ['0x202', '0x8002', '0x8200', '0x8202', '0x800002', '0x800200', '0x800202', '0x808000', '0x808002', '0x808200', '0x808202'] |
到这里这道题就变得很简单了,把这几个可能值当做已知去用,直接做差分分析猜测轮密钥,再从轮密钥恢复主密钥就结束了。——原本这道题是给了静态密文不打算部署在云端的,考虑到公平性和防作弊还是部署在云端生成密文了,这里会有极低的可能性出现15轮的输出差分不落在diffs上面的情况,这组不行建议多试试几组,总能行的。
详细差分是怎么作用的之后我会放在个人博客里面,这里只给出简单的分析和解法:
先补完解密函数,写一个正常的DES,用作还原FLAG:
1 | from operator import add |
接下来做差分分析,重点落在进入S盒前后研究的部分,以及补完线性部件的逆向函数:
1 | from Crypto.Util.number import * |
注意这里生成轮密钥的时候,由于是猜测差分我们取可能性最高的那几个位置的密钥就可,但是由于未知原因0号密钥不一定能够猜出来(多半是受错误差分影响了),但这里可以稳定猜出一个轮密钥的7/8这样就够了。
关于这步猜密钥有一些小细节:
1.由于输入差分只有十种可能,遍历这十种,当出现猜测的密钥可能性比较高的情况时,有很大概率这组差分输入是正确的,但是在脚本的解法中,我直接拿所有的可能取值去进行遍历猜测密钥,肯定会存在很多种错误的猜测,但是即使是这样出现次数最高的猜测值也会是对的。
2.密文块的数量越多肯定猜的越准,这里测试的时候发现五组这样差不多就够了,就没管太多,实际上还可以更少也说不定。
3.本质上这题感觉就是三轮DES差分分析,能够在一个比较快的时间内解出正确答案,如果对三轮以上DES差分研究的话可能还要用到概率统计等数学知识,我个人暂时也不太会,但是肯定是能解的,一个比较有意思的情况就是在现实中,如果我们能够在目标的机器上面植入一个硬件后门,在DES 12轮以后的加密注入错误,就可以实现唯密文解密了,感觉是挺奇妙的。
接下来还原主密钥,并用主密钥去解密:
1 | from operator import add |
因为一个确定的轮密钥会对应256种不同的主密钥,加上一个未知的轮密钥要爆破(也可以从猜测值里面找,实测可行),所以我们大概会得到65536份解密的明文——只有一份全是可打印字符是对的,从那份提取出主密钥去还原flag就可。
CalabiYau
二维世界的奇思妙想。
密钥交换方案是基于RLWE难题的DingKeyExChange,出这题也算是跟一波潮流了,虽然这个方案好像没被NIST选上,偶然看看之后决定打打试试看,于是就有了这道题。
详细题目细节篇幅有限不再阐述,直接放攻击流程:
- 第一部分获取Alice.s,关注到有个w的信号处理函数,用来同步双方mod2时候出现“跨域”的问题,所以Alice回复的时候会有两个信息一个是Alice.pk一个是Alice.w,前一个是幌子,我们只要构造好交换的Eve.pk,交给Alice就能一次性拿到Alice.s。
- 第二部分获取Bob.s,发现一个小细节,Bob的e参数没了,直接拿到Bob.a*Bob.s,这时候的问题就不是RLWE了,由于多项式的卷积运算(如果在签到题选择逃课了,大概率会想不到?),这个问题可以视作ahssp,而且生成公钥的时候Bob.a是静态的(甚至是可排序的),直接用正交格(格子造法同样在下面的文章中)打就完了,但问题是维度有点大,还是需要优化的方案(同时还需要一些好的工具),可以参考这篇文章文章地址,既然是ahssp,再看一篇经典论文(论文地址),solution的脚本部分就是取自论文里面提供的代码。
要注意的是,这个解法并不是百分百能够打通的,因为维度比较大的情况下,规约后的结果不一定对(使用Nguyen-Stern attack的情况下,论文中有另外一种攻击的办法,但是因为懒而且128维很容易跑不出来就没做测试),在攻击的时候可能要多试几遍看看脚本的结果是否符合预期。
这个概率大概如下:
100次里面能成功30次左右,还是比较高的。
接下来知道这两部分的玩法和难题之后,我感觉就没什么难度了,唯一制约的是时间。先放solution:
(其中正交格的构造和脚本可以直接在论文里面找到,之后爆改一下就行)
1 | #sage |
本着CTF是为了相互学习知识,拓展未知领域的精神,我缩短了交互时间,与之对应的有几种推荐工具——g6k,SageMath10.2。
SageMath10.2的LLL应该是内置了加速算法flatter还有一些优化之类的,这里放几组测试结果对比一下:
左边是在Windows本地用SageMath9.2 notebook跑的三组LLL规约(CPU:i7-10870),
右边是在阿里云的轻量应用服务器上用SageMath10.2跑的同一个脚本,
这组是应用Flatter算法跑的结果,算法链接:
可以看到SageMath10.2的加速效果还是很大的,那么就在服务器上跑这个Solution就可以
大概280秒这样,服务器交互时间是320秒,给的挺宽松了。
另外一个办法就是用g6k,感兴趣的师傅可以用自己构造的格去放到g6k里面跑,我没试过,但是理论上肯定是可行的。推荐安装链接:
写的WP很乱,毕竟就是交个报告,证明这题是可解的,详细的分析期末过后我会在博客里补上。
CodeInfinite
代号:无限大——问题无限大
原题基本上是直接抄了LakeCTF的,参数基本上没改,有师傅猜都猜中了,一个人出五道题确实累,这题算是水水了。实际上要改的话可以把参数设置成随机的,提供基点(base point)和公钥(public key)去求groebner基,然后再让大伙去本地爆一下这个参数b——实测下来太累太麻烦了(也为了防止非预期),思来想去为了方便大家、方便自己,干脆直接就抄抄已有的参数吧!
简单分析:题目没有提供任何曲线参数,以及基点。但是注意到曲线的倍点运算过程中,不会对“点是否在曲线上”做校验。
那么我们考虑:因为椭圆曲线本质上也是个有限域上的多项式,既然是多项式就想到多元多项式的求根办法,自然而然地就想应用groebner基去交互两次以上(实测两次就可以)拿到原本正确曲线上的点。
本地fuzz一下题目的脚本发现——我们提供的点不一定会在曲线上!或者直接审计源码就会发现,我们提供点给Alice后,Alice竟然就自然而然地拿去做计算了,也没有做任何检验。究其原因是因为脚本采用的点乘运算中,会忽略掉参数b的影响,使得我们能够注入不同的曲线。
之后就利用故障注入(主要是曲线的b参数不同),去注入其他曲线上的点(要求阶比较小),得到信息之后求DLP再CRT就完了。
1 | #part1 |
拿到曲线参数发现是NIST192的参数,可以去试着找找文献,这里提供一篇
论文地址:参考文献
接下来打就完了:
1 | # Finite field prime |
Sign
密码签到题,就扣了一个解密函数,加密方案用的是NTRU,学会SageMath的基本那几句命令就能秒,实在不行用搜索引擎查一下NTRU格密码的加密方案是怎么操作的,手动写个解密函数也可以。
1 | # Sage |