medium
这两天在打比赛,耽搁了一些时间,这篇博客晚了两天,不过还好,接着冲!( o=^•ェ•)o ┏━┓
Aslv1 (59 solves)
附件代码
#!/usr/bin/env python3
from Crypto.Util.number import *
from flag import flag
def base(n, l):
D = []
while n > 0:
n, r = divmod(n, l)
D.append(r)
return ''.join(str(d) for d in reversed(D)) or '0'
def asiv_prng(seed):
l = len(seed)
_seed = base(bytes_to_long(seed), 3)#三进制
_seed = [int(_) for _ in _seed]
_l = len(_seed)
R = [[getRandomRange(0, 3) for _ in range(_l)] for _ in range(_l**2)]
S = []
for r in R:
s = 0
for _ in range(_l):
s += (r[_] * _seed[_]) % 3
# s += getRandomRange(0, 3)
s %= 3
S.append(s)
return R, S
seed = flag.lstrip(b'CCTF{').rstrip(b'}')
R, S = asiv_prng(seed)
f = open('output.txt', 'w')
f.write(f'R = {R}\nS = {S}')
f.close()
output.txt
我的理解
题目生成了两个加密函数,将flag作为seed转化三进制后,再次化为一维列表进行加密,任意成成了一组二维随机数组,其中的数据在0到3之间,然后依次对两组数组之间展开计算,并且计算的结果全部模三,最后返回加密结果。
我的方法
首先对第一个函数进行解析,
def base(n, l):
D = []
while n > 0:
n, r = divmod(n, l)
D.append(r)
return ''.join(str(d) for d in reversed(D)) or '0'
经过测试分析,可以随机带进去一下数据进行测试,发现是转为三进制,既然如此,那么在恢复的时候只要int(,3)就可以进行恢复
然后是主加密程序
def asiv_prng(seed):
l = len(seed)
_seed = base(bytes_to_long(seed), 3)#三进制
_seed = [int(_) for _ in _seed]
_l = len(_seed)
R = [[getRandomRange(0, 3) for _ in range(_l)] for _ in range(_l**2)]
S = []
for r in R:
s = 0
for _ in range(_l):
s += (r[_] * _seed[_]) % 3
# s += getRandomRange(0, 3)
s %= 3
S.append(s)
return R, S
R是随机生成的二位矩阵,seed在前面提到,是由flag转化的矩阵。
在运算中,R中每一行的数据,会依次与seed数组的每一个数据进行模三乘运算,然后这一行再相加起来进行模三。
下图可以表示
(
r
[
0
]
r
[
1
]
r
[
2
]
⋯
r
[
l
−
1
]
)
∗
(
s
e
e
d
[
0
]
s
e
e
d
[
1
]
⋮
s
e
e
d
[
l
−
1
]
)
=
S
[
0
]
\begin{pmatrix} r[0]&r[1]&r[2] \cdots r[l-1] \end{pmatrix} *\begin{pmatrix}seed[0]\\seed[1]\\ \vdots\\seed[l-1]\end{pmatrix}=S[0]
(r[0]r[1]r[2]⋯r[l−1])∗
seed[0]seed[1]⋮seed[l−1]
=S[0]
(
R
[
0
]
R
[
1
]
R
[
2
]
⋮
R
[
l
2
−
1
]
)
∗
(
s
e
e
d
)
=
S
\begin{pmatrix} R[0]\\R[1]\\R[2]\\\vdots\\R[l^{2}-1]\end{pmatrix} *\begin{pmatrix}seed\end{pmatrix}=S
R[0]R[1]R[2]⋮R[l2−1]
∗(seed)=S
(第一个矩阵的r代表的即是R的第一行数据R[0])
而题目中已经给出R,S,显而易见,就是简单的矩阵相乘,AX=B型,直接求取后即可得到seed矩阵,然后转为列表,三进制,换成flag就可的
我的代码
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/8 16:37
# @Author : lingfeng
# @File : WP.py
from Crypto.Util.number import *
with open("output.txt") as output:
f=output.readlines()
R=eval(f[0][4:])
S=eval(f[1][4:])#110
R_=R
r=matrix(GF(3),R_)
s=matrix(GF(3),S).transpose()
seed=r\s
print(seed)
m=seed.list()
flag="".join(map(str,m))
FLAG=int(flag,3)
print(b'CCTF{'+long_to_bytes(FLAG)+B'}')#b'CCTF{3Xpl0i7eD_bY_AtT4ck3r!}'
TPSD (57 solves)
附件代码
本题没有附件,是一道服务器交互题
我的理解
题目中要求我们给出三个数据。
并且给出了三组限制条件
p 3 + q 3 + r 3 = 1 p^3+q^3+r^3=1 p3+q3+r3=1
p , q , r 至少有一个是素数 p,q,r 至少有一个是素数 p,q,r至少有一个是素数
对 p b i t s , q b i t s , r b i t s 进行限制 对p_{bits},q_{bits},r_{bits}进行限制 对pbits,qbits,rbits进行限制
我的方法
题目的介绍中提示这是一个丢番图方程,没有见过的方程,google求解。
成功找到关于该丢番图的论文(为什么会查找呢?因为我实在是推不出了😭)论文地址
观察一番发现,只有z有可能是素数。
根据论文构造关系并且爆破t最终查找出合适关系的p,q,r。
输入p,q,r成功后发现还有第二步,要完成多次提交,无奈写交互脚本。
我的代码
import random
from pwn import *
from tqdm import *
from Crypto.Util.number import *
r=remote("05.cr.yp.toc.tf",11137)
def get_prime(t):
t=-2**(t-1)
while True:
z=1-9*t**3
if isPrime(z):
x=9*t**4
y=3*t-9*t**4
return x,y,z,t
t -= 1
for _ in trange(19):
r.recvuntil(b'(')
bit_s=(int(r.recvuntil(b',')[:-1])//3+1)
p,q,R,t=get_prime(bit_s)
r.recvline()
r.sendline(str(p).encode()+b','+str(q).encode()+b','+str(R).encode())
t=r.recvline()
print(t)#CCTF{pr1m3S_in_7ErnArY_Cu8!c_3qu4tI0nS!}
r.close()
Trex (45 solves)
附件代码
#!/usr/bin/env python3
import random
import sys
from flag import flag
def die(*args):
pr(*args)
quit()
def pr(*args):
s = " ".join(map(str, args))
sys.stdout.write(s + "\n")
sys.stdout.flush()
def sc():
return sys.stdin.buffer.readline()
def check_inputs(a, b, c):
if not all(isinstance(x, int) for x in [a, b, c]):
return False
if a == 0 or b == 0 or c == 0:
return False
if a == b or b == c or a == c:
return False
return True
def check_solution(a, x, y, z):
return (x*x + y*y - x*y - a*(z**3)) == 0
def main():
border = "|"
pr(border*72)
pr(border, ".:: Hi all, she DID it, you should do it too! Are you ready? ::. ", border)
pr(border, "Welcome to the Ternary World! You need to pass each level until 20 ", border)
pr(border, "to get the flag. Pay attention that your solutions should be nonzero", border)
pr(border, "distinct integers. Let's start! ", border)
pr(border*72)
level, step = 0, 19
while level <= step:
a = random.randint(2**(level * 12), 2**(level*12 + 12))
equation = f'x^2 + y^2 - xy = {a}*z^3'
pr(f"Level {level + 1}: {equation}")
inputs = input().strip().split(",")
try:
x, y, z = map(int, inputs)
except:
die(border, "Inva9lid input, Bye!!")
if check_inputs(x, y, z):
if check_solution(a, x, y, z):
pr(border, "Correct! Try the next level :)")
level += 1
else:
pr(border, "You didn't provide the correct solution.")
die(border, "Better luck next time!")
else:
pr(border, "Your solutions should be non-zero distinct integers")
die(border, "Quiting...")
if level == step:
pr(border, "Congratulations! You've successfully solved all the equations!")
die(border, f"flag: {flag}")
if __name__ == '__main__':
main()
我的理解
服务器会随机生成一个随机数为a,
然后产生一组
x
2
+
y
2
−
x
y
=
a
z
3
x^2+y^2-xy=az^3
x2+y2−xy=az3的方程,我们需要提供一组xyz来满足这个方程式。
依然具有一些限制条件
x ≠ y ≠ z x\ne y \ne z x=y=z
x ≠ 0 , y ≠ 0 , z ≠ 0 x\ne 0 ,y \ne 0, z \ne 0 x=0,y=0,z=0
并且 x y z 不可以重复使用 x y z不可以重复使用 xyz不可以重复使用
我们需要完成19次这样的交互,并且每次交互的随机生成的a值会变的更大
我的方法
同样是一个丢番图方程,既然题目不让x=y=z,可以设一下x=-y
x
=
−
y
,
x
2
+
y
2
−
x
y
=
a
z
3
=
>
3
x
2
=
a
z
3
=
>
x
2
z
3
=
a
3
x=-y,x^2+y^2-xy=az^3\\ =>3x^2=az^3\\ => \frac{x^2}{z^3}=\frac{a}{3}
x=−y,x2+y2−xy=az3=>3x2=az3=>z3x2=3a
到这里之后,发现了x,z的关系所在,我们假设
x
=
k
a
,
那么
z
3
=
3
a
k
2
,
使得
z
是一个整数,则可以推出,
k
=
3
a
,
这样的化,
z
3
正好可以开出一个
3
a
x=ka,那么z^3=3ak^2,使得z是一个整数,则可以推出,k=3a,这样的化,z^3正好可以开出一个3a
x=ka,那么z3=3ak2,使得z是一个整数,则可以推出,k=3a,这样的化,z3正好可以开出一个3a
然后就可以得到完整的x,y,z的关系式
x = 3 a 2 , y = − 3 a 2 , z = 3 a x=3a^2,y=-3a^2,z=3a x=3a2,y=−3a2,z=3a
根据这个关系式,然后接收每次服务器生成的a来对x,y,z进行构造,就可以啦🥰
我的代码
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/8 21:33
# @Author : lingfeng
# @File : wp.py
from pwn import *
from tqdm import *
r=remote("03.cr.yp.toc.tf" ,31317)
for i in trange(19):
r.recvuntil(b'=')
a=r.recvline().decode().strip()[:-4]
print(a)
a=eval(a)
print(a)
x=3*a**2
y=-x
z=3*a
r.sendline((str(x)+','+str(y)+','+str(z)).encode())
t=r.recvline()
print(t)
r.interactive()
#CCTF{T3rn3ry_Tr3x_3Qu4t!0n}
Keymoted (28 solves)
附件代码
keymoted.sage
#!/usr/bin/env sage
from Crypto.Util.number import *
from flag import flag
def gen_koymoted(nbit):
p = getPrime(nbit)
a, b = [randint(1, p - 1) for _ in '__']
Ep = EllipticCurve(GF(p), [a, b])
tp = p + 1 - Ep.order()
_s = p ^^ ((2 ** (nbit - 1)) + 2 ** (nbit // 2))
q = next_prime(2 * _s + 1)
Eq = EllipticCurve(GF(q), [a, b])
n = p * q
tq = q + 1 - Eq.order()
e = 65537
while True:
if gcd(e, (p**2 - tp**2) * (q**2 - tq**2)) == 1:
break
else:
e = next_prime(e)
pkey, skey = (n, e, a, b), (p, q)
return pkey, skey
def encrypt(msg, pkey, skey):
n, e, a, b = pkey
p, q = skey
m = bytes_to_long(msg)
assert m < n
while True:
xp = (m**3 + a*m + b) % p
xq = (m**3 + a*m + b) % q
if pow(xp, (p-1)//2, p) == pow(xq, (q-1)//2, q) == 1:
break
else:
m += 1
eq1, eq2 = Mod(xp, p), Mod(xq, q)
rp, rq = sqrt(eq1), sqrt(eq2)
_, x, y = xgcd(p, q)
Z = Zmod(n)
x = (Z(rp) * Z(q) * Z(y) + Z(rq) * Z(p) * Z(x)) % n
E = EllipticCurve(Z, [a, b])
P = E(m, x)
enc = e * P
return enc
nbit = 256
pkey, skey = gen_koymoted(nbit)
enc = encrypt(flag, pkey, skey)
print(f'pkey = {pkey}')
print(f'enc = {enc}')
output.txt
pkey = (6660938713055850877314255610895820875305739186102790477966786501810416821294442374977193379731704125177528590285016474818841859956990486067573436301232301, 65537, 5539256645640498184116966196249666621079506508209770360679460869295427007578, 20151017657582479433586370393795140515103572865771721775868586710594524816458)
enc = (6641320679869421443758875467781930795132746694454926965779628505713445486895274490835545942727970688359873955019634877304270220728625521646208912044469433 : 2856872654927815636828860866843721158889474116106462420201092148493803550131351543372740950198853438539317164093538508795630146854596724019329887894933972 : 1)
我的理解
题目使用两个函数gen_koymoted以及encrypt,题目使用gen_koymoted获得了一组公钥以及私钥,并将公钥私钥带入到encrypt函数中对flag进行加密,加密采用的式构建分别两组椭圆曲线,分别是(
G
F
(
p
)
GF(p)
GF(p),和
G
F
(
q
)
GF(q)
GF(q)上的一样的曲线),然后通过crt定理合并在Zmod(n)上,并且使得Q=e*P,
P和Q是Zmod(n)的曲线上的两个点,其中flag作为P点的x坐标
我的方法
第一步
观察gen_koymoted,发现关键代码
_s = p ^^ ((2 ** (nbit - 1)) + 2 ** (nbit // 2))
q = next_prime(2 * _s + 1)
p,q的生成存在漏洞,两者存在线性关系。
p
与
2
∗
_
s
,
两者只有第一位以及第
n
b
i
t
s
2
位上的比特是相反的,其他都是一样的,
而且
p
的第一位
b
i
t
一定是
1
,
p
与
2
∗
_
s
只有两种可能
_
s
=
2
∗
(
p
+
2
128
−
2
255
)
或者
_
s
=
2
∗
(
p
−
2
128
−
2
255
)
p与2*\_s,两者只有第一位以及第\frac{nbits}{2}位上的比特是相反的,其他都是一样的,\\而且p的第一位bit一定是1,p与2*\_s只有两种可能\\ \_s=2*(p+2^{128}-2^{255})或者\_s=2*(p-2^{128}-2^{255})
p与2∗_s,两者只有第一位以及第2nbits位上的比特是相反的,其他都是一样的,而且p的第一位bit一定是1,p与2∗_s只有两种可能_s=2∗(p+2128−2255)或者_s=2∗(p−2128−2255)
而q是i的下一个素数,则
q
=
_
s
+
i
(
i
是一个小常数,一般不会大于
1000
)
q=\_s+i(i是一个小常数,一般不会大于1000)
q=_s+i(i是一个小常数,一般不会大于1000)
得到了
q
=
(
2
∗
(
p
+
2
128
−
2
255
)
+
i
)
q=(2*(p+2^{128}-2^{255})+i)
q=(2∗(p+2128−2255)+i)联立
n
=
p
q
n=pq
n=pq可以解方程得到
p
,
q
p,q
p,q
第二步
根据GF§,GF(q),a,b构建出两条椭圆曲线,将Q带入两条曲线中,生成p_Q,q_Q
而P=e*Q,我们只需要求出e关于p_Q,q_Q的逆元,然后得到原始的p_P,q_P通过CRT定理求解出P点,然后P_x为flag即可
我的代码
# !/usr/bin/env python3.10
# -*- coding: utf-8 -*-
# @Time : 2023/7/8 16:58
# @Author : lingfeng
# @File : wp.sage
from Crypto.Util.number import *
from gmpy2 import *
p=getPrime(256)
pkey = (6660938713055850877314255610895820875305739186102790477966786501810416821294442374977193379731704125177528590285016474818841859956990486067573436301232301, 65537, 5539256645640498184116966196249666621079506508209770360679460869295427007578, 20151017657582479433586370393795140515103572865771721775868586710594524816458)
enc = (6641320679869421443758875467781930795132746694454926965779628505713445486895274490835545942727970688359873955019634877304270220728625521646208912044469433 ,2856872654927815636828860866843721158889474116106462420201092148493803550131351543372740950198853438539317164093538508795630146854596724019329887894933972)
nbit=256
from tqdm import *
import re
from Crypto.Util.number import *
n=6660938713055850877314255610895820875305739186102790477966786501810416821294442374977193379731704125177528590285016474818841859956990486067573436301232301
# var('p')
# import re
# def extract_numbers(string):
# numbers = re.findall(r'\d+', string)
# return numbers
# for i in trange(1,1000):
# f=(2*(p+2**128-2**255)+i)*p==n
# t=str(solve(f,p))
# s=extract_numbers(t)
# for a in s:
# if isPrime(int(a)) and n%int(a)==0:
# print(t)
#
p=93511613846272978051774379195449772332692693333173612296021789501865098047641
q=n//p
n,e,a,b = (6660938713055850877314255610895820875305739186102790477966786501810416821294442374977193379731704125177528590285016474818841859956990486067573436301232301, 65537, 5539256645640498184116966196249666621079506508209770360679460869295427007578, 20151017657582479433586370393795140515103572865771721775868586710594524816458)
Z=Zmod(n)
Ep = EllipticCurve(Zmod(p), [a, b])
Eq= EllipticCurve(Zmod(q), [a, b])
enc1 = Ep(6641320679869421443758875467781930795132746694454926965779628505713445486895274490835545942727970688359873955019634877304270220728625521646208912044469433 ,2856872654927815636828860866843721158889474116106462420201092148493803550131351543372740950198853438539317164093538508795630146854596724019329887894933972)
enc2 = Eq(6641320679869421443758875467781930795132746694454926965779628505713445486895274490835545942727970688359873955019634877304270220728625521646208912044469433 ,2856872654927815636828860866843721158889474116106462420201092148493803550131351543372740950198853438539317164093538508795630146854596724019329887894933972)
phi=(p-1)*(q-1)
d1=inverse(e,Ep.order())
d2=inverse(e,Eq.order())
dec1=int(d1)*enc1
dec2=int(d2)*enc2
m=crt([int(dec1[0]),int(dec2[0])],[p,q])
#CCTF{a_n3W_4t7aCk_0n_RSA_a9ain!?\x82
至此,medium已经全部完成了,medium总归来说难度不多,很好玩的,接下来就进入了hard篇的学习,难度不小,再接再励!,一起加油!
愿护一枚银杏叶
我们的明天都是未知的,但是我们的今天是可以把控的,今日复明日,明日何其多,继续加油,时日短暂,为了更好的明日,为了心中的目标,冲<(^-^)>,共勉!!