BUAACTF-Writeup

本文最后更新于:2023年7月8日 上午

BUAACTF-Writeup

$\mathscr{Author:CoolWind}$

大一鼠鼠完全不会别的只能水水misc拿点分数罢了QnQ

仅供参考,大佬勿喷

第一次打CTF扔点入不了眼的战绩:

  • 总排名:10/45
  • 新星赛赛道:5/23
  • Misc:6/8 一血x1 二血x1 三血x2
  • Web:1/8
  • Reserve:3/9

所以说啊,鼠鼠是一柱擎天型选手,除了misc完全都不会捏。

求大佬教我本领。

Misc向

1、zhuzhu*

屑X2喜欢的鬼灭开更啦!特地把flag藏在了猪猪的图片里,你能找到吗?

hint1:糟了,X2貌似在gift里放了不得了的东西?

下载压缩包之后,直接解压压缩包,得到如下四个文件。

解压压缩包

因为本身下载了MATLAB,所以自动识别出了enc1.m是MATLAB的代码文件。

用MATLAB打开之后发现其为一份傅里叶变换的加密的代码,同时发现其中有flag.png

enc1.m

剩下两个文件enc.pngenc2.py分别打开,发现enc.png并不能打开为图片文件,而enc.py是我看不懂的一份python,所以我们先不管他

建议会密码学的可以研究一下这份代码,但是鼠鼠我完全不会密码学,所以我选择了忽视。

注意到有一份没有后缀名的gift4u文件,用winHex打开之后发现其文件头有一串MATLAB的说明,则猜测其为MATLAB下的一种文件。

gift4u用winHex打开

因为有一份MATLAB的代码,所以优先考虑是不是.m文件,可惜并打不开。

通过万能的互联网搜索我们可以知道,MATLAB的数据文件.mat符合这样的文件头格式,所以我们将其改为.mat,然后用MATLAB打开,可以发现这份文件正对应着enc1.m中的变量。

gift4u.mat

在之前的代码中我们可以知道W变量对应着从flag.png中提取出来的数据,所以我们直接将W变量写入flag.png

1
imwrite(W,'flag.png');

这样我们就得到了flag.png

flag

此题有一个小插曲,因为作者过于SB导致肉眼识别flag失败,多打了一个_,本想前去拷打出题人结果被出题人狠狠拷打。

罪证

被狠狠拷打

2、zhuzhu’s revenge*

X2藏flag的时候不小心把中间数据给你们了,现在他带着完全版回来了!

同上一题一样,我们直接下载文件之后解压发现跟上一题的文件几乎完全相同。

所以我们同样的直接打开MATLAB然后熟练地改掉gift4u的后缀名将其导入。

发现如题目描述所说的一样,出题人真的没有直接给我们W矩阵的数据,真的是丧尽天良

但是我们可以敏锐的发现,出题人仍然不小心给了我们足够的中间数据,所以我们也不用去看enc2.py

打开文件

我们想要的数据是W矩阵,而我们发现W矩阵被分解为了Uw,Sw,Vw三个奇异值,那么意味着我们用这三个奇异值可以反向计算出W矩阵。

但是我们发现,万恶的出题人同时给我们删掉了我们需要的Sw矩阵。

接着向下分析我们可以发现,Sw矩阵可以用S2矩阵逆推,但是出题人也没给我们S2矩阵。

那就接着向下分析,我们可以发现,S2矩阵居然可以用LL2,U,V三个矩阵逆推出来,但是出题人只给了我们LL2矩阵,这不是什么好文明

但是U,S,VLL矩阵分解的奇异值,而我们有LL矩阵,所以逻辑闭环,从后往前逆推就可以计算。

这里附上逆推的代码,其中用奇异值反推矩阵的方式来自神奇的ChatGPT,大家可以自己去搜索。

1
2
3
4
5
6
7
[U,S,V]=svd(LL);
S2=LL2/U/V';
af=0.514;
Sw=(S2-S)/af;
sigma=diag(Sw);
Img=Uw*diag(sigma)*Vw';
imwrite(\img,'flag.png');

然后我们就得到了如下flag。

flag

