Skip to content

Security:HttpService::GetProxyUrl`反向代理 URL 纯字符串拼接未校验目标主机导致服务端请求伪造(SSRF)漏洞 #915

Open

Description

@EtERnity0Sky

漏洞概述 / Summary

在 libhv 的 HTTP 反向代理实现中,HttpService::GetProxyUrl() 通过直接拼接配置的后端 URL 字符串与剥离前缀后的客户端请求路径来构造代理目标 URL:

url = iter->second + (path + iter->first.length());

当反向代理后端 URL 配置为不含尾部 / 的 authority 形式(如 service.Proxy("/api/", "http://127.0.0.1:18081"))时,未认证攻击者可发送以 @ 开头的子路径请求(例如 GET /api/@169.254.169.254:80/latest/meta-data/)。拼接后的 URL 变为 http://127.0.0.1:18081@169.254.169.254:80/latest/meta-data/。随后 HUrl::parse() 将 @ 之前的 127.0.0.1:18081 解析为 URL 的 userinfo 字段,而将攻击者控制的 169.254.169.254:80 解析为真正的目标 host 与 port。

此外,HttpHandler::connectProxy() 仅在 forward_proxy(正向代理)模式下检查 service->IsTrustProxy(url.host.c_str()),对 reverse_proxy(反向代理)不做任何目标主机一致性校验,导致反向代理服务器直接向攻击者指定的任意内网或外部 host:port 发起 HTTP 请求并返回响应内容。

2. 漏洞成因与源码分析 / Root Cause & Vulnerable Code

源码位置 1:[http/server/HttpService.cpp#L195-L204](https://github.com/ithewei/libhv/blob/060a4b6c0cf8ae252741d0a58976376eac02e8c5/http/server/HttpService.cpp#L195-L204)

std::string HttpService::GetProxyUrl(const char* path) {
    std::string url;
    for (auto iter = proxies.begin(); iter != proxies.end(); ++iter) {
        if (hv_strstartswith(path, iter->first.c_str())) {
            // 直接进行字符串拼接,未保证路径分隔符 '/',也未校验拼接后解析出的 host 是否改变
            url = iter->second + (path + iter->first.length());
            break;
        }
    }
    return url;
}

源码位置 2:[http/server/HttpHandler.cpp#L1163-L1171](https://github.com/ithewei/libhv/blob/060a4b6c0cf8ae252741d0a58976376eac02e8c5/http/server/HttpHandler.cpp#L1163-L1171)

int HttpHandler::connectProxy(const std::string& raw_url) {
    HUrl url;
    if (!url.parse(raw_url)) {
        return SendHttpStatusResponse(HTTP_STATUS_BAD_REQUEST);
    }
    if (forward_proxy && !service->IsTrustProxy(url.host.c_str())) {   // 反向代理跳过主机校验
        hlogw("[%s:%d] Forbidden to proxy %s", ip, port, url.host.c_str());
        return SendHttpStatusResponse(HTTP_STATUS_FORBIDDEN);
    }

影响范围与危害评估 / Impact

确认影响:未认证远程攻击者可将 libhv 反向代理作为跳板,向云环境元数据服务、内网未授权 HTTP 管理服务或任意内部端口发起全回显 SSRF 请求。

修复建议 / Remediation

  1. 在 HttpService::Proxy() 注册或 GetProxyUrl() 拼接时,若配置的目标 URL 路径为空且不以 / 结尾,自动补齐 /;并确保拼接的子路径始终以 / 开头。
  2. 在 HttpService::GetProxyUrl() 或 HttpHandler::connectProxy() 中解析拼接前后的 HUrl,强制校验最终请求的 url.host 与 url.port 必须与反向代理配置的目标 host 和 port 完全一致。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions