[DownUnderCTF 2022] crypto部分复现

一个良心的比赛,赛完后给出wp,都是挺好的题,有必要都试一下

1 baby-arx

签到题,直接给了加密算法和密文,逐位的进行异或移位操作,这个东西都可以爆破

class baby_arx():
    def __init__(self, key):
        assert len(key) == 64
        self.state = list(key)

    def b(self):
        b1 = self.state[0]
        b2 = self.state[1]
        b1 = (b1 ^ ((b1 << 1) | (b1 & 1))) & 0xff
        b2 = (b2 ^ ((b2 >> 5) | (b2 << 3))) & 0xff
        b = (b1 + b2) % 256
        self.state = self.state[1:] + [b]
        return b

    def stream(self, n):
        return bytes([self.b() for _ in range(n)])


FLAG = open('./flag.txt', 'rb').read().strip()
cipher = baby_arx(FLAG)
out = cipher.stream(64).hex()
print(out)

# cb57ba706aae5f275d6d8941b7c7706fe261b7c74d3384390b691c3d982941ac4931c6a4394a1a7b7a336bc3662fd0edab3ff8b31b96d112a026f93fff07e61b

爆破

#!/usr/bin/env python3
def b_cust(b1, b2):
    c1 = (b1 ^ ((b1 << 1) | (b1 & 1))) & 0xff
    c2 = (b2 ^ ((b2 >> 5) | (b2 << 3))) & 0xff
    b = (c1 + c2) % 256
    return b

out = 'cb57ba706aae5f275d6d8941b7c7706fe261b7c74d3384390b691c3d982941ac4931c6a4394a1a7b7a336bc3662fd0edab3ff8b31b96d112a026f93fff07e61b'
enc = bytes.fromhex(out)

flag = 'D'
for i in range(len(enc) - 1):
    for code in range(32, 127):
        b = b_cust(ord(flag[-1]), code)
        if b == enc[i]:
            flag += chr(code)
            break

print(flag)
#DUCTF{i_d0nt_th1nk_th4ts_h0w_1t_w0rks_actu4lly_92f45fb961ecf420}

当然也可以不爆破,用z3可以直接出.

2 oracle-for-block-cipher-enthusiasts

AES的OFB模式加密,可以交互两次,用户提供IV给出加密结果

#!/usr/bin/env python3

from os import urandom, path
from Crypto.Cipher import AES


FLAG = open(path.join(path.dirname(__file__), 'flag.txt'), 'r').read().strip()
MESSAGE = f'Decrypt this... {urandom(300).hex()} {FLAG}'


def main():
    key = urandom(16)
    for _ in range(2):
        iv = bytes.fromhex(input('iv: '))
        aes = AES.new(key, iv=iv, mode=AES.MODE_OFB)
        ct = aes.encrypt(MESSAGE.encode())
        print(ct.hex())


if __name__ == '__main__':
    main()

#nc 2022.ductf.dev 30009

OFB的加密方式为先用iv=z0加密得到z1然后用m0⊕z1得到c0,也就是用iv得到的加密流z1,z2,...与明文异或得到密文。所以如果把z1作为iv得到的加密的就是z2,z3,z4...这些z与上边的z相同。

由于第1段明文已知,那么第一次随意输入一个iv得到密文c0,然后用已知的第1断明文和c0的第1段得到z1,第2次输入iv为z1得到c1,同样通过c1与已知明文异或得到z2,然后用z2和c0的第2段异或得到第2段明文,最后得到flag

#!/usr/bin/env python3
from pwn import *
from os import urandom, path
from Crypto.Cipher import AES


#FLAG = open(path.join(path.dirname(__file__), 'flag.txt'), 'r').read().strip()
#MESSAGE = f'Decrypt this... {urandom(300).hex()} {FLAG}'


def main():
    key = urandom(16)
    for _ in range(2):
        iv = bytes.fromhex(input('iv: '))
        aes = AES.new(key, iv=iv, mode=AES.MODE_OFB)
        ct = aes.encrypt(MESSAGE.encode())
        print(ct.hex())

#nc 2022.ductf.dev 30009
'''
   m0 m1 m2 m3
iv z1 z2 z3 z4 ...
   c0 c1 c2 c3

   m0 m1 m2 m3
z1 z2 z3 z4 z5 ...
   x0 x1 x2 x3

OFB模式每轮只对iv加密,然后由c=m^z
第1次随意输入
第2次输入由第1次得到的z1 
这样循环 m0^x0 => z2得到下一轮的z, c1^z2 => m1 得到下一块明文
'''
m = [b'Decrypt this... ']
iv = b'\x00'*16

