感觉突然啥都不会了,后来拿到官方WP,也没整明白,这官方的WP没有代码只讲了些道理,复现一下也不容易。
1,easySign
这是个自制的签名题。
from secret import r, t
from Crypto.Util.number import *
flag = b'xxx'
flag = bytes_to_long(flag)
e = 0x10001
def gen_keys():
p = getPrime(1024)
q = getPrime(1024)
phi = (p-1)*(q-1)
d = inverse(e,phi)
n = p*q
print(f'n = {n}')
WHATF = (d ** 3 + 3) % phi
print(f'WHATF= {WHATF}')
return d, n, WHATF
def easy_sign(n,d):
m = flag * pow(r,e**2+d**2,n) % n
s = pow(m,d,n) #用d加密用e解密
return s
def gift():
assert t > 0
gift = pow(r,t) - 1
#print(t)
print(isPrime(gift))
d,n,WHATF = gen_keys()
gift()
sign = easy_sign(n,d)
print(f'sign = {sign}')
gen_keys先生成RSA的d,由于e是已知的,给出d以后基本解密不是问题。并给出了
WHATF = d^3 + 3 (mod phi)
签名是用: m = flag * r^(e^2+d^2) (mod n)
这里r未知,但给出一个:gift = pow(r,t) - 1 (t>0,gift是素数)
由于素数只有2是偶数,所以如果r不是2的话一定是奇数再减1就是偶数(t>1时),当t==1时r可以是任意一个素数+1,所以r的取值只能是2或随便什么数。。。猜测是题出漏了,这里是想让r==2
这里式子里没有d^3需要作个变换: 两边乘d次幂
并且sign = m^d
所以这里就能得到flag^d 由于e已知,直接乘e次幂flag^d^e = flag
(这里猜r=2)
n = 17501785470905115084530641937586010443633001681612179692218171935474388105810758340844015368385708349722992595891293984847291588862799310921139505076364559140770828784719022502905431468825797666445114531707625227170492272392144861677408547696040355055483067831733807927267488677560035243230884564063878855983123740667214237638766779250729115967995715398679183680360515620300448887396447013941026492557540060990171678742387611013736894406804530109193638867704765955683067309269778890269186100476308998155078252336943147988308936856121869803970807195714727873626949774272831321358988667427984601788595656519292763705699
WHATF = 7550872408895903340469549867088737779221735042983487867888690747510707575208917229455135563614675077641314504029666714424242441219246566431788414277587183624484845351111624500646035107614221756706581150918776828118482092241867365644233950852801286481603893259029733993572417125002284605243126366683373762688802313288572798197775563793405251353957529601737375987762230223965539018597115373258092875512799931693493522478726661976059512568029782074142871019609980899851702029278565972205831732184397965899892253392769838212803823816067145737697311648549879049613081017925387808738647333178075446683195899683981412014732
# 1
sign = 12029865785359077271888851642408932941748698222400692402967271078485911077035193062225857653592806498565936667868784327397659271889359852555292426797695393591842279629975530499882434299824406229989496470187187565025826834367095435441393901750671657454855301104151016192695436071059013094114929109806658331209302942624722867961155156665675500638029626815869590842939369327466155186891537025880396861428410389552502395963071259114101340089657190695306100646728391832337848064478382298002033457224425654731106858054291015385823564302151351406917158392454536296555530524352049490745470215338669859669599380477470525863815
'''
#1, sign = m^d
#2, m = flag * r^(e^2 + d^2) 两边取d次幂=> m^d = flag^d * r^(d*e^2 + d^3)
=> sign = flag^d * r^(e + d^3) = flag^d * r^(e + WHATF-3)
#flag = (flag^d)^e
#3, r=2
'''
from Crypto.Util.number import *
fd = sign * pow(pow(2,e+WHATF-3,n),-1,n)
flag = pow(fd,e,n)
print(long_to_bytes(int(flag)))
#DASCTF{RSA_Bl1nd_Signatur3_With_M4th}
2,ECC
这是个椭圆曲线和RSA结合的题,这种叫ECRSA
from Crypto.Util.number import *
from secret import flag, p, q, a, b, e1, e2, e3
assert isPrime(p) and isPrime(q)
assert flag.startswith("DASCTF{") and flag.endswith("}")
class ECC():
def __init__(self, a, b, p, q, e):
self.p, self.q = p, q
self.a, self.b = a, b
self.N = p * q
self.e = e
self.Kbits = 8
self.Ep = EllipticCurve(IntegerModRing(p), [a, b])
self.Eq = EllipticCurve(IntegerModRing(q), [a, b])
N1 = self.Ep.order()
N2 = 2 * p + 2 - N1
N3 = self.Eq.order()
N4 = 2 * q + 2 - N3
self.d = {
( 1, 1): inverse_mod(e, lcm(N1, N3)),
( 1, -1): inverse_mod(e, lcm(N1, N4)),
(-1, 1): inverse_mod(e, lcm(N2, N3)),
(-1, -1): inverse_mod(e, lcm(N2, N4))
}
self.E = EllipticCurve(IntegerModRing(self.N), [a, b])
def enc(self, plaintext):
msg_point = self.msg_to_point(plaintext, True)
mp = self.Ep(msg_point)
mq = self.Eq(msg_point)
cp = (self.e * mp).xy()
cq = (self.e * mq).xy()
cp = (int(cp[0]), int(cp[1]))
cq = (int(cq[0]), int(cq[1]))
c = (int(crt([cp[0], cq[0]], [self.p, self.q])), \
int(crt([cp[1], cq[1]], [self.p, self.q])))
c = self.E(c)
return c.xy()
def dec(self, ciphertext):
x = ciphertext
w = x^3 + self.a*x + self.b % self.N
P.<Yp> = PolynomialRing(Zmod(self.p))
fp = x^3 + self.a*x + self.b -Yp^2
yp = fp.roots()[0][0]
P.<Yq> = PolynomialRing(Zmod(self.q))
fq = x^3 + self.a*x + self.b -Yq^2
yq = fq.roots()[0][0]
y = crt([int(yp), int(yq)], [self.p, self.q])
cp, cq = self.Ep((x, y)), self.Eq((x, y))
legendre_symbol_p = legendre_symbol(w, self.p)
legendre_symbol_q = legendre_symbol(w, self.q)
mp = (self.d[(legendre_symbol_p, legendre_symbol_q)] * cp).xy()
mq = (self.d[(legendre_symbol_p, legendre_symbol_q)] * cq).xy()
return crt([int(mp[0]), int(mq[0])], [self.p, self.q]) >> self.Kbits
def msg_to_point(self, x, shift=False):
if shift:
x <<= self.Kbits
res_point = None
for _ in range(2 << self.Kbits):
P.<Yp> = PolynomialRing(Zmod(self.p))
fp = x^3 + self.a*x + self.b - Yp^2
P.<Yq> = PolynomialRing(Zmod(self.q))
fq = x^3 + self.a*x + self.b - Yq^2
try:
yp, yq = int(fp.roots()[0][0]), int(fq.roots()[0][0])
y = crt([yp, yq], [self.p, self.q])
E = EllipticCurve(IntegerModRing(self.p*self.q), [self.a, self.b])
res_point = E.point((x, y))
return res_point
except:
x += 1
return res_point
ecc1 = ECC(a, b, p, q, e1)
ecc2 = ECC(a, b, p, q, e2)
ecc3 = ECC(a, b ,p, q, e3)
gift = p * q * getPrime(1000)
secret = bytes_to_long(flag[7:-1].encode())
ciphertext1 = ecc1.enc(secret)
ciphertext2 = ecc2.enc(secret)
ciphertext3 = ecc3.enc(secret)
with open("output.txt", "w") as f:
f.write(f"e1 = {e1}\n")
f.write(f"e2 = {e2}\n")
f.write(f"e3 = {e3}\n")
f.write(f"gift = {gift}\n")
f.write(f"C1 = {ciphertext1}\n")
f.write(f"C2 = {ciphertext2}\n")
f.write(f"C3 = {ciphertext3}\n")
给了3个e和3个椭圆曲线上的点,我一个gift(则p,q,和一个大因子组成,本来n就分解不了,再加一个因子完全没有意义)
1,椭圆曲线的参数:n,a,b都没有给出。
2,加密是用明文作为点G的x值,C[i]=e[i]*G
所以第一步是获取参数,原来有个模板:
e1 = 516257683822598401
e2 = 391427904712695553
e3 = 431785901506020973
gift = 10954621221812651197619957228527372749810730943802288293715079353550311138677754821746522832935330138708418986232770630995550582619687239759917418738050269898943719822278514605075330569827210725314869039623167495140328454254640051293396463956732280673238182897228775094614386379902845973838934549168736103799539422716766688822243954145073458283746306858717624769112552867126607212724068484647333634548047278790589999183913
C1 = (1206929895217993244310816423179846824808172528120308055773133254871707902120929022352908110998765937447485028662679732041, 652060368795242052052268674691241294013033011634464089331399905627588366001436638328894634036437584845563026979258880828)
C2 = (1819289899794579183151870678118089723240127083264590266958711858768481876209114055565064148870164568925012329554392844153, 1110245535005295568283994217305072930348872582935452177061131445872842458573911993488746144360725164302010081437373324551)
C3 = (1112175463080774353628562547288706975571507012326470665917118873336738873653792420189391867408691423887642725415133046354, 1820636035485820691083758790204536675748006232767111209985774382700260408550258280489088658228739971137550264759084468620)
from gmpy2 import *
#3个c都是曲线上的点
x1,y1 = C1
x2,y2 = C2
x3,y3 = C3
#求n
n = (y1^2 - y2^2 - x1^3 + x2^3) * (x2 - x3) - (y2^2 - y3^2 - x2^3 + x3^3 ) * (x1 - x2)
n = gcd(n, gift)
#1858284081017011776897142483530706248351014197676168270132930318711536519639284920939350511528325590655669305434894548271
#求a,b
def ecc(a, b, x, y):
return y ^ 2 - (x ^ 3 + a * x + b)
def solve_lin(f):
return ZZ(-f[0] / f[1])
P.<a, b> = Zmod(n)[]
f = ecc(a, b, x1,y1)
g = ecc(a, b, x2,y2)
h1 = f.sylvester_matrix(g, b).det().univariate_polynomial()
h2 = f.sylvester_matrix(g, a).det().univariate_polynomial()
a = solve_lin(h1)
b = solve_lin(h2)
print(a,b)
里边好几个字我都不认识,不过模板存下了就能用:三点求模。后边a,b不懂不过不用模板也能用3点求a,b。
官方WP用的另一种方法,感觉更方便也好理解。(我收藏了)
##############################################
#三点求参数a,b,p
# 根据椭圆曲线表达式构造 groebner_basis()
P.<a,b>=PolynomialRing(Zmod(gift))
f1 = C1[0]^3 + a*C1[0] + b - C1[1]^2
f2 = C2[0]^3 + a*C2[0] + b - C2[1]^2
f3 = C3[0]^3 + a*C3[0] + b - C3[1]^2
F = [f1,f2,f3]
Ideal = Ideal(F)
I = Ideal.groebner_basis()
print(I)
# 求解参数a b n
res=[x.constant_coefficient() for x in I]
n = res[2]
a = -res[0]%n
b = -res[1]%n
后边就是根据3组e,C求G,这个就是ECRSA,比照RSA的共模攻击,求CRT,CRT也支持这样的。
E=EllipticCurve(Zmod(n),[a,b])
P1=E(C1)
P2=E(C2)
P3=E(C3)
# 三个e的ECRSA共模攻击
g1,s1,t1=xgcd(e1,e2)
g2,s2,t2=xgcd(g1,e3)
assert g2 == 1
M=s2*s1*P1 + s2*t1*P2 + t2*P3
from Crypto.Util.number import *
print(long_to_bytes(int(M[0])))
#b'RSA_0n_ECC_1s_mor3_Ineres7ing\x00'
3,easy_hash
这个题比较难了,没有环境,而且WP上全是字,不大懂,在本地试了一下,大概还有些出入。
import socketserver
import signal
from Crypto.Util.number import *
from random import randint
from gmpy2 import next_prime
from SECRET import flag
banner = br'''
_____ ______ ______ ______ ______ ______
/\ __-. /\ __ \ /\ ___\ /\ ___\ /\__ _\ /\ ___\
\ \ \/\ \ \ \ __ \ \ \___ \ \ \ \____ \/_/\ \/ \ \ __\
\ \____- \ \_\ \_\ \/\_____\ \ \_____\ \ \_\ \ \_\
\/____/ \/_/\/_/ \/_____/ \/_____/ \/_/ \/_/
'''
n0 = 30798082519452208630254982405300548841337042015746308462162479889627080155514391987610153873334549377764946092629701
g = 91289086035117540959154051117940771730039965839291520308840839624996039016929
class LCG():
def __init__(self) -> None:
self.n = next_prime(2**240)
self.a = getRandomNBitInteger(85)
self.seed = randint(1, self.n-1)
def next(self):
self.seed = (self.seed ** 3 * self.a + randint(-2**230, 2**230) + randint(1,100)) % self.n
return self.seed
def gift():
lcg = LCG()
outputs = []
for i in range(30):
outputs.append(int(lcg.next()))
return outputs, lcg.a
gift, a = gift()
def hash(msg):
key = bin(a)[2:]
n = n0
msg = list(map(ord,msg))
for i in range(len(msg)):
if key[i] == '1':
n = g * (2 * n + msg[i])
else:
continue
n = n & ((1 << 383) - 1)
return (n - 0xdeedbeef114514) % (1 << 100)
MENU = br'''
<OPTION>
'''
class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 2048
data = b''
while True:
part = self.request.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
break
return data.strip()
def send(self, msg, newline=True):
try:
if newline:
msg += b'\n'
self.request.sendall(msg)
except:
pass
def recv(self, prompt=b'SERVER <INPUT>: '):
self.send(prompt, newline=False)
return self._recvall()
def proof_of_work(self):
rounds = 1000
pseudo_prime = int(self.recv(prompt=b'[+] Plz Tell Me your number: '))
if isPrime(pseudo_prime):
self.send(b"\nNo! it's a prime, go away!")
self.request.close()
for i in range(rounds):
if pow(randint(2, pseudo_prime), pseudo_prime - 1, pseudo_prime) != 1:
self.send(b"\nYou failed in round " + str(i + 1).encode() + b', bye~~')
self.request.close()
self.send(b"\nCongratulations, you have passed the proof_of_work!\n")
return True
def handle(self):
signal.alarm(300)
step1 = self.proof_of_work()
if not step1:
self.request.close()
self.send(banner)
self.send(b"\nNew challenge now! Please give me 2 diff strings whose hashes are the same~~")
self.send(b'Here is my gift for you: \n' + str(gift).encode())
string1 = self.recv().decode()
string2 = self.recv().decode()
if string1 == string2:
self.send(b"\nNo! They are the same one!")
self.request.close()
if hash(string1) == hash(string2):
self.send(b'\nGood job~ Here you are!')
self.send(flag)
self.send(b"\nConnection has been closed.")
self.request.close()
class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 9999
print("HOST:POST " + HOST+":" + str(PORT))
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()
题目很长,大概分3部分:
1,proof_of_work
一般情况下proof就是个工作量,比如爆破个4位hash啥的,但这个不是,它是要求一个1000内无法验伪的伪素数:卡迈尔数。WP里给了伪素数生成方法的说明。这类题原来作过,用这个公式生成伪素数是极其困难的。不过这题并不需要生成,只有你知道一个就行了,这个数是一个300以下base的卡迈尔数,就是不存在300以下的因子。对于1000来说已经足够大了,1000以下全是小因子。所以输入这个数就能过。
p = 29674495668685510550154174642905332730771991799853043350995075531276838753171770199594238596428121188033664754218345562493168782883
n = p *(313*(p - 1) + 1)*(353 * (p - 1) + 1)
proof_of_work(n)
2,LCG
这个LCG生成30个随机数,生成公式是: f(n) = f(n-1)^3*a + e (mod p)
这是个标准的hnp问题,有个lbc_toolkit有这个函数,官方WP也给出了这个函数构造矩阵的方法,有函数了还整矩阵干啥。
不过这有个问题,不知道官方是不是作了这题,这题给出的
self.n = next_prime(2**240)
非常特殊,会造成LLL算法不出结果。经过测试,这里改成随机一个getPrime(240)数就没问题。
class LCG():
def __init__(self) -> None:
#self.n = next_prime(2**240)
self.n = getPrime(240) #不用原来的p
self.a = getRandomNBitInteger(85)
self.seed = randint(1, self.n-1)
print(self.n,self.a)
def next(self):
self.seed = (self.seed ** 3 * self.a + randint(-2**60, 2**60) + randint(1,100)) % self.n #
return self.seed
def gift():
lcg = LCG()
outputs = []
for i in range(30):
outputs.append(int(lcg.next()))
return outputs, lcg.a
outputs,a = gift()
#----------------
p = 1113328282865438363384064798855439755994226962679762330059112876627863681
B = 2^230
T = [(i^3)%p for i in outputs[:-1]]
A = outputs[1:]
sol = hnp(p, T, A, B, verbose=True)
print(sol)
说明确实那个p有问题
可以根据这一步求出a或者绕过这一步(非官方预期),所以下边也就非预期了
3,hash
def hash(msg):
key = bin(a)[2:]
n = n0
msg = list(map(ord,msg))
for i in range(len(msg)):
if key[i] == '1':
n = g * (2 * n + msg[i])
else:
continue
n = n & ((1 << 383) - 1)
return (n - 0xdeedbeef114514) % (1 << 100)
这个hash代码比较特殊,他没有对所有位处理,只对key为1的位处理,那么当输入两位的值时,如果key='10.....'也就跟后边无关,两个值只有第1位相同,大概率hash值也相同。并且key的前两位是10的概率也非常大,大概都是50%,所以就输入AA和AB,由于环境及时关闭了,所以只能本地测
v = 0
for i in range(10000):
a = getRandomNBitInteger(85)
if hash('AA') == hash('AB'):
v+=1
print(v/10000)
#4961/10000
确实对于随机的a可以预期得到大概率的正确值。
这个题由于proof可以绕过,hnp可以不作,50%*50%的概率应该很容易爆破成功。