第一周在忙保研的事情所以没打。现在发现第一周的题都没法交了,就从第二周的开始写了。
算是我打过比较顺的一个CTF了,对我的水平来说刚刚好,赞一个。
Crypto
Affine
加密代码:
from secret import flagfrom Crypto.Util.number import * a = getPrime(8 ) b = getPrime(8 ) ciphertext = []for f in flag: ciphertext.append((a*f + b) % 0x100 )print (bytes (ciphertext))
输出:
1 b "\xb1\x83 \x82 T\x10 \x80 \xc9O\x84 \xc9<\x0f\xf2\x82 \x9a\xc9\x9b8'\x9b<\xdb\x9b\x9b\x82 \xc8\xe0V"
a,b的大小很小,直接爆破即可。简单的仿射变换。只需要计算一下a的逆即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import itertoolsfrom Crypto.Util.number import *import base64 data = b"\xb1\x83\x82T\x10\x80\xc9O\x84\xc9<\x0f\xf2\x82\x9a\xc9\x9b8'\x9b<\xdb\x9b\x9b\x82\xc8\xe0V" prime_set = []for i in range (2 **7 , 2 **8 -1 ): if isPrime(i): prime_set.append(i)def decode (data, a, b, m ) : try : a_ = inverse(a,m) except : return b"" plaintext = [] for i in data: plaintext.append(((i-b)*a_) % m) return bytes (plaintext)for param in itertools.product(*(prime_set,prime_set)): if len (str (decode(data, param[0 ], param[1 ], 0x100 ))) < 35 : print (decode(data, param[0 ], param[1 ], 0x100 ))
unusual_base
加密代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from secret import flagfrom Crypto.Util.number import *from random import shufflefrom string import ascii_lowercase, ascii_uppercase, digits alphabet = ascii_uppercase + ascii_lowercase + digits +'$&' alphabet = list (alphabet) bits = '' pad_len = len (flag) % 3 for f in flag: bits += bin (f)[2 :].rjust(8 ,'0' ) bits += '0000' *pad_len encoded = '' shuffle(alphabet) alphabet = "" .join(alphabet)for i in range (0 , len (bits), 6 ): encoded += alphabet[int (bits[i:i+6 ], 2 )] encoded += '%' *pad_lenprint (f'encoded = "{encoded} "' )print (f'alphabet = "{alphabet} "' )
输出:
1 2 encoded = "GjN3G$B3de58ym&7wQh9dgVNGQhfG2hndsGjlOyEdaxRFY%" alphabet = "c5PKAQmgI&qSdyDZYCbOV2seXGloLwtFW3f9n7j481UMHBp6vNETRJa$rxuz0hik"
alphabet的长度是64,所以是base64换表,这里这个百分号要去掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import base64import stringfrom libnum import b2s str1 = "GjN3G$B3de58ym&7wQh9dgVNGQhfG2hndsGjlOyEdaxRFY" char_set = "c5PKAQmgI&qSdyDZYCbOV2seXGloLwtFW3f9n7j481UMHBp6vNETRJa$rxuz0hik" data = "" for i in str1: data += str (bin (char_set.rfind(i))[2 :]).zfill(6 ) data = data[:(len (data)//8 )*8 ]print (b2s(data))
robot
加密脚本与输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from hashlib import sha256from secret import flagfrom base64 import *import random cipher = []def fun1 (x ): return sha256(x.encode()).hexdigest()def fun2 (x ): return pow (114514 ,ord (x),1919810 )def fun3 (x ): key = random.randint(0 ,1145141919810 ) ans = x.encode() if key & 1 : ans = b32encode(ans) key >>= 1 if key & 1 : ans = b64encode(ans) key >>= 1 if key & 1 : ans = b16encode(ans) key >>= 1 return ansdef encrypt (msg ): res = [] for i in msg: tmp = list (map (random.choice([fun1,fun2,fun3]),[i]))[0 ] res.append(tmp) return resprint (encrypt(flag))''' ['252f10c83610ebca1a059c0bae8255eba2f95be4d1d7bcfa89d7248a82d9f111', 1495846, 1452754, b'M4======', '021fb596db81e6d02bf3d2586ee3981fe519f275c0ac9ca76bbcf2ebb4097d96', '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6', '4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a', b'Tg==', '1b16b1df538ba12dc3f97edbb85caa7050d46c148134290feba80f8236c83db9', b'52304539505430395054303D', 'e3b98a4da31a127d4bde6e43033f66ba274cab0eb7eb1c70ec41402bf6273dd8', b'58773D3D', '3f39d5c348e5b79d06e842c114e6cc571583bbf44e4b0ebfda1a01ec05745d43', '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce', '2e7d2c03a9507ae265ecf5b5356885a53393a2029d241394997265a1a25aefc6', b'T0k9PT09PT0=', '18f5384d58bcb1bba0bcd9e6a6781d1a6ac2cc280c330ecbab6cb7931b721552', b'T0E9PT09PT0=', 825026, 'd2e2adf7177b7a8afddbc12d1634cf23ea1a71020f6a1308070a16400fb68fde', 1455816, b'4F553D3D3D3D3D3D', 1165366, 1242964, b'4F493D3D3D3D3D3D', 652094, 597296, '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce', '4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a', b'54314539505430395054303D', 1242964, 368664, b'TVU9PT09PT0=', b'cw==', 1602214] '''
根据每一项的类型进行爆破即可,也就base那边麻烦点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from sympy import *from base64 import *from hashlib import sha256from string import * data = ...def generateHashMap (): return {sha256(x.encode("utf-8" )).hexdigest():x for x in ascii_letters + digits + "{}_" }def generateDiscreteLog (): return {pow (114514 ,ord (x),1919810 ):x for x in ascii_letters + digits + "{}_" }def generateBaseEncode (): res = {} for i in ascii_letters + digits + "{}_" : for j in range (8 ): tmp = i.encode("utf-8" ) if j & 1 : tmp = b32encode(tmp) if (j >> 1 ) & 1 : tmp = b64encode(tmp) if (j >> 2 ) & 1 : tmp = b16encode(tmp) res[tmp] = i return res def decode (unit ): hashMap = generateHashMap() DiscreteLog = generateDiscreteLog() BaseEncode = generateBaseEncode() if type (unit) is str : return hashMap[unit] if type (unit) is int : return DiscreteLog[unit] if type (unit) is bytes : return BaseEncode[unit] res = []for i in data: res.append(decode(i))print ("" .join(res))
代码写的烂,但我不想改。反正也不是什么太大运算量。
ezRabin
加密脚本与输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from Crypto.Util.number import *from somewhere_you_do_not_know import flagdef ezprime (n ): p=getPrime(n) while p%4 !=3 : p=getPrime(n) return p p=ezprime(512 ) q=ezprime(512 ) n=p*q m=bytes_to_long(flag) m=(m<<(300 ))+getRandomNBitInteger(300 )assert m**2 >n and m<n c=pow (m,4 ,n)print ('c=' ,c)print ('p=' ,p)print ('q=' ,q)''' c= 59087040011818617875466940950576089096932769518087477304162753047334728508009365510335057824251636964132317478310267427589970177277870220660958570994888152191522928881774614096675980017700457666192609573774572571582962861504174396725705862549311100229145101667835438230371282904888448863223898642183925834109 p= 10522889477508921233145726452630168129218487981917965097647277937267556441871668611904567713868254050044587941828674788953975031679913879970887998582514571 q= 11287822338267163056031463255265099337492571870189068887689824393221951058498526362126606231275830844407608185240702408947800715624427717739233431252556379 就要花里胡哨( '''
这里Rabin其实就是e=2的RSA,但这里他用的e是4,所以解两个Rabin即可。一层rabin有4个解,所以两层就有16个解,从里面找有意义的即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from gmpy2 import *from libnum import *from Crypto.Util.number import * c= ... p= ... q= ... n = p*qdef Rabin_decrypt (c, p, q ) : n = p*q m_p = pow (c, (p+1 )//4 , p) m_q = pow (c, (q+1 )//4 , q) _, y_p, y_q = gcdext(p, q) m = [] m.append((y_p*p*m_q + y_q*q*m_p)%n) m.append(n - m[-1 ]) m.append((y_p*p*m_q - y_q*q*m_p) % n) m.append(n - m[-1 ]) return m res = []for i in Rabin_decrypt(c,p,q): res += Rabin_decrypt(i,p,q)print (res)for i in res: print (n2s(int (i >> 300 ))) """ ... b'flag{R4bin_3ncrypti0n_with_3_mod_4_is_two_e4sy}' ... """
ezPRNG
加密脚本与输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from Crypto.Util.number import * flag = b'xxxxxxxxxxxxxxxxxxxx' class my_prng_lcg : def __init__ (self, seed, multiplier, increment, modulus ): self.state = seed self.multiplier = multiplier self.increment = increment self.modulus = modulus def random (self ): self.state = (self.state * self.multiplier + self.increment) % self.modulus return self.state PRNG = my_prng_lcg(bytes_to_long(flag), getRandomInteger(256 ), getRandomInteger(256 ), getRandomInteger(256 )) gift = []for i in range (6 ): gift.append(PRNG.random()) qwq = bytes_to_long(flag)print (qwq.bit_length())print (gift)
这题直接套脚本就行了,很标准的LCG题。
如果想要看原理的,这里https://www.dazhuanlan.com/oec2003/topics/1015881或者https://blog.csdn.net/superprintf/article/details/108964563。有些文章我也不知道他们是从哪里搬的,所以我没法给出最初的原网址,这里见谅。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from math import gcdfrom Crypto.Util.number import *from functools import reducefrom libnum import n2s Xn = ...def crack_unknown_modulus (states ): diffs = [s1 - s0 for s0, s1 in zip (states, states[1 :])] zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip (diffs, diffs[1 :], diffs[2 :])] modulus = abs (reduce(gcd, zeroes)) return crack_unknown_multiplier(states, modulus)def crack_unknown_multiplier (states, modulus ): multiplier = (states[2 ] - states[1 ]) * inverse(states[1 ] - states[0 ], modulus) % modulus return crack_unknown_increment(states, modulus, multiplier)def crack_unknown_increment (states, modulus, multiplier ): increment = (states[1 ] - states[0 ]*multiplier) % modulus return modulus, multiplier, increment n,a,b = crack_unknown_modulus(Xn) m = (Xn[0 ] - b)*inverse(a, n) % nprint (n2s(m))
Prof. Shamir’s Secret
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from Crypto.Util.number import *from secret import flag a = getPrime(256 ) b = getPrime(256 ) c = getPrime(256 ) d = bytes_to_long(flag) n = getStrongPrime(2048 )def poly (x ): return (a * x ** 3 + b * x ** 2 + c * x + d) % nfor _ in range (4 ): x = getRandomNBitInteger(256 ) print (f'({x} , {poly(x)} )' )print (n)
看到这个多项式还有shamir,一眼秘密共享。
输出给了4项,可以直接重构多项式。
M = ( 1 x 1 x 1 2 x 1 3 1 x 2 x 2 2 x 2 3 1 x 3 x 3 2 x 3 3 1 x 4 x 4 2 x 4 3 ) , Y = ( r e s 1 r e s 2 r e s 3 r e s 4 ) M =
\begin{pmatrix}
1 \ x_1 \ x_1^2 \ x_1^3 \\
1 \ x_2 \ x_2^2 \ x_2^3 \\
1 \ x_3 \ x_3^2 \ x_3^3 \\
1 \ x_4 \ x_4^2 \ x_4^3 \\
\end{pmatrix} , \ \ \
Y =
\begin{pmatrix}
res_1 \\ res_2 \\ res_3 \\ res_4
\end{pmatrix}
M = ⎝ ⎜ ⎜ ⎛ 1 x 1 x 1 2 x 1 3 1 x 2 x 2 2 x 2 3 1 x 3 x 3 2 x 3 3 1 x 4 x 4 2 x 4 3 ⎠ ⎟ ⎟ ⎞ , Y = ⎝ ⎜ ⎜ ⎛ r e s 1 r e s 2 r e s 3 r e s 4 ⎠ ⎟ ⎟ ⎞
那么即有:
( 1 x 1 x 1 2 x 1 3 1 x 2 x 2 2 x 2 3 1 x 3 x 3 2 x 3 3 1 x 4 x 4 2 x 4 3 ) ( d c b a ) = ( r e s 1 r e s 2 r e s 3 r e s 4 ) \begin{pmatrix}
1 \ x_1 \ x_1^2 \ x_1^3 \\
1 \ x_2 \ x_2^2 \ x_2^3 \\
1 \ x_3 \ x_3^2 \ x_3^3 \\
1 \ x_4 \ x_4^2 \ x_4^3 \\
\end{pmatrix}
\begin{pmatrix}
d \\ c \\ b \\ a
\end{pmatrix}
=
\begin{pmatrix}
res_1 \\ res_2 \\ res_3 \\ res_4
\end{pmatrix}
⎝ ⎜ ⎜ ⎛ 1 x 1 x 1 2 x 1 3 1 x 2 x 2 2 x 2 3 1 x 3 x 3 2 x 3 3 1 x 4 x 4 2 x 4 3 ⎠ ⎟ ⎟ ⎞ ⎝ ⎜ ⎜ ⎛ d c b a ⎠ ⎟ ⎟ ⎞ = ⎝ ⎜ ⎜ ⎛ r e s 1 r e s 2 r e s 3 r e s 4 ⎠ ⎟ ⎟ ⎞
所以我们构造出矩阵然后求解即可,这里的d就是我们的flag。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from libnum import * n = ... x1, res1 = ... x2, res2 = ... x3, res3 = ... x4, res4 = ... x = [x1, x2, x3, x4] y = [res1, res2, res3, res4] M = Matrix(GF(n), 4 , 4 )for i in range (len (x)): for j in range (len (x)): M[i,j] = x[i]**j Y = vector(GF(n), y) res = M.solve_right(Y)print (n2s(int (res[0 ])))
下面是加密脚本的输出:
1 2 3 4 5 (107156592202708719207677242145785380370925248573491581679548864240229105117413 , 130345771647598884054430192964980389494531690916321281560051538057910945565624075918097771618618910263287152864051564635195578796179646674192491555857366963976329072793625649841007238934532144994966695961491116944111900519450656607199501654544809304677384301432194356761274376314501143216649135187625964931902 ) (90629424458637844580841178302065768114471702341586161908858665404968070428143 , 78858394764644720845979385422903377630845158220853604360871859882044655577246282808874532941560824773914594412415345616068416548364923695233972936176087206729847544516343237888024173952758718279163069742944961359652574962129434781851767007643037433981750489254639449637677610354746497770492254725894119193662 ) (100626477579781167218124067468465940736522526684796828200460725563611057086831 , 107938673826832098883774065383352754899611421173786919174851524067358319831595518533880365335333592351382030254987030861475878447430100862628809476494215295084769705787398168068863060859122952000010558086859754975554734850230223040925027217057055876423229204027280075168615462165634569977166298865366648414270 ) (93935717805931479760310332373603550626215862380271563609987050092246456803681 , 87807687834883656794449107852803757931909462710953942209358337840912886376275257864214018767300085688088981183791568376874906785193974861264511995029891797395218085734556515485224508250678274640400740193260888803386269425525930551167801371074041851406813322268615707951973495879968706624649318162995708734670 )31332583438236375592937719796184754941510418106758544436807128579095975774977164550965999210436423180868482749439792419270701760326867558983833590368116755394302102816558834270767750410927007254951332459412016857259923960095221831744199277859298274645778838122123090174549834537459028702418645316659860963695912411044490603690484176741018002722235584411422885336520840416125528921196994346534698226763483608314982898155320734426983215291745003213365884087604024203316024824786079501166114638727651689476288442288919373885358425210859822108037791909364199015379638899887715692181883916583183449343868694265742569597579
keyExchange
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from secret import flag, gb, g, p, Diffie_Hellman_KEY_EXCHANGEfrom Crypto.Util.number import *from base64 import b64encodefrom Crypto.Cipher import AESfrom hashlib import md5from Crypto.Util.Padding import pad plaintext = pad(flag, 16 ) a = getRandomNBitInteger(1024 ) shared = Diffie_Hellman_KEY_EXCHANGE(a) key = md5(str (shared).encode()).digest() cipher = AES.new(key, AES.MODE_ECB) ciphertext = cipher.encrypt(plaintext)print (f'师傅给你送了一个flag' )print (f'加密的flag = {b64encode(ciphertext)} ' )print (f'p = {p} ' )print (f'your secret key {a} ' )print (f'g = {g} ' )print (f'师傅的公钥 = {gb} ' )""" 师傅给你送了一个flag 加密的flag = b'w8OCrexPPqnv2hR+xKeHhXIp0Blp1DYCV4LeZeeLpv5MzUL71raTOeOs4SQBySHH' p = 133448764119399847876731592238604881175769007976799828874328988761588128500145459082023001027383524831194316266946485380737147372837136403065060245135035225976604193830121124575947440188318348815263642243784574567832213775382081426762862856428888257126982268557543952549848053225651398101391048467656128070913 your secret key 141940531741414073502483547551457269459744373002985569536254444581939073930343975447649087549033350166772929396986965301002444997704537487577508504709368627174241095027876996113941220579274986994026832534664179333669861059196192190040046004398523932288881838011696679341328520530265002776147308306715042734185 g = 3 师傅的公钥 = 89434791765835058026108803508194156525355359465406829253856379139334424137549915669535243140614128105195584073112084994777148895681804127886440617684648237403345873311011154293855911891719204975035914932661810961867593769891076834656437254428353814290948181922438812745384577094827728409350756648446941874382 """
DH密钥分配协议,这个比赛倒是复习了不少现代密码学。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from base64 import b64decodefrom Crypto.Cipher import AESfrom Crypto.Util.number import *from hashlib import md5 data = b64decode(b'w8OCrexPPqnv2hR+xKeHhXIp0Blp1DYCV4LeZeeLpv5MzUL71raTOeOs4SQBySHH' ) p = ... s = ... (your secret key ) g = 3 pk = ... secret = pow (pk, s, p) key = md5(str (secret).encode()).digest() decoder = AES.new(key, AES.MODE_ECB)print (decoder.decrypt(data))
One Time Pad
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import xorfrom string import ascii_lowercasefrom secret import keylengthimport random key = []for _ in range (keylength): key.append(random.choice(ascii_lowercase.encode())) key = bytes (key) plaintext = open ('books.txt' ,'rb' ).read() ciphertext = xor(plaintext, key)print (key)open ('encrypted_book' , 'wb' ).write(ciphertext)
题目给了三个文件,一个是这个加密用的文件,一个是一个Kasiski test文件,这个是用来做词频分析用的。加密的文件是encrypted book。这说明这是一本书,所以其中的信息是有意义的,也就暗示我们用词频分析来破解这个一次一密。
1 2 3 4 5 6 7 8 9 10 11 12 with open (r"encrypted_book" , "rb" ) as fp: data = fp.read() gift_from_sias27.kasiski_test(data)""" 你查完资料了吗? (Y/N) > y key length: 5 with probability 0.5880384087791495 key length: 10 with probability 0.14260631001371743 ... """
所以这里密钥的长度我们选择5。随后来爆破密钥。这里我用的是permutations,但实际上应该要product。因为product数据集比较大,我就先想着用小一点的能不能跑出来,毕竟26选5,选出全不一样的概率还是挺大的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import itertoolsimport gift_from_sias27from Crypto.Util.strxor import strxorfrom string import *with open (r"encrypted_book" , "rb" ) as fp: data = fp.read()for i in itertools.permutations(ascii_lowercase, 5 ): key = ("" .join(i).encode("utf-8" )*(len (data)//5 + 1 ))[:len (data)] res = strxor(key, data) if res.decode("utf-8" ).isprintable() and b"flag{" in res: print (res) """ b'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Sic secretum dabo tibi: flag{frequency_analysis_could_be_useful#A6DC1D643A29}. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. ' """
这里我们不要用pwntools里面的xor,太慢了。
不过他这个给的Kasiski test脚本吗,好东西,我留着了。
AES?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 from Crypto.Cipher import AESfrom binascii import b2a_hexfrom libnum import s2nfrom random import *from secret import flagdef add_to_16 (text ): if len (text.encode('utf-8' )) % 16 : add = 16 - (len (text.encode('utf-8' )) % 16 ) else : add = 0 text = text + ('\0' * add) return text.encode('utf-8' )def init (): r1 = getrandbits(64 ) r2 = getrandbits(32 ) m = "{:X}" .format (r1).encode('utf-8' ) salt = "{:X}" .format (r2).encode('utf-8' ) m += salt return add_to_16(m.decode()) def encrypt (m, key, iv ): mode = AES.MODE_CBC cryptos = AES.new(key, mode, iv) cipher_text = cryptos.encrypt(m) return cipher_textdef chall (key, iv ): old_m = init() c = encrypt(old_m, key, iv) return b2a_hex(c)if __name__=="__main__" : f = open ("msg.txt" , 'w+' ) old_key = b'73E5602B54FE63A5' old_iv = b'B435AE462FBAA662' for i in range (208 ): old_c = chall(old_key, old_iv) f.write("{}\n" .format (old_c.decode())) salt = "{:X}" .format (getrandbits(32 )).encode('utf-8' ) m = flag.encode() + salt key = "{:X}" .format (getrandbits(64 )).encode('utf-8' ) iv = "{:X}" .format (getrandbits(64 )).encode('utf-8' ) c = encrypt(add_to_16(m.decode()), key, iv) print ("c = %r" %(b2a_hex(c)))
输出的文件给了很多数据。emm,给这么多数据总感觉有种PRNG的感觉。百度了一下getrandbits确实有这个伪随机数生成的问题。所以可以解。可以看这个文章:https://blog.csdn.net/m0_62506844/article/details/124278580
但是反解出来的随机数数据有点麻烦。他这个16进制化并不满足偶数字节,我们也可以不管这个我问题,因为我们使用拟合的方式,有些点在外面其实没啥问题。但是这个里面存在错误的数已经达到了63个,这就有点多了…没有办法做到完全预测。我们需要降低这个错误的数字数量。这题考的点竟在如何处理数据上面…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 import binasciiimport itertoolsfrom Crypto.Cipher import AESfrom randcrack import RandCrackwith open (r"msg.txt" , "r" ) as fp: data = fp.read().splitlines() data = list (map (binascii.unhexlify, data)) old_key = b'73E5602B54FE63A5' old_iv = b'B435AE462FBAA662' def decrypted (data, key, iv ): decoder = AES.new(key, AES.MODE_CBC, iv) m = decoder.decrypt(data) return m m = []for i in data: m.append(decrypted(i,old_key,old_iv).replace(b"\x00" , b"" ))for i in range (len (m)): if len (m[i]) != 24 : if len (m[i]) == 22 : tmp = b"0" + m[i][:15 ] + b"0" + m[i][15 :] m[i] = int (tmp,16 ) continue if len (m[i])== 23 : tmp = [] tmp.append(int (b"0" + m[i],16 )) tmp.append(int (m[i][:15 ] + b"0" + m[i][15 :], 16 )) m[i] = tmp else : m[i] = int (m[i], 16 )for i in range (len (m)): if type (m[i]) == int : m[i] = [m[i]]for seq in itertools.product(*m): rc = RandCrack() for i in seq: num_64 = i >> 32 num_32 = i & 2 **32 -1 rc.submit(num_64 & 2 **32 -1 ) rc.submit(num_64 >> 32 ) rc.submit(num_32) flag = binascii.unhexlify(b'c82dc20b7512d03f1a0982eb8a6e855db20f6fe3ff8d202a6fb74c6522fa6e623c6abe6725cafe78f9624ad59f3e90af6f985f38f75ec4d62ff7e02bd7c2f051' ) salt = "{:X}" .format (rc.predict_getrandbits(32 )).encode('utf-8' ) key = "{:X}" .format (rc.predict_getrandbits(64 )).encode('utf-8' ) iv = "{:X}" .format (rc.predict_getrandbits(64 )).encode('utf-8' ) decoder = AES.new(key, AES.MODE_CBC, iv) res = decoder.decrypt(flag) if b"flag" in res: print (res) exit(0 )
还好跑出来了。
LCG Revenge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from Crypto.Util.number import *from secret import FLAG p = getPrime(128 ) step = len (FLAG) // 3 xs = [bytes_to_long(FLAG[:step]), bytes_to_long(FLAG[step:2 *step]), bytes_to_long(FLAG[2 *step:])] a = getPrime(64 ) b = getPrime(64 ) c = getPrime(64 ) a = 18038175596386287827 b = 15503291946093443851 c = 17270168560153510007 p = 307956849617421078439840909609638388517 for _ in range (10 ): new_state = (a*xs[0 ] + b*xs[1 ] + c*xs[2 ]) % p xs = xs[1 :] + [new_state] print (xs)print (a, b, c, p)""" [255290883651191064919890629542861653873, 221128501895959214555166046983862519384, 108104020183858879999084358722168548984] 18038175596386287827 15503291946093443851 17270168560153510007 307956849617421078439840909609638388517 """
很简单的一个递推序列,直接往前解就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from Crypto.Util.number import * xs = [255290883651191064919890629542861653873 , 221128501895959214555166046983862519384 , 108104020183858879999084358722168548984 ] a,b,c,p = 18038175596386287827 , 15503291946093443851 , 17270168560153510007 , 307956849617421078439840909609638388517 for _ in range (10 ): pre_state = (((xs[-1 ] - b*xs[0 ] - c*xs[1 ]) % p)*inverse(a, p))%p xs = [pre_state] + xs[:-1 ]print (xs)print (b"" .join(list (map (long_to_bytes, xs))))""" [8114814712001258480140047838565, 7560227278457332921618993210722, 9062145970196464608031907603837] b'flag{try_some_linear_algebra_technique}' """
代数关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from Crypto.Util.number import *from secret import flag p = getStrongPrime(2048 ) q = getStrongPrime(2048 ) n = p*q phi = (p-1 )*(q-1 ) e1 = getPrime(16 ) e2 = getPrime(16 ) e3 = getPrime(16 ) d1 = inverse(e1, phi) d2 = inverse(e2, phi) d3 = inverse(e3, phi) gift_from_fallwind = d2 - d1 sp = len (flag) // 3 m1 = bytes_to_long(flag[0 :sp]) m2 = bytes_to_long(flag[sp: 2 *sp]) m3 = bytes_to_long(flag[2 *sp:]) c1= pow (m1, e1, n) c2= pow (m2, e2, n) c3= pow (m3, e3, n)print (f'n = {n} ' )print (f'e1 = {e1} ' )print (f'e2 = {e2} ' )print (f'e3 = {e3} ' )print (f'c1 = {c1} ' )print (f'c2 = {c2} ' )print (f'c3 = {c3} ' )print (f'gift_from_fallwind = {gift_from_fallwind} ' )
这里数据就不放了。
这题怎么说呢,我好像写了一部分非预期…我也顺便把我的非预期解说一下吧,虽然这个非预期只能解前两段。
给了一个gift,这个挺有用的,这里我们将gift简写为g。我们可以得到:
g e 1 = ( d 2 − d 1 ) e 1 = e 1 d 2 − 1 g e 2 = ( d 2 − d 1 ) e 2 = 1 − e 2 d 1 g e 1 e 2 = d 2 e 1 e 2 − d 1 e 1 e 2 = e 1 − e 2 ge_1 = (d_2 -d_1)e_1 = e_1d_2 -1 \\
ge_2 = (d_2 -d_1)e_2 = 1 -e_2d_1 \\
ge_1e_2 = d_2e_1e_2 - d_1e_1e_2 = e1 - e2
g e 1 = ( d 2 − d 1 ) e 1 = e 1 d 2 − 1 g e 2 = ( d 2 − d 1 ) e 2 = 1 − e 2 d 1 g e 1 e 2 = d 2 e 1 e 2 − d 1 e 1 e 2 = e 1 − e 2
那么对于第一段密文和第二段密文我们有:
c 1 g e 2 = m 1 e 1 − e 2 ⇒ m 1 e 2 = ( c 1 g e 2 − 1 ) − 1 c 2 g e 1 = m 2 e 1 − e 2 ⇒ m 2 e 1 = c 2 g e 1 + 1 c_1^{ge_2} = m_1^{e_1-e_2} \Rightarrow m_1^{e_2} = (c_1^{ge_2 - 1})^{-1}\\
c_2^{ge_1} = m_2^{e_1-e_2} \Rightarrow m_2^{e_1} = c_2^{ge_1 + 1}
c 1 g e 2 = m 1 e 1 − e 2 ⇒ m 1 e 2 = ( c 1 g e 2 − 1 ) − 1 c 2 g e 1 = m 2 e 1 − e 2 ⇒ m 2 e 1 = c 2 g e 1 + 1
那么我可以组合出两组式子:
{ m 1 e 2 = ( c 1 g e 2 − 1 ) − 1 m 1 e 1 = c 1 { m 2 e 1 = c 2 g e 1 + 1 m 2 e 2 = c 2 \begin{cases}
m_1^{e_2} = (c_1^{ge_2 - 1})^{-1} \\
m_1^{e_1} = c_1
\end{cases}
\qquad
\begin{cases}
m_2^{e_1} = c_2^{ge_1 + 1} \\
m_2^{e_2} = c_2
\end{cases}
{ m 1 e 2 = ( c 1 g e 2 − 1 ) − 1 m 1 e 1 = c 1 { m 2 e 1 = c 2 g e 1 + 1 m 2 e 2 = c 2
由于e1与e2是互素的。我们可以使用共模攻击求解这两段明文。
但是这样的方法遇到第三段明文没辙了。需要另外找办法做。
注意这个式子:
g e 1 e 2 = d 2 e 1 e 2 − d 1 e 1 e 2 = e 1 − e 2 ge_1e_2 = d_2e_1e_2 - d_1e_1e_2 = e1 - e2
g e 1 e 2 = d 2 e 1 e 2 − d 1 e 1 e 2 = e 1 − e 2
这个其实有点问题,因为我们没有带上模。如果带上模的话:
g e 1 e 2 = d 2 e 1 e 2 − d 1 e 1 e 2 = e 1 ( 1 + k 1 ϕ ( n ) ) − e 2 ( 1 + k 2 ϕ ( n ) ) g e 1 e 2 = e 1 − e 2 + ( e 1 k 1 − e 2 k 2 ) ϕ ( n ) = e 1 − e 2 + K ϕ ( n ) ge_1e_2 = d_2e_1e_2 - d_1e_1e_2 = e_1(1 + k_1\phi(n)) - e_2(1+k_2\phi(n))\\
ge_1e_2 = e_1 - e_2 + (e_1k_1 - e_2k_2)\phi(n) = e_1 - e_2 + K\phi(n)
g e 1 e 2 = d 2 e 1 e 2 − d 1 e 1 e 2 = e 1 ( 1 + k 1 ϕ ( n ) ) − e 2 ( 1 + k 2 ϕ ( n ) ) g e 1 e 2 = e 1 − e 2 + ( e 1 k 1 − e 2 k 2 ) ϕ ( n ) = e 1 − e 2 + K ϕ ( n )
那么:
g e 1 e 2 − e 1 + e 2 = K ϕ ( n ) ge_1e_2 - e_1 + e_2 = K\phi(n)
g e 1 e 2 − e 1 + e 2 = K ϕ ( n )
好了,那么我们就可以来求d了。为什么?回顾一下RSA解密的推导:
m e = c m o d p m ϕ ( n ) = 1 m o d p m^e = c \ mod \ p \\
m^{\phi(n)} = 1 \ mod \ p
m e = c m o d p m ϕ ( n ) = 1 m o d p
这里我们有:
m k ϕ ( n ) = 1 m o d p m k ϕ ( n ) + 1 = m m o d p e d = k ϕ ( n ) + 1 d = e − 1 m o d k ϕ ( n ) m^{k\phi(n)} = 1 \ mod \ p \\
m^{k\phi(n) + 1} = m \ mod \ p \\
ed = k\phi(n) + 1 \\
d = e^{-1} \ mod \ k\phi(n)
m k ϕ ( n ) = 1 m o d p m k ϕ ( n ) + 1 = m m o d p e d = k ϕ ( n ) + 1 d = e − 1 m o d k ϕ ( n )
所以我们可以直接用$$K\phi(n)$$来求d,并用这个d来求解我们的明文。而且,这个也可以用来解前面两段明文。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 from Crypto.Util.number import * n = ... e1 = 42407 e2 = 42299 e3 = 64279 c1 = ... c2 = ... c3 = ... gift_from_fallwind = ... he1 = gift_from_fallwind*e1 he2 = gift_from_fallwind*e2def common_modulus (n, c1, c2, e1, e2 ): def egcd (a, b ): if b == 0 : return a, 0 else : x, y = egcd(b, a % b) return y, x - (a // b) * y s = egcd(e1, e2) s1 = s[0 ] s2 = s[1 ] if s1 < 0 : s1 = - s1 c1 = inverse(c1, n) elif s2 < 0 : s2 = - s2 c2 = inverse(c2, n) m = pow (c1, s1, n) * pow (c2, s2, n) % n return m m1c1 = c1 m1c2 = inverse(pow (c1,he2-1 , n), n) m1e1 = e1 m1e2 = e2print (long_to_bytes(common_modulus(n,m1c1,m1c2, m1e1,m1e2))) m2c1 = pow (c2, he1+1 , n) m2c2 = c2 m2e1 = e1 m2e2 = e2print (long_to_bytes(common_modulus(n,m2c1,m2c2, m2e1,m2e2))) t_phi = gift_from_fallwind*e1*e2 - e1 + e2 d3 = inverse(e3, t_phi)print (long_to_bytes(pow (c3, d3, n)))""" b'flag{a1g3br' b'4ic_re1at1o' b'n_i5_us3ful}' """
移位与异或
拿到的竟然是一个程序…
虽然最近一直在做密码学和逆向,不得不说这两个做到最后很多地方是一样的…
IDA打开又是没有符号表,各种流程乱的一堆,找不到关键的函数。
还是用动态调试看看吧。切入点是输出函数。
x64dbg打开,查看符号表:
1 2 3 4 地址= 00007 FF672CD1320 类型= 导入 符号= ucrtbased.strlen 地址= 00007 FF672CD1328 类型= 导入 符号= ucrtbased.__stdio_common_vfprintf 地址= 00007 FF672CD1330 类型= 导入 符号= ucrtbased.__acrt_iob_func 地址= 00007 FF672CD1338 类型= 导入 符号= ucrtbased.__stdio_common_vsprintf_s
只有这里有两个和输出相关的函数。所以通过这里打断点来看输出点在哪。__stdio_common_vsprintf_s
这个函数断点不进入,所以断点打在了__stdio_common_vfprintf
上,输入好启动参数。运行到我们的断点。
1 2 3 4 5 6 7 8 9 10 RAX : 00007 FF672CCD190 encrypt_msvc_x86_64.00007 FF672CCD190RBX : 0000000000000000 RCX : 0000000000000024 '$'RDX : 00007 FF8EFFF7C88 ucrtbased.00007 FF8EFFF7C88RBP : 000000217 C39F450RSP : 000000217 C39F418RSI : 0000000000000000 RDI : 000000217 C39F5B8R8 : 00007 FF672CCAC28 "%016llx" R9 : 0000000000000000
在传入的参数中发现了"%016llx"。这个是传入的格式。通过这个我们能找到具体的输出点。IDA打开,在Strings视图中查找这个字符串。
查找这个字符串的xrefs。有一个引用,确定了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 __int64 __fastcall sub_140011990 (int a1, __int64 a2) { size_t v3; __int64 v4; int v5; int i; sub_140011370((__int64)&unk_140022015); if ( a1 != 2 ) return 0 i64; v5 = j_strlen(*(const char **)(a2 + 8 )); if ( v5 % 8 ) v5 = 8 * (v5 / 8 ) + 8 ; Block = malloc (v5); j_memset(Block, 0 , v5); v3 = j_strlen(*(const char **)(a2 + 8 )); j_memcpy(Block, *(const void **)(a2 + 8 ), v3); for ( i = 0 ; i < v5 / 8 ; ++i ) { v4 = sub_14001137A((__int64)Block + 8 * i); sub_140011195("%016llx" , v4); } free (Block); sub_140011195("\n" ); Block = 0 i64; return 0 i64; }
转到引用的函数。这个就是我们的加密函数了。sub_140011195就是printf,而上方的sub_14001137A应该就是加密函数。Block里面装的就是我们的输入数据。
进入到sub_14001137A函数,这个函数最终指向sub_1400117C0。
1 2 3 4 5 6 7 8 9 10 11 unsigned __int64 __fastcall sub_1400117C0 (__int64 a1) { unsigned __int64 v2; int i; sub_140011370((__int64)&unk_140022015); v2 = 0 i64; for ( i = 0 ; i < 8 ; ++i ) v2 = *(char *)(a1 + i) + (v2 << 8 ); return qword_14001D000 ^ (v2 >> 5 ) ^ (8 * v2); }
这个循环实际上就是字符串转QWORD。所以求解即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from z3 import *from binascii import *from Crypto.Util.number import * QWORD = 0x4472ACBF7784FF37 cipher = unhexlify("7422c48f97e43ff4bd326c7e36c43ff4bd421c6e26e41f2cf4eaa566e725cee4341a24e73fa4170434e944bf7784ff37" ) x = BitVec("x" , 64 ) data = []for i in range (len (cipher)//8 ): s = Solver() block = bytes_to_long(cipher[8 *i:8 *i + 8 ]) s.add(block == QWORD ^ (x >> 5 ) ^ (x << 3 & 0xffffffffffffffff )) if s.check() == sat: res = s.model() data.append(res[x].as_long())print (b"" .join(list (map (long_to_bytes, data))))
flip-flop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import osfrom Crypto.Cipher import AESfrom secret import FLAG auth_major_key = os.urandom(16 ) BANNER = """ Login as admin to get the flag ! """ MENU = """ Enter your choice [1] Create NewStarCTF Account [2] Create Admin Account [3] Login [4] Exit """ print (BANNER)while True : print (MENU) option = int (input ('> ' )) if option == 1 : auth_pt = b'NewStarCTFer____' user_key = os.urandom(16 ) cipher = AES.new(auth_major_key, AES.MODE_CBC, user_key) code = cipher.encrypt(auth_pt) print (f'here is your authcode: {user_key.hex () + code.hex ()} ' ) elif option == 2 : print ('GET OUT !!!!!!' ) elif option == 3 : authcode = input ('Enter your authcode > ' ) user_key = bytes .fromhex(authcode)[:16 ] code = bytes .fromhex(authcode)[16 :] cipher = AES.new(auth_major_key, AES.MODE_CBC, user_key) auth_pt = cipher.decrypt(code) if auth_pt == b'AdminAdmin______' : print (FLAG) elif auth_pt == b'NewStarCTFer____' : print ('Have fun!!' ) else : print ('Who are you?' ) elif option == 4 : print ('ByeBye' ) exit(0 ) else : print ("WTF" )
使用的是AES的CBC模式,加上名字的flip就直到是字节反转攻击了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import binasciifrom Crypto.Cipher import AESfrom pwn import *from Crypto.Util.strxor import strxor sh = remote("node4.buuoj.cn" ,25597 ) sh.recvuntil(b">" ) sh.sendline(b"1" ) line = sh.recvline() auth_code = line[line.index(b":" ) + 2 : -1 ]print (auth_code) origin = b"NewStarCTFer____" target = b"AdminAdmin______" auth_pt = binascii.unhexlify(auth_code)[:16 ] random_code = binascii.unhexlify(auth_code)[16 :] new_auth_pt = strxor(auth_pt, strxor(origin, target)) new_auth_code = (new_auth_pt + random_code).hex ().encode("utf-8" )print (new_auth_code) sh.recvuntil(b">" ) sh.sendline(b"3" ) sh.sendline(new_auth_code)print (sh.recvline())""" [x] Opening connection to node4.buuoj.cn on port 25597 [x] Opening connection to node4.buuoj.cn on port 25597: Trying 117.21.200.166 [+] Opening connection to node4.buuoj.cn on port 25597: Done b'd0b08894f8ad2bbae5877acdfd99e354e36ad068f4a1284b6f65b451fac513c1' b'dfb192aee28d3d94d8af40e0fd99e354e36ad068f4a1284b6f65b451fac513c1' b" Enter your authcode > b'flag{filp_the_word!!!!!!!!}'\n" [*] Closed connection to node4.buuoj.cn port 25597 """
An der schönen Elliptische Kurve
椭圆曲线的题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 from secret import FLAG, ECDH_KEY_EXCHANGEfrom Crypto.Cipher import AESfrom hashlib import md5from os import urandom iv = urandom(16 ) a = 14489 b = 10289 p = 7486573182795736771889604737751889118967735916352298289975055815020934891723453392369540853603360270847848895677903334441530052977221688450741083448029661 F = GF(p) E = EllipticCurve(F, [a, b]) G = E.random_point() my_private_key = random_prime(2 ^256 ) shared, sender_public_key = ECDH_KEY_EXCHANGE(G, my_private_key) key = md5(str (int (shared.xy()[0 ])).encode()).digest() cipher = AES.new(key, AES.MODE_CBC, iv) ciphretext = cipher.encrypt(FLAG)print (a)print (b)print (p)print (sender_public_key)print (my_private_key)print (ciphretext.hex ())print (iv.hex ())""" 14489 10289 7486573182795736771889604737751889118967735916352298289975055815020934891723453392369540853603360270847848895677903334441530052977221688450741083448029661 (1285788649714386836892440333012889444698233333809489364474616947934542770724999997145538088456652601147045234490019282952264340541239682982255115303711207 : 1081635450946385063319483423983665253792071829707039194609541132041775615770167048603029155228167113450196436786905820356216200242445665942628721193713459 : 1) 2549545681219766023689977461986014915946503806253877534915175093306317852773 2f65ff4a97e0e05c06eab06b58ea38a3d5b6d2a65ea4907bc46493b30081a211d7cffc872a23dbd565ef307f9492bb23 d151c04c645c3e2a8d3f1ae44589ef20 “”“
考的是ECDH,这个形式上和DH密钥交换协议没啥区别。所以很好写。
对于一方A来说:
S = d A ∗ H B S = d_A*H_B
S = d A ∗ H B
d A d_A d A 是A的私钥,而H B H_B H B 是另一方的公钥。题目中给出了一个另一方的公钥,所以直接计算共享密钥即可。只不过他这个代码一开始没看到这几个变量到底是谁跟谁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from Crypto.Cipher import AESfrom hashlib import md5from binascii import *from sage.all import * a = 14489 b = 10289 p = 7486573182795736771889604737751889118967735916352298289975055815020934891723453392369540853603360270847848895677903334441530052977221688450741083448029661 F = GF(p) E = EllipticCurve(F, [a, b]) sender_public_key = E(1285788649714386836892440333012889444698233333809489364474616947934542770724999997145538088456652601147045234490019282952264340541239682982255115303711207 , 1081635450946385063319483423983665253792071829707039194609541132041775615770167048603029155228167113450196436786905820356216200242445665942628721193713459 ) my_private_key = 2549545681219766023689977461986014915946503806253877534915175093306317852773 shared = my_private_key*sender_public_key key = md5(str (int (shared.xy()[0 ])).encode()).digest() cipher = "2f65ff4a97e0e05c06eab06b58ea38a3d5b6d2a65ea4907bc46493b30081a211d7cffc872a23dbd565ef307f9492bb23" iv = "d151c04c645c3e2a8d3f1ae44589ef20" decoder = AES.new(key, AES.MODE_CBC, unhexlify(iv))print (decoder.decrypt(unhexlify(cipher)))
密码学,AK!
Reverse
前可见古人,后得见来者
IDA反编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int __cdecl main_0 (int argc, const char **argv, const char **envp) { char v4; char v5; signed int i; signed int v7; char Str[40 ]; __CheckForDebuggerJustMyCode(&unk_41C015); j__atexit(sub_41117C); printf ("Please Input Flag: \n" , v4); cin ("%s" , (char )Str); v7 = j_strlen(Str); sub_4113DE(Str, v7); for ( i = 0 ; ; ++i ) { if ( i >= v7 ) { printf ("Congratulations!!!\n" , v5); system("pause" ); return 0 ; } if ( Str[i] != (unsigned __int8)byte_41A004[i] ) break ; } printf ("No...Try again!!!\n" , v5); return 0 ; }
关键函数为sub_4113DE
要求在经过sub_4113DE函数之后,数据要与byte_41A004相等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int __cdecl sub_4118C0 (int a1, int a2) { int result; int j; int i; __CheckForDebuggerJustMyCode(&unk_41C015); for ( i = 0 ; i < a2; ++i ) { if ( *(char *)(i + a1) < 65 || *(char *)(i + a1) > 90 ) { if ( *(char *)(i + a1) >= 97 && *(char *)(i + a1) <= 122 ) *(_BYTE *)(i + a1) = (*(char *)(i + a1) + dword_41A000 - 97 ) % 0x1A u + 97 ; } else { *(_BYTE *)(i + a1) = (*(char *)(i + a1) + dword_41A000 - 65 ) % 0x1A u + 65 ; } } for ( j = 0 ; ; ++j ) { result = j; if ( j >= a2 ) break ; *(_BYTE *)(j + a1) ^= 0x22 u; } return result; }
这里是对数据进行处理,看样子是位移。最后有个循环进行异或加密。先得到异或之前的数据:
1 2 3 4 5 6 7 8 9 10 11 from Crypto.Util.strxor import strxorfrom string import ascii_lowercase,ascii_uppercase STR = b"Q[LVYMPVTC}LCS}PCS}GP}LCS}N@J_" Key = b"\x22" *len (STR) reverse_STR = strxor(STR,Key).decode("UTF-8" )print (reverse_STR)
一眼ROT13,解得:
1 flag {begin_and_end_re_and_you}
FindME
IDA反编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 __int64 __fastcall main (int a1, char **a2, char **a3) { puts ("## ## ####### ## ## ######## ###### #### ######## ########" ); puts (" ## ## ## ## ## ## ## ## ## ## ## ## ## " ); puts (" #### ## ## ## ## ## ## ## ## ## ## " ); puts (" ## ## ## ## ## ######## ## #### ## ###### ## " ); puts (" ## ## ## ## ## ## ## ## ## ## ## ## " ); puts (" ## ## ## ## ## ## ## ## ## ## ## ## " ); puts (" ## ####### ####### ## ## ###### #### ## ## " ); printf ("Gift key:" ); __isoc99_scanf("%s" , byte_50A0); if ( strlen (byte_50A0) != 32 ) { puts ("Wrong Length!" ); exit (-1 ); } puts ("Sorry Sir, I need some time." ); sub_1BCD(); if ( dword_5040 ) { printf ("You Gift:%s\n" , byte_50A0); } else { puts ("Wait....." ); puts ("Warning,hacker robbed your gift!!!" ); } return 0LL ; }
关键的判断是dword_5040这个值,查一下引用。在sub_1253处有引用修改。
sub_1BCD函数里面嵌套了很多函数,有意义的函数是sub_1B10,sub_19B6,sub_192E
,sub_151D,sub_1253。
观察可以发现这几个函数其实可以连在一起。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 s = malloc (0x20 uLL);memset (s, 0 , 0x20 uLL);int i; int j; for ( i = 0 ; i <= 31 ; i += 4 ) { for ( j = 0 ; j <= 3 ; ++j ) *((_DWORD *)s + i / 4 ) |= byte_50A0[i + j] << (8 * j); }int i; for ( i = 0 ; i <= 7 ; ++i ) *((_DWORD *)s + i) ^= 0x2022 u; int i; for ( i = 0 ; i <= 7 ; ++i ) *((_DWORD *)s + i) ^= *((_DWORD *)s + i) >> 17 ; int i; for ( i = 0 ; i <= 7 ; ++i ) { if ( *((_DWORD *)s + i) != dword_5020[i] ) { dword_5040 = 0 ; return sub_123E(); } }
第一个for循环其实就是赋值操作。这里全部是DWORD类型,所以每次操作都是4字节的。
每四个字节先进行异或0x2022,然后位移异或得到最终结果。所以我们来爆破求解即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import itertoolsfrom Crypto.Util.number import *from string import * data = [0x67617FF4 , 0x6E305341 , 0x656C4DE0 , 0x69744BEC , 0x625F7460 , 0x6F7348F4 , 0x656871C9 , 0x7D216ED3 ]for i in range (8 ): target = data[i] for j in itertools.product(*(ascii_uppercase + ascii_lowercase + digits + "_{}!" for _ in range (4 ))): temp = bytes_to_long("" .join(j).encode("UTF-8" )) temp ^= 0x2022 temp ^= temp >> 17 if temp == target: print ("" .join(j)[::-1 ], end="" )
里面这个bo可把我吓坏了,然而结果确实是这个。
Petals
IDA反汇编,这里有些变量我自己修饰了一些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 __int64 __fastcall main (int a1, char **a2, char **a3) { unsigned int lens; puts ("Here is a pack of flowers, to my best love --- you." ); puts ("But I must check your identity, please input the right passwd" ); __isoc99_scanf("%s" , input_data); lens = strlen (input_data); if ( strlen (input_data) != 25 ) { puts ("Please check your input's format!" ); exit (-1 ); } ((void (__fastcall *)(char *, _QWORD))((char *)&sub_1208 + 1 ))(input_data, lens); sub_160C(input_data, &target, lens); printf ("If you are succeed, the flag is flag{md5(your input)}" ); return 0LL ; }
sub_160C起始就是一个比较函数。关键点在于
1 ((void (__fastcall *)(char *, _QWORD))((char *)&sub_1208 + 1 ))(input_data , lens)
这里反汇编出的结果不正确,我们函数错位了。手动修正。
1 2 3 4 5 6 7 8 9 10 11 12 13 .text: 0000000000001208 db 0FFh .text: 0000000000001209 .text: 0000000000001209 .text: 0000000000001209 endbr64 .text: 000000000000120D push rbp .text: 000000000000120E mov rbp , rsp .text: 0000000000001211 sub rsp , 130h .text: 0000000000001218 mov [rbp -128h ], rdi .text: 000000000000121F mov [rbp -12Ch ], esi .text: 0000000000001225 mov rax , fs :28h .text: 000000000000122E mov [rbp -8 ], rax .text: 0000000000001232 xor eax , eax ...
从1209位置重新生成函数。下面:
1 2 3 4 5 .text:00000000000013AE jnz short near ptr loc_13B0+1 .text:00000000000013B0 .text:00000000000013B0 loc_13B0: ; CODE XREF: .text:00000000000013AC ↑j .text:00000000000013B0 ; .text:00000000000013AE ↑j .text:00000000000013B0 call near ptr 0FFFFFFFFFEEC997Ch
这个地方出现了loc_13B0+1,而且call的值也不正确,这里也是地址出问题。我们也需要手动修改。
1 2 3 4 5 6 7 8 9 10 11 12 .text:00000000000013A0 cmp dword ptr [rbp-118h], 0FFh .text:00000000000013AA jle short loc_1376 .text:00000000000013AC jz short loc_13B1 .text:00000000000013AE jnz short loc_13B1 .text:00000000000013AE ; --------------------------------------------------------------------------- .text:00000000000013B0 db 0E8h .text:00000000000013B1 ; --------------------------------------------------------------------------- .text:00000000000013B1 .text:00000000000013B1 loc_13B1: ; CODE XREF: .text:00000000000013AC ↑j .text:00000000000013B1 ; .text:00000000000013AE ↑j .text:00000000000013B1 mov dword ptr [rbp-114h], 0 .text:00000000000013BB jmp short loc_13FC
但是这里出现了一个问题,13B0处并不是指令。这会让我们无法创建函数。
1 2 .text:00000000000013B0 : The function has undefined instruction/data at the specified address. Your request has been put in the autoanalysis queue.
创建的时候果然失败了。这里手动修改,13b0上面是两条跳转jz和jnz其实就相当于jmp,我们改13b0不会有任何问题。这个直接用二进制查看器重新这个字节修改成0x90。
1 2 3 4 5 6 7 8 9 10 11 12 .text:00000000000013A0 .text:00000000000013A0 loc_13A0: ; CODE XREF: .text:0000000000001374 ↑j .text:00000000000013A0 cmp dword ptr [rbp-118h], 0FFh .text:00000000000013AA jle short loc_1376 .text:00000000000013AC jz short loc_13B1 .text:00000000000013AE jnz short loc_13B1 .text:00000000000013B0 nop .text:00000000000013B1 .text:00000000000013B1 loc_13B1: ; CODE XREF: .text:00000000000013AC ↑j .text:00000000000013B1 ; .text:00000000000013AE ↑j .text:00000000000013B1 mov dword ptr [rbp-114h], 0 .text:00000000000013BB jmp short loc_13FC
然后再重新生成函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 unsigned __int64 __fastcall sub_1209 (__int64 input_data, unsigned int lens) { int i; signed int j; __int64 v5[33 ]; unsigned __int64 v6; v6 = __readfsqword(0x28 u); v5[0 ] = 0LL ; v5[1 ] = 0LL ; v5[2 ] = 0LL ; v5[3 ] = 0LL ; v5[4 ] = 0LL ; v5[5 ] = 0LL ; v5[6 ] = 0LL ; v5[7 ] = 0LL ; v5[8 ] = 0LL ; v5[9 ] = 0LL ; v5[10 ] = 0LL ; v5[11 ] = 0LL ; v5[12 ] = 0LL ; v5[13 ] = 0LL ; v5[14 ] = 0LL ; v5[15 ] = 0LL ; v5[16 ] = 0LL ; v5[17 ] = 0LL ; v5[18 ] = 0LL ; v5[19 ] = 0LL ; v5[20 ] = 0LL ; v5[21 ] = 0LL ; v5[22 ] = 0LL ; v5[23 ] = 0LL ; v5[24 ] = 0LL ; v5[25 ] = 0LL ; v5[26 ] = 0LL ; v5[27 ] = 0LL ; v5[28 ] = 0LL ; v5[29 ] = 0LL ; v5[30 ] = 0LL ; v5[31 ] = 0LL ; for ( i = 0 ; i <= 255 ; ++i ) *((_BYTE *)v5 + i) = ~(i ^ lens); for ( j = 0 ; lens > j; ++j ) *(_BYTE *)(j + input_data) = *((_BYTE *)v5 + *(unsigned __int8 *)(j + input_data)); return v6 - __readfsqword(0x28 u); }
这样我们就得到了这个函数的正确结果。
这里第一个for循环先是生成了一个表。然后第二for循环根据输入的值,来从表里面找对应的数据覆盖原来的位置。所以我们字节生成一个表进行反查即可。反查的数据所在的index就是原本的值。然后转字符,生成md5即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from Crypto.Util.number import *from hashlib import md5 buffer = []for i in range (256 ): buffer.append(~(i^25 ) & 255 ) target = "D0 D0 85 85 80 80 C5 8A 93 89 92 8F 87 88 9F 8F C5 84 D6 D1 D2 82 D3 DE 87" .replace(" " ,"" ) target = long_to_bytes(int (target,16 )) Res = "" for i in target: Res += chr (buffer.index(i))print (Res)print (md5(Res.encode("utf-8" )).hexdigest())
Zzzzzz3333
这题名字已经给提示了,我看到这个z3就怀疑是不是里面给了很多等式要来解。没想到果然。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 int __cdecl main (int argc, const char **argv, const char **envp) { char v3; int i; int result; char v6; char Arglist[8 ]; int v8; int v9; int v10; int v11; int v12; int v13; *(_DWORD *)&Arglist[4 ] = 0 ; sub_401020("Please Give the Key:" , 0 ); sub_401050("%s" , (char )Arglist); if ( strlen (Arglist) != 8 ) exit (0 ); v13 = Arglist[5 ]; v11 = Arglist[4 ]; v9 = Arglist[1 ]; v12 = Arglist[3 ]; v8 = Arglist[7 ]; v10 = Arglist[0 ]; if ( Arglist[3 ] + 4 * Arglist[2 ] + Arglist[7 ] + 4 * (Arglist[3 ] + 4 * Arglist[2 ]) + 3 * (Arglist[4 ] + 4 * Arglist[0 ]) + 2 * (Arglist[5 ] + 4 * Arglist[6 ]) + 11 * Arglist[1 ] == 6426 && 11 * (v10 + v8 + v12) + 4 * (v13 + 2 * v11) + Arglist[2 ] + 45 * v9 + 7 * Arglist[6 ] == 9801 && 5 * v9 + 2 * (v11 + Arglist[6 ] + v13 + 2 * (v8 + v12) + Arglist[2 ] + 2 * (Arglist[6 ] + v13 + 2 * (v8 + v12)) + 8 * v10) == 6021 && 19 * v10 + 9 * v9 + 67 * v8 + 5 * (Arglist[2 ] + Arglist[6 ]) + 7 * (v13 + 4 * v12) + 4 * v11 == 14444 && 22 * v13 + 5 * (v11 + 2 * (v12 + v9 + 2 * v10)) + 4 * (v8 + Arglist[6 ]) + 6 * Arglist[2 ] == 7251 && 19 * v12 + 3 * (v8 + Arglist[2 ] + 4 * v8 + Arglist[6 ] + 2 * (v8 + Arglist[2 ] + 4 * v8)) + 4 * (v10 + v13 + v9 + 2 * (v10 + v13)) == 10054 && (v9 *= 2 , 7 * v10 + 17 * (v12 + v9) + 11 * (v11 + 2 * v13) + 2 * (Arglist[2 ] + Arglist[6 ] + 4 * Arglist[2 ] + 6 * v8) == 10735 ) && Arglist[6 ] + v11 + 11 * Arglist[2 ] + 15 * (v12 + 2 * v8) + v9 + 43 * v10 + 21 * v13 == 11646 ) { sub_401020("The Key is Right!!\n" , v3); sub_401020("Your flag is here:)\n" , v6); for ( i = 0 ; i < 24 ; ++i ) sub_401020( "%c" , byte_402168[i] & (Arglist[i & 7 ] ^ byte_402168[i]) | ~byte_402168[i] & (~byte_402168[i] ^ ~Arglist[i & 7 ])); result = 0 ; } else { sub_401020("Your Key is Wrong\n" , v3); result = 0 ; } return result; }
这里把等式提出来,然后手动修一下表示。
1 2 3 4 5 6 7 8 if ( d + 4 * c + h + 4 * (d + 4 * c) + 3 * (e + 4 * a) + 2 * (f + 4 * g)+ 11 * b == 6426 && 11 * (a + h + d) + 4 * (f + 2 * e) + c + 45 * b + 7 * g == 9801 && 5 * b + 2 * (e + g + f + 2 * (h + d) + c + 2 * (g + f + 2 * (h + d)) + 8 * a) == 6021 && 19 * a + 9 * b + 67 * h + 5 * (c + g) + 7 * (f + 4 * d) + 4 * e == 14444 && 22 * f + 5 * (e + 2 * (d + b + 2 * a)) + 4 * (h + g) + 6 * c == 7251 && 19 * d + 3 * (h + c + 4 * h + g + 2 * (h + c + 4 * h)) + 4 * (a + f + b + 2 * (a + f)) == 10054 && (b *= 2 , 7 * a + 17 * (d + b) + 11 * (e + 2 * f) + 2 * (c + g + 4 * c + 6 * h) == 10735 ) && g + e + 11 * c + 15 * (d + 2 * h) + b + 43 * a + 21 * f == 11646 )
对了,这里倒数第二个式子里面包了一个b = b*2…
最后是这样子:
1 2 3 4 5 6 7 8 if ( d + 4 * c + h + 4 * (d + 4 * c) + 3 * (e + 4 * a) + 2 * (f + 4 * g)+ 11 * b == 6426 && 11 * (a + h + d) + 4 * (f + 2 * e) + c + 45 * b + 7 * g == 9801 && 5 * b + 2 * (e + g + f + 2 * (h + d) + c + 2 * (g + f + 2 * (h + d)) + 8 * a) == 6021 && 19 * a + 9 * b + 67 * h + 5 * (c + g) + 7 * (f + 4 * d) + 4 * e == 14444 && 22 * f + 5 * (e + 2 * (d + b + 2 * a)) + 4 * (h + g) + 6 * c == 7251 && 19 * d + 3 * (h + c + 4 * h + g + 2 * (h + c + 4 * h)) + 4 * (a + f + b + 2 * (a + f)) == 10054 && 7 * a + 17 * (d + b*2 ) + 11 * (e + 2 * f) + 2 * (c + g + 4 * c + 6 * h) == 10735 && g + e + 11 * c + 15 * (d + 2 * h) + b*2 + 43 * a + 21 * f == 11646 )
然后用z3约束求解即可。(用sagemath也行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import binasciifrom z3 import * a,b,c,d,e,f,g, h = Ints("a b c d e f g h" ) sl = Solver() sl.add( d + 4 * c + h + 4 * (d + 4 * c) + 3 * (e + 4 * a) + 2 * (f + 4 * g) + 11 * b == 6426 , 11 * (a + h + d) + 4 * (f + 2 * e) + c + 45 * b + 7 * g == 9801 , 5 * b + 2 * (e + g + f + 2 * (h + d) + c + 2 * (g + f + 2 * (h + d)) + 8 * a) == 6021 , 19 * a + 9 * b + 67 * h + 5 * (c + g) + 7 * (f + 4 * d) + 4 * e == 14444 , 22 * f + 5 * (e + 2 * (d + b + 2 * a)) + 4 * (h + g) + 6 * c == 7251 , 19 * d + 3 * (h + c + 4 * h + g + 2 * (h + c + 4 * h)) + 4 * (a + f + b + 2 * (a + f)) == 10054 , 7 * a + 17 * (d + b * 2 ) + 11 * (e + 2 * f) + 2 * (c + g + 4 * c + 6 * h) == 10735 , g + e + 11 * c + 15 * (d + 2 * h) + b * 2 + 43 * a + 21 * f == 11646 )if sl.check() == sat: ans = sl.model() byte_402168 = binascii.unhexlify("000D0D0B0C6B141E1C525F5F28781D3B250E030056104F19" ) Arglist = bytes ([ans[a].as_long(),ans[b].as_long(),ans[c].as_long(),ans[d].as_long(),ans[e].as_long(),ans[f].as_long(),ans[g].as_long(),ans[h].as_long()])for i in range (24 ): print (chr (byte_402168[i] & (Arglist[i & 7 ] ^ byte_402168[i]) | ~byte_402168[i] & (~byte_402168[i] ^ ~Arglist[i & 7 ])), end="" )
z3这个输出数据使用简直折磨。
还是sagemath好用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from sage.all import *import binascii var("a,b,c,d,e,f,g,h" ) res = solve(4 [ d + 4 * c + h + 4 * (d + 4 * c) + 3 * (e + 4 * a) + 2 * (f + 4 * g) + 11 * b == 6426 , 11 * (a + h + d) + 4 * (f + 2 * e) + c + 45 * b + 7 * g == 9801 , 5 * b + 2 * (e + g + f + 2 * (h + d) + c + 2 * (g + f + 2 * (h + d)) + 8 * a) == 6021 , 19 * a + 9 * b + 67 * h + 5 * (c + g) + 7 * (f + 4 * d) + 4 * e == 14444 , 22 * f + 5 * (e + 2 * (d + b + 2 * a)) + 4 * (h + g) + 6 * c == 7251 , 19 * d + 3 * (h + c + 4 * h + g + 2 * (h + c + 4 * h)) + 4 * (a + f + b + 2 * (a + f)) == 10054 , 7 * a + 17 * (d + b * 2 ) + 11 * (e + 2 * f) + 2 * (c + g + 4 * c + 6 * h) == 10735 , g + e + 11 * c + 15 * (d + 2 * h) + b * 2 + 43 * a + 21 * f == 11646 ], a,b,c,d,e,f,g,h )[0 ] key = []for i in res: key.append(int (i.operands()[1 ])) byte_402168 = binascii.unhexlify("000D0D0B0C6B141E1C525F5F28781D3B250E030056104F19" ) Arglist = bytes (key)for i in range (24 ): print (chr (byte_402168[i] & (Arglist[i & 7 ] ^^ byte_402168[i]) | ~byte_402168[i] & (~byte_402168[i] ^^ ~Arglist[i & 7 ])), end="" )
Hash
IDA反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int __cdecl main_0 (int argc, const char **argv, const char **envp) { char v4; char v5; int i; int v7; size_t v8; DWORD v9[3 ]; BYTE v10[264 ]; char Destination; __int16 v12; char v13; char Str; char v15[49 ]; int v16; v16 = 0 ; Str = 0 ; j_memset(v15, 0 , sizeof (v15)); Destination = 0 ; v12 = 0 ; v13 = 0 ; v9[0 ] = 50 ; printf ("Please Input Your flag:" , v4); sub_411037("%s" , (char )&Str); if ( j_strlen(&Str) != 42 ) exit (0 ); v8 = 0 ; v7 = 0 ; while ( v8 < j_strlen(&Str) ) { strncpy (&Destination, &Str + v8, 3u ); sub_4113F2((BYTE *)&Destination, 3u , v10, v9); for ( i = 0 ; i < 20 ; ++i ) { if ( v10[i] != (unsigned __int8)byte_41A000[40 * v7 + i] ) v16 = 1 ; } v8 += 3 ; ++v7; } if ( v16 == 1 ) printf ("Wrong!!!Please Try Again!!\n" , v5); else printf ("Correct!!Your input is flag!!\n" , v5); return 0 ; }
sub_4113F2函数是sha1哈希函数。将明文分成3个字符一组进行加密,每一组和byte_41A000的一部分比较。爆破求解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from hashlib import sha1from string import *from itertools import product data = "A2F17ED1C6A8BC31769CDF654DF4B8A937042CB600000000000000000000000000000000000000000CA8A2EDB0C1D34A432A5A4464E0D6ABD847C8310000000000000000000000000000000000000000C359D69F3F08BB920F2C3B51133205533462093E0000000000000000000000000000000000000000CC5C3FE6E7356A26A134CFF5633349F597C40A9D00000000000000000000000000000000000000004AC4BB3F27F245BA9178651AA5CDEDCBB2862E2A0000000000000000000000000000000000000000A01E33F4DCDB6BA1AE9F34A97CF8F6DEEEDF1A8D0000000000000000000000000000000000000000D3AF70912A8C1B22CFDECE071BA36BC4662B58FA00000000000000000000000000000000000000009395EAB195D25B676D7D07075D3838A9AC19DF210000000000000000000000000000000000000000FDB43C5EF76ECDA0C1661D6D199B5BFAC1DB538A0000000000000000000000000000000000000000DA8E9997A010BE78B20108CE79FEC1FB9C63D8DC0000000000000000000000000000000000000000809DA627F1AD01D65864C376E3179B62D9D7426100000000000000000000000000000000000000008F61EE21AC7579626934E0FFB6A62B3D4A82EEC40000000000000000000000000000000000000000E2A954758FDB61F869998E9788B7B7E48480B8320000000000000000000000000000000000000000B8E3349B97532B27AA62B8718B68240179158144" target_hash = []for i in range (42 //3 ): target_hash.append(data[80 *i:80 *i+40 ].lower()) char_set = ascii_lowercase + ascii_uppercase + digits + "{_!,.}" for target in target_hash: for piece in product(*(char_set for _ in range (3 ))): byte_piece = "" .join(piece) if sha1(byte_piece.encode("utf-8" )).hexdigest() == target: print (byte_piece, end="" )
Exception
IDA反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 int __cdecl main_0 (int argc, const char **argv, const char **envp) { char v4; char v5; int i; int v7; int v8; size_t v9; char Str; char v11[47 ]; int v12[6 ]; int v13[9 ]; v13[0 ] = 0 ; v13[1 ] = 0 ; v13[2 ] = 0 ; v13[3 ] = 0 ; v13[4 ] = 0 ; v13[5 ] = 0 ; v13[6 ] = 0 ; v13[7 ] = 0 ; v12[0 ] = 1 ; v12[1 ] = 2 ; v12[2 ] = 3 ; v12[3 ] = 4 ; Str = 0 ; j_memset(v11, 0 , 0x27 u); sub_4110E1("Please input Your flag:" , v4); sub_41103C("%s" , (char )&Str); if ( j_strlen(&Str) != 32 ) exit (0 ); v9 = 0 ; v8 = 0 ; while ( v9 < j_strlen(&Str) ) { v13[v8] = (v11[v9 + 2 ] << 24 ) | (v11[v9 + 1 ] << 16 ) | (v11[v9] << 8 ) | *(&Str + v9); v9 += 4 ; ++v8; } sub_41100A((int )v13, v12); v7 = 0 ; for ( i = 0 ; i < 8 ; ++i ) { if ( dword_41A000[i] != v13[i] ) v7 = 1 ; } if ( v7 == 1 ) sub_4110E1("Wrong\n" , v5); else sub_4110E1("Right,flag:flag{%s}\n" , (char )&Str); return 0 ; }
关键函数sub_41100A。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 for ( i = 0 ; i < 8 ; i += 2 ) { v11 = a1[i]; v10 = a1[i + 1 ]; v9 = 0 ; for ( j = 0 ; j < 32 ; ++j ) { v9 -= 0x61C88647 ; MEMORY[0 ] = 1 ; v11 += (v7 + (v10 >> 5 )) ^ (v9 + v10) ^ (v8 + 16 * v10); v10 += (v5 + (v11 >> 5 )) ^ (v9 + v11) ^ (v6 + 16 * v11); } a1[i] = v11; a1[i + 1 ] = v10; result = i + 2 ; }
tea加密,没有变化。这里这个v9和MEMORY有点问题。看汇编代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 .text: 004118 A4 .text: 004118 A4 mov [ebp+ms_exc.registration.TryLevel], 0 .text: 004118 AB mov edx, 0 .text: 004118 B0 mov byte ptr [edx], 1 .text: 004118 B0 .text: 004118 B3 mov [ebp+ms_exc.registration.TryLevel], 0 FFFFFFFEh.text: 004118 BA jmp short loc_4118DE.text: 004118 BC .text: 004118 BC.text: 004118 BC loc_4118BC: .text: 004118 BC .text: 004118 BC lea eax, [ebp+var_48].text: 004118 BF push eax.text: 004118 C0 call sub_411087.text: 004118 C5 add esp, 4 .text: 004118 C8 retn.text: 004118 C9 .text: 004118 C9.text: 004118 C9 loc_4118C9: .text: 004118 C9 .text: 004118 C9 mov esp, [ebp+ms_exc.old_esp].text: 004118 CC mov eax, var_48.text: 004118 CF xor eax, 12345678 h.text: 004118 D4 mov [ebp+var_48], eax.text: 004118 D7 mov [ebp+ms_exc.registration.TryLevel], 0 FFFFFFFEh
这个必定触发异常,所以一定会进入异常处理,在004118C9处是我们的异常,这里我们将var_48
这个值与0x12345678异或了,并且写回。
1 2 3 4 5 6 .text: 0041188E loc_41188E: .text: 0041188E cmp [ebp +var_90], 20h .text: 00411895 jge loc_411927.text: 0041189B mov eax , [ebp +var_3C].text: 0041189E add eax , [ebp +var_48].text: 004118A1 mov [ebp +var_3C], eax
var_3C
其实是tea里面的sum。这里他与var_48
相加,var_48
初始值为0x9E3779B9,其实就是我们的delta。
所以这里的加密算法变成了:
1 2 3 4 sum += delta; delta ^= 0x12345678 ; v11 += (v7 + (v10 >> 5 )) ^ (v9 + v10) ^ (v8 + 16 * v10); v10 += (v5 + (v11 >> 5 )) ^ (v9 + v11) ^ (v6 + 16 * v11);
所以对应解密即可。得到的结果逆置一下,满足数值变量和字符串的转换。一开始我是以Crypto里面的long_to_bytes转的,但是在C语言里面其实不能这样,也算是踩了一个小坑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from ctypes import *from Crypto.Util.number import *from libnum import *def decrypt (v, k ): v0, v1 = c_uint32(v[0 ]), c_uint32(v[1 ]) delta = 0x9E3779B9 k0, k1, k2, k3 = k[0 ], k[1 ], k[2 ], k[3 ] total = c_uint32(2745866144 ) for i in range (32 ): v1.value -= ((v0.value << 4 ) + k2) ^ (v0.value + total.value) ^ ((v0.value >> 5 ) + k3) v0.value -= ((v1.value << 4 ) + k0) ^ (v1.value + total.value) ^ ((v1.value >> 5 ) + k1) delta ^= 0x12345678 total.value -= delta return v0.value, v1.valueif __name__ == "__main__" : value = [0x88E821CE , 0x0B009D70 , 0x91B1E68F , 0x131EA96 , 0x0A3209D7D , 0x0A9187DFB , 0x0C452C5CA , 0x0A9696753 ] key = [0x1 , 0x2 , 0x3 , 0x4 ] for i in range (len (value) // 2 ): res = decrypt(value[2 * i:2 * i + 2 ], key) print (long_to_bytes(res[0 ]).decode("utf-8" )[::-1 ] + long_to_bytes(res[1 ]).decode("utf-8" )[::-1 ], end="" )
拔丝溜肆 (easy)
通过字符串来找主要的函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 __int64 sub_140011CF0 () { char *v0; __int64 i; char v3[32 ]; char v4; char Str[88 ]; char Str1[104 ]; char Str2[57 ]; char v8[3 ]; v0 = &v4; for ( i = 72 i64; i; --i ) { *(_DWORD *)v0 = 0xCCCCCCCC ; v0 += 4 ; } sub_14001137F((__int64)&unk_140023014); srand(0x1BF52 u); memset (Str, 0 , 0x32 ui64); memset (Str1, 0 , 0x50 ui64); strcpy (Str2, "CPaKBfUZFcNwW9qCKgyvuS2PGPQ9mttGc/wCNS0w6hDwGOSsOkOEkL5V" ); memset (v8, 0 , sizeof (v8)); printf ("Congratulations on making it to the end and enjoying the game\n" ); printf ("Please input Your flag:" ); scanf ("%s" , Str); if ( j_strlen(Str) != 42 ) exit (0 ); sub_140011069((__int64)Str, (__int64)Str1); if ( !j_strcmp(Str1, Str2) ) printf ("Right!!!\n" ); else printf ("Wrong!!!\n" ); sub_14001131B(v3, &unk_14001ACC8); return 0 i64; }
长度要求42,关键函数sub_140011069指向sub_1400118B0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 __int64 __fastcall sub_1400118B0 (const char *input, __int64 output) { __int64 v2; __int64 v3; int v5; int v6; int v7; sub_14001137F((__int64)&unk_140023014); v5 = j_strlen(input); v3 = (unsigned int )(v5 >> 31 ); v2 = 3 i64; LODWORD(v3) = v5 % 3 ; v6 = 0 ; v7 = 0 ; while ( v6 < v5 ) { sub_14001127B(v2, v3); *(_BYTE *)(output + v7) = byte_14001E1C0[input[v6] >> 2 ]; *(_BYTE *)(output + v7 + 1 ) = byte_14001E1C0[((input[v6 + 1 ] & 0xF0 ) >> 4 ) | (16 * (input[v6] & 3 ))]; *(_BYTE *)(output + v7 + 2 ) = byte_14001E1C0[((input[v6 + 2 ] & 0xC0 ) >> 6 ) | (4 * (input[v6 + 1 ] & 0xF ))]; v2 = (__int64)byte_14001E1C0; v3 = v7 + 3 ; *(_BYTE *)(output + v3) = byte_14001E1C0[input[v6 + 2 ] & 0x3F ]; v6 += 3 ; v7 += 4 ; } if ( v5 % 3 == 1 ) { *(_BYTE *)(output + v7 - 1 ) = '=' ; *(_BYTE *)(output + v7 - 2 ) = '=' ; } else if ( v5 % 3 == 2 ) { *(_BYTE *)(output + v7 - 1 ) = '=' ; } return output; }
很明显是一个base64的编码算法。看看sub_14001127B。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 size_t sub_1400117E0 () { size_t result; int v1; int i; sub_14001137F((__int64)&unk_140023014); v1 = rand() % 64 ; for ( i = 0 ; ; ++i ) { result = j_strlen(Str); if ( i >= result ) break ; byte_14001E1C0[i] = Str[(v1 + i) % 64 ]; } return result; }
这里的Str和主函数的不是一个。看了一下是初始的base64码表,所以这个函数实际上是生成一个新的码表。这里的rand可预测。
所以整体就是每三个明文使用一个base64码表编码。逆过来即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 from libnum import * random_list = [13545 , 19112 , 12729 , 29322 , 16702 , 2078 , 10043 , 2201 , 6796 , 11264 , 9582 , 25786 , 17729 , 9432 , 11586 , 24167 , 27649 , 8072 , 29092 , 28713 , 9533 , 1675 , 27056 , 20736 , 14087 , 1663 , 21282 , 15621 , 19748 , 15328 , 14606 , 12622 , 21859 , 28183 , 21202 , 7954 , 30254 , 31667 , 20296 , 23053 , 21343 , 23542 , 24640 , 17958 , 5461 , 22931 , 6701 , 2128 , 26182 , 27318 ] origin_str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def generate_new_base64_table (random_num, origin_str ): inc = random_num % 64 new_str = "" for i in range (len (origin_str)): new_str += origin_str[(inc + i) % 64 ] return new_strdef base64encode (data, table ): bin_data = s2b(data) res = "" for i in range (len (bin_data)//6 ): index = int (bin_data[6 *i:6 *i+6 ], 2 ) res += table[index] return resdef base64decode (data, table:str ): bin_res = "" for i in data: bin_res += bin (table.index(i))[2 :].zfill(6 ) return b2s(bin_res)if __name__=="__main__" : cipher = "CPaKBfUZFcNwW9qCKgyvuS2PGPQ9mttGc/wCNS0w6hDwGOSsOkOEkL5V" for i in range (len (cipher)//4 ): table = generate_new_base64_table(random_list[i], origin_str) data = cipher[4 *i:4 *i+4 ] print (base64decode(data, table).decode("utf-8" ), end="" )
E4sy_Mix (hard)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; _BYTE *v4; int v5; int v6; int v7; char *v8; _OWORD v10[2 ]; char v11; char v12; int v13; _DWORD v14[3 ]; void *retaddr; v14[0 ] = v3; v14[1 ] = retaddr; v11 = 0 ; v10[0 ] = 0 i64; v10[1 ] = 0 i64; sub_401020("Please input your flag:" , 0 ); sub_401050("%s" , (char )v10); v4 = &loc_402000; v5 = 53 ; do { *v4++ ^= 0x54 u; --v5; } while ( v5 ); ((void (__stdcall *)(_OWORD *))loc_402000)(v10); v6 = 0 ; while ( byte_4030FC[v6] == *((_BYTE *)v10 + v6) ) { if ( ++v6 >= 33 ) { v7 = 1 ; goto LABEL_8; } } v7 = 0 ; LABEL_8: v8 = "Your input is Wrong! Try again!\n" ; if ( v7 ) v8 = "Your input is flag!!\n" ; sub_401020(v8, v12); return sub_401286((unsigned int )v14 ^ v13); }
中间的函数调用出了点问题。看到上面的while循环有一个异或操作。其实就是把代码段的那段数据异或一下,得到的才是真正要执行的代码。所以我们提取出来手动修正一下。
1 2 3 4 while ( v5 ); sub_402000((int )v10); v6 = 0 ;while ( byte_4030FC[v6] == *((_BYTE *)v10 + v6) )
完成后,函数可以正常的反汇编。
关键处理函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 char __cdecl sub_401080 (int a1, unsigned int a2) { int i; int v3; int j; unsigned __int8 v5; char result; for ( i = 0 ; i < 256 ; ++i ) { byte_404490[i] = i; byte_404390[i] = byte_404018[i % a2]; } v3 = 0 ; for ( j = 0 ; j < 256 ; ++j ) { v5 = byte_404490[j]; v3 = (v5 + (unsigned __int8)byte_404390[j] + v3) % 256 ; result = byte_404490[v3]; byte_404490[j] = result; byte_404490[v3] = v5; } return result; }unsigned int __usercall sub_401110@<eax>(int a1@<edx>, const char *a2) { unsigned int result; int v3; int v4; unsigned __int8 v5; unsigned int i; result = strlen (a2); v3 = 0 ; v4 = 0 ; for ( i = 0 ; i < result; ++i ) { v3 = (v3 + 1 ) % 256 ; v5 = byte_404490[v3]; v4 = (v5 + v4) % 256 ; byte_404490[v3] = byte_404490[v4]; byte_404490[v4] = v5; *(_BYTE *)(i + a1) ^= byte_404490[(unsigned __int8)(v5 + byte_404490[v3])]; } return result; }
由于我们的数据只在最后这个异或进行加密。所以直接dump关键数据出来处理即可。
这里我把ecx寄存器的值dump出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 target_data = bytes ( [0xA1 , 0xBF , 0xB6 , 0x70 , 0x63 , 0x5B , 0x3B , 0xED , 0xF4 , 0x91 , 0x81 , 0xA4 , 0xBD , 0x3A , 0x53 , 0x86 , 0x5B , 0x8C , 0xDB , 0x41 , 0x1B , 0x73 , 0xE1 , 0xD1 , 0xF2 , 0xB2 , 0xDF , 0x6E , 0x16 , 0x56 , 0x22 , 0x42 , 0xFC ]) xor_data = [0xc7 , 0xd3 , 0xd7 , 0x17 , 0x18 , 0x9 , 0x78 , 0xd9 , 0xab , 0xf0 , 0xef , 0xc0 , 0xe2 , 0x69 , 0x1e , 0xc5 , 0x04 , 0xe5 , 0xa8 , 0x1e , 0x72 , 0x1d , 0x95 , 0xb4 , 0x80 , 0xd7 , 0xac , 0x1a , 0x7f , 0x38 , 0x45 , 0x63 ]for i in range (len (xor_data)): print (chr (xor_data[i] ^ target_data[i]), end="" )
看了flag才知道是RC4和SMC,我自己复现算法来解这题完全行不通,我也不知道为啥。