context.log_level = 'debug'

p = remote('2022.ductf.dev', 30009)

#由已知明文得到z(i)
p.sendlineafter(b'iv: ', iv.hex().encode())
c0 = unhex(p.recvline()[:-1])
z0 = xor(c0[:16],m[0])
z = [z0]

#以z(i)为iv,由已知明文0得到z(i+1)
p.sendlineafter(b'iv: ', z0.hex().encode())
c1 = unhex(p.recvline()[:-1])

for i in range(0, len(c0), 16):
    #c(i+1) xor z(i+1) => m(i)
    z.append(xor(m[-1], c1[i: i+16]))
    m.append(xor(z[-1], c0[i+16: i+16+16]))

print(b''.join(m))
#DUCTF{0fb_mu5t_4ctu4lly_st4nd_f0r_0bvi0usly_f4ul7y_bl0ck_c1ph3r_m0d3_0f_0p3ra710n_7b9cb403e8332c980456b17a00abd51049cb8207581c274fcb233f3a43df4a}

'''
C:\2022_DownUnderCTF\crypto_2_oracle-for-block-cipher-enthusiasts>py a.py
[x] Opening connection to 2022.ductf.dev on port 30009
[x] Opening connection to 2022.ductf.dev on port 30009: Trying 34.87.217.252
[+] Opening connection to 2022.ductf.dev on port 30009: Done
[DEBUG] Received 0x4 bytes:
    b'iv: '
[DEBUG] Sent 0x21 bytes:
    b'00000000000000000000000000000000\n'
[DEBUG] Received 0x5f9 bytes:
    b'c5d2cc11722803d1c34f2b0d59938a06f58c4f0e2b7acbd08f30115bb58e08f8726c4226c125282faa8a50687573f8aac72d58f83b531cff4a
8264bb7dbdfce9839165ec3b943ceaf7a8da0234e100f720407004441b28bb82cbe48cc4f017b4385783468e239391ffe8aad14fdfb37e5685d31a6a
234830b7d032bfa8f82543616bc6be663b7d098a46e3c147b51f1bbded127a303dc6d762a3429aa2f05d6526dff55799179f33e4e2062850be1b26dd
dc471b010b15bca6aa5b7ba802cd16d69cd34ac959f7042919d6ed88804c4253f298328b5cd0f6f4ba9aff14eea8d7b0dc728780fa895dc52c9cd083
08f4c1c75e8d529f8cee054df4e1e615c50798b06b95439152d8a90778afa5c17944cdf3dd753147245f1331fc84c18111ca4a13013150acee86bbb5
8cd16fa9371f570b23f80269443e1bf7d58bd2dde86217ddd6ce40f9c67308ac9c870c9901e14874bdd608ba6cde6ef662e7b0683458fae41cee5d8c
e565f7add226c7379359c0b0853dc56199971ad924b04033e3969905acf68ba3bdb21331df811edbb3dda90b4bff4413ccd0164f72e83e1e4312ff12
bcd5fc3ea4af22955c229d90113813cba196afe84492331c880761a6d0dd3fcaa881dda419826f8bc42bbd834d1f5247621547f1d325c5716b086ba6
b841092ad451decdfd50590e2b767af754c4ae271cea75e4ffbd6a1a255a2f2f93dac2291b1c814a34395c0ee6cb9b45dd1ad925ff4db46a2a61c06f
dbe256c62dc26cff7907757eddb5855fd1e8dd4f2ae6df0a08f48799b5e0ffcc62fcaee4f9a73598190677d7e6235598cc383c2f9b01e1cb47523f3e
f733700ee7ab7ff5fadce14000ca46b671346796573781232069743733e6be7a16838b837d8ea9371d6b4174ff29362f8061168f71cbf7b7cce5d83c
f5e1cd3f47450d8ccf521a44cea4ef83f8a2c8cb25a30a31c6987c1b6f5dcee3579c44a8907bfec9fa20a55aa1cfedf7dd0a3242da1481eeff93fc8b
7d1ff848314ca0df57756a28ae2d6bfa1d09adced1340b85ea3fa6a3330daec73a9226e5030f31dbfae51a2c43\n'
    b'iv: '
[DEBUG] Sent 0x21 bytes:
    b'81b7af630b5877f1b727427e77bda426\n'
[DEBUG] Received 0x5f5 bytes:
    b'd7df494e346bdbc7c26c1e49a29211ed706e45759377747da78c0e6a7b70fdfac22f0dff3b5046a218826fbc7deaafbdda9364b93fc166e0a5
a8880566bf57f529127255154626bfd59db7df95f114b13c53d5418e29979effeffdd21dd5bf2906d6804e3e204b3eb28562bea8ae7844603fc2b66b
692d06d949e3c045b51d48bcea45213b6d96dd34a81490f6ad0c6322dff5049c1d9d60beb4062654b61473dd8e411a000544e4aaf80c78ab53c51181
9fd34a980ca7592b16d4ef8dd04c1100a5cb37db5cd4f1f3b39ba845edaf80ed8d7587d4ac8f5f977ecad3d259fac19b538e54ccdebd024df3e2b112
940b99ed36934398528faa532eacf195731090f2d922374d245e4f62aed693d01c9b424e066706aebb82b1e6d8813afa341955597daa026d443810f9
d18ed5dde632468fdace14afc57603f5ccd954990ee51a74ead15abe6fd839f560e9b9683159f2e046ea5ad9b234aafdd326c665c4589cb3dd3c9430
99c21adc27b24067e3c69b57a8ab8cf6eeb24b628a8c1ed8e3dcf90c1ef64846cad5451376e73b4f4715fb14bbd2a068a2fe279c092298c4126f409b
a793fced1797601cdc5564f4d788389faf818cf41cda328f9478e8d41e4b5712361414a3d42a9d753d0b6fa5ba470b2dd050df9ca8540c5d7d772ff6
00c3a97818be23e5f4bd3f4a26562b2dc1d891214c49d7183c3a5e59b294cb428a4cd876ab1be338236097688eb4549e20966bad2f51707ed1b28959
d7ee8f4627b4dd5e5aa183ceb9eea59034faacedfea061c94b067186bb2c0195ce3b3d22cb52bdc8110c6d3fa663200bb4f97cf0ae8bea45059416e3
226165867103f6465020766235dfb53e45c4b0975aaf9f57377c084dee026f34d14a2f8b60e0b2e1d6e09116f5f9800e452e1ed08a747544d4fdbc9d
d4ad80e42ba40d029cd1474a5c5dcbd7388f46aa997fbda6f94ff60bc79ce99cdd496408881082bda8f4fbdd714af84d361ef5d705243c7ea6256ff7
4b5df7cdd6620e8dba3ef0a1365bf99c6bc32db1005930d0afe01c7a0ac01e9aa4b18a27f9f3315197ec12b680\n'

b'Decrypt this... f6e2fad794fa9275d4ba438e428c712ea67f40b8f23d7fa184630e8242ace8631f4ba866cd2049565bbea229cce3f39ae11151
17f652fedfde598ca85953def5ebbb3312c2c90873abb1693a9dc7408fa0d077b956447a0a63d0fb2d79662102ed756b6c007ac27e8505b40abbab2c
9ed8331fcfbeb5525d9e538f4e63aaa8a98ab52f59ea330501ef4c031365cfc66378fc437881fac8fbe4f7b023aa89c7b0ecaef34dbc60c2c8a9eb3d
e92b611ebdbad3bd097a41ee2f0e94770ad491f5367008b15280647142177d47d71e61c2caa1271dad11d4156f462faa995a7affbcab353f6a174eed
daff24b324ba50d0cf9bfbc82a2b94fba51534101ce6fda574baecc904bc0f98c3073fc578de640511defb499b8306926e57fded6651374be96a86d5
ff0ba416223dff1f3d DUCTF{0fb_mu5t_4ctu4lly_st4nd_f0r_0bvi0usly_f4ul7y_bl0ck_c1ph3r_m0d3_0f_0p3ra710n_7b9cb403e8332c98045
6b17a00abd51049cb8207581c274fcb233f3a43df4a}C~\xf7\xa7YCA\xca\x92\x05b\xf3\x8a&\xd7\xfdd\x87\x04\x96\x08\xd4'
'''

3 cheap-ring-theory

这是个脑筋急转弯的题。题目是一个很难计算的算式C = A^n * B^m 给出3组解,然后让输入3组解使式子成立

p = 55899879511190230528616866117179357211
V = GF(p)^3
R.<x> = PolynomialRing(GF(p))
f = x^3 + 36174005300402816514311230770140802253*x^2 + 35632245244482815363927956306821829684*x + 10704085182912790916669912997954900147
Q = R.quotient(f)

def V_pow(A, n):
    return V([a^n for a in list(A)])

n, m = randint(1, p), randint(1, p)
A = Q.random_element()
B = Q.random_element()
C = A^n * B^m

print(' '.join(map(str, list(A))))
print(' '.join(map(str, list(B))))
print(' '.join(map(str, list(C))))

phi_A = V(list(map(int, input().split())))
phi_B = V(list(map(int, input().split())))
phi_C = V(list(map(int, input().split())))

check_phi_C = V_pow(phi_A, n).pairwise_product(V_pow(phi_B, m))

if phi_C == check_phi_C:
    print(open('./flag.txt', 'r').read().strip())

但是这个算式有两个特殊解0,1当ABC都是0或者都是1时算式恒成立,所以直接输入1组即可

$ nc 2022.ductf.dev 30012
47778318564842784566021875964043407448 14225130399845907372219257728746212147 8574612587622189283548244260000651099
54562657673431046093572570377554257218 13201212589661363040860414827784708283 6559649814714882162760393923770035994
42068356418631381456403302803023702216 2953351887187401071583384926766279486 28509402046235153374884180763878587638
0 0 0
0 0 0 
0 0 0
DUCTF{CRT_e4sy_as_0ne_tw0_thr3e}


