道隐于小成,言隐于荣华

Teaser CONFidence CTF 2019 Crypto WriteUp

该部分为本科期间wp备份。

Count me in!

题目

I've been lately using this cool AES-CTR, but it was super slow, so I made a parallel version. Now it's blazing fast, but for some reason I have trouble decrypting the data...

count_me_in.tar.gz

The flag format is: p4{letters_digits_and_special_characters}.

解题过程

打开提供的压缩包,发现里面有两个文件,一个是output.txt,一个是count.py
打开count.py,代码如下:

import multiprocessing

from Crypto.Cipher import AES

from secret import key, flag

counter = 0
aes = AES.new(key, AES.MODE_ECB)


def chunk(input_data, size):
    return [input_data[i:i + size] for i in range(0, len(input_data), size)]


def xor(*t):
    from functools import reduce
    from operator import xor
    return [reduce(xor, x, 0) for x in zip(*t)]


def xor_string(t1, t2):
    t1 = map(ord, t1)
    t2 = map(ord, t2)
    return "".join(map(chr, xor(t1, t2)))


def pad(data):
    pad_byte = 16 - len(data) % 16
    return data + (chr(pad_byte) * pad_byte)


def worker_function(block):
    global counter
    key_stream = aes.encrypt(pad(str(counter)))
    result = xor_string(block, key_stream)
    counter += 1
    return result


def distribute_work(worker, data_list, processes=8):
    pool = multiprocessing.Pool(processes=processes)
    result = pool.map(worker, data_list)
    pool.close()
    return result


def encrypt_parallel(plaintext, workers_number):
    chunks = chunk(pad(plaintext), 16)
    results = distribute_work(worker_function, chunks, workers_number)
    return "".join(results)


def main():
    plaintext = """The Song of the Count

You know that I am called the Count
Because I really love to count
I could sit and count all day
Sometimes I get carried away
I count slowly, slowly, slowly getting faster
Once I've started counting it's really hard to stop
Faster, faster. It is so exciting!
I could count forever, count until I drop
1! 2! 3! 4!
1-2-3-4, 1-2-3-4,
1-2, i love couning whatever the ammount haha!
1-2-3-4, heyyayayay heyayayay that's the sound of the count
I count the spiders on the wall...
I count the cobwebs in the hall...
I count the candles on the shelf...
When I'm alone, I count myself!
I count slowly, slowly, slowly getting faster
Once I've started counting it's really hard to stop
Faster, faster. It is so exciting!
I could count forever, count until I drop
1! 2! 3! 4!
1-2-3-4, 1-2-3-4, 1,
2 I love counting whatever the
ammount! 1-2-3-4 heyayayay heayayay 1-2-3-4
That's the song of the Count!
""" + flag
    encrypted = encrypt_parallel(plaintext, 32)
    print(encrypted.encode("hex"))


if __name__ == '__main__':
    multiprocessing.freeze_support()
    main()

仔细观察这个代码,我们可以很明显地知道这是一个类似于AES(CTR)的加密。但是这里用到了多线程进行加密。

我们考虑多线程,加密脚本中有个函数引起我们的注意:

def worker_function(block):
    global counter
    key_stream = aes.encrypt(pad(str(counter)))
    result = xor_string(block, key_stream)
    counter += 1
    return result

由于为一个明文块分配一个工作程序,因此并行运行的所有工作程序将使用相同的计数器,即并行运行的工作程序使用了相同的密钥。

此外,题目提供了大量的明文对与密文对,因此我们可以利用这些来恢复随机数。同时,flag部分可能就是异或相同的密钥块,并且密钥块的先前的块已被异或。但是我们并不知道哪部分使用了,所以我们必须尝试所有的密钥块。我们将所有可能的密钥块与密文部分内容进行异或处理,得到的正常字母部分就留下来并拼接在一起,就出现了flag。

注意:请在linux下执行代码,windows 10下可能跑不出来或者跑出来flag不完整orz。

python脚本如下:

import string
pt="""he Song of the Count

You know that I am called the Count
Because I really love to count
I could sit and count all day
Sometimes I get carried away
I count slowly, slowly, slowly getting faster
Once I've started counting it's really hard to stop
Faster, faster. It is so exciting!
I could count forever, count until I drop
1! 2! 3! 4!
1-2-3-4, 1-2-3-4,
1-2, i love couning whatever the ammount haha!
1-2-3-4, heyyayayay heyayayay that's the sound of the count
I count the spiders on the wall...
I count the cobwebs in the hall...
I count the candles on the shelf...
When I'm alone, I count myself!
I count slowly, slowly, slowly getting faster
Once I've started counting it's really hard to stop
Faster, faster. It is so exciting!
I could count forever, count until I drop
1! 2! 3! 4
1-2-3-4, 1-2-3-4, 1,
2 I love counting whatever the
ammount! 1-2-3-4 heyayayay heayayay 1-2-3-4
That's the song of the Count!
"""
ct = "9d5c66e65fae92af9c8a55d9d3bf640e8a5b76a878cbf691d3901392c9b8760ebd5c62b22c88dca9d1c55098cbbb644ae9406ba32c8293bdd29139bbc2b4605bba51238f2cb399a9d0894ad9cbb8774be9406ce66fae89a6c8ef7ad9c4b87442ad1470af78e19da6d8c55096d2b9750ea8586fe668a085c2ef8a5e9cd3be6c4bba144ae66ba488e8df84418bceb2650ea84362bf0688dcabd3905d8d87a46d414626be04a58215b84263a620de3203fa4626be08e2940da35c61b82c98201ce15438cd67eb921cf77c28a969de321bf4433ea24ca59216a25b7bb662996106e11639e75ae09015bb4c2fb76d8c254fe15e6ab45cea817391547cab698c6d4ff35039b34df7df599e412fb67fde3200b55432a441f19817b01405962c9d2e1af9556aa447f09f0df75360ad6988241db91129a85deb8559a25b7bb660de084ff1cc3a0e8041bc71d71fb29883931d9d3e8f784ca743b065c91ea386909e1a9100925f4fa742b1718c1efec4d4d609df5bcb3b17e417bd268d5fe6ced4d65b9c40d6305eeb1df03e9050e68bcad241dd15b46453b85dae7cd112b2c3c7ca50dd4ddf2c1ff350f5349c5febcadbd2509c40d6340aad03bd258d5bb2d8cdc647d814d1335efe18f8718651e7c5d6b9609c57d12010fe50e939801ee1dbcbd74cce4739c1eeceba8ef87360b629ed82a6eb9d508ee381bb88e97363bf20a1cfe7a7e07cccf3cea788bd277fb265e9cde4a9b937808aa7ee85f22679a365f5c4ede5f478c0e482ab95bd3c79f731e9c9a8b6ff7cc2e6c0e0c897047fb22ba1e5afa8b778c2ef80abcabd1a37b42af4c2fce5fa60dde582a8c7971a37b42af4c2fce5e475c1f782b7cabd207bb832edd5a4e5e4130a976ad4a2fe86ac99c18491f9f6c86adae59cc4a9f33072f70ca6daede5e40b049272c8e6b980b798c69e9fb7f7891611c7758df0fc82b481d1ca9eb8e2cd5f118f26def6f693d2abc99982bce2855f038175d9e7ebcdf8a4dcca9faab0da1045857eceebed8ab68a89e0bff9f3c60a098426ceedec8daccdce8584bce6cc0d49c065c2f7f797f898c69e9fb5b0e05f019269dd88a8c2f8df89cac5f8b09d00ad16dc760ead7c3518baed47603c5b5251cc269cae93d1f8a4888699aff58942c8529f304af0362143f2bd1e37670d538753992129ff3c6c5befb21e7331590c950ac26917be39644dfba50b2b70117fcbccba57b209ae95721a1c9d36073d934b75014109102be14fb6a044a7cf9e468748976457f6342177f5a9042630622f97d2ba5a8c04a7890d4e5fcb445b7600d7c1be71b711b6b32b4444f078557ef82e4f0559225437b455aa9a0bbaff89c834531a45115125c933d6cd6cdca8f8".decode('hex')

chars = string.ascii_lowercase+string.digits+"{}_"

def chunk(input_data, size):
    return [input_data[i:i + size] for i in range(0, len(input_data), size)]

def xor(a,b):
    from itertools import cycle
    return ''.join(chr(ord(i)^ord(j)) for i,j in zip(a,cycle(b)))

# 可能的密钥
k = xor(ct,pt)[:len(pt)]
keys = chunk(k,16)

ctflag = ct[-64:]
flag = ''
for key in set(keys):
    out = chunk(xor(ctflag,key),16)
    for i in out:
        if all(char in chars for char in i[:8]):
            flag+=i
print flag

得到flag:

p4

Bro, do you even lift?

题目

So I just rolled out my own crypto...

bo_do_u_even_lift.tar.gz

The flag format is: p4{letters_digits_and_special_characters}.

解题过程

打开压缩包,发现里面有个sage文件描述加密过程:

flag = int(open('flag.txt','r').read().encode("hex"),16)
ranges = int(log(flag,2))
p = next_prime(ZZ.random_element(2^15, 2^16))
k = 100
N = p^k
d = 5
P.<x> = PolynomialRing(Zmod(N), implementation='NTL')
pol = 0
for c in range(d):
    pol += ZZ.random_element(2^ranges, 2^(ranges+1))*x^c
remainder = pol(flag)
pol = pol - remainder
assert pol(flag) == 0

print(p)
print(pol)

看下out.txt:

35671
12172655049735206766902704703038559858384636896299329359049381021748*x^4 + 11349632906292428218038992315252727065628405382223597973250830870345*x^3 + 9188725924715231519926481580171897766710554662167067944757835186451*x^2 + 8640134917502441100824547249422817926745071806483482930174015978801*x + 170423096151399242531943631075016082117474571389010646663163733960337669863762406085472678450206495375341400002076986312777537466715254543510453341546006440265217992449199424909061809647640636052570307868161063402607743165324091856116789213643943407874991700761651741114881108492638404942954408505222152223605412516092742190317989684590782541294253512675164049148557663016927886803673382663921583479090048005883115303905133335418178354255826423404513286728

根据sage中内容,我们转化代码为数学公式,得到以下方程:

\[0 \equiv ax^4+bx^3+cx^2+dx \equiv e \mod N \]

\[N=p^{100} \quad p=35671 \]

因此,求解方程的根就是求解flag。

为了解决这个方程,我们先根据给定的多项式,模上p,之后使用亨泽尔引理,根据 mod p^k= mod N找到给定多项式的一个解。

关于亨泽尔引理