但是我们交上去并不对,这次在确保了自己的眼睛完全没有出现任何问题之后,我选择了再次拷打出题人。

拷打!

拷打出题人成功,获得flag。

注:出题人对我们完全不需要用enc2.py感到十分高兴

3、Which Element*

小z同学的师傅为了考验小z,将炼制“再无ddl”药的最后一种配方元素藏到了文件中,小z同学承诺如果你帮他解出答案,他炼制出药后会给你一份,你愿意帮助他吗?

hint1:Try search “6 color block cipher”.

下载了文件之后,我们发现这是一个流量分析题。

将流量包直接扔到WireShark中分析,发现其中有很多TCP流量和一些http流量,筛选出其中http流量。

筛选http

发现是用户与服务器的应答流量包,直接抓取其分组字节流然后保存到本地,我们可以得到四个文件,分别为fakepw.png,flag.wav,passwd.png,flag.zip四个文件。

抓取文件

Audacity打开flag.wav我们可以得到摩斯电码的图像,手动分离之后解码,发现是个骗子!

恭喜我们又浪费了几分钟的时间。

打开flag.zip我们可以发现这个压缩包有密码,因为不想winHex看加密,所以我选择了直接扔到ZenCenOp里面去解伪加密,发现解密成功,随后解压,发现文件损坏,修复损坏文件之后发现密码又回来了!所以我们可以知道这个zip是个真加密。

恭喜我们又浪费了几分钟的时间。

然后就是攻克passwd.png的时间,但是发现这个东西不能够解密一点点。

那是一个焦头烂额的晚上,CoolWind正对着一张彩色图片疯狂发电

他实在是无法得到图片中的秘密,所以他使用了「奥义之球球hint之术」

终于出题人经受不住考验给出了hint:Try search “6 color block cipher”.

我们百度此关键词无果,只能使用科学去谷歌搜索,果不其然我们很容易就得到了如下图片:

密码表

用此密码表对应所给的passwd.png我们可以得到压缩包的密码为3.1415

解压压缩包得到如下三个文件:

解压

hint.png扔到winHex中分析可以在文件的最后得到一串base64

hex分析

解密之后可以得到一句话:What's that in my hand

通过识图可以知道图片中的人是托里拆利,其手中拿的东西是水银气压计。

到了这里我们完全看不懂这个hint,在报答出题人之后我们了解到这个题和图片的频域变换有关,搜索相关知识我们可以知道,CTF中常用的一种关于图片的频域变换的加密是盲水印。

所以我们此时就可以理解这个hint:“水银”意味着“水印”。

这是一个很烂的谐音梗,是一个坏文明,大家不要学习

所以用BlindWaterMark-masterpython2直接运行或者python3使用--oldseed运行即可得到其中盲水印。

此处python3版本不能过高,不然在运行时会报错,因为脚本中使用了一个最新版中已经被删除的语法。

因此我们可以得到flag:

盲水印

4、ChatGPT

