只有第一周的题目,后面被拉去出题组了。

没有写题解的题不一定是不会做,很大概率是觉得不是特别有意思所以懒得做(写)了。

根本进不去啊!

悄悄告诉你: flag 在 flag.basectf.fun

进不去!怎么想都进不去吧?

直接一手 dig

1
2
3
4
5
6
7
8
9
$ dig -t TXT flag.basectf.fun

; <<>> DiG [REDACTED] <<>> -t TXT flag.basectf.fun
;; ... [REDACTED] ...

;; ANSWER SECTION:
flag.basectf.fun. 600 IN TXT "FLAG: BaseCTF{h0h0_th1s_15_dns_rec0rd}"

;; ... [REDACTED] ...

捂住 X 只耳

BaseCTF 解题指定 BGM。

题目给定一个音频文件,你会发现无论是听上去(指直接播放)、看上去(指直接看十六进制)或是莽上去(指把能想到的常见音频隐写工具全试一遍)都没有异常。事实上,该音频的左声道与右声道有着细微的差别。

将音频丢入 Auduacity,先把左右声道分开:

Screenshot-202408211402.webp

然后选中其中一个声道,应用上下反相效果:

Screenshot-202408211403.webp

接着选中两个声道,合并它们:

Screenshot-202408211404.webp

现在你应该很熟悉这是什么东西了:

Screenshot-202408211405.webp

人生苦短,我用 Python

Python 语法大杂烩,下面有请 ChatGPT 帮我写一下详细内容:

