NewStar 2022 公开赛道

第一周在忙保研的事情所以没打。现在发现第一周的题都没法交了,就从第二周的开始写了。

算是我打过比较顺的一个CTF了,对我的水平来说刚刚好,赞一个。

Crypto

Affine

加密代码:

1
2
3
4
5
6
7
8
9
10
11
12
from secret import flag
from 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\x82T\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 itertools
from 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))

# b'flag{Kn0wn_p1aint3xt_4ttack}'
# b'V\\QWk;^ g^O`!QY^d#hdO$ddQS[m'

# flag{Kn0wn_p1aint3xt_4ttack}

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 flag
from Crypto.Util.number import *
from random import shuffle
from 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_len
print(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 base64
import string
from 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))

# b'flag{a1ph4bet_c0u1d_be_d1ffi3r3nt}'

# flag{a1ph4bet_c0u1d_be_d1ffi3r3nt}

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 sha256
from secret import flag
from 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 ans

def encrypt(msg):
res = []
for i in msg:
tmp = list(map(random.choice([fun1,fun2,fun3]),[i]))[0]
res.append(tmp)
return res

print(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 sha256
from 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))

# flag{c4Nn0t_D3crYpt_buT_r3p34t_Yes}

代码写的烂,但我不想改。反正也不是什么太大运算量。

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 flag
#flag格式为 flag{XXXX}
def 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*q

def 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}'
...
"""

# 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)
# [32579077549265101609729134002322479188058664203229584246639330306875565342934, 30627296760863751873213598737521260410801961411772904859782399797798775242121, 59045755507520598673072877669036271379314362490837080079400207813316110037822, 29714794521560972198312794885289362350476307292503308718904661896314434077717, 3378007627369454232183998646610752441039379051735310926898417029172995488622, 35893579613746468714922176435597562302206699188445795487657524606666534642489]

这题直接套脚本就行了,很标准的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 gcd
from Crypto.Util.number import *
from functools import reduce
from 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) % n
print(n2s(m))


# b'flag{lcG_1s_s0_34sy}'

# flag{lcG_1s_s0_34sy}

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) % n

for _ in range(4):
x = getRandomNBitInteger(256)
print(f'({x}, {poly(x)})')

print(n)

看到这个多项式还有shamir,一眼秘密共享。

输出给了4项,可以直接重构多项式。

M=(1 x1 x12 x131 x2 x22 x231 x3 x32 x331 x4 x42 x43),   Y=(res1res2res3res4)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}

那么即有:

(1 x1 x12 x131 x2 x22 x231 x3 x32 x331 x4 x42 x43)(dcba)=(res1res2res3res4)\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}

所以我们构造出矩阵然后求解即可,这里的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 *
# sage
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])))

# b'flag{w0w_y0u_k0nw_sham1r_s3cret_5h4r1ng_4nd_1agrange_interpolation!!!}'

# flag{w0w_y0u_k0nw_sham1r_s3cret_5h4r1ng_4nd_1agrange_interpolation!!!}

下面是加密脚本的输出:

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_EXCHANGE
from Crypto.Util.number import *
from base64 import b64encode
from Crypto.Cipher import AES
from hashlib import md5
from Crypto.Util.Padding import pad

plaintext = pad(flag, 16)

a = getRandomNBitInteger(1024)
shared = Diffie_Hellman_KEY_EXCHANGE(a) # the original one, not the elliptic curve version!!!!
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 b64decode
from Crypto.Cipher import AES
from 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))

# b'flag{d1ff1e_h311m4n_is_4_p13c3_0f_c4k3}\t\t\t\t\t\t\t\t\t'
# flag{d1ff1e_h311m4n_is_4_p13c3_0f_c4k3}

One Time Pad

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import xor
from string import ascii_lowercase
from secret import keylength
import 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 itertools
import gift_from_sias27
from Crypto.Util.strxor import strxor
from string import *

with open(r"encrypted_book", "rb") as fp:
data = fp.read()

# gift_from_sias27.kasiski_test(data)
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. '
"""

