SCTF2023

  1. Barter
  2. Math forbidden

Barter

两个文件:

homework

from Crypto.Util.number import *
from params import N, E, D
from leak_data import P, Q, r_1
import re

def challenge():
    meum = '''option:
    1: get pubkey
    2: get sign
    3: verify
    4: exit'''
    print("Hi, I am John. If you help me with my homework, I'll give you the data that I know ( ̄o ̄) . z Z")
    print(meum)
    sign = None

    while True:
        print('[+]input your option: ', end='')
        your_input = input()

        if your_input == '1':
            print(f'[+]N = {N}')
            print(f'[+]e = {E}')
            continue

        elif your_input == '2':
            sign = pow(bytes_to_long(MSG.encode()), D, N)
            print(f'[+]sign = {sign}')
            continue

        elif your_input == '3':
            if sign is None:
                print('[+]Please input option 2 to generate sign first.')
                continue
            msg_user = input("[+]Please input your message: ")
            n = int(input("[+]Please input n: "))
            e = int(input("[+]Please input e: "))
            if e <= 3:
                print('[+]e is invalid')
                break
            else:
                if re.match(r'I can not agree more!!!$', msg_user):
                    if pow(bytes_to_long(msg_user.encode()), e, n) == sign:
                        print("Goooooooood! You are my hero! I can give you the data that I know ╰(*°▽°*)╯")
                        print(f'Leak_data: \n P={P}\n Q={Q}\n first num in r_list={r_1}')
                        break
                    else:
                        print('[+]Error signature!')
                        break
                else:
                    print('[+]Error message!')
                    break

        elif your_input == '4':
            break

if __name__ == '__main__':
    MSG = 'This is an easy challenge'
    challenge()

chal

from Crypto.Util.number import *
from random import *
from secrets import flag


def gen_random(seed, P, Q, r_list, times):
    s = seed
    for i in range(times):
        s = int((s * P)[0])
        r = int((s * Q)[0])
        r_list.append(r)
    return r_list

def gen_seed():
    seed = getRandomNBitInteger(32)
    return seed

def getP_Q():
    Q = Curve.random_point()
    P = 114514*Q
    return P, Q

def enc(flag, rlist):
    seq = list(randint(0, 1) for _ in range(4))
    add = rlist[55]*(seq[0]*rlist[66] + seq[1]*rlist[77] + seq[2]*rlist[88] + seq[3]*rlist[99])
    xor = pow(rlist[114], rlist[514], rlist[233]*rlist[223])
    enc = (bytes_to_long(flag)^^xor)+add
    return enc

nums = 600

seed = gen_seed()

p = 58836547289031152641641668761108233140346455328711205590162376160181002854061
F = GF(p)
a = F(114)
b = F(514)
Curve = EllipticCurve(F, [a, b])
P, Q = getP_Q()
r_list = []
r_list = gen_random(seed, P, Q, r_list, nums)
ENCFLAG = enc(flag, r_list)
print(ENCFLAG)
print(P, Q)
print(r_list[0])

'''
4911741083112145038719536311222612998219730565328651097326896414315857050336523018712625917027324116103593300559128797807261543857571883314990480072241188
#####################################
#####################################
Oh no, P, Q and r_list[0] are accidentally lost, but John seems to know, you can ask him about these missing values ::>_<::
'''

需要先通过homework求出P、Q、r_list[0]来。

第一步就是输入合适的n和e,且e>3,保证pow(bytes_to_long(msg_user.encode()), e, n) == sign,有两种方法:

法一:因为e>3,取个较小的e(4、5),这样可以直接求出n:

$n = m^e-sign$

法二:生成光滑n后离散对数求出e(光滑数较于普通的大整数更容易求出离散对数)

def gen_primes(nbit, imbalance):
    """
    :param nbit: 最终光滑数比特数
    :param imbalance: 最小单位比特数
    :return: 比特数
    """
    p = int(2)
    FACTORS = [p]
    while p.bit_length() < nbit - 2 * imbalance:
        factor = getPrime(imbalance)
        FACTORS.append(factor)
        p *= factor
    rbit = (nbit - p.bit_length()) // 2

    while True:
        r, s = [getPrime(rbit) for _ in '01']
        _p = p * r * s
        if _p.bit_length() < nbit: rbit += 1
        if _p.bit_length() > nbit: rbit -= 1
        if isPrime(_p + 1):
            FACTORS.extend((r, s))
            p = _p + 1
            break

    FACTORS.sort()
    return (p, FACTORS)

MSG = 'This is an easy challenge'
msg = 'I can not agree more!!!'
sign = 2007693265003793531961671894515047380652880039368379717802504726955615563153911755018745338145752718217463688949530928466521446062822626671546131855463622

M = bytes_to_long(MSG.encode())
m = bytes_to_long(msg.encode())

n, n_fac = gen_primes(512, 20)

# 离散对数sage求解(模数小 / 阶光滑)
# h = g^x mod p
# c1 = m^e mod n1
p = n
g = m
h = sign
x = discrete_log(Mod(h,p),Mod(g,p))
print(pow(m,x,p)==sign)
print('n =', p)
print('e =', x)

