卷王杯-部分Crypto-wp

CTFSHOW—卷王杯—部分CryptoWp

密码签到

密文

image-20220227152038202

Y3NldHRfZl9jc2FyaF95b3Nwd2l0JTdCbW9qcCF1bCU3RA==

考点

  • base64
  • URL编码
  • 栅栏密码

解密

base64

image-20220227151248476

URL解码

image-20220227151342621

栅栏密码爆破

image-20220227151635956

真·简单·不卷·现代密码签到

题目

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(中国剩余定理)

分析

  1. e = 2首先想到的就是rabin攻击了,但是公钥n是由五个素数组成,所以不是一般的rabin;
  2. n = p * q * r * s * t 这么多参数,再加上一般的rabin解出来都有4个结果(虽然咱用的是有限域开方),那用一用CRT不过分吧;
  3. 在模 p p p q , r , s , t q,r,s,t q,r,s,t)的条件下,对 c c c进行有限域开方,这样就有五个结果,再用中国剩余定理求出 m m m
  4. 实际情况中,对步骤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)(原理我也不知道,做题的时候发现做不出来,然后去尝试试出来的);
  5. 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)吧,看看大佬的博客

image-20220227155852189

简单分析

​ 之前做的时候并不知道是原题,后来看到 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 01

之后就是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=((2m)e)%n

因为 m m m 的两倍可能大于 n n n,所以经过解密得到的明文是 m ′ = ( 2 ⋅ m ) % n m′=(2⋅m)\%n m=(2m)%n

我们还能够知道 m ′ m′ m 的最低位lsb 是1还是0。 因为 n n n 是奇数,而 2 ⋅ m 2⋅m 2m 是偶数,所以如果lsb 是0,说明 ( 2 ⋅ m ) (2⋅m)%n (2m) 是偶数,没有超过 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=(4m)%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
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模式:

image-20220227164625171

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。 keyctmsg  iv  iv0iv

 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 keyiv是这样生成的,而 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 = 8getrandbits(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 keyiv,然后有了 k e y , i v 和 c t key,iv和ct key,ivct之后岂不是可以为所欲为得到 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()

铱锡锡

题目

image-20220227171325411

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

不会以为我真的会吧,你在期待什么呢?

two thousand years later

在经过一系列的摸鱼划水,被人催着复现,以及重装电脑之后,我还并不是很会地复现了(内心恐惧ECC,不是很懂)。

找到一个类似的题目,以及hash_hash写的Wp,然后缝缝补补,修修改改弄了个能跑的脚

这道题大概啥意思呢?可能就是加密系统不检测你输入的点在不在椭圆曲线上,然后你就可以利用这个漏洞得到 k k k的值。

  1. 首先在本地构造容易计算的 b b b的值(也不是很随便地随便选几个数字);
  2. 然后真地很随便的在椭圆曲线(用自己的 b b b)上找一个点 G G G,并求出它的阶,顺便分解一下子;
  3. G G G发给加密系统,并接收到 G 1 = k e y ∗ G G1 = key * G G1=keyG
  4. 再用 P o h l i g − H e l l m a n Pohlig-Hellman PohligHellman算法(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分解一下;
  5. 已知 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}*... orderG=0; keyG=G1; order=p1e1p2e2...,那么 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 pieiorderkeyG=pieiorderG1,即会在模 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次尝试机会,所以最多有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

image-20220312201153518

脚本

跑完脚本时间并不是很短,需要等一会会;正如题目,并不是每次跑都能跑出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()

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mxx307

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值