CNSS_Recruit_2020_crypto
(baby) Crypto做题须知
Description
crypto 方向 flag 形式如下 cnss{这是flag格式}
flag = int(b"cnss".hex(), 16)
flag = "cnss{%s}" % (hex(pow(flag, 0x10001, 19260817)))
Solution
运行代码得到flag
cnss{0x2f06f3}
(Baby)BabyFib
Description
n = 1926081719260817
f = [0, 1, 1]
for i in range(3, n + 1):
f.append( ( f[i - 1] + f[i - 2] ) % n )
flag = "cnss{" + hex(f[n])[2:] + "}"
print(flag)
solution
求斐波那契数第\(n\)项\(\mod n\)
矩阵快速幂或者其他算法
cnss{4de158c740f0e}
Code
没存
(baby) 龙王的代码I
Description
“三年! 三年不准再用超算!隐忍!”
隐忍的戳戳龙王不能再用超算轻易拿到flag了,只能向你求助。
戳戳龙王的代码如下:
def g(x):
if x <= 1:
return False
cnt = 0
for i in range(1, x + 1):
if x % i == 0:
cnt += 1
if cnt > 2:
return False
return True
ans = 0
for i in range(1000000):
if g(i):
ans += i * i
print("Flag is cnss{%d}" % (ans))
Solution
线性筛质数
cnss{24693298341834533}
Code
没存
(baby) 龙王的代码II
Description
“三年! 三年不准再用超算!隐忍!”
隐忍的戳戳龙王不能再用超算轻易拿到flag了,只能向你求助。
戳戳龙王的代码如下:
from Crypto.Util.number import long_to_bytes
from hashlib import md5
p = [312609034968489560095816917649428218927, 201093538614623911247134492490252725577, 298359341518180165381217901632470444187, 283779784104916354790175596793270218363]
a = [135563869850958998185249517440045295817, 78188242776496129424059232264784298435, 120640187605672057179209488273239034888, 75233765181335440789936728233620033742]
flag = ""
def check(cur):
for i in range(4):
if cur % p[i] != a[i]:
return False
return True
for i in range(2**384):
if(check(i)):
flag = "cnss{" + md5(long_to_bytes(i)).hexdigest() + "}"
# 在给定的条件内,必有一个i满足check
Solution
中国剩余定理
cnss{089969da277f7a7ccc19c483c42d58c4}
Code
见\((easy) NotFib\)
(easy) 龙王的代码III
Description
“三年! 三年不准再用超算!隐忍!”
隐忍的戳戳龙王不能再用超算轻易拿到flag了,只能向你求助。
戳戳龙王的代码如下:
def find_Residue(x,p):
for i in range(2,p):
if pow(i,2,p) == x:
return i
return -1
print ("Flag is cnss{%d}" % (find_Residue(7705321458598879497,12979492918818656033)))
Solution
求二次剩余
可以使用cipolla
算法
cnss{4380399041077346226}
Code
见\((Boss)PRNG2\)
(easy) Feistel
Description
Do you know Feistel ?
import os
real_flag = 'cnss{....}'
fake_flag = 'cnss{c91cac1ae8106c3aa80d49bf60f1c8d8}'
def xor(a, b):
return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b))
def str_to_hex(s):
return s.encode().hex()
def swap(a):
return a[16: ] + a[ :16]
def single(m, k):
assert len(m) == 32
l = m[: 16]
r = m[16: ]
nl, nr = r, xor(k, xor(l, r))
return nl + nr
def encrypt(m, k):
for i in k:
m = single(m, i)
return swap(m)
k = []
for i in range(0, 16):
k.append(os.urandom(8).hex())
print(str_to_hex(encrypt(fake_flag[5: -1], k)))
print(str_to_hex(encrypt(real_flag[5: -1], k)))
# 6464686f66626639303668303a613f6937646d696c36356d3664356d30303463
# 535f3d6f34240607302510203a3d68322503383f2b76086d30260c25637d0f68
Solution
可以看见encrypt
函数中
依次用\(16\)个随机串\(k_i\)操作\(32\)位原始串
令\(l\)为前\(16\)位
\(r\)为后\(16\)位
\((l,r)\)经\(k_i\)操作后变成\(r,l \oplus r \oplus k_i\)
其中\(\oplus\)表示异或运算
现在假装\(k_i\)全是\(0\)
\((l,r)\)的变化如下(应该大概可能也许没错吧)
算上\(k_i\)后也就是\((K_1\oplus r,K_2 \oplus l)\)
其中\(K_1,K_2\)都是与\(\{k_n\}\)相关的常数
所以可以先利用fake_flag
和结果算出\(K_1,K_2\)
再用\(K_1,K_2\)和结果算出real_flag
cnss{Fe15tel_cipher_1s_e2sy_f0r_y0u_3}
Code
晚自习手机写的
比较丑陋
import os
real_flag = 'cnss{Fe15tel_cipher_1s_e2sy_f0r_y0u_3}'
fake_flag = 'cnss{c91cac1ae8106c3aa80d49bf60f1c8d8}'
def xor(a, b):
return ''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b))
def str_to_hex(s):
return s.encode().hex()
def swap(a):
return a[16: ] + a[ :16]
def single(m, k):
assert len(m) == 32
l = m[: 16]
r = m[16: ]
nl, nr = r, xor(k, xor(l, r))
return nl + nr
def encrypt(m, k):
for i in k:
m = single(m, i)
return swap(m)
#k = []
#for i in range(0, 16):
# k.append(os.urandom(8).hex())
#print(str_to_hex(encrypt(fake_flag[5: -1], k)))
#print(str_to_hex(encrypt(real_flag[5: -1], k)))
fake=fake_flag[5: -1]
def hex_to_str(s):
return s.decode('hex')
fake2=hex_to_str('6464686f66626639303668303a613f6937646d696c36356d3664356d30303463')
real2=hex_to_str('535f3d6f34240607302510203a3d68322503383f2b76086d30260c25637d0f68')
lfake = fake[: 16]
rfake = fake[16: ]
lfake2=fake2[: 16]
rfake2=fake2[16: ]
lreal2=real2[: 16]
rreal2=real2[16: ]
rreal=xor(rfake,xor(rfake2,rreal2))
lreal=xor(lfake,xor(lfake2,xor(rfake,xor(rreal,lreal2))))
real=lreal+rreal
print(real_flag)
(easy) Not Vigenere
Description
这看起来跟维吉尼亚密码很像 task.py
# -*- coding: utf-8 -*-
import base64
from itertools import *
from data import flag, plain
flag = "cnss{....}"
# plain是只有大小写字母(没有空格和其他符号)的英文文章
a = [chr(i) for i in range(32,127)]
key = flag.strip("cnss{").strip("}")
assert len(key) < 40
ki = cycle(key)
cipher = ''.join([a[(ord(p) + ord(next(ki))) % 95] for p in plain])
print(base64.b64encode(cipher.encode()))
# b'YGYxXT5sRy8mU1olPXpELz1oOD1ZbGQkUzJiOGZGKDVmXzVNejU7Q2IpMGloPSxaOGk3aUgwNWlXNDwsNTtDVjozZm1ieGY+ZjFtPiwiXmU2SzBCKEhdKUBoXFUmZjhhMXU5OS9TYjRHIzkqPWk5Mlk1ZnxrOGk5b0A2N1dtKkx+ODBBaDg3YGA9e2E3aDtvQz5paVcvTH45NEhkJz1hWVZ4VTQ9Nmk5My1WZTVAd0Q+OWEwOlloVjFZOGI1dDYsI2tdMEZ7Q2c6aSlAVWBgL1c/WTJmOTUuU2gzQXs0LUNnNzNqWWIsVy5iSWY1OTQ7YyI6KT8oOGItPFhZWC9hNlU+Sks2Nl5aL0x+MT05Yi08WFlYL1oqaDhmODAlU2ktRyU3KEc+LzxZa2InZjFdPmg1KTBnaipMXjU0SWg4OWJjayxaKmg4akcwL1hXNU13RDBDYzs9YmhgeGU9PTZpOTMtVWUuPXgxKj9jM0VZalkqaz1cOW87KiJgWCZLJD82SF0pMmNqWSpTN1g+cEM1JmlfLUQiPjZLVjJHaFxdJlkqVj92SDA1O2okQCA8MzlZMTNVYF0sZjVZRGk1O21kaXNMKTkqP2ElPFhnXCdnNVgyZjc2L1VbM0Z7ND49aSw1Y2dnIWIvY0JKODAlYGU1QyU/PkhdKTxcY2t+ZC5VRGJEKDNmXzRIIzFAOVktPGtjYXxgPGA5ZzkpOmZeJkcnOTU9ZDI9WmNoIFc7Z3h1SC8zYW00OSo4KDhkOz1aXWIrWzdXNXM9OzphbCZKKzgsPWcxPWdoWHxXOWBJZzkzNVdjMEwgPzVHPjhFVWdfJmFAYkdpOTkmRWozQXk7MzVjKEVVZ2csU0JdPmh7MDRiVzNMJTU5PWMlRF1jYHxgPWA1dUgsM2VbL0wrPy89aCYvYl9ceFY9VUVvSCwlWl8uTyBELzxeKDdiW1whZUBcNXM5KCNhazVLdz4rJ2k2N1dfYHhgLV0+YjdAL1tZIkR3Pis8ajE9aWZjLWU7WUBtTS8iVmowRHo4MEdlJUBoYlkqV0FVM3VAQDhaWzM9Kz8tPWMoNl1hPHxpKmcxcUQoM1dkNUQwPDBKXjI1XWJVJjo4aDVtfD0mYFs3PSk4LDVnKD1aXWgrUzJYfHNHeTVkXyRDIzE1ODc5QjpmWXtdN2NHdD07OFdiLSB7QyhNaC1CZ2pZKmsubEBmQjoqaFtzQHs2M0loLDNYWFUqXTVteGpBKChbZCY8KzgoSGgsM2dVayBXO1xFdDYoL1ZfL0srMTNAWig3YlVgLWo+ZjlwSTo0Z181PSY2OUNkMUFYXWIhYDBVRHBCLDRfVzNMKTU6SFY5QFViaHhYPVlCYkI2NVpbMzklNDo8WjQ3V2hpKlctXDl0OCg6ZWkxPSVEKEhnJTFZYVl8ZjJiN3Q1NSVaXzQ9LTU1PWMrQVVoaCBXOWAxenw7JFNkNT8mPzU1aSw3Z1VbfGUxWUNiPSthWGomSnc8MzxaNzRjZmgxOyxjRW04PC9WWzNLKzE1OF44N2JVbSdnN1s9YkIpNmY/NUAgPjI9aTc2Y2ZmIVQ1WTlvNTQiYGUnQCBDQDlWNkFrXWggVTFdPGVGLC9pXjA5KTU1OVY2Om1bZidpN2lAST06KVdXLUx+RzBAYTIzallmK2YqYjRqSGcvWVszSytCPDtcMDNYXWIgVztWQmY1OjVpXzVAJDk6OWc9IllgYCBbNmg4Ykg2NmReMEV7Mzk9Wjc9aWhaJ2QxXT1GSiwza2opQSU3MEdfOUFoaFx8ZSphNWJCKzpXaiZOe0JASF0tPFtdZ3tbL1o1czk1NTtZIkYrPDBKWjs3aFxjLWYxXT1KODowYWQmSiI5M0BiPUFZYFprUzVfRHA8MC5TWDBNK0QvOWUlQWhVYntTNWBHZkosKGFkJkx+QjZJXCxCY1tZLFouZidpNTsiXz81RyoxQEhkODZZV1whXi1mNW9LLyZgaik9MDE6P1szQFxdYV9bPGY/cEEwNFduIjsrPEA1aC1Ca1VnL1ouYjhmQCwnZl81IStDPjVeODdiW1onZDFdPVg5OSZTYi1Pdzk7PWMrNGNmXCFfdmNHdDwsNWFiJUV7NT81WDg6bWtceGZxZzhwSTMlZVc6K341LjVrKTtZWWB4VDhmMXU5KC9lbSZKKkQ2OWspQG1kYytlMlY8ZkMpNFdoNzkrOTZCZCo2XWdNJ2dAXTxtODYmaFszUSs4MEJcPT1pV1UmWDhmPWZHLyZlVyo8Jzk7PVs5OmBtSHxeNVw5bksvImZXNEx3RCx8Yi08PWdVL2YxVUR0PCw4W2kpPXo9LEhkJT5kWVUkZjhcOXRHQC5iVzVAIDU6Nm4pRFlmbSVXKmJDakI0OmJlOD0pIy85bCVBa1lZKFs3WzZzOSwtaz84OSo1P0hnJT1mWF0mUztdPHpINjZVXiY8XzYsQGktPFhdWyZTN2gxdSc7M1tZLER3PitHWDM6WFdmLVc1aEliQitpYmgwRSBDLDhpMzJjVWAkOyxjRW04OzBUaCpGfTgwQVclMV89VX5kLlk0dUMuMGFsJkomPjs8WjIzbGhYeGsraURwQiwiYFo1RypEKE1eMn1VZl0rZjJgPEo8KCVTWSlBe0YsOGgzO1loXCFgMEg4ZkIoNFtqODkqNzlDbC08W2BVLFcqYjR4OT4mZFsjRys4LExdJUNnaFl7VEJnP25JKilXYzBMID81fGEpNGhcWSo='
Solution
都说了看起来跟维吉尼亚密码很像
那就去学呗
维吉尼亚密码是升级版的凯撒密码
凯撒密码可以用频率统计破解
而维吉尼亚密码因为不同位置移位不同所以频率统计失效
那么我们可以先尝试找出key
的长度\(l\)
因为每隔\(l\)的移位相同
就可以得到一堆凯撒密码
然后就可以破解了
既然要找\(l\)的长度
我们可以利用英语中存在的单词频率
频率表和英语相似的就是可能\(l\)长度的倍数
怎么判断是否相似?
cnss{V1g3n@re_15_vUner4bl333}
Code
当时是C++
和Python
换着写的
比较杂乱
而且也没存
(easy) LFSR
Description
task.py
mask = 0xef6a61417539686431326b4a6166686b2131265e402a4a414c
limit = 0xffffffffffffffffffffffffffffffffffffffffffffffffff
def LFSR(input):
output = (input << 1) & limit
i = (input & mask) & limit
lsb = 0
while i != 0:
lsb ^= (i & 1)
i = i >> 1
output |= lsb
return (output, lsb)
flag = 'cnss{...}'
assert len(flag) == 32
R = int(flag[5: -1], 16)
tmp = 0
for i in range(200):
(R, lsb) = LFSR(R)
tmp = (tmp << 1) | lsb
print(hex(tmp))
#0xf3c166646bce88af4be9c2c22fc681f2ee6641d0334f635842
Solution
考虑每次操作
其实就是左移一位
去掉最高位
最后一位统计某些位(包括最高位)的异或和
最后输出这些最后一位
可以注意到最后操作完后\(tmp\)就是操作完的\(R\)
根据现在的最后一位\(lsb\)
可以恢复出最后一次操作前的\(R\)
依次回溯
可以求出原始的\(R\)
cnss{6c416869730616960383964b4a}
Code
mask = 0xef6a61417539686431326b4a6166686b2131265e402a4a414c
limit = 0xffffffffffffffffffffffffffffffffffffffffffffffffff
R = 0xf3c166646bce88af4be9c2c22fc681f2ee6641d0334f635842
for i in range(200):
lsb = R & 1
R >>= 1
ret = lsb
for j in range (199):
if((mask >> j) & 1):
ret ^= ((R >> j) & 1)
R |= ret<<199
print(hex(R))
(easy)NotFib
Description
给\(2020\)个数\(a_i\),\([2,10^3]\)的\(1000\)个,\((10^3 ,10^6]\)的\(1000\)个,\((10^{15} ,10^{17})\)的\(20\)个
定义数列\(F\)
\(r(n)\)表示\(n\)是这\(2020\)个数中的\(r(n)\)个的倍数
求\(F(p)\mod p\)
Solution
一个显然的套路是先提出来一个\(Fib_n\)
然后对每个\(a_i\)枚举贡献
斐波那契数列可以拆成两个等比数列求和
但是模数\(M=1926081719260817=17*5882353*19260817\)
所以求不出\(\sqrt5\)
所以手动实现复数类
再加个\(CRT\)
cnss{323c5ba9317e7}
Code
from json import load
def qpow(a, b, p):
result = 1
while b != 0:
if (b&1) == 1:
result = (result * a)%p
b >>= 1
a = (a*a) % p
return result
def inv(x,p):
return qpow(x%p,p-2,p)
p0=1926081719260817
p1=17
p2=5882353
p3=19260817
p12=p1*p2
p13=p1*p3
p23=p2*p3
p=p0
C1
C2
inv2=(p+1)//2
inv5=(7*p+1)//5
class Complex:
def __init__(self,a=0,b=0):
self.a=a%p
self.b=b%p
def __add__(self, comp):
if type(comp) is type(self):
return Complex((self.a+comp.a)%p,(self.b+comp.b)%p)
else:
return Complex(self.a,(self.b+comp+p)%p)
def __sub__(self, comp):
if type(comp) is type(self):
return Complex((self.a-comp.a)%p,(self.b-comp.b)%p)
else:
return Complex(self.a,(self.b-comp+p)%p)
def __mul__(self, comp):
if type(comp) is type(self):
return Complex((self.a*comp.b+self.b*comp.a)%p,(self.b*comp.b+5*self.a*comp.a)%p)
else:
return Complex(self.a*comp%p,self.b*comp%p)
def __pow__(self,b):
ret=Complex(0,1)
a=self
while b!=0:
if (b&1)==1:
ret=ret*a
b>>=1
a=a*a
return ret
def __div__(self,comp):
return self*Complex(comp.a,-comp.b)*inv(5*comp.a**2-comp.b**2,p)
def matrix_multiplication(n, A, B):
C = []
for line in range(n):
line_arr = []
for column in range(n):
item = 0
for i in range(n):
item += A[line][i] * B[column][i]%p
line_arr.append(item%p)
C.append(line_arr)
return C
def fib(n):
return (C1**n-C2**n).a
def SumGP(a,n,q):
if(q.a==0 and q.b==1):
return a*n
return a*(q**n-1)/(q-1)
x1=p23*inv(p23,p1)
x2=p13*inv(p13,p2)
x3=p12*inv(p12,p3)
def CRT(a,b,c):
return (a*x1+b*x2+c*x3)%p0
a = load(open("a", "r"))
N=p0
p=p1
C1=Complex( 1,1)*inv(2,p)
C2=Complex(-1,1)*inv(2,p)
ans1=fib(N)
for ai in a:
start=N%ai
cnt=N//ai
ans1+=(SumGP(C1**start,cnt,C1**ai)-SumGP(C2**start,cnt,C2**ai)).a
ans1%=p
p=p2
C1=Complex( 1,1)*inv(2,p)
C2=Complex(-1,1)*inv(2,p)
ans2=fib(N)
for ai in a:
start=N%ai
cnt=N//ai
ans2+=(SumGP(C1**start,cnt,C1**ai)-SumGP(C2**start,cnt,C2**ai)).a
ans2%=p
p=p3
C1=Complex( 1,1)*inv(2,p)
C2=Complex(-1,1)*inv(2,p)
ans3=fib(N)
for ai in a:
start=N%ai
cnt=N//ai
ans3+=(SumGP(C1**start,cnt,C1**ai)-SumGP(C2**start,cnt,C2**ai)).a
ans3%=p
print(ans1)
print(ans2)
print(ans3)
ans=CRT(ans1,ans2,ans3)
print(ans)
flag = "cnss{" + hex(ans)[2:] + "}"
print(flag)
(medium) PRNG
Description
nc 101.37.175.115 10000
下面的代码是隐去了flag的后端代码,用于分析攻击方法,和提供的服务交互才能获取flag
import random
import signal
import socketserver
import string
from hashlib import sha256
from os import urandom
FLAG = b'cnss{...}'
LEAK_LIMIT = 6
TIME_LIMIT = 2
TOTAL = 20
BITS = 64
BANNER = b'''
_ __ ___ _ _ __ _
| _ \ | _ \ | \ | | / _ |
| __/ | '_'| | | \ | | \__| |
|_|__ _|_|\_| |_| \|_| |___/
'''
class PRNG1(object):
def __init__(self, bits):
self.n = random.getrandbits(bits)
self.a = 0
self.b = 0
self.s = 0
while not (self.a < self.n and self.gcd(self.a, self.n) == 1):
self.a = random.getrandbits(bits)
while not (self.b < self.n and self.gcd(self.b, self.n) == 1):
self.b = random.getrandbits(bits)
while not (self.s < self.n and self.gcd(self.s, self.n) == 1):
self.s = random.getrandbits(bits)
def gcd(self, a, b):
while b:
a, b = b, a % b
return a
def next(self):
self.s = (self.a * self.s + self.b) % self.n
return self.s
class Task(socketserver.BaseRequestHandler):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def proof_of_work(self):
random.seed(urandom(8))
proof = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])
digest = sha256(proof.encode()).hexdigest()
self.dosend(str.encode(("sha256(XXXX + %s) == %s" % (proof[4: ], digest))))
self.dosend(str.encode('Give me XXXX:'))
x = self.request.recv(10)
x = (x.strip()).decode("utf-8")
if len(x) != 4 or sha256((x + proof[4: ]).encode()).hexdigest() != digest:
return False
return True
def dosend(self, msg):
try:
self.request.sendall(msg + b'\n')
except:
pass
def timeout_handler(self, signum, frame):
raise TimeoutError
def handle(self):
try:
signal.signal(signal.SIGALRM, self.timeout_handler)
signal.alarm(60)
if not self.proof_of_work():
self.dosend(b'You must pass the PoW!')
return
self.dosend(BANNER)
signal.alarm(3)
prng = PRNG1(BITS)
output = []
for i in range(TOTAL):
output.append(prng.next())
known = output[: LEAK_LIMIT]
hint = sha256(''.join(map(str, output)).encode()).hexdigest()
self.dosend(str(known).encode())
self.dosend(str(hint).encode())
signal.alarm(TIME_LIMIT)
for i in range(TOTAL):
self.dosend(b'guess: ')
guess = int(self.request.recv(1024).strip())
if guess != output[i]:
self.dosend(b'Wrong!')
self.request.close()
return
self.dosend(b'Congrats! Here is the flag: %s' % FLAG)
except TimeoutError:
self.dosend(b'Timeout!')
self.request.close()
except:
self.dosend(b'What\' wrong???')
self.request.close()
class ThreadedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 10000
server = ThreadedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()
Solution
可以看到这是个伪随机数生成器
先来观察它是如何工作的
首先生成模数\(n\)
然后生成与\(n\)互质且小于的数\(a,b,s\)
每次更新随机种子\(s\)
并输出\(s\)
输出了生成的连续\(6\)个伪随机数
要预测接下来的数
显然要算出\(a,b,n\)
如果知道\(n\)
\(a,b\)是很好算的
直接解方程就可以了
所以关键是求解\(n\)
那么现在已知的是一堆同余等式
先作差干掉\(b\)
那么\(T_1*T_3-T_2^2\equiv 0\mod n\)
所以我们可以构造出一堆\(n\)的倍数
然后求gcd
就有大概率得到\(n\)
有多大?
我也不知道
不是还有hint
吗
错了大不了枚举因数再试或者重来嘛
离线会做了就在逆向做题大师
的教导下去学了pwn
库
然后就可以做了
前面的proof_of_work
直接暴力枚举即可
cnss{L6G_1s_k1nd_0f_PRNG}
Code
好丑
import random
import signal
import string
from hashlib import sha256
import os
from libnum import *
from pwn import *
Shell = remote("101.37.175.115", 10000)
def part1(tmp):
proof = tmp[14: 30]
tmp.strip(proof)
digest = tmp[35: -1]
tmp = Shell.read().decode()
print(tmp)
print("Proof == " + proof)
print("digest == " + digest)
st = string.ascii_letters + string.digits
l = len(st)
for a in range(l):
for b in range(l):
for c in range(l):
for d in range(l):
x = st[a] + st[b] + st[c] + st[d]
if(sha256((x + proof).encode()).hexdigest() == digest):
print(x)
Shell.sendline(x)
return
s = []
t = []
f = []
def part2():
tmp = Shell.read().decode()
# print(tmp)
tmp = Shell.read().decode()
# print(tmp)
# tmp = Shell.read().decode()
# print(tmp)
tmp = tmp.strip('\n')
x = 0
flag = 0
h = ''
for c in tmp:
if(flag == 1):
h = h + c
elif(c == ','):
s.append(x)
x = 0
elif(ord('0')<=ord(c)<=ord('9')):
x = x * 10 + (ord(c) - ord('0'))
elif(c == ']' ):
s.append(x)
flag = 1
h = h.strip('\n')
print("h == " + h)
# for a in s:
# print(a)
for i in range(5):
t.append(s[i + 1] - s[i])
f.append(t[0]*t[2]-t[1]*t[1])
f.append(t[0]*t[3]-t[1]*t[2])
f.append(t[0]*t[4]-t[1]*t[3])
f.append(t[0]*t[4]-t[2]*t[2])
f.append(t[1]*t[4]-t[2]*t[3])
n = reduce(gcd, f)
a = (s[2] - s[1]) * invmod(s[1] - s[0],n) % n
b = (s[1] - a * s[0]) % n
for i in range(6, 20):
s.append((s[i - 1] * a + b) % n)
print("sha == " + sha256(''.join(map(str, s)).encode()).hexdigest())
for a in s:
print(tmp)
print(a)
Shell.sendline(str(a))
tmp = Shell.read()
# tmp = Shell.read()
print(tmp)
# tmp = Shell.read()
# print(tmp)
tmp = Shell.read().decode()
print(tmp.strip('\n'))
part1(tmp)
part2()
(medium) RSA1
Description
task.py
from Crypto.Util.number import *
from FLAG import flag
pad = 0xffffffffffffffff
e = 0x10001
p = getPrime(512)
q = getPrime(512)
n = p * q
p1 = p | (pad<<256)
m = bytes_to_long(flag)
c = pow(m,e,n)
print("N = " + str(n))
print("p1 = " + str(p1))
print("C = " + str(c))
'''
N = 86408538200832738441053630701690666311817036711977563149301775118732430273230358416737501485116387663917737556078719403747856822036528056282885059518192563451129578002341407051322036731121256653431134476455920858137005774303574215108317286768867700749149891191528196389188438844616918232526286820074438880377
p1 = 10998891917537143150459750921909883345610102724514190176534372768205070915213213609183031930454528272713963998587029669016588933905791773046497403754249185
C = 68292422038497943439892795905110150184125214037089941633605604063649686183640244152480155007814154909237215405202836115044035959593825361680613619772337201416261487191174080496578821167356754019320515793576314281432288552185973532924139246292664889586539023761898983279339564228515410405048163144083480125433
'''
Solution
RSA
三个大字都写出来了就去学呗
一直到学会Coppersmith’s Theorem
就可以做了
给定\(f(x)\)和模数\(n\)
Coppersmith’s Theorem
可以求出\(f(x)\equiv 0 \mod p\)一定范围内的解
注意其中\(p\)是\(n\)的约数
问题的关键在于恢复\(p\)
我们可以得到等式
其中\(x\in[0,2^{64})\)
所以转化为模多项式求小根问题
用Coppersmith’s Theorem
解决
关于Coppersmith’s Theorem
是怎么搞的
可以啃论文
Youtube
上也有不错的资料
包括LLL算法
cnss{R5A_C0ppersm1th_Atta@k}
Code
去了学下sage
用sage
算出\(p\)
再求解明文
e = 0x10001
n = 86408538200832738441053630701690666311817036711977563149301775118732430273230358416737501485116387663917737556078719403747856822036528056282885059518192563451129578002341407051322036731121256653431134476455920858137005774303574215108317286768867700749149891191528196389188438844616918232526286820074438880377
p1 = 10998891917537143150459750921909883345610102724514190176534372768205070915213213609183031930454528272713963998587029669016588933905791773046497403754249185
c = 68292422038497943439892795905110150184125214037089941633605604063649686183640244152480155007814154909237215405202836115044035959593825361680613619772337201416261487191174080496578821167356754019320515793576314281432288552185973532924139246292664889586539023761898983279339564228515410405048163144083480125433
pbar1 = p1 >> (256 + 64) << (256 + 64)
pbar2 = p1 & ((1 << 256) - 1)
pbar1 = p1 >> (256 + 64) << (256 + 64)
pbar2 = p1 & ((1 << 256) - 1)
PR.<x> = PolynomialRing(Zmod(n))
f = pbar1 + x * (1<<256) + pbar2
f = f.monic()
x0 = f.small_roots(X=1 << 64, beta=0.4)[0]
p = pbar1 + (x0<<256) + pbar2
q = n // int(p)
d = inverse_mod(e, (p-1)*(q-1))
print(pow(c, d, n))
然后就算出flag
了
from Crypto.Util.number import *
m = 10471353773279109191869802738615216594859774076390846086058283658109
flag = long_to_bytes(m)
print(flag)
(hard) RSA2
Description
task.py
from Crypto.Util.number import *
from hashlib import md5
from secret import flag,hint
e = 3
p = getPrime(1024)
q = getPrime(1024)
n = p * q
M = flag + md5(flag).digest()
M1 = bytes_to_long(M)
M2 = bytes_to_long(M) + bytes_to_long(hint)
assert bytes_to_long(hint) < 2**128
assert M1 > 2 ** 750
C1 = pow(M1,e,n)
C2 = pow(M2,e,n)
print('N = ' + str(n))
print('C1 = ' + str(C1))
print('C2 = ' + str(C2))
'''
N = 22442678583519738983875074494058317598953875091381485783474848899214372205308945778540436851868967325137345091940740014862547580741174054236020501945339638133676732757133495830887497041331279332265032399268349620619422748303225015657848578372961589906126191164760331145308411163193010021299096283654647819117761621523991044525447078790761543558044629807481102852079854601589180527157241472560749235428267203317706008764197762224910936290211233190050894154304054543248357637956355524415421010219367399546414598977082870279393651857044046504401815969856110997577293409021960020445330447400985069841819725986288236543701
C1 = 19147099662825459227041634257418668407014448481717356392863005714107719802643476432685575627636406454330638759832978044091390574463466219122993780222077952262832269308108913374274251415574958300603681615816648933630844835751717352928657042039813937362010767143442769545486494013505993044882411304529342944297612523540079251411942845605182337671650483110470600570835440095355126274761833163079271088317898736354574831732581857563584088137991682489896458566996898248046835340206406439210543681370234885920237209167954859763216593186191293807656190085622699043088985125286358185593838708581646734922040978936799483220889
C2 = 19147099662825459227041634257418668407014448481717356392863005714107719802643476432685575627636406454330638761546709794100651628661069612340661829908007439389895784066487257952836656633203397756140944963394710464681422607718942814464489448913219074442566929743760347663583732333484696916984559261761469654688715758497928074380359617464521128237695604447249781882333998082996023427280464522991785582915073853201966590263116649792425067159866432049304534660986406166250165231516761376072183367940186293420444436213176479948121681559332107035970038715247095673923035162891624827353031815715581766590303811737166454804769
'''
Solution
已知
其中\(hint<2^{128}\)
所以我们考虑先利用Coppersmith’s Theorem
解出\(hint\)
高次方程组的求解需要用到结式
结式
的资料B站
就有
属于抽象代数
学完后我们构造结式就可以解出\(hint\)了
然后代回去
则\(M\)是两个方程的公共根
所以\(M-x\)是两个多项式的公约数
求出即可
cnss{C@PPER_SM1TH-SHORT_P4D0ING_ATTACK-AND F4ANKL1N-RE1TER RELATED!ME55AGE_ATTACK}
Code
sage
真好用啊
e = 3
n = 22442678583519738983875074494058317598953875091381485783474848899214372205308945778540436851868967325137345091940740014862547580741174054236020501945339638133676732757133495830887497041331279332265032399268349620619422748303225015657848578372961589906126191164760331145308411163193010021299096283654647819117761621523991044525447078790761543558044629807481102852079854601589180527157241472560749235428267203317706008764197762224910936290211233190050894154304054543248357637956355524415421010219367399546414598977082870279393651857044046504401815969856110997577293409021960020445330447400985069841819725986288236543701
c1 = 19147099662825459227041634257418668407014448481717356392863005714107719802643476432685575627636406454330638759832978044091390574463466219122993780222077952262832269308108913374274251415574958300603681615816648933630844835751717352928657042039813937362010767143442769545486494013505993044882411304529342944297612523540079251411942845605182337671650483110470600570835440095355126274761833163079271088317898736354574831732581857563584088137991682489896458566996898248046835340206406439210543681370234885920237209167954859763216593186191293807656190085622699043088985125286358185593838708581646734922040978936799483220889
c2 = 19147099662825459227041634257418668407014448481717356392863005714107719802643476432685575627636406454330638761546709794100651628661069612340661829908007439389895784066487257952836656633203397756140944963394710464681422607718942814464489448913219074442566929743760347663583732333484696916984559261761469654688715758497928074380359617464521128237695604447249781882333998082996023427280464522991785582915073853201966590263116649792425067159866432049304534660986406166250165231516761376072183367940186293420444436213176479948121681559332107035970038715247095673923035162891624827353031815715581766590303811737166454804769
PRxy.<x,y> = PolynomialRing(Zmod(n))
PRx.<x> = PolynomialRing(Zmod(n))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n))
a1 = x^e - c1
a2 = (x+y)^e - c2
b1 = a1.change_ring(PRZZ)
b2 = a2.change_ring(PRZZ)
F = b2.resultant(b1)
F = F.univariate_polynomial()
F = F.change_ring(PRx).subs(y=x)
F = F.monic()
hint = F.small_roots(X=2^128, beta=0.4)[0]
PRxx.<x> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+hint)^e - c2
while g2:
g1, g2 = g2, g1 % g2
m = -g1.monic()[0]
print(m)
from Crypto.Util.number import *
m = 39518495677363217000696899942255667430680230135832350489449900327922613215783274707217952032432592698988785513737756293149854856875724183968562821411002675077248046343784651651857017469370733498843436949677296484917016545069883670639024
print(long_to_bytes(m))
(boss) PRNG2
Description
nc 101.37.175.115 10001
task.py 如下
import random
import signal
import socketserver
import string
from hashlib import sha256
from os import urandom
FLAG = b'cnss{...}'
LEAK_LIMIT = 8
TIME_LIMIT = 35
TOTAL = 20
BITS = 64
BANNER = b'''
_ __ ___ _ _ ____ ___ ___
| _ \ | _ \ | \ | | / _ | | | | |
| __/ | '_'| | | \ | | \__| | | | | |
|_|__ _|_|\_| |_| \|_| |___/ |_| |_|
'''
class PRNG4(object):
def __init__(self, bits):
self.n = 2 ** bits
self.truncated = 16
self.N = 2 ** 256 - 2 ** 224 + 2 ** 192 + 2 ** 96 - 1
self.A = -3
self.B = 41058363725152142129326129780047268409114441015993725554835256314039467401291
self.INF = (None, None)
self.P = (48439561293906451759052585252797914202762949526041747995844080717082404635286, 36134250956749795798585127919587881956611106672985015071877198253568414405109)
self.Q = self.mul(random.getrandbits(bits // 2), self.P)
self.a = random.getrandbits(bits)
self.b = -1
self.cnt = 256
def inverse(self, a, p):
return pow(a, p - 2, p)
def add(self, p_a, p_b):
x1, y1 = p_a
x2, y2 = p_b
if p_a == self.INF:
return p_b
if p_b == self.INF:
return p_a
if x1 == x2 and y1 == y2:
l = (3 * x1 * x1 + self.A) * self.inverse(2 * y1, self.N) % self.N
elif x1 == x2 and (y1 + y2) == self.N:
return self.INF
else:
l = (y2 - y1) * self.inverse(x2 - x1, self.N) % self.N
x3 = (l * l - x1 - x2) % self.N
y3 = (l * (x1 - x3) - y1) % self.N
return (x3, y3)
def mul(self, a, p_a):
t = p_a
r = self.INF
if t == r:
return self.INF
while a:
if a & 1:
r = self.add(r, t)
t = self.add(t, t)
a >>= 1
return r
def next(self):
if self.b == -1:
self.b = self.Q[0]
else:
self.b >>= self.n.bit_length() - 1
if self.cnt - self.n.bit_length() + 1 < 0:
self.a = self.mul(self.a, self.P)[0]
self.b += (self.mul(self.a, self.Q)[0] >> self.truncated) << self.cnt
self.cnt += 256 - self.truncated
self.cnt -= self.n.bit_length() - 1
return self.b % self.n
class Task(socketserver.BaseRequestHandler):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def proof_of_work(self):
random.seed(urandom(8))
proof = ''.join([random.choice(string.ascii_letters + string.digits) for _ in range(20)])
digest = sha256(proof.encode()).hexdigest()
self.dosend(str.encode(("sha256(XXXX + %s) == %s" % (proof[4: ], digest))))
self.dosend(str.encode('Give me XXXX:'))
x = self.request.recv(10)
x = (x.strip()).decode("utf-8")
if len(x) != 4 or sha256((x + proof[4: ]).encode()).hexdigest() != digest:
return False
return True
def dosend(self, msg):
try:
self.request.sendall(msg + b'\n')
except:
pass
def timeout_handler(self, signum, frame):
raise TimeoutError
def handle(self):
try:
signal.signal(signal.SIGALRM, self.timeout_handler)
signal.alarm(60)
if not self.proof_of_work():
self.dosend(b'You must pass the PoW!')
return
self.dosend(BANNER)
signal.alarm(3)
prng = PRNG4(BITS)
output = []
for i in range(TOTAL):
output.append(prng.next())
known = output[: LEAK_LIMIT]
hint = sha256(''.join(map(str, output)).encode()).hexdigest()
self.dosend(str(known).encode())
self.dosend(str(hint).encode())
signal.alarm(TIME_LIMIT)
for i in range(TOTAL):
self.dosend(b'guess: ')
guess = int(self.request.recv(1024).strip())
if guess != output[i]:
self.dosend(b'Wrong!')
self.request.close()
return
self.dosend(b'Congrats! Here is the flag: %s' % FLAG)
except TimeoutError:
self.dosend(b'Timeout!')
self.request.close()
except:
self.dosend(b'Wtf?')
self.request.close()
class ThreadedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 10001
server = ThreadedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()
Solution
又是伪随机数生成器
观察代码发现生成随机数的方式异常毒瘤
先上factordb
查了下这些数
除了\(N\)是质数没什么特别的
于是继续观察
感觉关键在于add函数
不难猜出\(l\)是斜率之内的东西
但是算出的\(x_3,y_3\)又不在直线上
y3 = (l * (x1 - x3) - y1) % self.N
如果在直线上就该是这样才对
y3 = (y1 - l * (x1 - x3)) % self.N
莫名其妙
于是卡死了一段时间
突然发现参数\(B\)没有用
感觉有点东西
直接扔进Google
发现这是个椭圆曲线
而且参数都是一致的
Very nice
所以去学习了椭圆曲线
发现完全一致
然后开始想做法
显然现在给我的前\(4\)个随机数拼起来就是\(Q_x\)
\(cnt\)是\(b\)的位数(不过我觉得这修改\(cnt\)的位置有点反人类)
然后\((a*Q)_x\)只有\(16\)位不知道
因为\(Q\)已知
只要知道\(a\)就可以确定随机数列
但是感觉不可做
这要是能做不就破解椭圆曲线了吗
突然意识到不一定要求一开始的\(a\)
可以求更新后的\(a\)
也就是\((a*P)_x\)
感觉这个思路是对的
列下式子
如果我们枚举\((a*Q)_x\)未知的\(16\)位
那么只需要逆过来找\(a*P\)就可以了
正常来说这还是不可做的
否则椭圆曲线后门不是随便找出来了
但是现在我们还知道\(k<2^{32}\)
感觉有机会搞
显然\(k\)也是有办法求的
直接上BSGS
那么现在只要求出椭圆曲线的阶\(order\)就可以了
因为这样就可以求出\(k\)对阶的逆元\(d\)
就可以用\(a*Q\)算\(a*P\)了
再次google
居然还真能求
而且sage
还已经封装好了
所以直接上sage
求出\(order\)
然后就写出来了
然后就T掉了
重新算下复杂度
感觉确实差点
卡了下常
还是跑不了
感觉没救了
后来又想到可以开个Hash
表加快求逆
以及不管\((a*Q)_y\)正负不影响\((a*P)_x\)
改了之后快了不少
但还是跑不了
于是自闭了
过了两天发现这是个论文题
而且论文就是这么做的
感觉很离谱
跑去问了出题人
发现时限确实有问题
于是把flag
送我了
cnss{ECCCCCCCCCCCCCCC}
Code
import random
import signal
import socketserver
import string
from hashlib import sha256
from os import urandom
from libnum import *
from pwn import *
import time
FLAG = b'cnss{...}'
LEAK_LIMIT = 8
TIME_LIMIT = 35
TOTAL = 20
BITS = 64
BANNER = b'''
_ __ ___ _ _ ____ ___ ___
| _ \ | _ \ | \ | | / _ | | | | |
| __/ | '_'| | | \ | | \__| | | | | |
|_|__ _|_|\_| |_| \|_| |___/ |_| |_|
'''
class PRNG4(object):
def __init__(self, bits):
self.Hash = {1 : 1}
self.n = 2 ** bits
self.truncated = 16
self.N = 2 ** 256 - 2 ** 224 + 2 ** 192 + 2 ** 96 - 1
self.A = -3
self.B = 41058363725152142129326129780047268409114441015993725554835256314039467401291
self.INF = (None, None)
self.P = (48439561293906451759052585252797914202762949526041747995844080717082404635286, 36134250956749795798585127919587881956611106672985015071877198253568414405109)
self.Q = self.mul(random.getrandbits(bits // 2), self.P)
self.a = random.getrandbits(bits)
self.b = -1
self.cnt = 256
def inverse(self, a, p):
if not( a in self.Hash ):
self.Hash[a]=pow(a, p - 2, p)
return self.Hash[a]
def add(self, p_a, p_b):
x1, y1 = p_a
x2, y2 = p_b
if p_a == self.INF:
return p_b
if p_b == self.INF:
return p_a
if x1 == x2 and y1 == y2:
l = (3 * x1 * x1 + self.A) * self.inverse(2 * y1, self.N) % self.N
elif x1 == x2 and (y1 + y2) == self.N:
return self.INF
else:
l = (y2 - y1) * self.inverse(x2 - x1, self.N) % self.N
x3 = (l * l - x1 - x2) % self.N
y3 = (l * (x1 - x3) - y1) % self.N
return (x3, y3)
def mul(self, a, p_a):
t = p_a
r = self.INF
if t == r:
return self.INF
while a:
if a & 1:
r = self.add(r, t)
t = self.add(t, t)
a >>= 1
return r
def next(self):
if self.b == -1:
self.b = self.Q[0]
else:
self.b >>= self.n.bit_length() - 1
if self.cnt - self.n.bit_length() + 1 < 0:
self.a = self.mul(self.a, self.P)[0]
self.b += (self.mul(self.a, self.Q)[0] >> self.truncated) << self.cnt
self.cnt += 256 - self.truncated
self.cnt -= self.n.bit_length() - 1
return self.b % self.n
Shell = remote("101.37.175.115", 10001)
def part1(tmp):
proof = tmp[14: 30]
tmp.strip(proof)
digest = tmp[35: -1]
tmp = Shell.read().decode()
print(tmp)
print("Proof == " + proof)
print("digest == " + digest)
st = string.ascii_letters + string.digits
l = len(st)
for a in range(l):
for b in range(l):
for c in range(l):
for d in range(l):
x = st[a] + st[b] + st[c] + st[d]
if(sha256((x + proof).encode()).hexdigest() == digest):
print(x)
Shell.sendline(x)
return
tmp = Shell.read().decode()
print(tmp.strip('\n'))
part1(tmp)
tmp = Shell.read().decode()
tmp = Shell.read().decode()
tmp = tmp.strip('\n')
x = 0
flag = 0
h = ''
known = []
print(tmp)
for c in tmp:
if(flag == 1):
h = h + c
elif(c == ','):
known.append(x)
x = 0
elif(ord('0')<=ord(c)<=ord('9')):
x = x * 10 + (ord(c) - ord('0'))
elif(c == ']' ):
known.append(x)
flag = 1
tmp = Shell.read().decode()
h = tmp.strip('\n')
print("h == " + h)
time_start=time.time()
prng = PRNG4(BITS)
Order = 115792089210356248762697446949407573529996955224135760342422259061068512044369
Q1 = 0
for i in range(4):
Q1<<=64
Q1 |= known[3-i]
Q2 = 0
for i in range(4,8):
Q2<<=64
Q2 |= known[11-i]
Q2<<=16
Q2%=(2**256)
def add(A, B):
return prng.add(A, B)
def sub(A, B):
x, y = B
return prng.add(A, (x, (-y) % prng.N))
def mul(a, P):
return prng.mul(a, P)
def bsgs(P, Q):
N = 2 ** 16
T = Q
S = {T : 0}
for i in range(1,N):
T = add(T, P)
S[T] = i
c = add(sub(T,Q),P)
# assert(add(sub(T,Q),P) == mul(N,P))
T = (None,None)
for i in range(1,N+1):
T = add(T,c)
if T in S:
return i * N - S[T]
return -1
def check(x,p):
if (pow(x,(p-1)//2,p)==p-1):
return -1
else :
return 1;
def mul0(A,B,w,p):
ar,ai=A
br,bi=B
return ((ar*br+ai*bi*w)%p,(ar*bi+ai*br)%p)
def qpow(a, b, p,w):
result = 1,0
while b != 0:
if (b&1) == 1:
result=mul0(result,a,w,p)
b >>= 1
a = mul0(a,a,w,p)
x,y=result
return x
def cipolla(x,p):
if(check(x, p)== -1):
return -1
t=random.randint(0,p-1)
w=(t*t-x)%p
while check(w,p)==1:
t=random.randint(0,p-1)
w=(t*t-x)%p
return qpow((t,1),(p+1)//2,p,w)%p;
def Y(x):
return cipolla(x**3 + prng.A * x + prng.B, prng.N)
P = (48439561293906451759052585252797914202762949526041747995844080717082404635286, 36134250956749795798585127919587881956611106672985015071877198253568414405109)
x = Q1
y = Y(x)
prng.Q = (x, y)
k = bsgs(P, prng.Q)
if k == -1:
prng.Q = (x, (-y) % prng.N)
k = bsgs(P, prng.Q)
assert k != -1
print(k)
d = pow(k, Order - 2, Order)
for i in range(2 ** 16):
x = Q2 | i
y = Y(x)
if y == -1:
continue
rQ = (x, y)
prng.a = mul(d, rQ)[0]
prng.b = (Q2 >> 208)
prng.cnt = 224
prng.b |= (mul(prng.a, prng.Q)[0] >> 16 << 48)
if(prng.b% prng.n == known[7]):
for i in range(8,20):
known.append(prng.next())
break
print(known)
time_end=time.time()
print('time cost',time_end-time_start,'s')
print("sha == " + sha256(''.join(map(str, known)).encode()).hexdigest())
for a in known:
print(tmp)
print(a)
Shell.sendline(str(a))
tmp = Shell.read()
print(tmp)