复现一下2022V&NCTF

Crypto

ezmath

生成数之后,挖了开头4位空,我们需要向服务端发送这4位,暴力求解

通过计算得知,把给出的数字*4,即可求出n

先了解一下pwntools的用法:

https://pwntools-docs-zh.readthedocs.io/zh_CN/dev/intro.html

连接上服务端,然后接受服务端发送的数据

1
2
3
from pwn import *
r =remote('node4.buuoj.cn',27061) #端口不固定
d = r.recvline()

从服务端发送的数据中截取所需的字符,

1
2
s = d[16:32]    
enc = d[37:101] #当然也有更好的方法

在本地进行sha256暴力破解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def check(s,enc):
table = '0123456789abcdefghijklmnopqrstuvwxyz' + 'abcdefghijklmnopqrstuvwxyz'.upper()
ans=''
for i in table:
for j in table:
for k in table:
for l in table:
a = i + j + k + l + s
if sha256(a.encode()).hexdigest() == enc:
ans+=a[:4]
print(ans)
return (ans)
tmp=check(s,enc)
r.sendline(tmp)

将得出的四位字符发送给服务端,服务段返回,一共循环777次才能满足

1
context.log_level = 'debug'

在开头加上以上的代码就能得到下面的形式

参考文档:https://lu1u.xyz/2022/02/12/VNCTF2022/

百感交集,查看源代码请参考上面的链接(同样的代码为什么在我电脑上出错)

flag{068f36ec-cbdb-4840-b069-039bee388b2a}

Reverse

BabyMaze 迷宫

下载下来的文件只是一个3k的pyc文件,而且是python3.8版本的。一开始我用的是网页的pyc解码,发现根本不支持。然后我用的是uncompyle6解码,还是不支持。最后我用的是pycdc,将其转换成了字节码。

当初看的关于pycdc的参考链接:

http://www.syjblog.com/?p=470

1
./pycdas path of pyc file

当初用的手算的,虽然解出来,但是太麻烦了……

最好用dfs(深度优先搜索算法)来解这道题

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
map = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 0, 1], [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
0, 1, 0, 1, 0, 0, 0, 1, 0, 1], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], [1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1, 0, 1,
0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1,
0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1], [1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1], [1,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
1], [1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0,
0, 0, 1, 0, 1, 0, 0, 0, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0,
1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1,
1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1, 0,
1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], [1, 0, 0, 0, 1, 0,
1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1], [1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1], [1, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1]]

usedmap = [[0 for i in range(len(map))] for i in range(len(map)) ] #创建对应的状态标志列表

flag = ''

direction = [[0,1],[1,0],[0,-1],[-1,0]] #设置四个方向的单次变化值

signs = ['d','s','a','w'] #设置对应方向的名称

def DFS(x, y , step):
global flag
if x == 29 and y == 29: #设置终点
print("path:")
print(flag)
print("steps:")
print(step)
return

for k in range(4):
tx = x+direction[k][0] #下一个位置的横坐标
ty = y+direction[k][1] ##下一个位置的纵坐标
if tx < 1 or tx > 30 or ty < 1 or ty > 30:
continue
if map[tx][ty] == 0 and usedmap[tx][ty] == 0:
usedmap[x][y] = 1 #探索过,则状态标志列表对应坐标置1
flag += signs[k]
DFS(tx,ty,step+1)
flag = flag[:-1] #回溯,则flag最后一位清除
usedmap[x][y] = 0 #回溯,则状态标志列表对应坐标置0

x = 1
y = 1
DFS(x, y ,0)

自己修改了一些代码,使其更简洁。用下面的图就能很好理解这段代码了,先定义一个地图列表,再定义一个使用空间列表。

搜索的方向为顺时针,且搜索的条件为下一个位置每次搜索成功,都会使对应坐标状态标志置1。如果其中一个分支没有成功到达,则回溯,回溯到最近的一个分支且分支后的状态标志都置为0

其中一个小知识点就是,在函数中使用全局变量时,需要使用global关键字,其作用是显式的告诉解释器flag为全局变量,否则解释器会认为flag为局部变量,从而报错。

查看官方文档后才发现这个题还有我没发现的盲点

没有用uncompyle6反汇编成功,是因为有==花指令==!!!

