eddielebelle/rangefinder
GitHub: eddielebelle/rangefinder
一款声明式网络靶场生成器,通过捕获真实环境行为生成高保真协议 facade 并输出 ECS 遥测数据,用于紫队评估和检测规则验证。
Stars: 0 | Forks: 0
# rangefinder
[](https://github.com/eddielebelle/rangefinder/actions/workflows/ci.yml)
[](LICENSE)
[](pyproject.toml)
用于生成轻量级协议 **facade** 的生成器,它们替代真实的网络和身份环境——准确地响应真实的侦察/枚举工具(nmap、curl、ldapsearch、impacket),并为每次交互生成 **适配 SIEM 的 ECS 遥测数据**,每个主机一个容器。专为**授权**的安全测试设计。
**核心在于捕获,而非编写。** rangefinder 可以将真实环境——SMB 共享与文件、LDAP 目录对象与 ACL、认证姿态——*捕获*为一个忠实且可丢弃的 **twin**(数字孪生),因此弱点是*从真实环境携带而来*的,而不是凭空植入的。将自主红队 agent 放到 twin 上运行,脱离生产环境,而它们发现的任何东西都能**反向转移**——因为 twin 是被捕获的,而不是被建模的。(你也可以将一个靶场环境编写为单个 JSON 文件,用于演示和测试。)
这是整个闭环中的一个阶段:
**捕获 → 忠实 twin → 使用 agent 测试 → 收集发现 → 触发检测。**
保真度是一项硬性契约,而不是期望:每个姿态字段都是*经过测量的,否则就会失败关闭并报告异常*——因此 twin 只会少报,绝不会伪造一个真实环境上不存在的、让 agent 会“发现”的漏洞。这就是为什么发现结果能够转移的原因。详见 **[DESIGN.md](DESIGN.md)**。
专为以下场景构建:检测/SOC 评估、代理化红队 / 紫队 pipeline,以及人类红队成员。
关于 *为什么*——它针对的问题、双平面架构、如何验证保真度、endpoint/EDR 接口,以及它如何融入代理化安全架构体系——请参见 **[DESIGN.md](DESIGN.md)**。
关于实际的端到端运行(部署 → 攻击 → 评分 → 验证,包含捕获的输出),请参见 **[演练指南](docs/walkthrough.md)**;关于如何使用蓝队 agent 将攻击转化为经过真值验证的 SIEM 规则,请参见 **[检测指南](docs/detection.md)**。
## 安装
```
pip install -e .
```
## 核心概念
一个靶场配置包含四个部分:
- **network** — 靶场所处的子网。
- **hosts** — 每个主机都成为一个具有静态 IP 的容器;每个主机都有各自的**服务**。
- **services** — 一个有类型的、可区分的列表。每个 `type` 都映射到一个 facade。已实现的有:
`http`、`banner`、`ssh`、`ldap`、`smb`、`dns`、`rdp`。
- **identities** / **objectives** — AD 用户/组(由 LDAP facade 渲染)以及场景目标(描述性元数据)。
### 服务类型
| type | facade | 备注 |
|------|--------|-------|
| `http` | HTTP/1.1(S) 服务器 | server header、预设路由表、植入漏洞的路由、HEAD、keep-alive。设置 `tls: true` 以启用 HTTPS。路由通过 Basic auth(`auth_realm`/`auth_users`)或 NTLM(`auth_ntlm: true`,根据身份的 NT 哈希进行验证——IIS/OWA 风格)进行网关控制;每一次凭证尝试都会被捕获为遥测数据 |
| `banner` | 通用 TCP banner | 文本 banner + regex 规则(FTP/SMTP/POP3),或对于二进制协议(MySQL greeting、RDP X.224)使用 `binary: true` 配合 `banner_hex` / 十六进制的 `match_hex`+`respond_hex` 规则,以便 nmap `-sV` 能对其进行版本识别 |
| `ssh` | 真实 SSH 服务器 | 基于 asyncssh:真正的密钥交换,因此客户端会进入认证阶段。将每一次密码/公钥尝试捕获为遥测数据并予以拒绝;`accept_creds` 允许一个植入的登录成功进入一个记录输入命令的诱饵 shell。`server_version` 可设置 nmap 读取的 OpenSSH banner |
| `kerberos` | KDC(roasting) | 在 88 端口(UDP+TCP)上响应 AS-REQ + TGS-REQ,带有 AES/RC4 预认证(通过 PA-ETYPE-INFO2 通告 salt)。**AS-REP roasting**:带有 `no_preauth` 的账户会产生真实的 `$krb5asrep$` (GetNPUsers)。**Kerberoasting**:带有 `spn` 的账户会在 AS→TGS 流程中产生 `$krb5tgs$`。票据可被破解,并以告警形式记录。这是一个用于 roasting 的诱饵,而非完整的 KDC |
| `ldap` | LDAPv3(S) 目录 | 真实的 BER 线协议;将 `identities`(用户/组/SPN)和靶场的 Windows 主机(计算机对象 + DC OU)渲染到 DIT 中;支持匿名绑定 + RootDSE + 子树搜索 + and/or/not/equality/present/substrings 过滤器。**根据 `identities` 的 NT 哈希验证 NTLM 绑定**(SASL GSS-SPNEGO + MS Sicily)。`tls: true` 可提供 LDAPS 服务 |
| `smb` | SMB2/3 文件服务器 | 基于 impacket;将 `shares` 渲染为真实的后端文件(`smbclient -L` / `enum4linux` 可枚举它们)。**根据 `identities` 的 NT 哈希验证 NTLM** —— pass-the-hash 会使用正确的哈希成功访问,错误的哈希会被拒绝(登录失败 → 告警)—— 同时空会话枚举依然有效。`readonly` 是建议性的。最高可协商至 **SMB 3.1.1**(`max_dialect`),带有每主机的 ServerGUID、真实的运行时间,以及 3.1.1 的 preauth-integrity/encryption/signing negotiate contexts;带有签名/凭证的 3.1.1 会话需要后端无法实现的 AES-CMAC,因此在 3.1.1 下签名被通告为不需要 |
| `dns` | DNS 服务器(UDP+TCP) | 根据 `records` 提供权威的 A/AAAA/CNAME/NS/PTR/MX/TXT/SRV 记录,自动为靶场主机填充 A 记录,并提供工具用于定位 DC 的 `_ldap._tcp` / `_kerberos._tcp` SRV 记录。不支持 recursion / AXFR / DNSSEC |
| `rdp` | RDP (NLA) endpoint | 响应 X.224 安全协商,使用主机证书升级到 TLS,并且(当启用了 `nla_required` 时)通过 CredSSP 发起挑战,从而使 `rdp-enum-encryption` 报告需要 NLA,`rdp-ntlm-info` 泄露 NetBIOS/DNS 名称、域和 OS 构建版本(`os_version`)。将 `mstshash` cookie + 任何 CredSSP 登录捕获为遥测数据。在到达 RDP 图形/MCS 层之前停止 |
## CLI
```
rangefinder validate examples/corp.json # validate + summarize (exit 2 on error)
rangefinder schema -o range.schema.json # export JSON Schema for editor autocomplete
rangefinder gen examples/corp.json -o build/ # emit build/docker-compose.yml + config.json
rangefinder import nmap scan.xml -o cfg.json # discover topology from an nmap scan
rangefinder capture http https://host/ -o cfg.json # record a live web server -> faithful facade
rangefinder capture ldap 10.0.0.10 -o cfg.json # record a live directory -> faithful facade
rangefinder capture smb 10.0.0.20 -o cfg.json # record live file shares -> faithful facade
rangefinder capture dns 10.0.0.10 --zone acme.corp # record a live DNS zone -> faithful facade
rangefinder verify http http://10.0.0.30/ # measure replica fidelity vs the live target
rangefinder score examples/acme.json log.jsonl # score objectives against a telemetry log
rangefinder run --host web01 --config examples/corp.json # serve one host (container entrypoint)
rangefinder up -o build/ # docker compose up -d (thin wrapper)
rangefinder down -o build/ # docker compose down
```
## 运行靶场
```
docker build -t rangefinder:latest . # facade runtime image
docker build -t rangefinder-attacker:latest docker/attacker # attacker toolbox (nmap, curl, ldap-utils, dnsutils)
rangefinder gen examples/corp.json -o build/
docker compose -f build/docker-compose.yml up -d
```
每个主机容器都在其静态 IP 的真实端口(80、22、445、……)上提供其所有的 facade 服务。将攻击者连接到同一网络进行测试:
```
docker compose -f build/docker-compose.yml --profile attacker run --rm attacker
# 在 attacker 容器内部:
nmap -sV 10.13.37.0/24
curl -i http://10.13.37.20/
dirb http://10.13.37.20/
ldapsearch -x -H ldap://10.13.37.10 -b "DC=corp,DC=local" -s sub "(objectClass=user)"
smbclient -N -L //10.13.37.10 # list shares; then //10.13.37.10/BACKUPS to browse
GetNPUsers.py -dc-ip 10.20.0.10 -no-pass -usersfile users.txt acme.corp/ # AS-REP roast
dig @10.13.37.10 _ldap._tcp.dc._msdcs.corp.local SRV # locate the "DC"
```
替代方案:将端口暴露给主机回环地址,而不是使用独立的攻击者容器。这会失去子网扫描的保真度并强制进行端口重映射(多个主机不能共享 `127.0.0.1:445`),因此推荐使用默认的网络内攻击者模式。
## 遥测
每个 facade 都会为每次交互向 stdout 写入一行 JSON 数据(由 `docker compose logs` 捕获)。字段遵循 **Elastic Common Schema** 命名,特定于靶场的详细信息放在 `rangefinder.*` 命名空间下。连接的打开/关闭、HTTP 请求(方法、路径、user-agent、状态)以及 banner 交换都会被记录。命中植入漏洞的路由会设置 `event.kind: "alert"` 并带有 `rangefinder.vuln_id`,因此 SIEM 检测规则可以轻易触发——这是用于检测/SOC 评估的核心钩子。
事件示例(HTTP 命中植入漏洞):
```
{"@timestamp":"2026-07-03T10:00:00.000Z","event":{"kind":"alert","category":["web"],
"action":"http_request","dataset":"rangefinder.http"},"source":{"ip":"10.13.37.99"},
"destination":{"ip":"10.13.37.20","port":80},"http":{"request":{"method":"GET"},
"response":{"status_code":200}},"url":{"path":"/.git/HEAD"},
"user_agent":{"original":"gobuster/3.6"},
"rangefinder":{"vuln_id":"exposed-git-dir","conn_id":"..."}}
```
## 还原真实基础设施
分两步构建一个镜像真实环境的靶场:**发现**拓扑,然后**捕获**每个服务的真实行为,以便其弱点能延续下来。
```
# 1. discover — nmap 识别正在监听的服务 -> 生成一个配置骨架
nmap -sV -oX scan.xml 10.0.0.0/24
rangefinder import nmap scan.xml --name prod-replica -o prod.json
# 2. capture — 将实时服务的实际响应记录为一个忠实的 facade
rangefinder capture http https://10.0.0.30/ -o web.json # crawl -> http facade
rangefinder capture ldap 10.0.0.10 -o dc.json # anonymous dir dump -> ldap facade
rangefinder capture smb 10.0.0.20 -o fs.json # null-session shares -> smb facade
rangefinder capture http https://10.0.0.30/ --scrub -o web.json # redact secrets to share
```
设计原则:**靶场携带弱点,而不是仅仅依靠一个命名了弱点的目录。**
`import` 是纯粹的发现过程(主机 → 靶场主机;端口 → facade)。`capture` 是记录-重放机制——它会探测实时服务并记录其返回的真实 `(status, headers, body)`,然后由 facade 重放。真实响应中的任何弱点(暴露的 `/.git`、目录列表、冗长的错误信息、泄露的配置、任何匿名访问返回的内容)都会自动重现,因为它是被捕获的——而不是因为有什么代码专门识别了它。
整个往返流程结束后,nmap 的 `http-git` 脚本会在*副本*上标记出暴露的代码库,就像在原始环境中一样。
**默认原样保留,可选 `--scrub`。** 捕获的内容包含真实数据,如果靶场由其所镜像的组织拥有,这是没问题的;`--scrub` 会将捕获的内容(跨越所有三个捕获器)通过一个脱敏程序,移除键/值机密信息(`password=…`、连接字符串)、私钥、云/提供商 token(AWS、GitHub、Slack 等)、JWT、bearer/basic auth、URL 凭据以及 PII(电子邮件、SSN、通过 Luhn 算法校验有效的信用卡),并以一致的方式对电子邮件进行假名化处理,以确保引用保持完整。由于结构保持忠实,弱点依然会被带过。这只是启发式方法,并非绝对的保证——在分享前请进行审查(例如,以自由文本形式书写且没有 `password=` 标记的密码将无法被捕获)。
### 验证保真度
你如何知道副本是一个*可行*的复刻品?`verify` 将其度量为黑盒差分等价测试——这是唯一可靠的测试,因为它比较的是**工具**所看到的,而不是实现本身。它会捕获实时目标,在进程内的回环端口上提供生成的 facade 服务,然后使用同一个客户端探测**两者**,并对具备协议感知能力的等价类进行差异对比:
```
rangefinder verify http http://10.0.0.30/ # per route: status + body + security headers
rangefinder verify ldap 10.0.0.10 --bind-dn cn=admin,... --password … # entry DNs + attribute sets
rangefinder verify smb 10.0.0.20 --username … --password … # share names + file tree + content
rangefinder verify dns 10.0.0.10 --zone acme.corp # record answer sets per name/type
```
```
Fidelity: http http://10.0.0.30/
7/7 faithful (100.0%) from the consumer's perspective
fidelity boundary:
- uncaptured paths diverge: real 404 vs replica 404 for a path that was never probed
=> FAITHFUL
```
它会报告一个**分数**(忠实/总数)、一个明确的**差异列表**,以及一条**保真度边界**——即标记出在何处应停止信任副本的映射图。内置了两条诚信规则:始终实时重新获取真实目标(将 facade 与其自身捕获的字节进行比较将是同义反复的),并且仅针对*捕获过程所演练过的视角*(例如匿名绑定——绝不会是它从未见过的凭证绑定或更深入的读取)声明保真度。
只要存在任何差异,退出代码就会是非零的,因此它可以作为 CI 中的门控。已通过真正的第三方软件(nginx、OpenLDAP、Samba、CoreDNS)进行了验证,而不仅仅是针对自身进行测试——针对 CoreDNS 的一次运行捕获到了一个真实的记录 bug(相对的 SRV/MX 目标),这正是该测试套件的意义所在。
每次运行还会检查**检测视角**:捕获副本在被探测时发出的遥测数据,因此报告会显示工具操作产生了多少 SIEM 事件和告警——并且对于 HTTP,facade 会提供服务但从不*记录*日志的路由会作为检测的“盲点”显露出来。一个响应了工具但在 SIEM 中保持沉默的靶场无法达到 SOC 评估的目的,因此 `verify` 只有在两者都成立时才会报告 `FAITHFUL & OBSERVABLE`。
`verify http --nmap` 增加了侦察工具的视角:它针对真实目标和副本运行 `nmap -sV`,并比较服务/版本指纹(如果未安装 nmap,则会带有一条提示跳过)。
等价性是根据*工具*所关心的层面按协议定义的——例如,SMB 共享和路径名称是不区分大小写进行比较的,因为 SMB 本身就是如此,所以对于真实的 `public`,一个提供 `PUBLIC` 服务的副本会被认为是忠实的(无论哪种方式,客户端都能访问到相同的共享),并且大小写标准化将作为边界注释显示,而不是报告为错误的失败。
## 评分
评分是遥测数据的一个**独立读取器**——facade 会继续输出完整日志;评分器只是对其进行评估,因此你可以两者兼得。每个 `objective` 都可以带有一个 `detect` 规:一个**信号**列表,当任何信号的条件在单个事件上全部成立时,该目标即被视为达成(MET)。条件通过 `equals` / `contains` / `regex` 匹配点分事件字段:
```
{ "id": "obj-svc-sql-cred", "title": "Recover the svc-sql credential",
"description": "Exposed via a web config and an SMB share.",
"detect": [
{ "label": "read the exposed web db.config",
"all": [ { "field": "event.action", "equals": "http_request" },
{ "field": "url.path", "contains": "/backup/db.config" } ] },
{ "label": "read the IT credential vault over SMB",
"all": [ { "field": "event.action", "equals": "smb_file_access" },
{ "field": "rangefinder.smb.path", "contains": "vault-export" } ] }
] }
```
针对捕获的日志(一个文件,或通过管道从 `docker compose logs` 传入)运行它:
```
docker compose -f build/docker-compose.yml logs | rangefinder score examples/acme.json -
# [MET ] obj-svc-sql-cred — 首先通过“读取暴露的 web db.config”,由 10.20.0.2 执行 (http_request)
# [UNMET] obj-ssh-foothold
# 总结:1/4 目标已达成
```
报告会针对每个目标给出:第一个匹配的事件(时间、源 IP、操作)、触发的信号,以及有多少来自哪些源的事件匹配上了。没有 `detect` 的目标会被报告为 `UNSCORED`。使用 `--json` 获取机器可读的输出。
### Kill chains(跨事件)
除了单事件的 `detect` 之外,一个目标还可以带有一个 `sequence`——这是一组必须**按顺序**发生的有序步骤,默认情况下由**同一来源**触发,可选择性地限制在某个时间窗口内。这用于对多阶段攻击进行评分(“认证通过 *然后* 读取文件”):
```
{ "id": "obj-killchain", "title": "Foothold then data theft",
"description": "The same attacker gains SSH access and then reads files over SMB.",
"sequence": {
"same_source": true, "within": "10m",
"steps": [
{ "label": "SSH foothold",
"all": [ { "field": "event.action", "equals": "ssh_auth" },
{ "field": "event.outcome", "equals": "success" } ] },
{ "label": "read a file over SMB",
"all": [ { "field": "event.action", "equals": "smb_file_access" } ] }
] } }
```
一个达成的序列会将该链路按每个攻击者的视角打印成一条叙事记录:
```
[MET] obj-killchain — Foothold then data theft
kill chain completed at 2026-… by 10.20.0.2:
1. SSH foothold at 2026-…:12 (ssh_auth)
2. read a file over SMB at 2026-…:13 (smb_file_access)
```
(一个目标可以使用 `detect`、`sequence`,或两者兼用——只要其中一个触发,即视为达成。)
## 架构
```
config/ pydantic models; JSON is the single source of truth (discriminated union on `type`)
facades/ registry + Facade base + http/banner/ssh/ldap/smb/dns facades
telemetry/ ECS-aligned event builders + stdout/file/list sinks
runtime/ per-host supervisor: serve all facades in one loop, graceful SIGTERM shutdown
orchestrate/ config -> docker-compose (JSON-as-YAML, static IPs, attacker profile)
scoring.py offline objective scorer over the telemetry log
```
## 扩展
添加 facade 的步骤:(1) 在 `config/services.py` 中定义其配置模型,并将其添加到 `BuiltinService` 联合类型中;(2) 编写一个使用 `@register("type")` 装饰器修饰的 `Facade` 子类,并在 `facades/__init__.py` 中导入它。运行时、遥测和编排层会自动识别它。
## 开发
```
pip install -e '.[dev]'
pytest
```
## 法律声明
仅限用于你有权测试的系统和网络。你需对如何部署和使用基于此工具构建的靶场自行承担责任。
标签:Homebrew安装, SIEM日志生成, SNMP, XXE攻击, 安全测试, 插件系统, 攻击性安全, 版权保护, 紫队演练, 红队评估, 网络协议仿真, 请求拦截, 逆向工具, 靶场生成