# flag{frequency_analysis_could_be_useful#A6DC1D643A29}

这里我们不要用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 AES
from binascii import b2a_hex
from libnum import s2n
from random import *
from secret import flag

def 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_text

def 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)))

# c = b'c82dc20b7512d03f1a0982eb8a6e855db20f6fe3ff8d202a6fb74c6522fa6e623c6abe6725cafe78f9624ad59f3e90af6f985f38f75ec4d62ff7e02bd7c2f051'

输出的文件给了很多数据。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 binascii
import itertools
from Crypto.Cipher import AES
from randcrack import RandCrack

with 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)

# b'flag{7h7gi3nl-ald4ssf8-eajfwm7t-rujquhmg}4AF4B023\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

# flag{7h7gi3nl-ald4ssf8-eajfwm7t-rujquhmg}

还好跑出来了。

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(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}'
"""

# 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。我们可以得到:

ge1=(d2d1)e1=e1d21ge2=(d2d1)e2=1e2d1ge1e2=d2e1e2d1e1e2=e1e2ge_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

那么对于第一段密文和第二段密文我们有:

c1ge2=m1e1e2m1e2=(c1ge21)1c2ge1=m2e1e2m2e1=c2ge1+1c_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}

那么我可以组合出两组式子:

{m1e2=(c1ge21)1m1e1=c1{m2e1=c2ge1+1m2e2=c2\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}

由于e1与e2是互素的。我们可以使用共模攻击求解这两段明文。

但是这样的方法遇到第三段明文没辙了。需要另外找办法做。

注意这个式子:

ge1e2=d2e1e2d1e1e2=e1e2ge_1e_2 = d_2e_1e_2 - d_1e_1e_2 = e1 - e2

这个其实有点问题,因为我们没有带上模。如果带上模的话:

ge1e2=d2e1e2d1e1e2=e1(1+k1ϕ(n))e2(1+k2ϕ(n))ge1e2=e1e2+(e1k1e2k2)ϕ(n)=e1e2+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)

那么:

ge1e2e1+e2=Kϕ(n)ge_1e_2 - e_1 + e_2 = K\phi(n)

好了,那么我们就可以来求d了。为什么?回顾一下RSA解密的推导:

me=c mod pmϕ(n)=1 mod pm^e = c \ mod \ p \\ m^{\phi(n)} = 1 \ mod \ p

这里我们有:

mkϕ(n)=1 mod pmkϕ(n)+1=m mod ped=kϕ(n)+1d=e1 mod 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)

所以我们可以直接用$$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*e2

def 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

# m1
# exp1: m1^e1 = c1
# exp2: m1^e2 = c1/c1^he2

m1c1 = c1
m1c2 = inverse(pow(c1,he2-1, n), n)
m1e1 = e1
m1e2 = e2
print(long_to_bytes(common_modulus(n,m1c1,m1c2, m1e1,m1e2)))

# m2
# exp1: m2^e1 = c2^(he1+1)
# exp2: m2^e2 = c2

m2c1 = pow(c2, he1+1, n)
m2c2 = c2
m2e1 = e1
m2e2 = e2
print(long_to_bytes(common_modulus(n,m2c1,m2c2, m2e1,m2e2)))

# m3

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}'
"""

# flag{a1g3br4ic_re1at1on_i5_us3ful}

移位与异或

拿到的竟然是一个程序…

虽然最近一直在做密码学和逆向,不得不说这两个做到最后很多地方是一样的…

IDA打开又是没有符号表,各种流程乱的一堆,找不到关键的函数。

还是用动态调试看看吧。切入点是输出函数。

x64dbg打开,查看符号表:

1
2
3
4
地址=00007FF672CD1320 类型=导入 符号=ucrtbased.strlen
地址=00007FF672CD1328 类型=导入 符号=ucrtbased.__stdio_common_vfprintf
地址=00007FF672CD1330 类型=导入 符号=ucrtbased.__acrt_iob_func
地址=00007FF672CD1338 类型=导入 符号=ucrtbased.__stdio_common_vsprintf_s

只有这里有两个和输出相关的函数。所以通过这里打断点来看输出点在哪。__stdio_common_vsprintf_s这个函数断点不进入,所以断点打在了__stdio_common_vfprintf上,输入好启动参数。运行到我们的断点。

1
2
3
4
5
6
7
8
9
10
RAX : 00007FF672CCD190     encrypt_msvc_x86_64.00007FF672CCD190
RBX : 0000000000000000
RCX : 0000000000000024 '$'
RDX : 00007FF8EFFF7C88 ucrtbased.00007FF8EFFF7C88
RBP : 000000217C39F450
RSP : 000000217C39F418
RSI : 0000000000000000
RDI : 000000217C39F5B8
R8 : 00007FF672CCAC28 "%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; // rax
__int64 v4; // rax
int v5; // [rsp+24h] [rbp+4h]
int i; // [rsp+44h] [rbp+24h]

sub_140011370((__int64)&unk_140022015);
if ( a1 != 2 )
return 0i64;
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 = 0i64;
return 0i64;
}

转到引用的函数。这个就是我们的加密函数了。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; // [rsp+28h] [rbp+8h]
int i; // [rsp+44h] [rbp+24h]

sub_140011370((__int64)&unk_140022015);
v2 = 0i64;
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))))

# b'flag{wow_wow_wow_you_solved_my_encryption}\x00\x00\x00\x00\x00\x00'

# flag{wow_wow_wow_you_solved_my_encryption}

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 os
from Crypto.Cipher import AES
from 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 binascii
from Crypto.Cipher import AES
from pwn import *
from Crypto.Util.strxor import strxor


