Host_Target

问题

burp中的Repeater模块中,右上角有一个target,这个是表示请求的host和port,但是在http请求体中也有host,如果这两个不同的话,比如我一个对aaa.com:8000正常的请求,在http请求体的host:aaa.com:8000,并且target模块也是host:aaa.com,port:8000,但是当我修改http请求体中的host为bbb.com:8000在进行重放操作,不更改target,该请求请求的目标还是aaa.com;就相当于,一个http请求,我更改host,最后还是按照更改前进行的发送数据

但是host的介绍:HTTP Host 请求标头指定了接收请求的服务器的主机名和端口号。为什么我在更改host之后还是按照之前的请求呢

image-20251001122807171

案例:

信呼OA的nickName注入通过修改host来绕过鉴权,形成前台注入
Host头注入

分析

  1. Target 决定了 TCP 连接发到哪台机器
  • Repeater 右上角的 Target(host:port),表示的是 真正建立 TCP 连接的 IP/端口
  • 也就是说,Burp 会用 aaa.com:8000 做 DNS 解析,然后发起 TCP 连接。
  • 这个值如果不改,底层连接就始终打到 aaa.com:8000
  1. Host 头部只是在 HTTP 协议层传递给服务器的一个字符串
  • HTTP 协议中 Host 头部告诉服务器:
    “我请求的是 bbb.com:8000 这个虚拟主机的资源”。
  • 但这只是 HTTP 报文内容,服务器怎么处理取决于配置(比如 Apache/Nginx 的虚拟主机根据 Host 做路由)。
  • 它不会影响 TCP 连接真正打到哪台机器
  1. 你的实验现象解释
  • 你改了 HTTP 请求体里的 Host: bbb.com:8000,但是 Repeater 的 Target 还是 aaa.com:8000
  • 所以:
    • TCP 连接依旧打到 aaa.com:8000
    • 请求报文发给 aaa.com 服务器时,Host 头变成了 bbb.com:8000
    • 如果 aaa.com 服务器没有配置 bbb.com:8000 的虚拟主机,就会返回 404 或默认站点。

这就是为什么即使改了 Host,Burp 还是“按之前的目标”去发送。

  1. 一个小比喻
  • Target = 我拿快递送到哪栋楼(真实物理地址)
  • Host 头 = 快递包裹上写的收件人名字/公司名
  • 你可以把“收件人”随便改掉,但快递还是送到那栋楼。至于门口保安要不要收,就看楼里的规则了。

总结:

  • Target 控制实际 TCP 连接目标(决定发到哪里)。
  • Host 头只是报文内容(服务器内部逻辑是否识别由它决定)。
  • 你遇到的情况是正常的,想真正把请求发到 bbb.com:8000,必须改 Target,而不仅仅是改 Host。
+-----------------------------+
| 应用层(HTTP 报文) |
| 请求行: GET /path HTTP/1.1 |
| Host: bbb.com:8000 | <-- 这只是 HTTP 头部(由客户端构造)
| (其他头/Body) |
+-----------------------------+

│ 发到已建立的 TCP 连接

+-----------------------------+
| 传输层(TCP 连接) |
| Target (connect到的IP/端口)|
| 例如:tcp -> 1.2.3.4:8000 | <-- 这决定报文“真正”送到哪台机器
+-----------------------------+


+-----------------------------+
| 目标主机(web server) |
| - 根据 TCP 目标地址接收流量 |
| - 再看 HTTP Host 决定虚拟主机|
+-----------------------------+

其他工具实现修改host

用 curl 演示(最直观)

  1. 连接到 aaa.com:8000,但把 Host 改为 bbb.com:8000
curl -v -H "Host: bbb.com:8000" http://aaa.com:8000/some/path

解释:curl 会 DNS 解析 aaa.com(或用 /etc/hosts),建立 TCP 到 aaa.com:8000,但发出的 HTTP 报文里 Host:bbb.com:8000

  1. 直接用 IP(绕过 DNS):
curl -v -H "Host: bbb.com:8000" http://1.2.3.4:8000/some/path

说明:1.2.3.4 是 aaa.com 的 IP,Host 仍为 bbb.com:8000

Python:requests(HTTP)——最常用、简单

适用于 HTTP(非 TLS)请求,指定连接目标为 IP 或域名,然后单独传 Host 头。

import requests

# 直接用目标 IP/域名作为 URL(这是“target”)
url = "http://1.2.3.4:8000/some/path" # 这里 1.2.3.4 = 实际要连的机器 (Target)
headers = {
"Host": "bbb.com:8000", # 这是 HTTP Host 头(你想伪造的)
"User-Agent": "my-agent/1.0"
}
r = requests.get(url, headers=headers)
print(r.status_code)
print(r.text[:500])

注意:requests 只决定 HTTP 报文里的 Host 值,不影响 TCP 目标(由 URL 决定)。


Python:http.client(更底层,适合展示原始请求)

import http.client

# 连接到 Target(IP 或主机名)和端口
conn = http.client.HTTPConnection("1.2.3.4", 8000, timeout=10)

# 发请求时手动写 Host 头
conn.request("GET", "/some/path", headers={"Host": "bbb.com:8000", "User-Agent":"my"})
resp = conn.getresponse()
print(resp.status, resp.reason)
print(resp.read().decode()[:500])
conn.close()

Python:socket(最底层,能完全自定义原始报文)

import socket

HOST = "1.2.3.4" # 目标 TCP 地址(Target)
PORT = 8000

req = (
"GET /some/path HTTP/1.1\r\n"
"Host: bbb.com:8000\r\n"
"User-Agent: raw-socket/1.0\r\n"
"Connection: close\r\n"
"\r\n"
)

with socket.create_connection((HOST, PORT), timeout=5) as s:
s.sendall(req.encode())
resp = b""
while True:
chunk = s.recv(4096)
if not chunk:
break
resp += chunk
print(resp.decode(errors="ignore")[:1000])

这个方式和 Burp 的工作原理最接近:你自己建立 TCP 连接(Target),把你构造的 HTTP 报文写过去(包含任意 Host)。

HTTPS 的特殊性(很重要)

  • 对 HTTPS(TLS)站点,仅修改 HTTP Host 头 通常不足够
    • TLS 握手阶段会出现 SNI (Server Name Indication),客户端在 TLS 握手里会把想访问的主机名(比如 bbb.com)放在 SNI;服务器据此选择证书/虚拟主机。
    • 如果你建立 TLS 连接到 1.2.3.4(Target),但 SNI 仍然是 1.2.3.4 或其他,服务器可能返回与 bbb.com 不匹配的证书,导致 TLS 握手失败或证书警告。
  • 在 Python 的 requests 中,如果你用 requests.get("https://1.2.3.4", headers={"Host":"bbb.com"})
    • requests 会把 SNI 默认为 URL 里的 host(即 1.2.3.4),不是 bbb.com,所以 SNI 与 Host 可能不一致。
    • 解决办法(较复杂):在建立 TLS 时把 server_hostname 设为 bbb.com(例如手动用 ssl + socket),或使用 urllib3/自定义 Adapter 指定 server_hostname。另外通常需要忽略证书校验(verify=False)或使用正确证书/信任链。
  • 简单示例(绕过校验,仅供测试环境用):
import ssl, socket

HOST = "1.2.3.4"
PORT = 443

context = ssl.create_default_context()
# 如果测试环境证书不匹配,可以临时禁止校验(不推荐在生产)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE

with socket.create_connection((HOST, PORT)) as sock:
# 这里我们指定 server_hostname=b b b.com 来发送 SNI
with context.wrap_socket(sock, server_hostname="bbb.com") as ssock:
# 现在发送 HTTPS 请求(HTTPS 需要先握手完成)
req = "GET / HTTP/1.1\r\nHost: bbb.com\r\nConnection: close\r\n\r\n"
ssock.send(req.encode())
resp = ssock.recv(4096)
print(resp.decode(errors="ignore")[:1000])

总结:HTTPS 需要同时处理 TCP 目标(Target)、Host 头和 TLS 的 SNI 与证书,少一步都会导致失败。

发送http请求到目标服务器,都要经过哪些步骤

发送一次 HTTP/HTTPS 请求,典型流程是:应用层准备报文 → 域名解析(DNS) → 建立传输层连接(TCP 三次握手)→(如果 HTTPS:TLS 握手,包含 SNI/ALPN/证书验证)→ 发送 HTTP 报文(包含 Host)→ 服务器处理并回复 → 客户端接收并可能复用/关闭连接。