都什么年代了还在玩传统chatgpt,试试这个能赚money的(

复现环境地址:nc 10.212.27.23 23693

Ubuntu打开地址,跳出一串随机的sha256编码。

sha256

通过python编写sha256碰撞代码:

1
2
3
4
5
6
7
8
9
10
11
12
from hashlib import sha256
flag_sha256="339e60716779f7b89754eb7e306a6a040d2b951661113ee160badd9f9d6c2b35"
strs="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&()*+,-./:;<=>?@[\]^_`{|}~ "
for a in strs:
for b in strs:
for c in strs:
for d in strs:
code=sha256()
flag=str(a)+str(b)+str(c)+str(d)+'ZFtxnRN3yVBvVKH7l2neXJfDDMzY'
code.update(flag.encode())
if code.hexdigest()==flag_sha256:
print(flag)

进行sha256碰撞可以得出XXXXQtKr

每次给的密码是随机的,因此需要自行进行sha256碰撞

进入ChatGPT,进行答题。

输入/exam可以进行答题,里面题目的答案需要大家自己探索,绝对不是因为作者不会

成功答对所给的所有题目之后我们可以得到第一段flag是BUAACTF{C0Ngr@~tuLati0N5_Y0u_R~

之后输入/pss可以和gpt进行猜拳游戏。结果可想而知,有一方后出的猜拳游戏是十分公平的,所以gpt会一直赢,那么我们就要思考如何hack。

既然输会扣掉相对应的钱,那么如果我们bet一个负数会怎么样呢?

hack /pss

惊喜的发现我们得到了很多money1,成功的hack了/pss

那么来到/hard-pss我们同样输入负整数,会发现提示No hack!证明直接输入负数的方式不能再次hack。

但是我们可以发现,不论我们bet多少整数的money2,其数量不会减少并且money2会变成0.0的浮点数。

通过查询python的数据类型可以知道,python的数据类型有整数、浮点数、复数。既然整数和浮点数都不行,那么为什么不试试复数呢?

hack /hard-pss

啊哈!又从gpt那里搞了好多米!

这样我们就可以愉快的去买flag了。

消费money1可以买到第二段flag:4~r3a1_MA5Ter_of_0ur_s1llY_CH@t

消费money2可以买到第三段flag:gpT__5_L0vvvvv3_fr0m_M1sc!}

将三段flag拼起来我们可以得到完整的flag:

BUAACTF{C0Ngr@~tuLati0N5_Y0u_R~4~r3a1_MA5Ter_of_0ur_s1llY_CH@tgpT__5_L0vvvvv3_fr0m_M1sc!}

5、Screenshot

小橘子在没事干的时候特别喜欢在各大社交平台灌水,某天他在某平台高强度冲浪的时候遇到了一位自称是BUAACTF2023出题人的网友。在交谈过程中,该网友不小心发送了一张电脑屏幕截图,这引起了小橘子的注意。如果能通过这张图片找到一些有用的信息,或许就能提前拿到比赛的flag……

小橘子在该社交平台上的id:@PaulGeo43512452

如果经常surfing freely on the Internet的朋友们看到这样的id会想到什么呢?

对了!要么是微博,要么是小蓝鸟。继续查看给我们的Screenshot:

Screenshot

我们可以发现右上角有很明显的小蓝鸟,因此我们可以锁定该id就是小蓝鸟id。

我们使用科学登录小蓝鸟之后搜索该id之后可以在Replies里面看到如下推文:

Replies

发现了一样的截图,这样我们就可以点开发送者@master0751的原版推文进行查看:

推文

可以知道原作者给出的线索指向其github。

在个人主页中可以找到github账号,进入github并搜索,可以发现这个github账号下有且只有一个blog的仓库:

github

我们可以看到有几个库在近期被修改过,因此可以知道出题人更新了这些库,而打开之后可以发现这些库都有关于文章的推送,所以我们可以查看其对于仓库的修改来试图查询flag。

点击任意一个文件中间的Site updated我们可以进入其历史修改记录的查询页面。

在这里我们可以发现有一篇文章被完全删掉了,题目为BUAACTF2022题解。

我们直接将其源码复制下来,在本地新建一个txt将源码复制进去之后将后缀改为.html等图片刷新一会可以得到如下网页:

screenshot.html

显而易见,我们得到了flag。

6、签到

别问为什么在这里,问就是后补的

打开所给链接,是o4ange战队的官方blog。

点开About页自然看到flag

Web

1、mota

鼠鼠很菜,只能签到

打开链接,发现是一个爬塔游戏。

点击f12进入开发者模式,打开network并刷新获取其源代码。

注意到Events.js,双击打开。

一般都是在游戏的最后给出flag,因此我们直接拉到最后可以发现如下语句:

将两句话在Node.js中打开可以率先得到最后一段flag:

最后一段flag

除此之外我们可以知道我们的flag都是这个叫Npc=3,仙子的npc给出的,所以直接在网页中搜索可得到第一段铭文储存的flag:

第一段flag

和第二段加密flag:

第二段flag

扔到Node.js中解码:

可得第二段flag,故完整flag为:

BUAACTF{HT5_mota_1s_s0_fun!}

Reserve向

1、Snake

下载exe之后用ida64打开,找到main函数并且反编译可以得到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl main(int argc, const char **argv, const char **envp)
{
_main(argc, argv, envp);
init();
while ( 1 )
{
draw();
move();
if ( (unsigned int)check_game_over() )
{
puts("\nGame Over!");
goto LABEL_7;
}
if ( score > 79 )
break;
Sleep(0xAu);
}
decrypt_flag();
printf("Congratulations! Here it is. %s\n", flag);
LABEL_7:
system("pause");
return 0;
}

可以知道逻辑是如果score如果大于79胜利得到flag,因此使用动态调试调整内存。

首先我们可以知道score变量存在0000000000408020内存处,在调试中找到并且修改

修改内存

得到flag:BUAACTF{debug_1s_useful}

2、oneQuiz’s revenge*

下载了一个安卓apk文件,用模拟器打开可以得到这样的界面:

看起来像上下左右走的方向,所以猜测是一个迷宫。

但是我做的时候并没有猜出来这是一个迷宫

用jadx-gui打开进行逆向可以得到java的码,打开AndroidManifest.xml查看可以知道主函数看起来就是一个迷宫的样子。

主函数

通过分析代码可以知道,我们成功的条件是当v2 == 11 && v3 == 11时我们走过路径的英文字母所构成的字符串。将其中v1列表的列表复制出来并且变成二维:

迷宫

可知我们的目标就是从(2,2)的位置走到*的位置,因此:

走出迷宫,根据摁键顺序得到flag:VaaaVaVVaaaaaVVVVAAJJAAVVVAVVaaaaaJa

3、Minesweep

不是很经典的扫雷游戏。

用ida64逆向并获取其主函数:

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
91
__int64 __fastcall main()
{
char flag[15]; // [rsp+21h] [rbp-4Fh] BYREF
char show[7][7]; // [rsp+30h] [rbp-40h] BYREF
int input; // [rsp+64h] [rbp-Ch] BYREF
int j; // [rsp+68h] [rbp-8h]
int i; // [rsp+6Ch] [rbp-4h]
__int64 savedregs; // [rsp+70h] [rbp+0h] BYREF

_main();
input = 0;
j = 0;
for ( i = 0; i <= 6; ++i )
{
for ( j = 0; j <= 6; ++j )
*((_BYTE *)&savedregs + 7 * i + j - 64) = 42;
}
menu();
while ( 1 )
{
printf(&byte_4050FE);
scanf("%d", &input);
if ( input == 1 )
break;
if ( !input )
{
puts(&byte_405112);
exit(0);
}
puts(&byte_40511D);
}
puts(&byte_405109);
Game(show);
scanf("%s", flag);
if ( strlen(flag) == 14 )
{
encrypt(flag);
if ( !strcmp(flag, result) )
printf("Succeed!!!!!");
}
else
{
printf("Wrong lenth!");
}
return 0i64;
}__int64 __fastcall main()
{
char flag[15]; // [rsp+21h] [rbp-4Fh] BYREF
char show[7][7]; // [rsp+30h] [rbp-40h] BYREF
int input; // [rsp+64h] [rbp-Ch] BYREF
int j; // [rsp+68h] [rbp-8h]
int i; // [rsp+6Ch] [rbp-4h]
__int64 savedregs; // [rsp+70h] [rbp+0h] BYREF

_main();
input = 0;
j = 0;
for ( i = 0; i <= 6; ++i )
{
for ( j = 0; j <= 6; ++j )
*((_BYTE *)&savedregs + 7 * i + j - 64) = 42;
}
menu();
while ( 1 )
{
printf(&byte_4050FE);
scanf("%d", &input);
if ( input == 1 )
break;
if ( !input )
{
puts(&byte_405112);
exit(0);
}
puts(&byte_40511D);
}
puts(&byte_405109);
Game(show);
scanf("%s", flag);
if ( strlen(flag) == 14 )
{
encrypt(flag);
if ( !strcmp(flag, result) )
printf("Succeed!!!!!");
}
else
{
printf("Wrong lenth!");
}
return 0i64;
}

我们首先可以知道flag长度为14位。查看encrypt函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __cdecl encrypt(char *s)
{
int temp; // [rsp+20h] [rbp-60h]
int k; // [rsp+24h] [rbp-5Ch]
int j; // [rsp+28h] [rbp-58h]
int i; // [rsp+2Ch] [rbp-54h]

for ( k = 0; k < strlen(s) - 7; ++k )
{
if ( s[k] > 96 && s[k] <= 122 )
{
temp = 0;
for ( i = 0; i <= 6; ++i )
{
for ( j = 0; j <= 6; ++j )
temp += (s[k + 1 + i] - 97) ^ (mine[8 * i + j] - 97);
}
s[k] = (temp ^ (s[k] - 97)) % 26 + 97;
}
}
}

一个这样的加密,其中比较重要的是mine数组的值。

查看之前的函数可以知道在执行过程中其mine数组的值是会改变的,因此我们需要在调试的过程中抓取其数值。

查看Game-Sweep函数可知,这根本不是一个什么扫雷游戏,而是一个趟雷游戏QnQ:

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
int __cdecl Sweep(char (*show)[7])
{
int y; // [rsp+24h] [rbp-Ch] BYREF
int x; // [rsp+28h] [rbp-8h] BYREF
int ret; // [rsp+2Ch] [rbp-4h]

x = 0;
y = 0;
printf(&byte_4050C1);
scanf("%d %d", &x, &y);
x = 0;
y = 0;
--HP;
while ( 1 )
{
if ( y == 6 )
{
++x;
y = 0;
}
else
{
++y;
}
if ( x > 6 )
break;
if ( !HP )
{
puts(&byte_4050D4);
return 0;
}
ret = get_num(x, y);
(*show)[7 * x + y] = ret + 48;
mine[8 * x + y] += ret;
}
puts(&byte_4050E1);
return 0;
}

所以意味着要得到修改之后的数组我们先要修改HP。HP储存在0000000000404058,mine数组在0000000000404020我们关注这两个地址进行修改和提取:

将数组的十六进制转化为十进制我们可以得到如下数组:

[97,99,99,100,100,99,100,0,99,101,100,99,100,99,97,0,99,102,100,101,98,99,99,0,100,100,100,98,100,99,99,0,100,100,101,98,100,98,99,0,99,99,97,100,101,99,99,0,97,100,99,100,98,97,99,0]

且result字符串为’vahii_Ts_nice!’

因为本人不会递推,所以采用正向爆破的方式进行:

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
#include <stdio.h>
#include <string.h>
#define forr(m) for (int m = 97; m <= 122; m++)
char s[20] = "vahii_Ts_nice!";
char ss[20] = "vahii_Ts_nice!";
char sss[20] = "vahii_Ts_nice!";
int mine[56]={97,99,99,100,100,99,100,0,99,101,100,99,100,99,97,0,99,102,100,101,98,99,99,0,100,100,100,98,100,99,99,0,100,100,101,98,100,98,99,0,99,99,97,100,101,99,99,0,97,100,99,100,98,97,99,0};
int main()
{
forr(a)
{
s[0] = a;
forr(b)
{
s[1] = b;
forr(c)
{
s[2] = c;
forr(d)
{
s[3] = d;
forr(e)
{
s[4] = e;
forr(f)
{
s[6] = f;
for (int k = 0; k < strlen(s) - 7; ++k)
{
if (s[k] > 96 && s[k] <= 122)
{
int temp = 0;
for (int i = 0; i <= 6; ++i)
{
for (int j = 0; j <= 6; ++j)
temp += (s[k + 1 + i] - 97) ^ (mine[8 * i + j] - 97);
}
ss[k] = (temp ^ (s[k] - 97)) % 26 + 97;
}
}
if(!strcmp(ss,sss))
puts(s);
}
}
}
}
}
}
printf("END");
return 0;
}

可以得到如下答案:

选择其中看起来最正常的答案patch_is_nice!试一下,正好是flag。

鼠鼠第一次写CTF题解,有不当之处还请大佬指教。


BUAACTF-Writeup
http://cool-wind.top/2023/04/27/BUAACTF-Writeup/
作者
CoolWind
发布于
2023年4月27日
许可协议