BaseCTF 2024 新生赛 Week 1 个人题解
August 21, 2024 · Legacy Blog ↗
只有第一周的题目,后面被拉去出题组了。
没有写题解的题不一定是不会做,很大概率是觉得不是特别有意思所以懒得做(写)了。
根本进不去啊!
悄悄告诉你: flag 在 flag.basectf.fun
进不去!怎么想都进不去吧?
直接一手 dig:
$ 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,先把左右声道分开:
然后选中其中一个声道,应用上下反相效果:
接着选中两个声道,合并它们:
现在你应该很熟悉这是什么东西了:
人生苦短,我用 Python
Python 语法大杂烩,下面有请 ChatGPT 帮我写一下详细内容:
题目给定的代码实际上是在定义一组验证规则,我们可以通过逆向思考,逐步构造满足所有条件的字符串。下面是针对每个条件的解析,以及在这个条件下可以确定的字符串部分。
-
长度检查:
if len(flag) != 38- 字符串长度必须是 38。
??????????????????????????????????????
-
前缀检查:
if not flag.startswith('BaseCTF{')- 字符串必须以
BaseCTF{开头。 BaseCTF{??????????????????????????????
- 字符串必须以
-
特定位置字符检查:
if flag.find('Mp') != 10- 在索 10 的地方必须找到
Mp,因此索 10 开始的地方应该是Mp。 BaseCTF{??Mp??????????????????????????
- 在索 10 的地方必须找到
-
后缀检查:
if flag[-3:] * 8 != '3x}3x}3x}3x}3x}3x}3x}3x}'- 最后三个字符必须是
3x}。 BaseCTF{??Mp???????????????????????3x}
- 最后三个字符必须是
-
最后一个字符 ASCII 检查:
if ord(flag[-1]) != 125- 最后一个字符的 ASCII 值应该是 125,即
}。
- 最后一个字符的 ASCII 值应该是 125,即
-
字符计数检查:
if flag.count('_') // 2 != 2- 下划线字符的个数除以 2 后必须等于 2,也就是说总共必须有 4 个下划线。
-
分段长度检查:
if list(map(len, flag.split('_'))) != [14, 2, 6, 4, 8]- 按照下划线分割后,每段的长度分别应该是 14, 2, 6, 4, 8。
BaseCTF{??Mp??_??_??????_????_?????3x}
-
间隔字符检查:
if flag[12:32:4] != 'lsT_n'- 从索引 12 到 32,每隔 4 个字符取一次,这些字符应该拼成
lsT_n。 BaseCTF{??Mpl?_?s_??T???_???n_?????3x}
- 从索引 12 到 32,每隔 4 个字符取一次,这些字符应该拼成
-
Emoji 连接的大写检查:
if '😺'.join([c.upper() for c in flag[:9]]) != 'B😺A😺S😺E😺C😺T😺F😺{😺S'- 前 9 个字符转换为大写后应该是
BASECTF{S。
- 前 9 个字符转换为大写后应该是
-
数值幂运算检查:
if not flag[-11].isnumeric() or int(flag[-11]) ** 5 != 1024- 倒数第 11 个字符必须是数字且其五次方等于 1024,因此这个数字是
4。 BaseCTF{??Mpl?_?s_??T???_??4n_?????3x}
- 倒数第 11 个字符必须是数字且其五次方等于 1024,因此这个数字是
-
Base64 编码检查:
if base64.b64encode(flag[-7:-3].encode()) != b'MG1QbA=='- 倒数第 7 到第 3 个字符的 Base64 编码结果必须是
MG1QbA==。 BaseCTF{??Mpl?_?s_??T???_??4n_?0mPl3x}
- 倒数第 7 到第 3 个字符的 Base64 编码结果必须是
-
倒序切片检查:
if flag[::-7].encode().hex() != '7d4372733173'- 字符串每隔 7 个字符倒序排列后,其十六进制结果必须是
7d4372733173。 BaseCTF{?1Mpl?_?s_??T??r_??4n_C0mPl3x}
- 字符串每隔 7 个字符倒序排列后,其十六进制结果必须是
-
集合检查:
if set(flag[12::11]) != {'l', 'r'}- 从索引 12 开始每隔 11 个字符取一次,这些字符的集合必须是
{'l', 'r'}。
- 从索引 12 开始每隔 11 个字符取一次,这些字符的集合必须是
-
子串编码检查:
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}
- 索引 21 到 27 的子串必须是编码后的
-
加权和检查:
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}
- 计算从索 17 到 20 的字符加权和,要求结果等于
-
字符类型检查:
if not all([flag[0].isalpha(), flag[8].islower(), flag[13].isdigit()])- 第 1 个字符必须是字母,第 9 个字符必须是小写字母,第 14 个字符必须是数字。
BaseCTF{s1Mpl?_?s_BeTt3r_Th4n_C0mPl3x}
-
字符串替换检查:
if '{whats} {up}'.format(whats=flag[13], up=flag[15]).replace('3', 'bro') != 'bro 1'- 字符替换后应该得到
bro 1。 BaseCTF{s1Mpl3_1s_BeTt3r_Th4n_C0mPl3x}
- 字符替换后应该得到
-
SHA-1哈希检查:
if hashlib.sha1(flag.encode()).hexdigest() != 'e40075055f34f88993f47efb3429bd0e44a7f479'- 整个字符串的 SHA-1 哈希值必须等于
e40075055f34f88993f47efb3429bd0e44a7f479。
- 整个字符串的 SHA-1 哈希值必须等于
基于这些条件,可以逐步构造出目标字符串,可以直接运行脚本来验证它是否符合所有条件。
Ez Xor
ChatGPT 是优秀的解混淆工具。
#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 这写法… 算了,也不是不能看。
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
真就一迷宫。
#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:
#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 又不是什么难事。
Tags: #CTF #Writeup #Misc #Reverse #BaseCTF
This article is authored by luoingly and licensed under CC BY-NC 4.0
Permalink: https://luoy.ing/posts/basectf-2024-week1-writeup/