$P = 114514 \times Q $

$s_0 = seed \cdot P = 114514 \cdot seed \cdot Q $

$r_0 = s_0 \cdot Q = (114514 \cdot seed \cdot Q) \cdot Q $

$\Rightarrow$

$s_1 = s_0 \cdot P = (114514 \cdot seed \cdot Q) \cdot 114514 \cdot Q = r_0 \cdot 114514$

$r_1 = s_1 \cdot Q$

通过求出的$r_0$和$Q$求出$s_0$,进而能得到每次迭代的s和r,完整的$r_{list}$便能求出:

rlist = [r0]
for i in range(600):
    s = (E.lift_x(rlist[-1])*114514)[0]
    rlist.append((s*Q)[0])

有了rlist就开始求flag:

seq = list(randint(0, 1) for _ in range(4))

seq总共4个元素,每个元素要么是0要么是1,总共16中情况,可爆破。

from Crypto.Util.number import *

p = ...
F = GF(p)
a = F(114)
b = F(514)
E = EllipticCurve(F, [a, b])


P = E(..., ...)
Q = E(..., ...)

r0 = ...
enc = ...

rlist = [r0]
for _ in range(600):
    s = (E.lift_x(rlist[-1])*114514)[0]
    rlist.append((s*Q)[0])

for i in range(0,2):
    for j in range(0,2):
        for m in range(0,2):
            for n in range(0,2):
                add = rlist[55] * (i * rlist[66] + j * rlist[77] + m * rlist[88] + n * rlist[99])
                xor = pow(rlist[114], rlist[514], rlist[233] * rlist[223])
                flag = long_to_bytes(int((enc - add)^^xor))
                if b'SCTF' in flag:
                    print(flag)

Math forbidden

from Crypto.Util.number import *
from Crypto.Cipher import AES
import os
from flag import flag

secret = os.urandom(16)*2
sysKEY = os.urandom(8)
aeskey = os.urandom(16)
iv = os.urandom(16)

p = getPrime(256)
q = getPrime(256)
e = 0x10001
d = inverse(e,(p-1)*(q-1))

n = p*q

def aes_enc(m,key,iv):
    aes = AES.new(key,AES.MODE_CBC,iv)
    c =  aes.encrypt(m)
    return c
def aes_dec(c,key,iv):
    aes = AES.new(key,AES.MODE_CBC,iv)
    m =  aes.decrypt(c)
    return m
def add_to_16(key):
    padding = 16 - (len(key) % 16)
    key += bytes([padding])*padding
    return(key)

def unpadding(key):
    padding = key[-1]
    if(padding==0):
        return key,False
    for i in range(padding):
        if key[-i-1]!=padding:
            return key,False
    key = key[:-padding]
    return key,True

def check_token():
    print("input your token")
    print("key")
    print(">",end='')
    enc_key = input()
    
    print("IV")
    print(">",end='')
    iv = input()
    enc_key =  bytes.fromhex(enc_key)
    iv =  bytes.fromhex(iv)
    key_padding = aes_dec(enc_key,aeskey,iv)    
    
    dec_key,flag= unpadding(key_padding)
    
    if(flag==False):
        print("fake token")
    else:
        if(dec_key == sysKEY):
            print("0.0")
            print('N',hex(n))
            print('E',hex(e))
            print('c',pow(bytes_to_long(secret),e,n))
        else:
            print("0.0??")

def adminadmin():
    print("input n:")
    print(">",end='')
    n = input()
    print("input c:")
    print(">",end='')
    c = input()
    n = bytes.fromhex(n)
    c = bytes.fromhex(c)
    n = bytes_to_long(n)
    c = bytes_to_long(c)
    
    secret = pow(c,d,n)
    print("only admin can touch the answer[yes/no]")
    op1 = input()
    if(op1=='yes'):
        print("input admin password:")
        print(">",end='')
        password = input()
        
        adminKEY = bytes.fromhex(password)
        
        if adminKEY==sysKEY:    
            m = long_to_bytes(secret,64)
            
            print(m[:2].hex())

key_padding = add_to_16(sysKEY)
enc_key = aes_enc(key_padding,aeskey,iv)
print('your token',enc_key.hex(),iv.hex())


menu = """
1.check
2.admin
3.getflag
"""

while 1:
    print(menu)
    print(">",end='')
    op = input()
    
    if(op=='1'):
        check_token()
        continue
    if(op=='2'):
        adminadmin()
        continue
    if(op=='3'):
        print("oops! I forget to hide the backdoor!\n>",end='')
        
        tmp = bytes.fromhex(input())
        if(tmp==secret):
            print(flag)
        continue
    
    print("try again")

考点:CBC Padding Oracle && MSB Oracle Attack

SCTF 2023 Writeup - 星盟安全团队 (xmcve.com)

[SCTF 2023] 小部分_石氏是时试的博客-CSDN博客


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1666739907@qq.com
github