CTFSHOW—卷王杯—部分CryptoWp
密码签到
密文
Y3NldHRfZl9jc2FyaF95b3Nwd2l0JTdCbW9qcCF1bCU3RA==
考点
- base64
- URL编码
- 栅栏密码
解密
base64
URL解码
栅栏密码爆破
真·简单·不卷·现代密码签到
题目
from Crypto.Util.number import bytes_to_long
from secrets import p,q,r,s,t,flag
n = p * q * r * s * t
e = 2
m = bytes_to_long(os.urandom(500) + flag)
c = pow(m,e,n)
print(p,q,r,s,t,sep='\n')
print(c)
'''
145332367700944303747548912160113939198078051436029477960348968315913956664143693347226702600438608693933768134575289286283267810723137895903153829001826223446477799895493265422562348917012216790077395795861238257357035152687833639085415763850743538206986781418939737511715957738982536382066693822159860701263
116660458253067608044065523310547233337730583902133756095473339390057738510707447906971188577217274861047379404014140178165569604404468897712846876108444468370709141219302291601408652742006268186059762087155933131837323952675627966299810891805398890428420575425160696531236660480933905879208166090591482794763
157931722402853245421436270609912823260313730941283152856444641969403238646482562190531038393124087232554754746464603598717356255570166081501573727336977292059427220330169044611674973569766966838498453232642731737958791706086957762244686953294662693939604300864961637325536379321027705854708492453330690705531
100973451687449518854742673778783266158999451072058606348222018797891147675959983616210003484476577612134482311993701677242007759556951494382833070563369964294544839433671087037596159753825249018950693369209927951667775267086896180395776150188902057785214767230658487267587289809918132337927575673868568976679
93960345071948255233882121683650797512129333868351496468898834736770441398743300745703393838320587998953678254272245400344928586394089488734271897540051673996675973642347859306921527430850673334243441180183460927865980713929789963587608547554858491264614271309608925634272282292964002897650355047792764365447
9144597920381774885442906257311149465702295057238600973973598305004391534618770363098565074541384771979931799878381439264848137810353858418200992191234142740194489573540381681161219332611454834544291634628456257670178843484698324641739324687497388018406214041657278323855749902661752448796122517061920880552011343608609622885787617238758769398972009949575526258430282648817039091284796330585349957724522615105102735930258969562103112238020133587096826386028128471852377225525357348919204333121695432662339443004327748973224423132988376298843862056631045488285859621661802413201793962883794915513510467912312842687601478117040419013468059983777273699192408773551806581458197324620065210523913467414181480875280203580147077789063808832356486197271376615883221558265591069223727607585313240243619515521180600435114131162272519949101464089935441251751426683447701142156416866113627126765919641034042927519834229168536331952275698122511502745177547569813354280565828372968703810158857859460406828090199683324760956105682902577189283246483314689365570862217407333103243336691401424548702387876409228977278498691200028282744239512091373110111792177228979867318546462714521296256938374618636206565791541769138267080789842400796973226733816939794717596194090232425688504890234304977612220790858557639246367437740975495450011676714198668471438814299689325208882261918460708833888406187912527346628912894921059735420931656953236560178909180587372589456926690219114173193202048332172538564489660440225377822914097420807957784201785024166011709377791129
'''
考点
- rabin?有限域开方?
- CRT(中国剩余定理)
分析
e = 2
首先想到的就是rabin攻击了,但是公钥n是由五个素数组成,所以不是一般的rabin;n = p * q * r * s * t
这么多参数,再加上一般的rabin解出来都有4个结果(虽然咱用的是有限域开方),那用一用CRT不过分吧;- 在模 p p p( q , r , s , t q,r,s,t q,r,s,t)的条件下,对 c c c进行有限域开方,这样就有五个结果,再用中国剩余定理求出 m m m;
- 实际情况中,对步骤3得出来的结果可能并非 m m o d p m\ mod\ p m mod p的结果,还有可能是 p − ( m m o d p ) p\ -(m\ mod\ p) p −(m mod p)(原理我也不知道,做题的时候发现做不出来,然后去尝试试出来的);
- 1个素数对应2种结果,5个素数就 2 5 2^5 25种结果,分别CRT看看哪里有 f l a g flag flag。
脚本
感觉脚本写得丑爆了
from Crypto.Util.number import *
from gmpy2 import *
from sympy.ntheory.modular import *
from sympy.ntheory.residue_ntheory import nthroot_mod
e = 2
p = 145332367700944303747548912160113939198078051436029477960348968315913956664143693347226702600438608693933768134575289286283267810723137895903153829001826223446477799895493265422562348917012216790077395795861238257357035152687833639085415763850743538206986781418939737511715957738982536382066693822159860701263
q = 116660458253067608044065523310547233337730583902133756095473339390057738510707447906971188577217274861047379404014140178165569604404468897712846876108444468370709141219302291601408652742006268186059762087155933131837323952675627966299810891805398890428420575425160696531236660480933905879208166090591482794763
r = 157931722402853245421436270609912823260313730941283152856444641969403238646482562190531038393124087232554754746464603598717356255570166081501573727336977292059427220330169044611674973569766966838498453232642731737958791706086957762244686953294662693939604300864961637325536379321027705854708492453330690705531
s = 100973451687449518854742673778783266158999451072058606348222018797891147675959983616210003484476577612134482311993701677242007759556951494382833070563369964294544839433671087037596159753825249018950693369209927951667775267086896180395776150188902057785214767230658487267587289809918132337927575673868568976679
t = 93960345071948255233882121683650797512129333868351496468898834736770441398743300745703393838320587998953678254272245400344928586394089488734271897540051673996675973642347859306921527430850673334243441180183460927865980713929789963587608547554858491264614271309608925634272282292964002897650355047792764365447
c = 9144597920381774885442906257311149465702295057238600973973598305004391534618770363098565074541384771979931799878381439264848137810353858418200992191234142740194489573540381681161219332611454834544291634628456257670178843484698324641739324687497388018406214041657278323855749902661752448796122517061920880552011343608609622885787617238758769398972009949575526258430282648817039091284796330585349957724522615105102735930258969562103112238020133587096826386028128471852377225525357348919204333121695432662339443004327748973224423132988376298843862056631045488285859621661802413201793962883794915513510467912312842687601478117040419013468059983777273699192408773551806581458197324620065210523913467414181480875280203580147077789063808832356486197271376615883221558265591069223727607585313240243619515521180600435114131162272519949101464089935441251751426683447701142156416866113627126765919641034042927519834229168536331952275698122511502745177547569813354280565828372968703810158857859460406828090199683324760956105682902577189283246483314689365570862217407333103243336691401424548702387876409228977278498691200028282744239512091373110111792177228979867318546462714521296256938374618636206565791541769138267080789842400796973226733816939794717596194090232425688504890234304977612220790858557639246367437740975495450011676714198668471438814299689325208882261918460708833888406187912527346628912894921059735420931656953236560178909180587372589456926690219114173193202048332172538564489660440225377822914097420807957784201785024166011709377791129
assert isPrime(p) and isPrime(q) and isPrime(r) and isPrime(s) and isPrime(t)
x = [p,q,r,s,t]
y = []
for i in x:
y.append(nthroot_mod(c,e,i))
y.append(i - nthroot_mod(c,e,i))
yy = []
for i in range(32):
s = bin(i)[2:].rjust(5,'0')
yyy = []
for j in range(5):
yyy.append(y[j*2+int(s[j])])
yy.append(yyy)
# print(yy)
for i in yy:
m = long_to_bytes(crt(x,i)[0])
if b'ctfshow' in m:
print(m)
'''
b'!Nb\x1bO\xc6\xd2\xfc\xa9\xe6PJ\xbf\\\xc2-\xe81\x85\x0cQ\x9e^\xc8.\xe4\xea\x186\xc0:\x14\xb9\xb2@d\x06\xd2\xe7\x07N8v\x13\'\x9d\x05-G)\x0b@\xd5h9\x8b\xabDI\xd8\xb9\xf3\xc0\xb5i\x94\x181\x02@\x8e\xcc\x82\r%K\xffE(-@Dv%k\xa9y\xd8tS\x8f\xcf\xa7s\xf3\x8b\xa5\xcd\x7f\x02)LdW}S\xbd\xf3\xcf\x9czo\xaevw$\xbf\x81\x8d\xbc\xdd\xc0\xc7p\xc1\xcb\xe5t\n\xfc\x99\xd1B\xf8~Cuq\xd7\x80\x1e\x97\x04\x05\x9a\xd6\xbe\xd61p\xd7U\xec\xc1#\xb1z\x0e\x04\x1e\x8a\x89p\xa2FYZ-EZD)\xe2\x0b\xef\xd1\xf0\xe2\xd0\x0b\xcb\x18\x9c\xd0\x9em\xac\xf6"\xd2D\x91Z5)l\xd4\x86x\xd0{[\tzH\xae\x1b\x10\x18\x81(\x07\xa7z\xa2\x88\xc2\xb0:\x92I\x95~\x0erD~\x93\x15}\x8b\x85\x96\xa0n\xb7\xc1\xf7(\x0f \xb4s\x06\xefe\x98\xc3\xe8\xa4\xf7\xae\x8e9j\xa8\xb5@\x01\x10*\x80\x11\xf4\xeb\x98\x9a\xc0\xe1\xb5\x18\x9c\x02\x82_\xa3\x05\xb2\x11\'\xfd\xc0\x1a\xcd\xa2\x0b\x90y\x87$\xa3\xbb$<P\x85R\xecu\xe3\x91(_\'\x84kY5\x16\xb3\xabCf\xa9\xfe\xe8\xb2\xe1\x0bM\xc9\xe8?U2\xcd\xcdS\x024\x07\x18*eL\x04u^RS[\x90\xb3\xc0\x99\x95\xe5\xab\x93\x8cGM\x9b\x18\xe6\xc4\xa1\x0c\xdc\xbc{R\xca\x81\x1c\xb4\xc0\xb5\xf1\xb6\xeb\xd8\xa7\x8f\x0e \xbc\xdf\xf0\x85\x8bT\xa2\x96k\x1d5\xc0\xb8\x96\xdd\x95\xd2\x85y\xef\x13\xed\x87\xac\xc3\xa1\xe2\xd2\x90\x0e\x9e\xd0Z \xd9\xce\xa04\x92\xa6\xb5\xe9H\xb8,\xe4\xe92\xdc\x1a\x87\x94\xdc\x9bq\xee\x16\xda\xf4\x15=|i\xc2i9|\xd6\x89\x19\x19ij\xb2\x0b\xc2E\x9d!Ey\xc0\x95\xea\x19\xa1\xb4g\xad\x94\xf9\xb6\xe5\x84\x80\xde\xd0\xe7G\xedR\x8d\x8d\x1c\x82g\xa9\xd2\x8a\xdc\xf0O\xe4\xd4\xe1\x9f\x7f\tn\xb3\x8a5\x83\xbcctfshow{D0_y0u_R3aLly_Kn0w_Ra8IN_alg0RI7HM?}'
'''
概率RSA
题目描述
怎么说呢,算原题(Backdoor CTF 2018 题目 BIT-LEAKER)吧,看看大佬的博客吧
简单分析
之前做的时候并不知道是原题,后来看到 2 ∗ ( r a n d o m . r a n d i n t ( 1 , 10000 ) ) 2*(random.randint(1,10000)) 2∗(random.randint(1,10000)),这个好像是偶数,然后就联想到了 0 和 1 0和1 0和1,
之后就是RSA的LSB了。
Least Significant Bit Oracle Attack (LSB Oracle Attack / Parity Oracle)
适用情况:可以选择密文并泄露明文的最低位(奇偶性)。
假设存在一个oracle,能对给定密文进行解密并给出对应明文的奇偶信息,则我们只需要 l o g ( N ) log(N) log(N) 次就能解密任意密文。
在一次RSA加密中,明文为 m m m,模数为 n n n,加密指数为 e e e,密文为 c c c。我们可以构造出
c ′ = ( ( 2 e ) ⋅ c ) % n = ( ( 2 e ) ⋅ ( m e ) ) % n = ( ( 2 ⋅ m ) e ) % n c′=((2^e)⋅c)\%n=((2^e)⋅(m^e))\%n=((2⋅m)^e)\%n c′=((2e)⋅c)%n=((2e)⋅(me))%n=((2⋅m)e)%n,
因为 m m m 的两倍可能大于 n n n,所以经过解密得到的明文是 m ′ = ( 2 ⋅ m ) % n m′=(2⋅m)\%n m′=(2⋅m)%n 。
我们还能够知道 m ′ m′ m′ 的最低位
lsb
是1还是0。 因为 n n n 是奇数,而 2 ⋅ m 2⋅m 2⋅m 是偶数,所以如果lsb
是0,说明 ( 2 ⋅ m ) (2⋅m)%n (2⋅m) 是偶数,没有超过 n n n,即 m < n 2 m<\frac n2 m<2n ,反之则 m > n 2 m>\frac n2 m>2n 。举个例子就能明白 2 % 3 = 2 2\%3=2 2%3=2 是偶数,而 4 % 3 = 1 4\%3=1 4%3=1 是奇数。以此类推,构造密文$ c′′=((4^e)⋅c)%n$ 使其解密后为 m ′ ′ = ( 4 ⋅ m ) % n m′′=(4⋅m)\%n m′′=(4⋅m)%n ,判断 的奇偶性可以知道 m ′ ′ m′′ m′′ 和 n 4 \frac n4 4n 的大小关系。所以我们就有了一个二分算法,可以在对数时间内将 m m m 的范围逼近到一个足够狭窄的空间。来自Lazzaro
不懂的可以两位大佬的博客,更详细
脚本
- 那篇博客中的脚本有些问题,本文已修正
- 交互时间较长,需要跑到 i = 1024 \ i\ =\ 1024 i = 1024,请耐心等待
# https://introspelliam.github.io/2018/03/27/crypto/RSA-Least-Significant-Bit-Oracle-Attack/
from pwn import *
from Crypto.Util.number import *
import re
if len(sys.argv) > 1:
p = remote('xxxxxxxxxxx', xxxxx)
else:
p = remote('xxxxxxxxxxx', xxxxx)
# context.log_level = 'debug'
def oracle(c):
l = []
for i in range(20):
p.sendline(str(c))
s = p.recvuntil(b"temp_c = ")
l.append(int(re.findall(b"\)\s*=\s*([0-9]*)", s)[0]))
flag0 = 0
flag2 = 0
for i in range(20):
if l[i] % 2 != 0:
flag0 = 1
if l[i] > 10000:
flag2 = 1
return [flag2, flag0]
def main():
ss = p.recvuntil(b"temp_c = ")
N = int(re.findall(b"N\s*=\s*(\d+)", ss)[0])
e = int(re.findall(b"e\s*=\s*(\d+)", ss)[0])
C = int(re.findall(b"c\s*=\s*(\d+)", ss)[0])
print("N=", N)
print("e=", e)
print("c=", C)
c = (pow(2, e, N) * C) % N
LB = 0
UB = N
i = 1
while LB != UB:
flag = oracle(c)
print(i, flag)
if flag[1] % 2 == 0:
UB = (LB + UB) // 2
else:
LB = (LB + UB) // 2
c = (pow(2, e, N) * c) % N
i += 1
print(LB)
print(UB)
for i in range(-128, 128):
temp = LB
temp += i
if pow(temp, e, N) == C:
print(long_to_bytes(temp))
exit(0)
if __name__ == '__main__':
main()
p.interactive()
芜
题目
![image-20220227163018885](https://i-blog.csdnimg.cn/blog_migrate/9f843e429be3f5f9947521a80580ff56.png)
from Crypto.Cipher import AES
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
import random
def woohoo():
a = random.getrandbits(32)
b = random.getrandbits(32)
c = random.getrandbits(32)
d = random.getrandbits(32)
return (a<<96)+(b<<64)+(c<<32)+(d)
BANNER = """
+-------------+
| * * |
| * * |
| *********** |
| * * |
| |
| ********* |
| * |
| * |
| *********** |
| * * |
| * * |
| * * * |
| ** ***** |
+-------------+
赛事预告:
比赛名称:ctfshow 卷王杯
题目难度:无
出题奖励:usb小台灯+周边钥匙扣
比赛奖励:单项第1送定制妹子贴纸,单项AK取前3
比赛时间:2022年2月25日(周五) 18时整
比赛时长:48小时
比赛地址:https://ctf.show/challenges
投稿邮箱:ctfshow@163.com
"""
MENU = """
+-----------------+
|[E]ncrypt |
|[T]est encryption|
|[8]86 |
+-----------------+
"""
if __name__ == '__main__':
print(BANNER)
# 我只泄露一点线索
# 是谁为爱走钢索
for i in range(130):
print(MENU)
option = input("> ").lower()
if option == "e":
print("Enter your message.")
msg = input("> ").encode()
msg = pad(msg, AES.block_size)
key = woohoo()
iv = woohoo()
key = long_to_bytes(key).rjust(AES.block_size, b"\x00")
iv = long_to_bytes(iv).rjust(AES.block_size, b"\x00")
c = AES.new(key, AES.MODE_CBC, iv=iv)
ct = c.encrypt(msg)
iv = b"never gonna give you up"
print(f"Ciphertext (hex): {ct.hex()}")
print(f"Key (hex): {key.hex()}")
print(f"IV (hex): {iv.hex()}")
elif option == "t":
flag = open("flag.txt", "rb").read()
msg = pad(flag, AES.block_size)
key = woohoo()
iv = woohoo()
key = long_to_bytes(key)
iv = long_to_bytes(iv)
c = AES.new(key, AES.MODE_CBC, iv=iv)
ct = c.encrypt(msg)
key = b"never gonna let you down"
print(f"Ciphertext (hex): {ct.hex()}")
print(f"Key (hex): {key.hex()}")
print(f"IV (hex): {iv.hex()}")
elif option == "8":
print("Sa yo na la!")
break
else:
print("What r u DOIN?")
else:
print("Never gonna run around and desert you")
考点
- AES的CBC加密模式
- mt19937predictor
- 已知明文攻击
分析
- CBC模式:
if option == "e":
print("Enter your message.")
msg = input("> ").encode()
msg = pad(msg, AES.block_size)
key = woohoo()
iv = woohoo()
key = long_to_bytes(key).rjust(AES.block_size, b"\x00")
iv = long_to_bytes(iv).rjust(AES.block_size, b"\x00")
c = AES.new(key, AES.MODE_CBC, iv=iv)
ct = c.encrypt(msg)
iv = b"never gonna give you up"
print(f"Ciphertext (hex): {ct.hex()}")
print(f"Key (hex): {key.hex()}")
print(f"IV (hex): {iv.hex()}")
其中我们已知
m
s
g
,
k
e
y
,
c
t
msg,\ key,\ ct
msg, key, ct,但我们可以通过这几参数把
i
v
iv
iv求出来
在
解
密
时
,
先
用
k
e
y
对
c
t
解
密
,
结
果
为
:
m
s
g
⊕
未
知
的
i
v
⊕
输
入
的
i
v
0
,
这
样
我
们
就
可
以
通
过
已
知
的
参
数
求
出
未
知
的
i
v
。
在解密时,先用key对ct解密,结果为:msg\ \oplus\ 未知的iv\ \oplus\ 输入的iv0,这样我们就可以通过已知的参数求出未知的iv。
在解密时,先用key对ct解密,结果为:msg ⊕ 未知的iv ⊕ 输入的iv0,这样我们就可以通过已知的参数求出未知的iv。
aes = AES.new(key, AES.MODE_CBC, iv = b'\x00' * 16) # b'\x00' * 16 即为上述输入的iv0
iv = aes.decrypt(ct).rjust(AES.block_size, b"\x00")# 这里的iv = 未知的iv ^ msg
iv = xor(iv, b'\x10' * 16) # b'\x10' * 16 为之前输入的msg
-
mt19937predictor
def woohoo(): a = random.getrandbits(32) b = random.getrandbits(32) c = random.getrandbits(32) d = random.getrandbits(32) return (a<<96)+(b<<64)+(c<<32)+(d)
题目中的 k e y 和 i v key和iv key和iv是这样生成的,而 g e t r a n d b i t s ( 32 ) getrandbits(32) getrandbits(32)是可以预测的,连续的624个 g e t r a n d b i t s ( 32 ) getrandbits(32) getrandbits(32)可以准确的预测出后面的 g e t r a n d b i t s ( 32 ) getrandbits(32) getrandbits(32)(亦可推出624个前的 g e t r a n d b i t s ( 32 ) getrandbits(32) getrandbits(32))
一次 [ E ] n c r y p t [E]ncrypt [E]ncrypt,可以得到 2 ∗ 4 = 8 个 g e t r a n d b i t s ( 32 ) 2\ *\ 4\ =\ 8个getrandbits(32) 2 ∗ 4 = 8个getrandbits(32),那么就需要 624 8 = 78 次 [ E ] n c r y p t \frac {624}8\ =\ 78次[E]ncrypt 8624 = 78次[E]ncrypt就可以准测预测后面的 k e y 和 i v key和iv key和iv,然后有了 k e y , i v 和 c t key,iv和ct key,iv和ct之后岂不是可以
为所欲为得到 f l a g flag flag了
脚本
代码长而臭,请见谅
from pwn import *
from Crypto.Cipher import AES
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
import mt19937predictor
re = remote('XXXXXXXXXXXXXX', xxxxx)
# context.log_level= 'DEBUG'
mt = []
for i in range(100):
re.recvuntil(b'> ')
re.sendline(b'e')
re.recvuntil(b'> ')
re.sendline(b'')
re.recvuntil(b'Ciphertext (hex): ')
ciphertext1 = int(re.recv(32).decode(), 16)
ciphertext2 = long_to_bytes(ciphertext1).rjust(AES.block_size, b"\x00")
re.recvuntil(b'Key (hex): ')
key1 = int(re.recv(32).decode(), 16)
key2 = long_to_bytes(key1).rjust(AES.block_size, b"\x00")
mt.append(key1)
aes = AES.new(key2, AES.MODE_CBC, iv=b'\x00' * 16)
iv2 = aes.decrypt(ciphertext2).rjust(AES.block_size, b"\x00")
iv2 = xor(iv2, b'\x10' * 16)
iv1 = bytes_to_long(iv2)
mt.append(iv1)
# print('ciphertext',ciphertext2)
aes = AES.new(key2, AES.MODE_CBC, iv=iv2)
assert ciphertext2 == aes.encrypt(pad(b'', AES.block_size))
# print(aes.encrypt(pad(b'',AES.block_size)))
# print()
pre = mt19937predictor.MT19937Predictor()
for i in mt:
d = i & (2 ** 32 - 1)
i = i >> 32
c = i & (2 ** 32 - 1)
i = i >> 32
b = i & (2 ** 32 - 1)
i = i >> 32
a = i & (2 ** 32 - 1)
pre.setrandbits(a, 32)
pre.setrandbits(b, 32)
pre.setrandbits(c, 32)
pre.setrandbits(d, 32)
a = pre.getrandbits(32)
b = pre.getrandbits(32)
c = pre.getrandbits(32)
d = pre.getrandbits(32)
key = long_to_bytes((a << 96) + (b << 64) + (c << 32) + (d))
a = pre.getrandbits(32)
b = pre.getrandbits(32)
c = pre.getrandbits(32)
d = pre.getrandbits(32)
iv = long_to_bytes((a << 96) + (b << 64) + (c << 32) + (d))
aes = AES.new(key, AES.MODE_CBC, iv=iv)
re.recvuntil(b'> ')
re.sendline(b't')
re.recvuntil(b'Ciphertext (hex): ')
ciphertext1 = int(re.recv(96).decode(), 16)
ciphertext2 = long_to_bytes(ciphertext1)
print(aes.decrypt(ciphertext2))
re.recvuntil(b'IV (hex): ')
IV1 = int(re.recv(32).decode(), 16)
IV2 = long_to_bytes(IV1)
# print(iv, IV2)
assert iv == IV2
aes = AES.new(key, AES.MODE_CBC, iv=IV2)
print(aes.decrypt(ciphertext2))
re.interactive()
铱锡锡
题目
from Crypto.Util.number import *
import random
class Point:
global p, a, b
def __init__(self, x, y):
self.x = x % p
self.y = y % p
def __str__(self):
return f'({self.x}, {self.y})'
def __add__(self, B):
x1, y1, x2, y2 = self.x, self.y, B.x, B.y
if (x1 == 0 and y1 == 0):
return B
if (x2 == 0 and y2 == 0):
return self
if (x1 == x2 and (y1 + y2) % p == 0):
return Point(0, 0)
if (x1 != x2 or y1 != y2):
lam = (y2 - y1) * inverse(x2-x1, p) % p
else:
lam = (3*x1**2+a) * inverse(2*y1, p) % p
x = (lam**2 - x1 - x2) % p
y = (lam * (x1 - x) - y1) % p
return Point(x, y)
def __rmul__(self, k):
B = Point(0, 0)
k = f'{k:b}'
for k_i in k:
B = B + B
if (k_i == '1'):
B = B + self
return B
def __eq__(self, B):
return self.x == B.x and self.y == B.y
p = 115792089210356248762697446949407573530086143415290314195533631308867097853951
order = 115792089210356248762697446949407573529996955224135760342422259061068512044369
a = -3
b = 41058363725152142129326129780047268409114441015993725554835256314039467401291
WELCOME = """
Welcome to my YeeSheShe!
As I have mentioned before, to predict the value of k, the possibility is also about O(yeesheshe)!
If you know how to capture the probability of O(yeesheshe), you can capture the flag!
"""
MENU = """
Choices:
[1] Test my YeeSheShe
[2] Submit the value of k
"""
def main():
print(WELCOME)
k = random.randint(1,order)
for i in range(7): # Clearlove7
print(MENU)
choice = input('> ')
if (choice == '1'):
print("As we all have known, Q = kP")
print("Give me the coordinate of P, and I'll give you the coordinate of Q.")
x, y = map(int, input("Enter the coordinate of point P: ")[1:-1].split(', '))
P = Point(x, y)
Q = k * P
print(f"The coordinate of Q is {Q}")
elif (choice == '2'):
print("Now please tell me the value of k.")
k_input = input('> ')
if (k == int(k_input)):
print("FUIYOH! You have captured the probability of O(yixixi) successfully!")
with open('flag.txt') as f:
flag = f.read()
print(f"Here is your flag: {flag}")
else:
print('HAIYAA! You just fucked up!')
print('GO AWAY!')
break
else:
print("You cannot finish it in the field of clearlove 7777777")
print("FAILURE!")
if __name__ == '__main__':
main()
考点
- ECC
分析
本题和上次ECC一样,漏洞点还是在输入椭圆曲线上的点坐标时,并未检测点是否在曲线上。
并且注意,倍点运算的过程中并未利用到 b b b的值进行计算。
所以就有我们可以调整 b b b的值,使得点 P P P在以我们调整的 b b b值所对应的椭圆曲线上。由倍点计算过程,点 Q Q Q也在这条椭圆曲线上。
而且题目给了6次尝试机会,所以我们可以把点 P P P弄成一个光滑的循环子群的生成元,其阶为 n i n_i ni(光滑),送到交互里面,会吐出一个 Q Q Q,这样就可以由Pohlig-Hellman算法算出 k m o d n i k \bmod n_i kmodni的值。
再由中国剩余定理,如果这些 n i n_i ni选择得当,那么便可以知道 k k k的值。
参考的 b b b值:1, 4, 12, 38.
来自CTFshow_卷王杯官方wp
不会以为我真的会吧,你在期待什么呢?
在经过一系列的摸鱼划水,被人催着复现,以及重装电脑之后,我还并不是很会地复现了(内心恐惧ECC,不是很懂)。
找到一个类似的题目,以及hash_hash写的Wp,然后缝缝补补,修修改改弄了个能跑的脚本。
这道题大概啥意思呢?可能就是加密系统不检测你输入的点在不在椭圆曲线上,然后你就可以利用这个漏洞得到 k k k的值。
- 首先在本地构造容易计算的 b b b的值(也不是很随便地随便选几个数字);
- 然后真地很随便的在椭圆曲线(用自己的 b b b)上找一个点 G G G,并求出它的阶,顺便分解一下子;
- 把 G G G发给加密系统,并接收到 G 1 = k e y ∗ G G1 = key * G G1=key∗G;
- 再用 P o h l i g − H e l l m a n Pohlig-Hellman Pohlig−Hellman算法(SageMath里面用 d i s c r e t e _ l o g ( ) discrete\_log() discrete_log()函数)算出 k e y m o d o r d e r key \bmod order keymodorder,但 k e y key key太大,直接求求不出来,所以需要将 G 1 G1 G1分解一下;
- 已知 o r d e r ∗ G = 0 ; k e y ∗ G = G 1 ; o r d e r = p 1 e 1 ∗ p 2 e 2 ∗ . . . order * G = 0;\ key*G=G1;\ order = p_1^{e_1}*p_2^{e_2}*... order∗G=0; key∗G=G1; order=p1e1∗p2e2∗...,那么 o r d e r p i e i ∗ k e y ∗ G = o r d e r p i e i ∗ G 1 \frac{order}{p_i^{e_i}}*key*G= \frac{order}{p_i^{e_i}}*G1 pieiorder∗key∗G=pieiorder∗G1,即会在模 p i e i p_i^{e_i} piei的情况下求出 k e y key key,也就是当 p i e i p_i^{e_i} piei不是很大时可以求出多组 k e y i key_i keyi,然后利用 C R T CRT CRT求出 k e y b ( m o d ( ∏ p i e i ) ) key_b(mod(\prod p_i^{e_i})) keyb(mod(∏piei));
- 题目给了6次尝试机会,所以最多有6组 k e y b ( m o d ( ∏ p i e i ) ) key_b(mod(\prod p_i^{e_i})) keyb(mod(∏piei)),最后用 C R T CRT CRT即可求出 k e y key key。
脚本
跑完脚本时间并不是很短,需要等一会会;正如题目,并不是每次跑都能跑出key来,只有铱锡锡的可能性……
参考的 b b b值:1, 4, 12, 38.
官方wp给的 b b b值真香。
# sage
from pwn import *
rm = remote('pwn.challenge.ctf.show', 28150)
rm.recv()
p = 115792089210356248762697446949407573530086143415290314195533631308867097853951
a = -3
def encryptPoint(G):
rm.sendline(b'1')
rm.recvuntil(b'Enter the coordinate of point P: ')
rm.sendline(str(G).encode())
rm.recvuntil(b'The coordinate of Q is ')
x = int(rm.recvuntil(b', ')[1:-2])
y = int(rm.recvuntil(b'\n')[:-2])
rm.recv()
return x, y
mods = []
quotients = []
bb = [1, 4, 12,38]
for B in bb:
# if len(quotients) == 6: break
# for random B
# B = B + 1
print('B =', B)
try:
E = EllipticCurve(GF(p), [a, int(B)]) # new curve
G = E.gen(0)
G1x, G1y = encryptPoint((G[0], G[1]))
G1 = E(G1x, G1y)
order = E.order()
factors = factor(order)
K = []
m = 1
M = []
for prime in factors:
if prime[0] > 2 ** 35:
break
print(prime)
m = m * prime[0] ** prime[1]
k = ((order // prime[0] ** prime[1]) * G).discrete_log((order // prime[0] ** prime[1]) * G1)
K.append(k)
M.append(prime[0] ** prime[1])
quotients.append(CRT_list(K, M))
mods.append(m)
except:
pass # wrong curve
print(quotients)
key = CRT_list(quotients, mods)
print(key)
rm.sendline(b'2')
rm.recv()
rm.sendline(str(key % p).encode())
rm.interactive()