NKCTF2023
NKCTF2023
ez_baby_apk
是一个apk文件,用jadx打开。
key是reversecarefully将e替换成3,iv是reversehavemagic的md5的值,然后使用DES加密,不过这个DES加密是自己写的其实他并不是真正的DES。就是名字一样而已
可以看到是AES加base64加密。(密文是使用jadx的其他窗口来查找的,在代码窗口里面找不到)
直接CyberChef一把梭。
也可以用python写脚本
import base64
from Crypto.Cipher import AES
def decrypt(iv, cipherText, key):
cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode())
decodedCipherText = base64.b64decode(cipherText)
original = cipher.decrypt(decodedCipherText)
return original.rstrip(b"\0").decode()
print(decrypt('r3v3rs3car3fully',b'BxLHc1KruiH31I94W171oal+9olDzgBIjnK/J1Db0IUyi+MbI38+nw62ejCPShRB','c368938a22ddbd9c77721fd203ac2c9c'))
#NKCTF{nI_k@i_sHi_zhu_j1an_il_Jie_RE_le}
PMKF
整个加密逻辑很简单就是有三个文件操作,第一个读取出key的长度为5,第二个读取出key的内容,第三个是读取16个字节,然后将每个字节分成4组比特位,每组两个比特位,范围是0,1,2,3。刚好对应的是迷宫的四个方向,没错最后一步就是走迷宫。
之后就是用脚本走迷宫了
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, ],
[1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, ],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, ],
[0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, ],
[0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, ],
[1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, ],
[0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, ],
[0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, ],
[0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, ],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ],]
def dfs(maze, start, end, path):
i, j = start
if i < 0 or j < 0 or i >= len(maze) or j >= len(maze[0]):
return False # 如果索引超出迷宫地图的范围,则返回 False
if maze[i][j] == 1:
return False
if start == end:
return True
if start in path:
return False
path.append(start)
if dfs(maze, (i-1, j), end, path):
return True
if dfs(maze, (i+1, j), end, path):
return True
if dfs(maze, (i, j-1), end, path):
return True
if dfs(maze, (i, j+1), end, path):
return True
path.pop() # 回溯,将最后一个节点从路径中删除
return False
start = (1, 0)
end = (9, 12)
path = []
dfs(maze, start, end, path)
print(path)
for i in range(1, len(path)):
if (path[i - 1][0] - 1 == path[i][0]):
print("w", end='')
elif (path[i-1][0] + 1 == path[i][0]):
print("s", end='')
elif (path[i-1][1] - 1 == path[i][1]):
print("a", end='')
elif (path[i-1][1] + 1 == path[i][1]):
print("d", end='')
#ddssaassdssassddwdddddwdwwwwwdwddsddwdddsssasaawaasassdddddssaa
#之后需要将wasd换成对应的0123
然后就是加上前面的长度05和key(nkman,转化为16进制)
path = [1, 1, 2, 2, 3, 3, 2, 2, 1, 2, 2, 3, 2, 2, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 2, 1, 1, 0, 1, 1, 1, 2, 2, 2, 3, 2, 3, 3, 0, 3, 3, 2, 3, 2, 2, 1, 1, 1, 1, 1, 2, 2, 3, 3, 3]
print("056e6b6d616e",end='')
for i in range(0,64,4):
tem = 0
tem += (path[i] << 6)
tem += (path[i + 1] << 4)
tem += (path[i + 2] << 2)
tem += (path[i + 3])
tem ^= 21
print(hex(tem)[2:],end = '')
"4fef7eb0004415047000bea9eeb043aa"
not_a_like
这个题目时加upx的壳,并且时pyinstaller打包的。我们的思路就是将upx脱掉,解包反编译。当然也有的师傅时直接解包的,然后将pyc的前16个字节换成struct.pyc的这里其实不换也可以用反编译网站反编译出来,但是在之前没有这个网站的时候我们都是用uncompyle6来反编译的,但是有了这个网站之后就不需要了,而且这个网站的功能比较强大所以不需要替换也行。
他的加密逻辑是RSA,base64,RC4,base64。后面三个可以直接CyberChef一把梭。
将获取到的数据再进行RSA解密,由于这个e特别大,所以可以使用维纳攻击(需要再github上下载相应的库才能使用)
from RSAwienerHacker import hack_RSA
import libnum
import base64
from Crypto.Cipher import ARC4
def decrypt_rc4(key, ciphertext):
cipher = ARC4.new(key)
plaintext = cipher.decrypt(ciphertext)
return plaintext
pub_key = [
0x1B6A7561D99E6FC35BA3C241159424698BF3CAC017CFCE8BB325CC9AF9CBCBDB3997B08D922C8705FC3EEAEF50D60ADAB2757A7204715483A1D61250297059535CFE9CD11C98CAD293EB921D777F4F910905D79CDCA5C1EC1FBA5DA74DB165F82BBE29EA0B2E597860FC6D2C51C12D46BF11AFA5018496DDFC3474B10B4457,
0x6C8E1CC5B384DE3B3316C22CF72D9895406298E172B5F4D890BDC04889BB43CD4892689DE701C84ED68B4CBC7193926BCCB0A4F259D2E752FAEF3CD590A793F120D15424AEB3CD53F5D59B5D41D699694ABF4F01532F0F1CE127B07958FB874982E757EF97643335376790BC990CEE9D7F0D05DA90AD62084C88BFA9C9BEB683]
pub_key = [
0x1B6A7561D99E6FC35BA3C241159424698BF3CAC017CFCE8BB325CC9AF9CBCBDB3997B08D922C8705FC3EEAEF50D60ADAB2757A7204715483A1D612502970595358BCFE9CD11C98CAD293EB921D777F4F910905D79CDCA5C1EC1FBA5DA74DB165F82BBE29EA0B2E597860FC6D2C51C12D46BF11AFA5018496DDFC3474B10B4457,
0x6C8E1CC5B384DE3B3316C22CF72D9895406298E172B5F4D890BDC04889BB43CD4892689DE701C84ED68B4CBC7193926BCCB0A4F259D2E752FAEF3CD590A793F120D15424AEB3CD53F5D59B5D41D699694ABF4F01532F0F1CE127B07958FB874982E757EF97643335376790BC990CEE9D7F0D05DA90AD62084C88BFA9C9BEB683
]
e = pub_key[0]
n = pub_key[1]
d = hack_RSA(e, n)
enc = b'EeJWrgtF+5ue9MRiq7drUAFPtrLATlBZMBW2CdWHRN73Hek7DPVIYDHtMIAfTcYiEV87W7poChqpyUXYI3+/zf5yyDOyE9ARLfa5qilXggu60lmQzFqvFv+1uOaeI2hs2wx+QZtxqGZzC0VCVWvbTQ52nA2UdUtnk8VezRMPMfmf7rOqPxDTv/aacLnI3RdLG2TbT52qtN4+naejI7Xe8HLOL765OZKdDBERKwd5ARQ3UL6YPbuOKOQahIFddnIX6rZ7dTNqCUDOjfJbMdrzJVDNjmNlkLNtYFo7M65Wfwj6PV5vvtT33FsmH50/YLEasnlCiJujYOgi2KCdf5msz1dPEvrXDDL6Csnjo+6m/44RzlluzcqMS5ZJFdrHEh68LIqtu+HCO+69Dyq4e22APq8wgN9kU6R8kikXSn/Ej0N/jOvomFCbkHskRl8xP1KgWFW0SMVDlaDCM4EKG812VgDWgSYOUnVhVpz65uOtg4Z8PrPI+BW4398dQYhD24D9EIPgvtmhNrHiEHouB46ElTGQgZBhtn6y9tL1sw=='
enc = base64.b64decode(enc)
key = b'911dcd09ad021d68780e3efed1aa8549'
enc = decrypt_rc4(key, enc)
enc = int(base64.b64decode(enc))
# print(int(enc))
m = pow(enc, d, n)
print(libnum.n2s(m).decode())
#Hacked!
#flag{chinese_zhenghan}
babyrust
这个题目不需要什么技巧,我们可以直接根据他程序的输出来求解。
这是一种输入输出,如图他会把字符加密后的结果打印出来,并且如果加密后的数据是不可打印字符那么它也可以把错误字符的索引输出出来,那我们就将所有的字符输入进去然后拿到他们的密文之后查表就可以。
src = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{}_"
src_enc = "lmnopqr#$%&'()*[\\]^_`abcdeLMNOPQRSTUVWXYZ;<=>?@ABCDEFHj"
enc = ")&n_qFb'NZXpj)*bLDmLnVj]@^_H"
for i in enc:
index = src_enc.find(i)
print(src[index], end='')
#NKCTF{WLcomE_NOWayBaCk_RuST}
earlier
这个题目考察了反调试和SMC,我自己在实际过程中是按照手动运行SMC来做的。这次我按照官方wp的去反调试然后运行自解SMC。首先拖进IDA,发现有很多的花指令。
这里我借鉴官方的wp来写脚本去除花指令。
import idc
start = idc.get_segm_start(0x0040152D)
end = idc.get_segm_end(0x0040152D)
def nop(addr, endaddr):
while addr < endaddr:
patch_byte(addr, 0x90)
addr += 1
pattern = "33 C0 85 C0 74 03 75 00 E8"
while(start < end):
cur_addr = idc.find_binary(start, SEARCH_DOWN, pattern)
if cur_addr == -1:
break
else:
print("path address:" + hex(cur_addr))
nop(cur_addr, cur_addr + 9)
start = idc.next_head(cur_addr)
找到第一个反调试函数sDebuggerPresent。将jz跳转改为jnz跳转。让程序在调试的时候运行,不调试的时候直接退出。
然后从上面可以看出还有一个TLS反调试。这个反调试运用到了ZwSetInformationThread和NtQueryInformationProcess函数,至于如何绕过这种反调试手段,我们可以按照wiki上面的说法将函数的参数0x11改成其他的任何数。
然后下断点运行起来就会断住,之后便可以查看他解密后的代码。
可以明显的看到这是个RC4加密,密钥是secret。然后提取密文就可以得到flag。
Virtualization Technologyt
这个题目是一个虚拟技术的题目,改题目是按照github上面的一个项目来写的,这个项目同样也可以帮助我们来初始VT技术,这里有一位大佬将VT技术讲解的比较详细,看完这些之后就可以将这个题目的执行流程清楚明白,主要的代码如下面所示。
我们要找到一个result然后用md5加密就可以得到flag。a1,a2如下图所示
这两个值的确定需要对这个项目的源码熟悉。
其实就是根据客户机退出的原因来选择对应的处理方式。好的那么这个代码就是算法的优化,因为原来的代码能跑出结果但是会很慢,所以需要我们对这个算法进行优化。主要的逻辑就是找到三个数的公倍数,从小到大排列,第一百个然后用md5加密即可。这里我自己没有写出来,就把官方的解法贴出来。这个题目主要是了解VT技术,题目偏密码。
from heapq import *
from hashlib import md5
a, b, c = (18, 10011, 10293)
heap = []
heappush(heap, 1)
visted = set()
visted.add(1)
def Print(num):
print(num)
s = str(num).encode (encoding='utf-8')
print("NKCTF{%s}"%(md5(s).hexdigest()))
cnt = 0
while True:
Min = heappop(heap)
cnt += 1
if (cnt == 99):
Print(Min)
break
Num = Min*a
if (not Num in visted):
visted.add(Num)
heappush(heap, Num)
Num = Min*b
if (not Num in visted):
visted.add(Num)
heappush(heap, Num)
Num = Min*c
if (not Num in visted):
visted.add(Num)
heappush(heap, Num)
# 2004120970778355552
# NKCTF{ead6054e98ae31f87b2340f065315090}