BaseCTF 2024
[Week1] 根本进不去啊
最开始通过 nslookup txt flag.basectf.fun
试图查询 DNS 记录,发现域名对应的主机地址不存在
那么 flag 可能被隐藏在了 DNS 的 txt 记录中
通过 dig 查询 DNS 解析的 txt 记录,键入命令 dig txt flag.basectf.fun
或者使用 nslookup 同理键入命令 nslookup -query=TXT flag.basectf.fun
即可得到
一段 txt 记录text = "FLAG: BaseCTF{h0h0_th1s_15_dns_rec0rd}"
[Week1] 海上遇到了鲨鱼
附件给了一个 .pcapang 文件,用 wireshark 打开这个流量包,主要为 HTTP 和 TCP 的流量
找到了GET /wireshark/flag.jpg HTTP/1.1
的 Info,我们主要关注 HTTP 协议
因此导出 HTTP 对象,在 flag.php 中找到了一段文本}67bf613763ca-50b3-4437-7a3a-b683fe51{FTCesaB
显然为 flag 的倒置,因此倒置回来就可以得到 flag
[Week1] Base
显然是一串 Base 系列编码后的字符串,直接丢到 Cyberchef 中 magic 一下就拿到了 flag
[Week1] 倒计时?海报!
公开了 Flag
BaseCTF{c0unt_d0wn_fro3_X_every_d@y_i5_re@11y_c0o1_@nd_h@rd_t0_do_1t_ev3ry_n1ght}
其实是每个海报放大后能看见水印
每一天的倒计时海报中水印的字符拼接起来就能够得到 Flag
[Week1] 签到!DK 盾!
签到题
关注 DK 盾公众号,发送 BaseCTF2024
,即可获得 Flag
[Week1] 喵喵太可爱了
娱乐题
貌似命令是
[Week1] 人生苦短,我用 python
从代码中,可以推测出验证输入的 flag 是否正确的过程是通过一系列的条件检查实现的。为了找到满足所有条件的 flag,我们可以逐步分析每个条件。 分析条件
-
长度检查:
flag 的长度必须为 38 个字符。 -
前缀检查:
flag 必须以 BaseCTF{ 开头。 -
特定位置字符检查:
flag 中 "Mp" 的位置必须是索引 10。 -
特定末尾检查:
flag 的最后 3 个字符乘 8 必须等于 '3x}3x}3x}3x}3x}3x}3x}3x}',因此最后 3 个字符为 '3x}'。 -
最后一个字符检查:
flag 的最后一个字符的 ASCII 码必须为 125,即 }。 -
下划线检查:
flag 中的下划线数量必须为 4(因为 4 // 2 == 2)。 -
下划线分割后的长度检查:
按下划线 split 后,每部分的长度应为 [14, 2, 6, 4, 8]。 -
步长为 4 的子串检查:
从索引 12 开始,每 4 个字符一个,必须得到 'lsT_n'。 -
字符替换检查:
flag 的前 9 个字符的大写必须是 'BASECTF{S' -
数值字符检查:
flag 的倒数第 11 个字符必须是一个数字,并且它的 5 次方为 1024,即 4。 -
Base64 编码检查:
flag 的倒数第 7 到第 3 个字符(不含倒数第 3 个)Base64 编码后应为 'MG1QbA==',即 'G1Pa'。 -
逆序步长检查:
逆序每 7 个字符的ASCII码十六进制表示为 '7d4372733173',即'}Crs1s' -
特定集合检查:
从索引 12 开始,每 11 个字符取一个,结果的集合应为 {'l', 'r'}。 -
字节序列检查:
索引 21 到 26 的子字符串的字节序列应为 [116, 51, 114, 95, 84, 104],即 't3r_Th'。 -
累加校验:
索引 17 到 19 的字符经过特定计算应得 41378751114180610。 可以写一个爆破脚本找到18及19位数,17位已知为'_'
key = 41378751114180610 - ord('_')
for i in range(128):
for j in range(128):
if i * 2024_08_15 + j * 2024_08_15 * 2 == key:
print(chr(i),chr(j))
得到 B,e
-
字符属性检查:
索引 0 的字符应为字母,索引 8 的字符应为小写字母,索引 13 的字符应为数字。 -
格式化检查:
格式化后的字符串应该是 '3 1',并替换 '3' 为 'bro' 得 'bro 1',即 flag[13] 为 '3',flag[15] 为 '1'。 这样其实就已经得到了 Flag 的内容
BaseCTF{s1Mpl3_1s_BeTt3r_Th4n_C0mPl3x}
- SHA1 校验: 整个 flag 的 SHA-1 哈希应为 'e40075055f34f88993f47efb3429bd0e44a7f479'。
分析完所有检查条件后,我们知道 flag 应该满足所有给定的检查条件,因此我们能推断出 flag 为
BaseCTF{SLsT_4_Th3rs_3x}
[Week1] 你也喜欢圣物吗
打开附件可以得到一个 .zip 文件 where_is_key 和一个 .png 文件
where_is_key 需要密码来解压,猜测密码被隐写在了 png 图片中,利用 zsteg 自动枚举识别图片中的隐写字段
可以得到密码为lud1_lud1
得到密码后继续解压得到一个需要密码的名为 it is fake 的 .zip 文件
根据提示 "where_is_key.zip 真的需要密码,再找找",猜测可能这个文件利用了 zip 伪加密技术
使用 010 Editor 打开该 zip 文件 , 将数据区和目录区的全局方式位标记都更改为 00 00 并保存
再次解压 zip 文件就可以得到 flag.txt,有两段明显为 Base 系列特征的编码
其中上方的是假 flag,From Base64 后得到flag{0h_n0_it's_f3ke}
许多个换行后下方的是真正的 flag,两次 From Base64 后得到BaseCTF{1u0_q1_x1_51k1}
[Week1] 正着看还是反着看
打开附件能够得到一个 data 类型即纯二进制数据的 flag 文件,并且 cat 将其输出后发现是一段乱码
结合题目名称可以猜测要将二进制数据反向,于是我们可以撰写一个 python 脚本来倒置二进制数据
def reverse_file(input_file, output_file):
# 打开输入文件并读取其内容
with open(input_file, 'rb') as f:
data = f.read()
# 将数据倒置
reversed_data = data[::-1]
# 将倒置后的数据写入输出文件
with open(output_file, 'wb') as f:
f.write(reversed_data)
# 示例用法
input_file = 'flag'
output_file = 'flag_reversed'
reverse_file(input_file, output_file)
file flag_reversed
发现该文件其实是一个 JPEG 类型的文件,猜测与图片隐写有关
binwalk 查看附加文件,-e 选项自动提取,可以提取出 flag.txt,查看可以得到 flag 为BaseCTF{h3ll0_h4cker}
[Week1] 捂住 X 只耳
[Week2] ez_crypto
import base64
string = "qMfZzunurNTuAdfZxZfZxZrUx2v6x2i0C2u2ngrLyZbKzx0=" # 欲解密的字符串
outtab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" # 原生字母表
intab = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=" # 换表之后的字母表
print (base64.b64decode(string.translate(str.maketrans(intab,outtab))).decode())
换表后利用 base64 解码就可以得到 Flag
[Week2] base?!
hS5VZPaBjN4IU6G2VFqZqNG-tPrJm64NgMKQuEa3nNIBIFbh0MLBZEpF4LqZn
9LpBjLoRjPqEV6Lo+
question response
Base64? X
Base32? X
利用自动编解码识别工具可知这是 xxencode 编码,可能题目中 Base64 和 Base32 的 Response "X" 就是告诉这段编码并非传统意义上的 Base64(Base32),还是有点难想到的
顺带介绍一下 xxencode 及其姊妹 uuencode --- 他们两者其实都是 Base64 编码的变种
标准 Base64 编码的字符表为 A-Za-z0-9+/=
而 xxencode 编码的字符表为 +-0-9A-Za-z,uuencode 编码的字符表为 ASCII 的 32-95(包含了较多“奇怪”字符)
xxencode 重复以三个字节为一组,如果剩余字节少于三个,则添加尾随零。这 24 位被分成 4 个 6 位数字,6 位数字的大小刚好是 0 - 63 ,按照下表将数字替换成字符
1 2 3 4 5 6
0123456789012345678901234567890123456789012345678901234567890123
| | | | | | |
+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
每组 60 个输出字符(对应于 60/4*3=45 个输入字节)作为单独的行输出,前面有一个编码字符,给出该行上的编码字节数。对于除最后一行之外的所有行,这将是字符“h”(映射到值 45 的字符
因此我们发现题目中的文本非常符合 xxencode的编码特征
uuencode 与 xxencode 类似,也是重复以三个字节为一组,尾随零。将 24 位二进制数分成 4 个 6 位数字,6 位数字的大小为 0 - 63,只不过 uuencode 的字母表替换方式不同,直接在 0 - 63 上加上 32,对应到 ASCII 字符表中的 32 - 95 号字符。同样每组 60 个输出字符作为单独的行输出,也有额外加在最前面的表示该行编码的字节数的编码字符,只不过满行 60 个输出字符所对应的字符应该是 "M"(映射到 ASCII 表中第 45+32=77 位字符)
[Week 2] 二维码 1- 街头小广告
--- 来自官方题解(逃
二维码本身具有纠错能力,只要损坏数量不太多,就能自动纠正。尤其是微信 AI 的纠错能力特别强,出题时凑了很久才凑出来“刚好补个角才能扫,否则不能扫”的效果。
在合适的位置补上右上角定位块(Windows 画图 或者 PPT 就行
这样就能扫了。如果直接用微信之类的扫,会直接跳转到www.basectf.fun
,看不到 flag
二维码本身只是数据的表示形式,二维码与它包含的数据(通常是字节)是等价的。为了检查未知来源二维码的安全性,我们希望只查看二维码内容,而不访问网址。生活中可以用微信小程序“草料二维码”的解码功能方便地做到这件事。CTF 中可以用 QR Research 或 QRazyBox 等工具处理二维码。
本题二维码信息:
https://www.bilibili.com@qr.xinshi.fun/BV11k4y1X7Rj/mal1ci0us?flag=BaseCTF%7BQR_Code_1s_A_f0rM_Of_m3s5ag3%7D
其中@
前面的www.bilibili.com
是用户名,在 HTTP 协议中无意义、会被忽略,qr.xinshi.fun
才是真正的主机名(域名qr.xinshi.fun
上的 NGINX 把访问者 302 重定向到www.basectf.fun
,这样就不能打开之后复制链接获得 flag 了。
[Week 2] 前辈什么的最喜欢了
观察 .txt 文档发现开头提示了是经过 Base64 编码后的 .png 文件
为此可以写一个简单的 python 脚本将其转换为原先的图像文件
import base64
with open("看着我吧,前辈.txt",'r') as f:
encoded_data = f.read()
decoded_data = base64.b64decode(encoded_data)
with open('data.png','wb') as f:
f.write(decoded_data)
一把梭:
-
利用 ImageMagic 查看图片基本信息 -- xdg-open 尝试打开图片 -- identify 尝试输出图片基本信息
-
使用 exiftool 检查图片元信息,看看有没有看起来会有用的信息
-
使用十六进制编辑器打开,观察文件中有无附带信息、图片基本格式是否正确 / 利用 pngcheck 检查图片是否存在异常
-
使用 binwalk 检查文件末尾是否叠加了多余的文件
-
使用 stegsolve 打开图片 / 或者使用 CyberChef
- 观察各个通道的 bit plane
-
使用 Extract LSB 尝试提取数据格式的 LSB(或者使用 zsteg 猜测)
-
考虑能否查找原图,如果找到了尝试进行比较
对于本题目,发现图片不能通过 xdg-open 正常显示,但可以直接使用 Windows 系统下的图片查看器打开。并且 pngcheck 后显示有 crc 校验错误,于是我们怀疑篡改了 IHDR 块的宽高数据
文件头数据块 IHDR (header chunk): 包含了 PNG 文件中储存的图像数据的基本信息,并作为第一个数据块出现在 PNG 数据流中
紧跟 IHDR 后的 8 个字节表示了图像的宽度和高度,例如
49 48 44 52 00 00 02 25 00 00 03
因此我们直接适当增大图像的宽度和高度,在 Windows 自带的图片查看器中查看图片便可以看到因高度截取后而隐藏的 flag 信息(貌似主要是 Windows 自带的图片查看器对 png 图像的要求并不是很严格,即使出现校验和错误也会忽视力图能将图像显示出来
[Week2] 黑丝上的 flag
原理是降低 flag 部分的透明度 , 因为取原图的黑色部分 , 以黑底显示时基本不影响图片 , 以白底显示时 flag 部分变亮 , 所以进行了部分加深 .
不推荐直接肉眼读取 flag, 出题人预期方法是编程遍历像素的alpha
(透明度)通道 , 重新用黑白写在新的图片上 --- 摘自官方 writeup
先用 zsteg 自动识别隐写发现没有什么成效,然后就直接利用 Cyberchef 的 View Bit Plane 功能,在最低位上对 red blue green alpha 做尝试便能找到 flag
[Week2] 海上又遇到了鲨鱼
--- 照搬了官方的 writeup
Wireshark 是强大的网络数据捕获与分析工具
打开附件,看到协议为 TCP 与 FTP 的分组,可以一个个点击看看是什么 FTP 协议,ftp-data 过滤流量包 发现 文件传输了 flag
追踪数据流
将传输文件的所抓到的数据进行导出 Show data as ASCII 改成 原始数据 另存为 flag.zip
打开压缩包 压缩包有密码且注释也提示我们需要密码而且是重复密码
(可以学一些过滤规则 ftp contains "230" (但数据流不多 这行为有些多余了
尝试使用 Ba3eBa3e!@# 成功解密压缩包
可以看一下整体数据包流量: 一直到流 15 攻击者爆破 ftp 服务密码 用户名为 admin 密码为 Ba3eBa3e!@# 流 16 攻击者拿着爆破成功的密码去登陆 ftp 服务器 执行了 dir 命令 获取了 ftp 共享服务目录 发现 flag.zip 然后 download 下载了文件
[Week2] 反方向的雪
题目给了一张图片,利用 xdg-open 打开没有发现什么异常,identify 查看也没有什么异常,属于 jpeg 文件
我们将图片放在 010 editor 中查看,发现 jpg 文件尾后还有多余的信息,仔细查看发现是逐字节逆序的 zip 文件,结合题目名字反方向的提示
将他单独提出来再逐字节逆序,网上可以找到很多类似的工具也可以自己写代码,得到逆序后正常的文件
with open('data.txt','r') as f:
data = f.read().strip()
byte_data = bytes.fromhex(data)
reversed_data = byte_data[::-1]
with open('data.zip','wb') as f:
f.write(reversed_data)
这样就得到了一个压缩包,需要密码,注释里面有一个提示是 The_key_is_n0secr3t,但是这好像并不是压缩包的密码,hint 给出密码为 6 位,利用妙妙小工具 ARCHPR 尝试爆破压缩包密码,得到密码是 123456
(注:无法在 Linux 环境下用 unzip 解压该压缩包,因为数据并不是以 zip 方式压缩的)
利用解压缩软件解压后得到 flag.txt,但是并没有出现 flag
其实文件中有很多空白字符,根据题目雪的提示,这里是 snow 隐写
利用妙妙小工具 SNOW.EXE (注:SNOW Home Page 官网提供的破解程序只能在 windows 系统下运行)解密,结合之前注释得到的 key: n0secr3t
就可以解密得到 flag
[Week2] 哇!珍德食泥鸭
把 gif 丢到 binwalk 中分离,得到了一个 zip 文件
解压后打开压缩包可以判断文件格式其实是 docx
我们补充一下对 Docx 文件格式的理解
DOCX 是 Microsoft Office 2007 引入的文件格式,基于 Open XML 标准。与 DOC 的二进制格式不同,DOCX 使用了 XML 和压缩的结构,更加开放和可解析。实际上,DOCX 文件就是一个 ZIP 文件,包含了多个 XML 文件和其他资源。
DOCX 文件的结构特点:
-
ZIP 压缩格式:DOCX 文件本质上是一个压缩文件,可以用 ZIP 工具(如 7-Zip、WinRAR)解压缩。
-
目录结构:解压 DOCX 文件后,可以看到一个包含多个 XML 文件和资源(如图片、字体等)的文件夹结构。常见的目录结构如下:
-
word/:包含文档的主要内容和设置。
document.xml
:存储文档的主要内容(文本) 。styles.xml
:定义文档中的样式信息(字体、段落样式等) 。header1.xml
,footer1.xml
:存储文档的页眉和页脚。
-
_rels/
:包含关系文件,定义各个 XML 文件和资源之间的关系。 -
docProps/
:包含文档的属性(如标题、作者、创建时间等) 。 -
[Content_Types].xml
:描述了文档中所有文件的 MIME 类型。
解析 DOCX 的 XML 文件:
- document.xml:是核心的文档内容,使用 XML 格式存储文档中的所有文本和格式信息。
- styles.xml:包含样式定义,用于指定文档中各种元素(如标题、段落、表格)的外观。
- 附加资源:如图片、图表等通常保存在
word/media/
目录中,并在 XML 文件中引用。
得到 Docx 文件后打开往下翻翻,就可以看到最下面有一张白色图片作为遮挡(通过图片方式是悬浮文字上方判断)
于是我们将其移开,但是并没有发现任何东西,点击工具栏中的显示隐藏文字,再全选将文字颜色改成其他颜色就可以看到 flag !
[Week3] 纯鹿人
打开 docx 文件 全选后发现有一段文字被隐藏,修改文字颜色后发现是一个 base64 对其解码 内容为压缩包密码
随后把 docx 改成 zip 提取出里面的 docx 图片并进行分解,拿到压缩包 输入前面拿到的解压密码即可得到 flag
[Week3] Base Revenge
把 Hint 拖到妙妙工具 -- 编解码工具中一键解码,发现经过 Atbash 解密后得到了 'This is hint' 的提示,说明可能需要用到 Atbash 解码
很明显源文本是一个 Base64 编码后的文本,我们将文本经过 Base64 解码后得到了一串英文文本,提示我们这段文本经过了 Base64 隐写
编写一个 Base64 隐写的解密脚本 ---- 现已归为妙妙工具
import re
path = './base_revenge.txt'
b64char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
with open(path, 'r')as f:
cipher = [i.strip() for i in f.readlines()]
plaintext = ''
for i in cipher:
if i[-2] == '=': # There are 4-bit hidden info while end with two '='
bin_message = bin(b64char.index(i[-3]))[2:].zfill(4)
plaintext += bin_message[-4:]
elif i[-1] == '=': # There are 2-bit hidden info while end with one '='
bin_message = bin(b64char.index(i[-2]))[2:].zfill(2)
plaintext += bin_message[-2:]
plaintext = re.findall('.{8}', plaintext) # 8bits/group
plaintext = ''.join([chr(int(i,2)) for i in plaintext])
print(plaintext)
得到隐写的结果后再 Atbash 解密(这里最好使用 Cyberchef,因为妙妙工具在 Atbash 中不区分大小写统一归为小写)
Cyberchef 自动识别经过 Base64 解码后就能得到 Flag
[Week3] 白丝上的 flag
题目描述:某出题人赠送大家 flag 时遭遇了信号干扰 , 幸好我们在不知名小网站找到了写入 flag 前的图片 , 尝试还原信息吧 !
将题目解压后为两张图片和一个 py 文件
打开 py 文件发现是一个图像混淆程序,主要功能是对输入的图像进行混淆处理,然后将结果保存为一个新的图像文件
猜测 flag 就写在 en_image 这张图片里,正好这里有原图,我们可以将没有写 flag 的图片进行相同的加密后再与 en_image 进行异或 xor处理即可得到 flag
from PIL import Image
import numpy as np
from task import confuse_image
image1 = Image.open('en_image.png')
image2 = confuse_image(Image.open('image.png'))
image1_data = np.array(image1)
image2_data = np.array(image2)
result_data = image1_data ^ image2_data
image_result = Image.fromarray(result_data)
image_result.save('flag.png')
[Week3]
[Week4] 二维码 2- 阿喀琉斯之踵
QRazyBox 打开二维码调整 Format Info Pattern
Error Correction Level 调整为 H,Mask Pattern 调整为 5,Extract QR Information 即可