$ nc 2022.ductf.dev 30012
14202332705912194922737102630420425225 25456741823861199641984966822367393116 49401604133125475446972443244172984778
35742550950811340910151890855042975986 30127926032118592751540416970212811542 37792889060452965734274017750212553062
4755274484612010897449319953362032907 33809699771879943330968717144050513513 20099247008524376332197577971971190549
1 1 1
1 1 1
1 1 1
DUCTF{CRT_e4sy_as_0ne_tw0_thr3e}

4 rsa-interval-oracle-i

以下4道都是RSA oracle的题,以前见到的都是LSB,这个第一回见,学习了。

第1题比较简单,给一N,e,c 然后可以输入密文,反回明文是否在区间内,区间自己输入。

#!/usr/bin/env python3

import signal, time
from os import urandom, path
from Crypto.Util.number import getPrime, bytes_to_long


FLAG = open(path.join(path.dirname(__file__), 'flag.txt'), 'r').read().strip()

N_BITS = 384
TIMEOUT = 20 * 60
MAX_INTERVALS = 384
MAX_QUERIES = 384


def main():
    p, q = getPrime(N_BITS//2), getPrime(N_BITS//2)
    N = p * q
    e = 0x10001
    d = pow(e, -1, (p - 1) * (q - 1))

    secret = bytes_to_long(urandom(N_BITS//9))
    c = pow(secret, e, N)

    print(N)
    print(c)

    intervals = []
    queries_used = 0

    while True:
        print('1. Add interval\n2. Request oracle\n3. Get flag')
        choice = int(input('> '))

        if choice == 1:
            if len(intervals) >= MAX_INTERVALS:
                print('No more intervals allowed!')
                continue

            lower = int(input(f'Lower bound: '))
            upper = int(input(f'Upper bound: '))
            intervals.insert(0, (lower, upper))     #从头部插入,如果判断时成功则返回0,不成功会返回上一次成功的位置>1,全部不成功才返回-1

        elif choice == 2:
            queries = input('queries: ')
            queries = [int(c.strip()) for c in queries.split(',')]
            queries_used += len(queries)
            if queries_used > MAX_QUERIES:
                print('No more queries allowed!')
                continue

            results = []
            for c in queries:
                m = pow(c, d, N)
                for i, (lower, upper) in enumerate(intervals):
                    in_interval = lower < m < upper
                    if in_interval:
                        results.append(i)
                        break
                else:
                    results.append(-1)

            print(','.join(map(str, results)), flush=True)

            time.sleep(MAX_INTERVALS * (MAX_QUERIES // N_BITS - 1))
        elif choice == 3:
            secret_guess = int(input('Enter secret: '))
            if secret == secret_guess:
                print(FLAG)
            else:
                print('Incorrect secret :(')
            exit()

        else:
            print('Invalid choice')


if __name__ == '__main__':
    signal.alarm(TIMEOUT)
    main()

解决办法:对于同样的c,划定m的区间这样就能通过区间确定m的大小,利用二分法每次确定1位。

from pwn import *

p = remote('2022.ductf.dev', 30008)

#context.log_level = 'debug'

N = int(p.recvline())
c = int(p.recvline())

N_BITS = 384
border = [-1, 2<<(N_BITS//9*8)]

def sendborder(low,high):
    p.sendlineafter(b'> ', b'1')
    print(low)
    print(high)
    p.sendlineafter(b'Lower bound: ', str(low).encode())
    p.sendlineafter(b'Upper bound: ', str(high).encode())

def get_v():
    p.sendlineafter(b'> ', b'2')
    p.sendlineafter(b'queries: ', str(c).encode())
    res = int(p.recvline())
    return res 

def get_flag(sec):
    p.sendlineafter(b'> ', b'3')
    p.sendlineafter(b'Enter secret: ', str(sec).encode())
    flag = p.recvline()
    return flag 

for i in range(N_BITS//9*8):
    tb = (border[0]+border[1])//2    #检测下半区是否命中
    sendborder(border[0], tb)
    res = get_v()
    print(i,'RES:' , res)
    if res != 0:                     #命中时返回i==0,其它-1,2,3,4都是未命中
        border[0] = tb-1             #未命中的情况, 值在上半区,更新下边界
    else:
        border[1] = tb               #命中,在本区更新上边界

context.log_level = 'debug'

print(get_flag(border[0]+1))

5 rsa-interval-oracle-ii

第2题在第1题的程序上改动了输入区间的次数为1,也就是只允许输入1次区间。那么这个区间就输入是固定的,难度就象羊了个羊第2关。

我理解是输入一个分数比如4/3m就会落在大于1的范围上,但在有限域上不能输入分数。就得用比较复杂的办法来模拟,但由于精度不够,在最后还需要爆破24位

from pwn import *
from gmpy2 import invert, c_div

p = remote('2022.ductf.dev', 30011)

context.log_level = 'debug'

N = int(p.recvline())
c = int(p.recvline())
e = 0x10001
N_BITS = 384
border = [-1, 2<<(N_BITS//9*8)]

def sendborder(low,high):
    p.sendlineafter(b'> ', b'1')
    #print(low)
    #print(high)
    p.sendlineafter(b'Lower bound: ', str(low).encode())
    p.sendlineafter(b'Upper bound: ', str(high).encode())

def get_v(fc):
    p.sendlineafter(b'> ', b'2')
    p.sendlineafter(b'queries: ', str(fc).encode())
    res = int(p.recvline())
    return res 

def get_flag(sec):
    p.sendlineafter(b'> ', b'3')
    p.sendlineafter(b'Enter secret: ', str(sec).encode())
    flag = p.recvline()
    return flag 


'''
#local 
from os import urandom
from Crypto.Util.number import getPrime, bytes_to_long

N_BITS = 384
p = 0xbe103afbae6d9ef1bf866a9fe44da3a9c3b2818e315de13b
q = 0xf206c05bd9f2cb28822f84874b94b2dbbf0e3e625426c2f5
N = p*q
e = 0x10001
d = pow(e, -1, (p - 1) * (q - 1))
print('d = ', hex(d))
secret = 0x1ad7dbd636f2eb28c75bd0e70a08e6a16a75ae540f3bced8c33e091bd588bf5c406e152431973476f930
c = pow(secret, e, N)
print('N =', hex(N))
print('secret =',hex(secret))

#发送tc 返回是否命中 0命中,-1未命中(本地模拟)
def get_v(tc):
    tm = pow(tc, d, N)
    print(hex(tm).rjust(100, ' '))
    print(hex(border[1]).rjust(100, ' '))
    if border[0] < tm < border[1]:
        return 0
    else:
        return -1

def get_flag(s):
    if s == secret:
        return "flag{test OK}"
    else:
        return "Fail "+hex(secret)
'''

def oracle(f):
    return get_v(c * pow(f,e,N) % N )

#1,secret长度384//9*8=336 边界设置为 B = 336+40
B = 1<<336+40
border = [-1, B]  
sendborder(border[0], border[1])

#2,查询secret到B前0的个数
f1 = 1<<40
while oracle(f1) == 0:
    f1 <<=1
f1>>=1

#3, ((N+B)/B + delta)*f1*m 是否在边界内
f2 = (N + B)//B * f1 
while oracle(f2) == -1:
    f2 += f1 

#4,
low = c_div(N, f2)
high= (N + B)//f2
while high > low:
    tmp = (2*B)//(high - low)
    i = c_div(tmp * low, N)
    f3 = c_div(i*N, low)
    if oracle(f3) == -1:
        low = c_div(i*N+B, f3) 
    else:
        high = (i*N+B)//f3 

#5 test 
assert high - low < (1<<24)
for f4 in range(low, high+1):
    if pow(f4,e,N) == c:
        print(get_flag(f4))
        break

'''
[DEBUG] Received 0x30 bytes:
    b'1. Add interval\n'
    b'2. Request oracle\n'
    b'3. Get flag\n'
    b'> '
[DEBUG] Sent 0x2 bytes:
    b'3\n'
[DEBUG] Received 0xe bytes:
    b'Enter secret: '
[DEBUG] Sent 0x67 bytes:
    b'131648374553052306210268283842344617331957330884679611956409650994486668851589013872428584385213442659\n'
[DEBUG] Received 0x24 bytes:
    b'DUCTF{Manger_w0uld_b3_pr0ud_0f_y0u}\n'
'''

6 rsa-interval-oracle-iii

这个又变了,sleep的时间更长,总timeout变小了,这个有一个工具集lbc_toolkit这里边有个hidden_number_problem 这里输入4个区间,每个差1位,然后输入4700个随机数,当某个数字落在这个区间内就会返回0,1,2,3不在区间内返回-1,当这个题至少50个的时候就可以很快算出m

from pwn import *
#from collections import Counter

# https://github.com/josephsurin/lattice-based-cryptanalysis
from lbc_toolkit import ehnp


def add_interval(lower, upper):
    conn.sendlineafter(b'> ', b'1')
    conn.sendlineafter(b'Lower bound: ', str(lower).encode())
    conn.sendlineafter(b'Upper bound: ', str(upper).encode())

def query_oracle(cts):
    conn.sendlineafter(b'> ', b'2')
    conn.sendlineafter(b'queries: ', ','.join(map(str, cts)).encode())
    r = list(map(int, conn.recvline().decode().split(',')))
    return r

N_BITS = 384
MAX_INTERVALS = 4
MAX_QUERIES = 4700
e = 0x10001

#context.log_level = 'debug'
# conn = process('./rsa-interval-oracle-iii.py')
def go():
    global conn
    
    conn = remote('2022.ductf.dev', 30010)

    N = int(conn.recvline().decode())
    secret_ct = int(conn.recvline().decode())
    print("N = ", N)
    print("secret_ct = ", secret_ct)

    for i in range(8, 8 + MAX_INTERVALS):
        add_interval(0, 1<<(N_BITS - i))


    rs = [randint(1, N) for _ in range(MAX_QUERIES)]
    cts = [pow(r, e, N) * secret_ct for r in rs]
    query_res = query_oracle(cts)   #测试4700个随机数
    #print(Counter(query_res))

    rs_and_Us = [(r, N_BITS - (MAX_INTERVALS - i + 7)) for r, i in zip(rs, query_res) if i != -1]

    ell = len(rs_and_Us)
    print('ell:', ell)
    if ell < 50:
        conn.close()
        print("Fail")
        return False
    
    print('rs_and_Us = ', rs_and_Us)
        
    open('data.py', 'w').write(f'ell = {ell}\nsecret_ct = {secret_ct}\nN = {N}\nrs_and_Us = {rs_and_Us}')
    print(f'e = {e}\nell = {ell}\nsecret_ct = {secret_ct}\nN = {N}\nrs_and_Us = {rs_and_Us}')
    conn.sendlineafter(b'> ', b'3')
    conn.interactive()
    '''
    xbar = 0
    Pi = [0]
    Nu = [336]
    Alpha = [r for r, _ in rs_and_Us]
    Rho = [[1]] * ell
    Mu = [[U] for _, U in rs_and_Us]
    Beta = [0] * ell
    print(xbar, N, Pi, Nu, Alpha, Rho, Mu, Beta)
    
    sol = ehnp(xbar, N, Pi, Nu, Alpha, Rho, Mu, Beta, delta=1/10**22, verbose=True)

    secret = -sol % N
    print('secret = ', secret)
    print('c = ', pow(secret,e,N))
    
    context.log_level = 'debug'
    conn.sendlineafter(b'> ', b'3')
    conn.sendlineafter(b'Enter secret: ', str(secret).encode())
    flag = conn.recvline().decode()
    print(flag)
    if 'DUCTF' in flag:
        conn.close()
        return True
    '''

while not go():
    pass

#DUCTF{rsa_1nt3rv4l_0r4cl3_1s_n0_m4tch_f0r_y0u!}

由于网站运行很慢,程序到最后提交的时候总是报错,我把运算的部分单出来,到生成超过50时,由另外一个程序运行,最后再把结果粘回来。

from lbc_toolkit import ehnp
from data import *

e = 0x10001
xbar = 0
Pi = [0]
Nu = [336]
Alpha = [r for r, _ in rs_and_Us]
Rho = [[1]] * ell
Mu = [[U] for _, U in rs_and_Us]
Beta = [0] * ell
print(xbar, N, Pi, Nu, Alpha, Rho, Mu, Beta)

sol = ehnp(xbar, N, Pi, Nu, Alpha, Rho, Mu, Beta, delta=1/10**22, verbose=True)

secret = -sol % N
print('secret = ', secret)
print('c = ', pow(secret,e,N))

7 rsa-interval-oracle-iv

这个题在上题基础上给了固定的区间,因为都是预期用同一个程序解,所以区间跟上一题一样。只不过不用输入而已,不过这题用那个官方程序和上边验证过的都试过,结果不正确。不知道原因。

8 time-locked

这个到最后也没看懂,先保留一下吧。应该是关于矩阵快速幂的,基本上纯数学问题。给的是一个可以出flag的程序,但由于n非常大,每次减1 不可能实现。这就需要一个简化的方法。

from hashlib import sha256
from Crypto.Util.Padding import unpad
from Crypto.Cipher import AES


ct = bytes.fromhex('85534f055c72f11369903af5a8ac64e2f4cbf27759803041083d0417b5f0aaeac0490f018b117dd4376edd6b1c15ba02')


p = 275344354044844896633734474527970577743
a = [2367876727, 2244612523, 2917227559, 2575298459, 3408491237, 3106829771, 3453352037]
α = [843080574448125383364376261369231843, 1039408776321575817285200998271834893, 712968634774716283037350592580404447, 1166166982652236924913773279075312777, 718531329791776442172712265596025287, 766989326986683912901762053647270531, 985639176179141999067719753673114239]


def f(n):
    if n < len(α):
        return α[n]

    n -= len(α) - 1
    t = α[::-1]
    while n > 0:
        x = sum([a_ * f_ for a_, f_ in zip(a, t)]) % p
        t = [x] + t[:-1]
        n -= 1

    return t[0]


n = 2**(2**1337)
key = sha256(str(f(n)).encode()).digest()
aes = AES.new(key, AES.MODE_ECB)
flag = unpad(aes.decrypt(ct), 16)
print(flag.decode())

官方结果

from Crypto.Util.number import long_to_bytes
from hashlib import sha256
from Crypto.Util.Padding import unpad
from Crypto.Cipher import AES

ct = bytes.fromhex('85534f055c72f11369903af5a8ac64e2f4cbf27759803041083d0417b5f0aaeac0490f018b117dd4376edd6b1c15ba02')

p = 275344354044844896633734474527970577743
a = [2367876727, 2244612523, 2917227559, 2575298459, 3408491237, 3106829771, 3453352037]
α = [843080574448125383364376261369231843, 1039408776321575817285200998271834893, 712968634774716283037350592580404447, 1166166982652236924913773279075312777, 718531329791776442172712265596025287, 766989326986683912901762053647270531, 985639176179141999067719753673114239]

M = Matrix(GF(p), [a])
M = M.stack(Matrix.identity(len(a) - 1).augment(Matrix.column([0] * (len(a) - 1))))
order = prod([p^len(a) - p^k for k in range(0, len(a))])
n = int(pow(2, 2^1337, order)) - 6
fn = (M^n * vector(α[::-1]))[0]

key = sha256(str(fn).encode()).digest()
aes = AES.new(key, AES.MODE_ECB)
flag = unpad(aes.decrypt(ct), 16)
print(flag.decode())

虽然题都不会,不过把sage安上了,可以直接在sage里from pwn import *了,还算是有收获的。

马上放假了,可以有时间把pwn都过一遍了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值