2022ASISCTF

2022ASISCTF

闲来垂钓碧溪上,记失踪人口回归,再不更就荣升年更博主咯x),被学弟们🐴惨,其实有人偷偷去奇安信恰钱,我不说是谁.-.Lu1u,但是频率确实是低了不少。亦如学弟所言: 厉害的不是我,是18岁~

Get Tips: blackbox、unicorn、hook、angr。

figole

Android题,java层逆向,通过混淆隐藏控制流。

timesnewroman.ttf是个SQLite数据库,程序通过l1和l2方法从中提取出数据moveToPosition方法用于选着哪一行。

image-20221016210943577

image-20221016210842327

关键加密为shctfdex下的ech函数,为cbc模式的aes,key和iv通过getkey获取,将一个拼接的字符取奇/偶下标分成两组。

image-20221016211410711

入口处是 DActivity,对从数据库拿到的第二段数据解密之后还要经过cdec解密,Android👴直接hook拿数据即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
s1='XPNXVO]PJYSJXTNYVM]PJ^RMX\OZVO\W'
t=''
kk=b'key'
for i in range(32):
t+=chr(ord(s1[i])^kk[i%3])
print(t)
s=bytes.fromhex(t).decode()+'8i7a4t6155263210'
k=''
iv=''
for i in range(len(s)):
if i%2==0:
k+=s[i]
else:
iv+=s[i]
print(k,iv)
from base64 import b64decode
from Crypto.Cipher import AES
c='7mePfqpM6Wd1El2sj4dlUboU6PieF7La8IJ1e76cfp4='
c=b64decode(c)
aes=AES.new(k.encode(),AES.MODE_CBC,iv.encode()[:16])
print(aes.decrypt(c))

traditional

rust逆向,符号健在。创建flag.txt调试即可,得到程序流程为base64加密、复杂的移位运算、base64解密。

image-20221016212336795

经典移位运算,跑出置换表即可,因为是在base64表之后做的变换,字母表就64个,patch程序获取即可。

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
import string
from base64 import *
s1='This is the flag: '
s2='This is the flag: MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYw'
s3='This is the flag: MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYw Just decode it :P'
#倒叙处理
s4='P: ti edoced tsuJ wYWZkNmYhlDO3YTN0MjMxAjZlR2YiFWO4cjN1QzMyEDM :galf eht si sihT'
#类似换表处理,直接通过patch base64表 跑出变换后的表规则
"""
' ' -> '/'
':' -> '+'
"""
s='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
ntb='OPQRSTUVWXYZABCDEFGHIJKLMNtuvwxyzabcdefghijklmnopqrs5678901234+/'

f=open(r'flag.enc','rb')
enc=f.read()
f.close()
enc=b64encode(enc).decode()
m1=''
for i in range(len(enc)):
m1+=s[ntb.index(enc[i])]
m1=m1[::-1]
print(m1)
print(b64decode('QVNJU3tzSU1wTDNfYlU3X20xeDNkX1IzdkVyNWVfN0FzSyF9'))

hannibal

做题时有点犯迷糊了,逻辑很简单,非得想着爆破时间戳… 导致忽略了原跟这一点

首先将输入转为二进制字符串,其中第一个字节转为7bit,故长度p=7+(n-1)*8,之后又通过随机数找到以p为模数的一个原跟,依次来遍历其简化剩余系,用于生成混淆表。

这样会丢掉一位,因为简化剩余中不存在0,第一个因为是在ascii表的范围内,所以恒定为0。

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 Crypto.Util.number import *
from libnum import s2b
def get_all_randk(p):
tb=set()
for i in range(2,p-1):
if (p-1)%i==0:
tb.add(i)
tb=list(tb)
kk=[]
for i in range(2,p-2):
if pow(i,p-1,p)==1:
if all([pow(i,j,p)!=1 for j in tb]):
kk.append(i)
return kk
def get_arr(x,p):
ans=[1]
for i in range(p-1):
ans.append((ans[i]*x)%p)
assert ans[-1]==1
return ans[:-1]

f=open(r'flag.enc','rb')
buf=f.read()
f.close()
enc_len=0x276
p=0x277
n=(p-7)/8
print(n)
kk=get_all_randk(p)
mm=s2b(buf)[1:-1]

for index in range(len(kk)):
res=['#']*p
sh_tb=get_arr(kk[index],p)
for i in range(len(sh_tb)):
res[sh_tb[i]]=mm[i]
ans0='00'+''.join(res[1:])
ans1='01'+''.join(res[1:])
ans0=(long_to_bytes(int(ans0,2)))
ans1=(long_to_bytes(int(ans1,2)))
if b'ASIS' in ans0 : print(ans0) ;break
if b'ASIS' in ans1 : print(ans1) ;break
#print('res: ' +ans)
#ASIS{Bi7_fL!p__Fl0P__w1Th_5ImPl3_pOW!!}

赛时思路 patch程序爆破,将随机序列写入到文件再读取,奈何索引已经大于了单个字节,不失为一种爆破思路。

vivbit

aarch64架构,hannibal升级版,变换更加复杂,单纯调试看就不合适了,不过加密的核心仍是bit位的混淆,各个字符间并无关系,可以考虑爆破/测试,测试每个字节影响的bit位。

Retard👴方法太帅了,qemu模拟执行,比较密文和测试flag字符影响的bit位,如果相同则当前位置是正确的flag字符,逐个字节进行爆破,进而getflag。x) 抄作业

image-20221016214440859

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
import os
import string
from libnum import s2b
def enc(flag, size=0):
if size:
flag = flag.ljust(size, b'*')
open('./flag.txt', 'wb').write(flag)
os.system('qemu-aarch64 vivbit_orig')
return open('./flag.enc', 'rb').read()

test_size = 64 #需要为4的倍数进行测试
flag_enc = open('./chall_flag.enc', 'rb').read()
verify_bins = s2b(flag_enc) #转成二进制串
flag = ''
while len(flag) < 64:
cached = dict()
diff = set()
a = s2b(enc((flag+'L').encode(), test_size)) #生成一个模板用于找出修改当前位置字节影响密文中的bit位
for ch in string.printable:
b = s2b(enc((flag+ch).encode(), test_size)) #爆破当前不同的位,生成一个字典
cached[ch] = b
for i in range(len(a)):
if a[i] != b[i]:
diff.add(i) #将当前字符可能影响的所有位置都记录到diff
diff = list(diff)
print(diff)

maybe = []
for ch in string.printable:
tmp = cached[ch]
if all([tmp[i] == verify_bins[i] for i in diff]): #影响的bit位与密文中的相同则为正确字符
maybe.append(ch)
print(maybe)
if len(maybe) == 1:
flag += maybe[0]
else:
flag += '?'
print(flag)


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