题目给定的代码实际上是在定义一组验证规则,我们可以通过逆向思考,逐步构造满足所有条件的字符串。下面是针对每个条件的解析,以及在这个条件下可以确定的字符串部分。

  1. 长度检查if len(flag) != 38

    • 字符串长度必须是 38。
    • ??????????????????????????????????????
  2. 前缀检查if not flag.startswith('BaseCTF{')

    • 字符串必须以 BaseCTF{ 开头。
    • BaseCTF{??????????????????????????????
  3. 特定位置字符检查if flag.find('Mp') != 10

    • 在索 10 的地方必须找到 Mp,因此索 10 开始的地方应该是 Mp
    • BaseCTF{??Mp??????????????????????????
  4. 后缀检查if flag[-3:] * 8 != '3x}3x}3x}3x}3x}3x}3x}3x}'

    • 最后三个字符必须是 3x}
    • BaseCTF{??Mp???????????????????????3x}
  5. 最后一个字符 ASCII 检查if ord(flag[-1]) != 125

    • 最后一个字符的 ASCII 值应该是 125,即 }
  6. 字符计数检查if flag.count('_') // 2 != 2

    • 下划线字符的个数除以 2 后必须等于 2,也就是说总共必须有 4 个下划线。
  7. 分段长度检查if list(map(len, flag.split('_'))) != [14, 2, 6, 4, 8]

    • 按照下划线分割后,每段的长度分别应该是 14, 2, 6, 4, 8。
    • BaseCTF{??Mp??_??_??????_????_?????3x}
  8. 间隔字符检查if flag[12:32:4] != 'lsT_n'

    • 从索引 12 到 32,每隔 4 个字符取一次,这些字符应该拼成 lsT_n
    • BaseCTF{??Mpl?_?s_??T???_???n_?????3x}
  9. Emoji 连接的大写检查if '😺'.join([c.upper() for c in flag[:9]]) != 'B😺A😺S😺E😺C😺T😺F😺{😺S'

    • 前 9 个字符转换为大写后应该是 BASECTF{S
  10. 数值幂运算检查if not flag[-11].isnumeric() or int(flag[-11]) ** 5 != 1024

    • 倒数第 11 个字符必须是数字且其五次方等于 1024,因此这个数字是 4
    • BaseCTF{??Mpl?_?s_??T???_??4n_?????3x}
  11. Base64 编码检查if base64.b64encode(flag[-7:-3].encode()) != b'MG1QbA=='

    • 倒数第 7 到第 3 个字符的 Base64 编码结果必须是 MG1QbA==
    • BaseCTF{??Mpl?_?s_??T???_??4n_?0mPl3x}
  12. 倒序切片检查if flag[::-7].encode().hex() != '7d4372733173'

    • 字符串每隔 7 个字符倒序排列后,其十六进制结果必须是 7d4372733173
    • BaseCTF{?1Mpl?_?s_??T??r_??4n_C0mPl3x}
  13. 集合检查if set(flag[12::11]) != {'l', 'r'}

    • 从索引 12 开始每隔 11 个字符取一次,这些字符的集合必须是 {'l', 'r'}
  14. 子串编码检查if flag[21:27].encode() != bytes([116, 51, 114, 95, 84, 104])

    • 索引 21 到 27 的子串必须是编码后的 [116, 51, 114, 95, 84, 104],即 t3r_Th
    • BaseCTF{?1Mpl?_?s_??Tt3r_Th4n_C0mPl3x}
  15. 加权和检查if sum(ord(c) * 2024_08_15 ** idx for idx, c in enumerate(flag[17:20])) != 41378751114180610

    • 计算从索 17 到 20 的字符加权和,要求结果等于 41378751114180610
    • BaseCTF{?1Mpl?_?s_BeTt3r_Th4n_C0mPl3x}
  16. 字符类型检查if not all([flag[0].isalpha(), flag[8].islower(), flag[13].isdigit()])

    • 第 1 个字符必须是字母,第 9 个字符必须是小写字母,第 14 个字符必须是数字。
    • BaseCTF{s1Mpl?_?s_BeTt3r_Th4n_C0mPl3x}
  17. 字符串替换检查if '{whats} {up}'.format(whats=flag[13], up=flag[15]).replace('3', 'bro') != 'bro 1'

    • 字符替换后应该得到 bro 1
    • BaseCTF{s1Mpl3_1s_BeTt3r_Th4n_C0mPl3x}
  18. SHA-1哈希检查if hashlib.sha1(flag.encode()).hexdigest() != 'e40075055f34f88993f47efb3429bd0e44a7f479'

    • 整个字符串的 SHA-1 哈希值必须等于 e40075055f34f88993f47efb3429bd0e44a7f479

基于这些条件,可以逐步构造出目标字符串,可以直接运行脚本来验证它是否符合所有条件。

Ez Xor

ChatGPT 是优秀的解混淆工具。

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
#include <bits/stdc++.h>

using namespace std;

int64_t KeyStream(int64_t key, int64_t stream, int length) {
for (int i = 0; i < length; ++i) {
*reinterpret_cast<uint8_t*>(stream + i) = i ^ *reinterpret_cast<uint8_t*>(key + i % 3);
}
return 1;
}

int64_t encrypt(int64_t input, int64_t output, int length) {
for (int i = 0; i < length; ++i) {
*reinterpret_cast<uint8_t*>(output + i) ^= *reinterpret_cast<uint8_t*>(input + length - i - 1);
}
return 1;
}

int64_t CheckFlag(int64_t original, int64_t encrypted, int length) {
for (int i = 0; i < length; ++i) {
if (*reinterpret_cast<uint8_t*>(original + i) != *reinterpret_cast<uint8_t*>(encrypted + i)) {
return 0;
}
}
return 1;
}

int main(int argc, const char **argv, const char **envp) {
int64_t keyStreamBuffer[3] = {0};
char inputBuffer[8] = {0};
char expectedFlag[24] = {0};
char key[] = "\"@;%";
unsigned int inputLength = 0;
unsigned int flagLength = strlen(expectedFlag);

// 初始化预期的 flag
*reinterpret_cast<int64_t*>(expectedFlag) = 0x1D0B2D2625050901;
*reinterpret_cast<int64_t*>(expectedFlag + 8) = 0x673D491E20317A24;
*reinterpret_cast<int64_t*>(expectedFlag + 16) = 0x34056E2E2508504D;

cout << "Please input your answer: ";
cin >> inputBuffer;

inputLength = strlen(inputBuffer);

if (inputLength == 28) {
int64_t key = 7499608;
KeyStream(reinterpret_cast<int64_t>(&key), reinterpret_cast<int64_t>(keyStreamBuffer), 28);
encrypt(reinterpret_cast<int64_t>(keyStreamBuffer), reinterpret_cast<int64_t>(inputBuffer), inputLength);

if (CheckFlag(reinterpret_cast<int64_t>(inputBuffer), reinterpret_cast<int64_t>(expectedFlag), flagLength)) {
cout << "You are good!" << endl;
} else {
cout << "It's not the flag!" << endl;
}
} else {
cout << "The length was wrong." << endl;
}

return 0;
}

ChatGPT 这写法… 算了,也不是不能看。

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
from Crypto.Util.number import long_to_bytes

def keystream(key, length):
stream = [0] * length
for i in range(length):
stream[i] = i ^ key[i % 3]
return bytes(stream)

def encrypt(input_data, key, length):
encrypted = list(input_data)
for i in range(length):
encrypted[i] ^= key[length - i - 1]
return bytes(encrypted)

cipher = \
long_to_bytes(0x1D0B2D2625050901)[::-1] +\
long_to_bytes(0x673D491E20317A24)[::-1] +\
long_to_bytes(0x34056E2E2508504D)[::-1] +\
b"\"@;%"

key = long_to_bytes(7499608)[::-1]
key_stream = keystream(key, 28)
flag = encrypt(cipher, key_stream, 28)

print(flag)

ez_maze

真就一迷宫。

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
92
93
94
#include <bits/stdc++.h>

using namespace std;

const char maze[] = "x$$$$$$$$$$$$$$"
"&&&&&&$$$$$$$$$"
"&$&$$&$$&&&&&$$"
"&$&$$$&&$$$$&$$"
"&$$$&&&$$$$$&$$"
"&$$$&$&&$&$$$$$"
"&$$$&$&$$&&&$$$"
"&&&&&$&&&&$&$$$"
"$$$$$$&&&&&&$$$"
"$$$$$$&$$$$$$$$"
"$$$&&&&$$&&&$$$"
"$$$&&&&&&&$$$$$"
"$$$$$$$$$&$$&$$"
"$$$$$$$$$&$&$$$"
"$$$$$$&&&&&&&&y";

int err_out_of_bounds()
{
cout << "Invalid move: out of bounds!" << endl;
return -1;
}

int err_invalid_input()
{
cout << "Invalid input!" << endl;
return -1;
}

int main()
{
cout << "Can you walk the maze???" << endl;
cout << "Take the shortest path to the finish line.OK?" << endl;
cout << "Show your time!!!" << endl;

char path[35];
memset(path, 0, sizeof(path));

// scanf("%34s", path);
cin >> path;

int pos = 0;
for (int i = 0; path[i]; ++i)
{
char move = path[i];
if (move == 'd')
{
if (pos % 15 == 14)
return err_out_of_bounds();
++pos;
}
else if (move > 'd')
{
if (move == 's')
{
if (pos > 209)
return err_out_of_bounds();
pos += 15;
}
else
{
if (move != 'w')
return err_invalid_input();
if (pos <= 14)
return err_out_of_bounds();
pos -= 15;
}
}
else
{
if (move != 'a')
return err_invalid_input();
if (!(pos % 15))
return err_out_of_bounds();
--pos;
}
if (maze[pos] == '$')
{
cout << "Invalid move: hit a wall!" << endl;
return -1;
}
if (maze[pos] == 'y')
{
cout << "You win!" << endl;
cout << "plz BaseCTF{lower.MD5{your path}} by 32bit" << endl;
return 0;
}
}
cout << "You didn't reach the end." << endl;
return 0;
}

x 是起点 y 是终点,$ 是墙,& 是路。自己看着迷宫走一下就行了。

BasePlus

Base64,但是自己指定了字符表并且还加了个 XOR:

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
#include <iostream>
#include <string>
#include <cstring>

using namespace std;

const string Secret = "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC";

size_t Encode(const char *input, char *output)
{
int inputLength = strlen(input);
if (inputLength <= 0) return 0;

size_t outputIndex = 0;
int inputIndex = 0;

while (inputIndex < inputLength)
{
unsigned char temp[3] = {0};
int tempLength = 0;

for (int i = 0; i < 3 && inputIndex < inputLength; ++i)
{
temp[i] = input[inputIndex++];
++tempLength;
}

output[outputIndex++] = Secret[(temp[0] >> 2) & 0x3F];
output[outputIndex++] = Secret[((temp[0] & 0x03) << 4) | ((temp[1] >> 4) & 0x0F)];
output[outputIndex++] = Secret[((temp[1] & 0x0F) << 2) | ((temp[2] >> 6) & 0x03)];
output[outputIndex++] = Secret[temp[2] & 0x3F];

for (int i = 0; i < 4; ++i)
output[outputIndex - 4 + i] ^= 0xE;
}

output[outputIndex] = '\0';
return outputIndex;
}

size_t Decode(const char *input, char *output)
{
size_t inputLength = strlen(input);
if (inputLength % 4 != 0) return 0;

size_t outputIndex = 0;
char decoded[4];

for (size_t i = 0; i < inputLength; i += 4)
{
for (int j = 0; j < 4; ++j)
decoded[j] = input[i + j] ^ 0xE;

int index1 = Secret.find(decoded[0]);
int index2 = Secret.find(decoded[1]);
int index3 = Secret.find(decoded[2]);
int index4 = Secret.find(decoded[3]);

if (index1 == string::npos || index2 == string::npos || index3 == string::npos || index4 == string::npos) return 0;

output[outputIndex++] = (index1 << 2) | (index2 >> 4);
if (decoded[2] != Secret[64])
output[outputIndex++] = ((index2 & 0x0F) << 4) | (index3 >> 2);
if (decoded[3] != Secret[64])
output[outputIndex++] = ((index3 & 0x03) << 6) | index4;
}

output[outputIndex] = '\0';
return outputIndex;
}

int main()
{
char decodedOutput[100];
const char *input = "lvfzBiZiOw7<lhF8dDOfEbmI]i@bdcZfEc^z>aD!";
size_t encodedLength = Decode(input, decodedOutput);

cout << "Decoded: " << decodedOutput << endl;
return 0;
}

麻烦 ChatGPT 写一下 Decoder 又不是什么难事。