# context(log_level='debug')
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
"""

# flag{filp_the_word!!!!!!!!}

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_EXCHANGE
from Crypto.Cipher import AES
from hashlib import md5
from 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=dAHBS = d_A*H_B

dAd_A是A的私钥,而HBH_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 AES
from hashlib import md5
from 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)))

# b'flag{ell1ptic_curv3_i5_be4ut1ful_right#4DF17ABE}'

# flag{ell1ptic_curv3_i5_be4ut1ful_right#4DF17ABE}

密码学,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; // [esp+0h] [ebp-114h]
char v5; // [esp+0h] [ebp-114h]
signed int i; // [esp+D0h] [ebp-44h]
signed int v7; // [esp+DCh] [ebp-38h]
char Str[40]; // [esp+E8h] [ebp-2Ch] BYREF

__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; // eax
int j; // [esp+D0h] [ebp-14h]
int i; // [esp+DCh] [ebp-8h]

__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) % 0x1Au + 97;
}
else
{
*(_BYTE *)(i + a1) = (*(char *)(i + a1) + dword_41A000 - 65) % 0x1Au + 65;
}
}
for ( j = 0; ; ++j )
{
result = j;
if ( j >= a2 )
break;
*(_BYTE *)(j + a1) ^= 0x22u;
}
return result;
}

这里是对数据进行处理,看样子是位移。最后有个循环进行异或加密。先得到异或之前的数据:

1
2
3
4
5
6
7
8
9
10
11
from Crypto.Util.strxor import strxor
from 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)

# synt{ortva_naq_raq_er_naq_lbh}

一眼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(0x20uLL);
memset(s, 0, 0x20uLL);

int i; // [rsp+8h] [rbp-8h]
int j; // [rsp+Ch] [rbp-4h]

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; // [rsp+Ch] [rbp-4h]

for ( i = 0; i <= 7; ++i )
*((_DWORD *)s + i) ^= 0x2022u;
int i; // [rsp+Ch] [rbp-4h]

for ( i = 0; i <= 7; ++i )
*((_DWORD *)s + i) ^= *((_DWORD *)s + i) >> 17;

int i; // [rsp+Ch] [rbp-4h]

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 itertools
from 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="")
# print("\n")


# flag{D0nt_let_time_bo_so_cheap!}

里面这个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; // [rsp+Ch] [rbp-4h]

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 ; __unwind {
.text:0000000000001209 endbr64 ; CODE XREF: main+9E↓p
.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; // [rsp+18h] [rbp-118h]
signed int j; // [rsp+1Ch] [rbp-114h]
__int64 v5[33]; // [rsp+20h] [rbp-110h]
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

v6 = __readfsqword(0x28u);
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(0x28u);
}

这样我们就得到了这个函数的正确结果。

这里第一个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())

# 66ccff#luotianyi#b074d58a
# d780c9b2d2aa9d40010a753bc15770de

# flag{d780c9b2d2aa9d40010a753bc15770de}

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; // di
int i; // edi
int result; // eax
char v6; // [esp-10h] [ebp-30h]
char Arglist[8]; // [esp+0h] [ebp-20h] BYREF
int v8; // [esp+8h] [ebp-18h]
int v9; // [esp+Ch] [ebp-14h]
int v10; // [esp+10h] [ebp-10h]
int v11; // [esp+14h] [ebp-Ch]
int v12; // [esp+18h] [ebp-8h]
int v13; // [esp+1Ch] [ebp-4h]

*(_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 binascii
from 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="")

# flag{Zzzz333_Is_Cool!!!}

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="")

# flag{Zzzz333_Is_Cool!!!}

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; // [esp+0h] [ebp-258h]
char v5; // [esp+0h] [ebp-258h]
int i; // [esp+D0h] [ebp-188h]
int v7; // [esp+DCh] [ebp-17Ch]
size_t v8; // [esp+E8h] [ebp-170h]
DWORD v9[3]; // [esp+F4h] [ebp-164h] BYREF
BYTE v10[264]; // [esp+100h] [ebp-158h] BYREF
char Destination; // [esp+208h] [ebp-50h] BYREF
__int16 v12; // [esp+209h] [ebp-4Fh]
char v13; // [esp+20Bh] [ebp-4Dh]
char Str; // [esp+214h] [ebp-44h] BYREF
char v15[49]; // [esp+215h] [ebp-43h] BYREF
int v16; // [esp+250h] [ebp-8h]

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 sha1
from 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="")

# flag{Easy_Hash_And_Y0u_Solve_1t_Quickly!!}

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; // [esp+0h] [ebp-17Ch]
char v5; // [esp+0h] [ebp-17Ch]
int i; // [esp+D0h] [ebp-ACh]
int v7; // [esp+DCh] [ebp-A0h]
int v8; // [esp+E8h] [ebp-94h]
size_t v9; // [esp+F4h] [ebp-88h]
char Str; // [esp+10Ch] [ebp-70h] BYREF
char v11[47]; // [esp+10Dh] [ebp-6Fh] BYREF
int v12[6]; // [esp+13Ch] [ebp-40h] BYREF
int v13[9]; // [esp+154h] [ebp-28h] BYREF

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, 0x27u);
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:004118A4 ;   __try { // __except at loc_4118C9
.text:004118A4 mov [ebp+ms_exc.registration.TryLevel], 0
.text:004118AB mov edx, 0
.text:004118B0 mov byte ptr [edx], 1
.text:004118B0 ; } // starts at 4118A4
.text:004118B3 mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
.text:004118BA jmp short loc_4118DE
.text:004118BC ; ---------------------------------------------------------------------------
.text:004118BC
.text:004118BC loc_4118BC: ; DATA XREF: .rdata:stru_4191D8↓o
.text:004118BC ; __except filter // owned by 4118A4
.text:004118BC lea eax, [ebp+var_48]
.text:004118BF push eax
.text:004118C0 call sub_411087
.text:004118C5 add esp, 4
.text:004118C8 retn
.text:004118C9 ; ---------------------------------------------------------------------------
.text:004118C9
.text:004118C9 loc_4118C9: ; DATA XREF: .rdata:stru_4191D8↓o
.text:004118C9 ; __except(loc_4118BC) // owned by 4118A4
.text:004118C9 mov esp, [ebp+ms_exc.old_esp]
.text:004118CC mov eax, var_48
.text:004118CF xor eax, 12345678h
.text:004118D4 mov [ebp+var_48], eax
.text:004118D7 mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh

这个必定触发异常,所以一定会进入异常处理,在004118C9处是我们的异常,这里我们将var_48这个值与0x12345678异或了,并且写回。

1
2
3
4
5
6
.text:0041188E loc_41188E:                             ; CODE XREF: sub_411790+ED↑j
.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.value


# test
if __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="")

# 44C3A7F112DA2BE728F451C5E2D09558
# flag{44C3A7F112DA2BE728F451C5E2D09558}

拔丝溜肆 (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; // rdi
__int64 i; // rcx
char v3[32]; // [rsp+0h] [rbp-20h] BYREF
char v4; // [rsp+20h] [rbp+0h] BYREF
char Str[88]; // [rsp+28h] [rbp+8h] BYREF
char Str1[104]; // [rsp+80h] [rbp+60h] BYREF
char Str2[57]; // [rsp+E8h] [rbp+C8h] BYREF
char v8[3]; // [rsp+121h] [rbp+101h] BYREF

v0 = &v4;
for ( i = 72i64; i; --i )
{
*(_DWORD *)v0 = 0xCCCCCCCC;
v0 += 4;
}
sub_14001137F((__int64)&unk_140023014);
srand(0x1BF52u);
memset(Str, 0, 0x32ui64);
memset(Str1, 0, 0x50ui64);
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 0i64;
}

长度要求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; // rcx
__int64 v3; // rdx
int v5; // [rsp+24h] [rbp+4h]
int v6; // [rsp+64h] [rbp+44h]
int v7; // [rsp+84h] [rbp+64h]

sub_14001137F((__int64)&unk_140023014);
v5 = j_strlen(input);
v3 = (unsigned int)(v5 >> 31);
v2 = 3i64;
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; // rax
int v1; // [rsp+24h] [rbp+4h]
int i; // [rsp+44h] [rbp+24h]

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_str

# 由于给的密文没有=,这里没有写pad的相关部分。
def 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 res

def 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="")

# flag{12573882-1CF1-EB5E-C965-035B1F263C38}

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; // ebp
_BYTE *v4; // eax
int v5; // ecx
int v6; // eax
int v7; // ecx
char *v8; // eax
_OWORD v10[2]; // [esp-40h] [ebp-4Ch] BYREF
char v11; // [esp-20h] [ebp-2Ch]
char v12; // [esp-8h] [ebp-14h]
int v13; // [esp-4h] [ebp-10h]
_DWORD v14[3]; // [esp+0h] [ebp-Ch] BYREF
void *retaddr; // [esp+Ch] [ebp+0h]

v14[0] = v3;
v14[1] = retaddr;
v11 = 0;
v10[0] = 0i64;
v10[1] = 0i64;
sub_401020("Please input your flag:", 0);
sub_401050("%s", (char)v10);
v4 = &loc_402000;
v5 = 53;
do
{
*v4++ ^= 0x54u;
--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; // ecx
int v3; // edi
int j; // esi
unsigned __int8 v5; // dl
char result; // al

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; // eax
int v3; // edi
int v4; // ebx
unsigned __int8 v5; // dl
unsigned int i; // [esp+10h] [ebp-4h]

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_and_SMC_is_interesting!
# flag{RC4_and_SMC_is_interesting!}

看了flag才知道是RC4和SMC,我自己复现算法来解这题完全行不通,我也不知道为啥。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!