详细步骤(HTTP,明文)

  1. 应用层准备请求
    • 客户端构造请求行、请求头(包括 Host)、请求体等(例如 GET /path HTTP/1.1 + Host: bbb.com)。
    • 这里可以伪造 Host 字段,但这只是报文内容,不决定 TCP 去向。
  2. 域名解析(DNS)(如果 URL 使用域名而不是 IP)
    • 客户端查询本地 DNS 缓存 → /etc/hosts → 递归/缓存 DNS 服务器,最终得到目标 IP(可能有多个 A/AAAA)。
    • 如果你在脚本里直接用了 IP(或已在 hosts 映射),则跳过网络 DNS 查询。
  3. 路由选择 & 本地网络
    • 操作系统决定出站接口、默认路由、可能经过 NAT、代理设置(HTTP_PROXY / system proxy)或 VPN。
    • 若系统/程序配置了 HTTP 代理(或你用了 Burp 代理),客户端会把请求发到代理而非最终主机。代理有两种:HTTP(明文)代理或 CONNECT 隧道(用于 HTTPS)。
  4. 建立 TCP 连接(Target)
    • 客户端向目标 IP:port 发起三次握手(SYN → SYN/ACK → ACK)。这一步决定了“数据包物理送到哪台机器/端口” —— 等同于 Burp Repeater 的 Target。
    • 如果你用代理,三次握手在代理与客户端之间,代理再与目标服务器建立连接(或代理直接把你的报文再发出)。
  5. 发送 HTTP 报文
    • TCP 连接建立后,客户端把构造好的 HTTP 报文(包含 Host)写入连接并发送。
    • 服务器收到后,根据 Host(虚拟主机)选择哪个站点配置/处理该请求。
  6. 服务器处理并返回响应
    • 应用层处理请求,返回状态行、响应头、响应体。
    • 服务器可能返回重定向、404、默认站点内容、或基于 Host 的特定站点内容。
  7. 连接复用或关闭
    • Connection: keep-alive(HTTP/1.1 默认),TCP 连接可复用;否则会在请求/响应后关闭(FIN 四次挥手)。
    • 客户端或服务器也可能在超时后关闭。

HTTPS(在上面基础上加的关键步骤)

在 TCP 三次握手成功之后,在真正发送 HTTP 报文前还要做 TLS(SSL)握手:

4.5 TLS 握手(客户端 Hello)

  • 客户端发 ClientHello,里面可能包含:
    • SNI(Server Name Indication):客户端告诉服务器“我想访问的主机名是什么”(例如 bbb.com)。
    • ALPN:协商协议(http/1.1 或 h2)。
  • SNI 很重要:服务器据此选择哪个证书/虚拟主机配置来响应握手。

4.6 服务器证书与验证

  • 服务器返回证书链,客户端校验证书(域名匹配、CA、吊销等)。
  • 若校验失败(证书不匹配 SNI/Host),客户端会报错(浏览器警告、requests 抛异常,除非关闭校验)。

4.7 完成握手并建立加密通道

  • 双方生成会话密钥,之后的 HTTP 报文在 TLS 隧道内传输。
  • 最终发送的 HTTP 报文依然包含 Host,但这已被加密在 TLS 隧道内。

小结:对于 HTTPS,要让服务器“认为”是访问 bbb.com,必须同时

  • 在 TLS 握手中用 server_hostname=b b b.com(SNI);
  • HTTP 报文里 Host: bbb.com
  • 目标 TCP 连接仍由 Target(IP:port)决定。
客户端应用                 本地OS/DNS           网络/路由       目标服务器
[1] 构造HTTP报文(Host=b) -> /etc/hosts/DNS -> 得到 IP -> 发 SYN -> 接收 SYN
[2] (若HTTPS) TLS ClientHello (SNI=b) ---------------> TLS ServerHello/Cert
[3] TCP 三次握手完成 (Target=IP:port)
[4] 发送 HTTP 报文 (包含 Host:b) -----------------> 服务器接收
[5] 服务器根据 Host 选择虚拟主机并处理
[6] 服务器返回响应 <------------------------------- 返回给客户端
[7] 连接 keep-alive/close

常见问题 / 陷阱(排障清单)

  • 修改 Host 但不改 Target:请求仍打到原目标 IP(正常)。
  • HTTPS 报文证书不匹配:因为 SNI 没设为你想要的主机名或证书与 Host 不匹配。
  • 通过 IP 访问并设置 Host:若服务器的虚拟主机按 Host 路由,这种方法常用于测试;但 HTTPS 需处理 SNI 与证书。
  • 系统/环境代理(HTTP_PROXY)会拦截请求:确认是否走了代理(Burp 也会)。
  • CDN / 负载均衡器:实际到达的后端主机可能不是你期望的那台,且 CDN 可能会忽略/覆盖 Host,或基于 Host 路由到不同后端。
  • ALPN/HTTP2:HTTP/2 支持多路复用,报文格式不同(头部压缩),但 Host/SNI 原理相同。