f(x)为整系数多项式,k为不少于2的整数,p为质数。若整数r是下面同余式的根:
\(f(r)\equiv0\mod {p^{k-1}}\)
对于\(f(r+tp^{k-1})\equiv0 \mod{p^k}\)(I),有:

  1. \(f'(r)\not\equiv0\mod{p}\),则存在唯一的整数\({0}\le{t}\le{p-1}\)存在。
  2. \(f'(r)\equiv0\mod{p}\)\(f(r)\equiv0\mod{p^k}\),则(I)对任意整数t成立。
  3. \(f'(r)\equiv0\mod{p}\)\(f(r)\not\equiv0\mod{p^k}\),则(I)无整数解。

首先找mod p的根:

p = 35671
k = 100
P. = PolynomialRing(Zmod(p), implementation='NTL')
f = 12172655049735206766902704703038559858384636896299329359049381021748*x^4 + 11349632906292428218038992315252727065628405382223597973250830870345*x^3 + 9188725924715231519926481580171897766710554662167067944757835186451*x^2 + 8640134917502441100824547249422817926745071806483482930174015978801*x + 170423096151399242531943631075016082117474571389010646663163733960337669863762406085472678450206495375341400002076986312777537466715254543510453341546006440265217992449199424909061809647640636052570307868161063402607743165324091856116789213643943407874991700761651741114881108492638404942954408505222152223605412516092742190317989684590782541294253512675164049148557663016927886803673382663921583479090048005883115303905133335418178354255826423404513286728
print f.monic().roots()

得到结果为:

[27020, 25020]

之后,使用亨泽尔引理,求解mod N的根。

脚本如下,根据sagemath社区内提供的脚本解决。

w = Integer(35671) ; q = 5 ; R = Zp(w,q) ; print 'Working in ring ',R

# 定义多项式函数
f(x) = 12172655049735206766902704703038559858384636896299329359049381021748*x^4 + 11349632906292428218038992315252727065628405382223597973250830870345*x^3 + 9188725924715231519926481580171897766710554662167067944757835186451*x^2 + 8640134917502441100824547249422817926745071806483482930174015978801*x + 170423096151399242531943631075016082117474571389010646663163733960337669863762406085472678450206495375341400002076986312777537466715254543510453341546006440265217992449199424909061809647640636052570307868161063402607743165324091856116789213643943407874991700761651741114881108492638404942954408505222152223605412516092742190317989684590782541294253512675164049148557663016927886803673382663921583479090048005883115303905133335418178354255826423404513286728

# 用F1表示f的1阶导数,用F0表示原来的f
F0 = f.diff(0) ; F1 = f.diff(1)

# 令 f(x) = 0,并找到一个解 
r0 = Integer(27020) ; f0r0 = F0(x=r0) ; f1r0 = F1(x=r0)
print 'Is ',r0,' one local solution ? ', (Mod(f0r0,w) == 0) and (Mod(f1r0,w) != 0)

# 从旧的式子中继续循环地找新的解
rk = r0 ; wk = Integer(1)

power = 100

for k in range(power-1):  
    print ; print '********** Step ',k,'***********' ; print

    # 计算第k次的乘方
    wk = w * wk

    # 计算F0(rk)以及F1(rk)的值
    f0rk = Integer(F0(x=rk)) ;# print 'F0(rk) = ', f0rk 
    f1rk = Integer(F1(x=rk)) ;# print 'F1(rk) = ', f1rk 

    # 最重要的部分: 
    #  - f0rk / wk is "Integer" division, removing the w^k factor
    #  - negative (-), multiply and inverse_mod operation in ring R
    t = Integer(f0rk / wk) * inverse_mod(-f1rk,w) ; print t
    rk = rk + Integer(t*wk) ; print rk
    rk = Integer(Mod(rk, wk*w)) # ADDED MODULO

print; print "sol", hex(rk);

得到hex值为:

70347b54683474355f35306d335f68333476795f6c316674316e397d

转换hex为ascii,得到flag:

p4

Really Suspicious Acronym

题目

You can't break my public key if you don't know it, amirite?

really_suspicious_acronym.tar.gz

The flag format is: p4{letters_digits_and_special_characters}.

解题过程

打开提供的task.sage

def bytes_to_long(data):
    return int(data.encode("hex"),16)

def rsa(msg,e,n):
    return pow(bytes_to_long(msg),e,n)

flag = open('flag.txt','r').read()
tmp = randint(2**1023, 2**1024)
e = 65537
p = next_prime(0xDEAD*tmp+randint(2, 2**500))
q = next_prime(0xBEEF*tmp+randint(2, 2**500))
N =  p*q
print('msg1 = '+str(rsa("You can't factor the modulus",e,N)))
print('msg2 = '+str(rsa("If you don't know the modulus!",e,N)))
print('flag = '+str(rsa(flag,e,N)))

打开output.txt

msg1 = 4249729541274832324831915101850978041491970958978013333892918723306168770472089196478720527554982764987079625218029445015042835412969986610407794962546486526768377464483162272541733624521350257458334912357961557141551376502679112069746250223130120067678503609054343306910481618502449487751467838568736395758064426403381068760701434585433915614901796040740316824283643177505677105619002929103619338876322183416750542848507631412106633630984867562243228659040403724671325236096319784525457674398019860558530212905126133378508676777200538275088387251038714220361173376355185449239483472545370043145325106307606431828449482078191
msg2 = 13075855845498384344820257559893309320125843093107442572680776872299102248743866420640323500087788163238819301260173322187978140866718036292385520509724506487692001245730298675731681509412177547061396861961413760298064385526657135656283464759479388590822600747903100354135682624356454872283852822117199641700847558605700370117557855396952083088645477966782338316017387406733063346986224014837246404581562813312855644424128648363175792786282857154624788625411070173092512834181678732914231669616670515512774709315620233482515821178277673737845032672993814500177126048019814877397547310166915188341668439101769932492677363463422
flag = 1325070956009103489249194637347510588506729608784127511926628895543304940415297099207601498626181915901848862854995077315475674257593360012633818395699000501896896712855638114932274873636706679536094148084825113213348693669110684534612150434985589138003619494080556587882502882245480530148296233019306164832959924719530089539412878605051284492900919153291539285764067215954480046474237129247005910958854570936626494664674014970792183182621261776942952172643573955950074108555363333808330455648256916095619261620286120748266415219259665310637340092503523139379869446053982200858497231506892485419429178671743186148288407233657

根据加密代码分析,得到以下结论:

  1. 本题没有提供公共模数
  2. n并不是素数而是合数

因此我们需要先找出公共模数

\[m^e_1\equiv c_1\mod{n} \]

\[m^e_2\equiv c_2\mod{n} \]

从而得到:

\[m^e_1-c_1={k_1*n} \]

\[m^e_2-c_2={k_2*n} \]

\[gcd({m^e_1-c_1},{m^e_2-c_2}) = gcd({k_1}*n,{k_2}*n) \]

因此,可以得到结果:

\[gcd({m^e_1-c_1},{m^e_2-c_2}) = {k'*n} \]

note:n应该很小

之后我们需要求解pq。观察以下式子:

\[n = p * q \]

\[p = x_1 * y + a \]

\[q = x_2 * y + b \]

根据上式,我们可以得到以下结果:

\[p' = \sqrt{n * \frac{x_1}{x_2}} \approx \sqrt{n * \frac{p}{q}} = p \]

ab都是500bit长度,而pq是1024bit长度,因此p'p相差大约500bit,相比较于1024bit已经很小了,因此我们使用Coppersmith理论寻找p。sagemath脚本如下:


def bytes_to_long(data):
    return int(data.encode("hex"),16)


msg1 = 4249729541274832324831915101850978041491970958978013333892918723306168770472089196478720527554982764987079625218029445015042835412969986610407794962546486526768377464483162272541733624521350257458334912357961557141551376502679112069746250223130120067678503609054343306910481618502449487751467838568736395758064426403381068760701434585433915614901796040740316824283643177505677105619002929103619338876322183416750542848507631412106633630984867562243228659040403724671325236096319784525457674398019860558530212905126133378508676777200538275088387251038714220361173376355185449239483472545370043145325106307606431828449482078191
msg2 = 13075855845498384344820257559893309320125843093107442572680776872299102248743866420640323500087788163238819301260173322187978140866718036292385520509724506487692001245730298675731681509412177547061396861961413760298064385526657135656283464759479388590822600747903100354135682624356454872283852822117199641700847558605700370117557855396952083088645477966782338316017387406733063346986224014837246404581562813312855644424128648363175792786282857154624788625411070173092512834181678732914231669616670515512774709315620233482515821178277673737845032672993814500177126048019814877397547310166915188341668439101769932492677363463422
flag = 1325070956009103489249194637347510588506729608784127511926628895543304940415297099207601498626181915901848862854995077315475674257593360012633818395699000501896896712855638114932274873636706679536094148084825113213348693669110684534612150434985589138003619494080556587882502882245480530148296233019306164832959924719530089539412878605051284492900919153291539285764067215954480046474237129247005910958854570936626494664674014970792183182621261776942952172643573955950074108555363333808330455648256916095619261620286120748266415219259665310637340092503523139379869446053982200858497231506892485419429178671743186148288407233657

pla1 = bytes_to_long("You can't factor the modulus")
pla2 = bytes_to_long("If you don't know the modulus!")

e = 65537
n = None
# n = 34825223743402829383680359547814183240817664070909938698674658390374124787235739502688056639022131897715513587903467527066065545399622834534513631867145432553730850980331789931667370903396032758515681278057031496814054828419443822343986117760958186984521716807347123949922837482460532728350223473430713058522361175980521908817215812291272284241848086260180382693014713901303747444753828636575351349026883294939561001468099252543181336195746032718177937417431101756313823635150129601855358558635996348271242920308406268552606733676301725088348399264293936151662467456410825402303921583389167882090767423931762347825907802328053
if n is None:
    n1 = pla1^e - msg1
    n2 = pla2^e - msg2
    n = gcd(n1, n2)

x1, x2 = 0xDEAD, 0xBEEF
r = x1 / x2

nn = RealField(2000)(n)
pp = int(sqrt(nn*r))  # p', close to p

F.<x> = PolynomialRing(Zmod(n), implementation='NTL'); 
pol = x - pp

beta = 0.4  # p >= n^beta
X = 2**500 # |pp - p| < X

roots = pol.small_roots(X=X, beta=beta)
print 'roots:', roots

p = int(pp - roots[0])
assert n % p == 0
print 'p: ', p

q = n // p
d = inverse_mod(e, (p-1)*(q-1))
flag_plain = int(pow(flag, d, n))
print 'flag: ', hex(flag_plain).lstrip('0x').rstrip('L').decode('hex')

得到以下结果:

roots: [34825223743402829383680359547814183240817664070909938698674658390374124787235739502688056639022131897715513587903467527066065545399622834534513631867145432553730850980331789931667370903396032758515681278057031496814054828419443822343986117760958186984521716807347123949922837482460532728350223473430713058522361175980521908817215812291272284241848086260180382693014713901303747444753828636575351349026883294939561001468099252543181336195746032718177937417431101756313823635149763023484357085219517415581001344943507006473057280568174481303816491696640925644996241911890856522174960396648700992744971745478374321777231114554900]
p:  6372975905868416117412643271076290098029632484472136455229293621574312002464099565227503018906562788149851932258813264092504143029195579682715201332927910081770552791548738518755413161806001832406651207088257424369670382480988120894685280764472916242514475060539101279784773630617990388487509349287688057345180529
flag:  p4{S3cur1ty_th0ru9h_0b5cur1ty_4t_i7s_fin3s7}

参考

  1. bi0s
  2. justCatTheFish_
posted @ 2022-05-06 16:27  FrancisQiu  阅读(13)  评论(0)    收藏  举报