这边看一下

1
2
3
4
5
6
7
import marshal, dis

f = open("BabyMaze.pyc", "rb").read()

code = marshal.loads(f[16:])#这边从16位开始取因为是python3 python2从8位开始取

dis.dis(code)

开头三个字节在不停的跳,这就是花指令了

下面的是我不加code = marshal.loads(f[16:])这个语句的输出结果,开头多出了23行的东西……

cm1 (安卓逆向)

昨天看这一题看的心态崩了,今天务必要把它复现出来。

发现这题用jadx有问题,参观大佬博客才发现用的都是GDA,下面是关于GDA的介绍:https://zhuanlan.zhihu.com/p/28354064

什么事dex文件?

https://www.cnblogs.com/zhaoyanjun/p/5736305.html

所以说.dex很重要,如果题目中出现dex文件,则必须要注重查看。

跟进这个copyFiles函数

1
2
3
4
5
6
obyteArray = bytearray(open(r"ooo", "rb").read())
bBytes = b"vn2022"
for i in range(len(obyteArray)):
vi5 = i % 1024
obyteArray[i] = ((obyteArray[i] ^ bBytes[(vi5 % len(bBytes))]) & 0x00ff)
open(r"classes.dex", "wb").write(obyteArray)

大佬的博客传送门:https://blog.shi1011.cn/ctf/2162

ooo文件是data文件,用more查看一下,emmm

也就是从ooo文件读取的数据与ooo进行异或操作等,转化成dex文件

下面是大佬z1r0写的代码

1
2
3
4
5
6
7
8
9
10
11
12
bBytes = b'vn2022'

with open("ooo", "rb") as f:
flag = f.read()
#print(flag)

with open('output.dex', 'wb') as f:
obyteArray = bytearray(open(r"ooo", "rb").read())
for i in range(len(flag)):
vi5 = i % 1024
obyteArray[i] = (obyteArray[i] ^ bBytes[(vi5 % len(bBytes))]) & 0x00ff
f.write(obyteArray)

在linux里,把out.dex放在dex2jar以下目录中

运行以下命令,转化为jar

把jar文件放到windows中,用jd-gui分析,加密算法出来了

可以看出此为xxtea算法。最核心的是要明白:XXTEA算法使用128bit的密钥对以32bit为单位的信息块进行加密。

H4pPY_VNCTF!!OvO为秘钥,给我们的字符一共16位,一位8bit,16*8=128bit。

而arrayofbyte1数组则是需要加密的信息,==不太懂为什么是11位==,一共44个数字,4个数字拼成一个32bit的信息块?

1
2
3
4
5
6
from struct import unpack
a = [68, 39, -92, 108, -82, -18, 72, -55, 74, -56, 38, 11, 60, 84, 97, -40, 87, 71, 99, -82, 120, 104, 47, -71, -58, -57, 0, 33, 42, 38, -44, -39, -60, 113, -2, 92, -75, 118, -77, 50, -121, 43, 32, -106]
bytes = bytes(i % 256 for i in a)
print(bytes, len(bytes))
print(unpack("<11L",bytes))
print(unpack("<4L", b"H4pPY_VNCTF!!OvO"))
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
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 6 + 52/n;
sum = 0;
z = v[n-1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p=0; p<n-1; p++)
{
y = v[p+1];
z = v[p] += MX;
}
y = v[0];
z = v[n-1] += MX;
}
while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52/n;
sum = rounds*DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p=n-1; p>0; p--)
{
z = v[p-1];
y = v[p] -= MX;
}
z = v[n-1];
y = v[0] -= MX;
sum -= DELTA;
}
while (--rounds);
}
}
int main() {
uint32_t key[4] = {1349530696, 1314283353, 558257219, 1333153569};
uint32_t v[12] = {1822697284, 3377000110, 187091018, 3630257212, 2925741911, 3106891896, 553699270, 3654559274, 1560179140, 850622133, 2518690695, 0};
btea(v, -11, key);
printf("%s", (char*)v);
return 0;
}

btea函数,一参v是加密的组元的起始地址,二参11则是需要加密的组元个数,负数位解密,三参则是秘钥的起始地址

VNCTF{93ee7688-f216-42cb-a5c2-191ff4e412ba}