近期比赛复现Vol.2
0xGame2023
Vigenere
1 | 0dGmqk{79ap4i0522g0a67m6i196he52357q60f} |
维吉尼亚解密,根据flag头手动爆一下密码即可,密码为game。
密码,觅码,先有*再密
1 | from secret import flag #从中导入秘密的flag,这是我们要破解的信息 |
一道极其无聊的签到题,根据题目代码逆回去即可,注意到这里的flag为中文,如果为bytes类型的输出需要进行decode才能得到中文结果。
BabyRSA
1 | from Crypto.Util.number import * |
比较简单:
1 | from Crypto.Util.number import * |
Take my bag!
1 | from Crypto.Util.number import * |
观察发现这是一个简单的背包加密,init背包向量可以自己求出,利用到超递增背包向量直接贪婪算法求解:
1 | from Crypto.Util.number import * |
猜谜
1 | from secret import flag,key |
这道加密题比较有意思:注意到我们知道flag头是0xGame{
,所以可以通过这个头求解得到7位key:
1 | from Crypto.Util.number import * |
What’s CBC?
1 | from Crypto.Util.number import * |
这题的CBC加密模式中,每轮的IV就是上一轮的加密结果,还是根据0xGame{
flag头来得到key,这题的块长度是8,不够八位,前七位key得到是b'\x8f\x8f\x8f\x8f\x8f\x8f\x8f'
,可以大胆猜测最后一位也是b'\x8f'
,利用key和IV求得剩下几块明文:
1 | from Crypto.Util.number import * |
中间的那个人
1 | from secret import flag |
一道比较好的DH入门题目,由于私钥比较小可以直接分解离散对数得到,根据题目代码直接求flag即可:
1 | from Crypto.Cipher import AES |
What’s CRT?
1 | from Crypto.Util.number import * |
题目给了个gift,可以通过gift求解pq,然后进行CRT,这题还存在一个不互素问题,一次CRT由于flag比较长不能在模n_
下求得,所以还需再一次求个不互素解后的CRT:
1 | from Crypto.Util.number import * |
EzRSA
呃,好好出题不好吗,为什么要整这么个大缝合怪……
1 | from challenges.challenge1 import RSAServe as challenge1 |
1 | from Crypto.Util.number import * |
1 | from Crypto.Util.number import * |
1 | from Crypto.Util.number import * |
刚开始看交互还以为整了个多大的活,结果发现是个大缝合怪。
task1提供:
所以费马小定理得到:
可以和$n$求公因子分解,就解出了flag1。
task2是NCTF2019的childRSA原题,$p-1$光滑数算法。
task3分解两个$n$的连分数可以得到两个$q$,进而求解。
分别计算三个题目得到这个题的flag,太懒了没写交互脚本。
ezLFSR(赛后)
1 | from Crypto.Util.number import * |
LFSR就是按照状态位计算,生成新的状态位。模二下的异或和按位与运算就是加法和乘法。
对于关键加密代码:
1 | for i in range(128): |
实质上就是:
1 | for i in range(128): |
所以如果有一个state向量,一个mask向量,得到的output就是两个向量相乘的结果。
进而,有一个state矩阵,一个mask向量,通过计算,得到的就是outputstate向量。
可以通过构造矩阵求解mask向量:
1 | from Crypto.Util.number import * |
Fault!Fault!(赛后)
1 | from Crypto.Util.number import * |
这题提到了一个之前没了解过的Fault Injection攻击。
Fault Injection攻击是啥呢,通俗地来说,就是将要攻击的目标(芯片、算法)看作是一个黑盒子,在能对黑盒内部的一些数据属性,进行一定程度的、可控的干涉影响的条件下,人为地构造一些错误,并通过观察这个黑盒在错误情况下的输出,来反推黑盒里的一些数据属性。
这个题目就是说我们可以人为反转私钥$d$的一个比特位,让某位的$0$为$1$或者$1$为$0$,但是我们并不知道这一位初始是$0$还是$1$。
假设第$i$个比特位为$0$,做手脚之后变为了$1$,记为:
设我们输入的构造密文$c$在篡改了私钥后的输出为$m’$。得到:
假设第$i$个比特位为$1$,做手脚之后变为了$0$,记为:
同理:
所以,根据分别在不同的$d$下得到的输出比值可以反推第$i$位,反复交互可以恢复$d$:
自己跑超时了,贴个官方的wp吧。
1 | from pwn import * |
EzECC
1 | from Crypto.Util.number import * |
一道比较容易的ECC,由于参数比较小,里面的几个参数都可以直接用sage分解离散对数求解,这题的$C_1$点采用它的算法居然还不是落在实际的椭圆曲线上的,直接用他的add
函数做个减法就行了:
1 | q=1139075593950729137191297 |
LLL-FirstBlood
1 | from random import randrange |
当时做这个题凭直觉一下就出了,现在来好好看一下:
result矩阵是一个由对角单位矩阵产生的一个行列式为1的矩阵,M是一个由flag行向量和三个noise向量组成的矩阵。flag行向量应该是要相对比较小的。
又有:
能用LLL求得其最短向量flag:
1 | from Crypto.Util.number import * |
EzMatrix
1 | from Crypto.Util.number import getPrime |
这题能找到原题,照着学了:
GDG Algiers CTF两道矩阵题wp - 华盟学院 (aqtd.com)
LLL-SecondBlood(赛后)
1 | from Crypto.Util.number import * |
展开:
左边是较大的已知参数,右边是较小的位置的质数。构造:
进行格基规约:
1 | from Crypto.Util.number import * |
此外,这道题还可以直接使用二元coppersmith进行求解:
因为未知量的位数都比较小,考虑使用多元Coppersmith,思路是在在有限域中解方程,可以通过展开和一定的方式换到整数域上,把问题变成简单的解方程问题(利用牛顿迭代法),然后再利用LLL算法去对构造的数学式子进行求解。
总而言之就是,实现有限域求根的算法(前提是这个根必须要比模数小很多)。因为求根的时候用到了LLL算法去规约基向量,得到的结果也是一组基(性质更好的),这种问题就被称之为SVP(最短向量)问题。
1 | from Crypto.Util.number import * |
Normal ECC
1 | from Crypto.Util.number import getPrime |
$P$阶数和$p$相同,使用smartattack梭哈:
1 | from hashlib import md5 |
鹏程杯 2023
Neltharion_and_Arthas
1 | import binascii |
先来看看以前没怎么了解的CTR模式:
发现这个比起其他的AES有个特点是key和计数器进行块运算,而密文和明文只参与了异或运算。
1 | assert flag.startswith(b'flag{') |
两次CTR采用了相同的key进行加密,异或的值是一致的,根据前面一堆乱七八糟的构造可以得到flag1的已知:2023: flag{
,这总共是11位,相当于我们可以知道gift1的11位,进行异或:
1 | from pwn import * |
根据输出的结果去百度搜索可以找到原台词:I am Deathwing, the destroyer, the end of all things,inevitable,indomitable,I am the cataclysm,根据这个台词就可以恢复flag的前部分,flag根据题目已知的应该是个uuid:
1 | from pwn import * |
得到前半部分flag。
后半部分采用CBC模式,key2一开始没发现,居然需要爆破其中四位,填充和key2都可以求得,直接爆破即可,根据输出2的已知部分即可得到一组唯一正确的key2,然后恢复CBC的iv向量,得到flag的后半部分:
1 | import string |
将得到的输出组合为uuid即可。
LeakyRSA
1 | from Crypto.Util.number import * |
题目给了leak = (p ^ q) >> (nbits - leakBits)
,我们知道了$p\oplus q$的前262位,可以恢复高位。
提供一种大佬的方法,可以对$p\oplus q$,进行剪枝爆破,根据四个限定条件可以逐位求出:
每当我们选择一个$p$的比特,那么$q$的比特也能确定。
根据测试可以发现,当我们确定的$pq$部分进行相乘的结果,高位一半的数是和$n$对应高位是相同的:
1 | tmp>>(tmp.bit_length()-i//2)== n>>(n.bit_length()-i//2) |
当我们确定了$p,q$的高位,剩下的全部补$0$,那么相乘的结果应该是要小于$n$;同样,剩下的全部补$1$,那么相乘的结果应该大于$n$:
1 | (int(pnew.ljust(512,'0'),2)*int(qnew.ljust(512,'0'),2) < n) |
1 | from Crypto.Util.number import * |
最终得到的是10个$p,q$,还需要解一个coppersmith,由于存在$p,q$两个质数,所以理论上应该是有两个解,修改一下相关参数,如果epsilon
需要调到很小的话不如直接爆破几位增大一下epsilon可以提升速度:
1 | from Crypto.Util.number import * |
colorful_matrix(赛后)
1 | import random |
发现:
1 | p = int(gmpy2.next_prime(bytes_to_long(key1 + os.urandom(64)))) |
这种情况叫做AGCD问题(近似 GCD 问题):
可以构造格子:
存在最短向量:
1 | ns = [38630062416586710341458654419912504176237737247477839749085033080367529539859992076587411537805430366799412095876782912512744262957062106155418341531142309858429218208463637096843365217114990765965110566415965985105403996944993619708417839598461935470469097206342256014086162845948208599334925650727933097059538199199685364793545286980392966271769914201657672004082101110775504946586957241075964270454872257405872181588544468173017149763827540561921126826597515171761064800381983526515300315517818122598179574900255685121991744205071544970, 41522753602903133841910260331594875922287719226997542592715810409935551768308104573333760854332533376702631593490915962706512143045107096658851885513727202513616813054397657610854303071682604806070009002234312854968365250748142324994926715544722158698813288131533399544263105858513134170084625526223987620550110255872688155827773099232631041345207194483609514502522566888883736218471849075697433311580004701384847571029783514418685068903758509270527252444771313048094566344002411364378658592832008194309873599342916391769027015343562030852, 41542983120532762175372001624404625565366126179958909731196555044290633581761361918706298428954501507557598076910710787422049443564800530253137695341299743714514361560156305534490483794181933110893966453220306980682146624294992100948497284459992930850081254114996830645068636306625330524465991656430799359422407117440063911943625477783216502523414967017151717597372146324488526509879620785458016456593044828784565522423332830549325397893426472247197776412026158371655860380929692662547882654137064941217130915364306358205055760044763651406, 42853015443318352230776688785915441259875645365236808434164117288657978345098324019250085686482568413223085548506789311679316323466083886556772338612177680666217592255234589446979456714341877135596118517098603502394776049958587301113539552072352462301070489369653155854389890761241450743607560719433910573462283304103064437843063566946231984094581307498714742271881862348689297267558023093643893310002803310596286441071314219020032740336515363830250477649030557311461077069407775907176409762823453607196260454965048316567154365877848652918, 31152961872836435078296602982779340735140569916125711058616435902653202922218293684857125091648631460215120167354825278469413413558325850576700866199515219603448136082693185200558425103833947831228064760642508443585470729998592994719564254894176473779555436230174300038353978808432410463449170865897259181312953584408177790825688497584119467820716449210429423337019604137134889051973100340798405991782200038835066294194815913887924272593864934325496116821854183293510325217934617021428710898873475027666892706022106386340733691632884942848] |
进而可以得到$ms$,$ms=ns\bmod p$。
1 | num2 = 37 |
检查一下生成位数,猜测可以使用随机数预测得到$iv$,发现这题的加密用到的是key1,跟key2毫无关系,所以直接求即可:
1 | aes = AES.new(key1,AES.MODE_CBC,iv) |
1 | import random |
SecretShare(赛后)
1 | import random |
题目给了提示:
已知的$A,X$都是21个1024位的数,$X$为全随机,直接顺着恢复即可:
1 | from extend_mt19937_predictor import ExtendMT19937Predictor |
from extend_mt19937_predictor import ExtendMT19937Predictor
这个库提供了回溯随机数的功能,但是如果想直接预测之前的随机数,需要先填充掉所有传入的随机数,接下来回溯的随机数就是预测之前的随机数:
1
2
3
4
5
6
7 predictor = ExtendMT19937Predictor()
for i in range(len(X)):
predictor.setrandbits(X[i], 1024)
_ = [predictor.backtrack_getrandbits(1024) for _ in range(len(X))]
A = []
for i in range(20):
A.append(predictor.backtrack_getrandbits(1024))