totekuh/snaffler-ng
GitHub: totekuh/snaffler-ng
snaffler-ng 是一款基于 Python/Impacket 的后渗透敏感信息发现工具,用于在 Windows 网络中自动扫描 SMB 共享、FTP 服务器和本地文件系统以识别凭据和敏感数据。
Stars: 3 | Forks: 1
# snaffler-ng
[Snaffler](https://github.com/SnaffCon/Snaffler) 的 Impactor 移植版本。
**snaffler-ng** 是一款后渗透工具,用于发现可读的 SMB 共享,遍历目录树,并识别 Windows 网络中的凭据和敏感数据。同时支持扫描 FTP 服务器、本地文件系统,并可作为 Python 库用于 C2 集成。
## 安装
```
pip install snaffler-ng
# 或
pipx install snaffler-ng
```
针对 Linux x86_64、Linux aarch64 和 Windows x86_64 的预编译二进制文件(无需 Python)可在 [Releases](https://github.com/totekuh/snaffler-ng/releases) 页面获取。Debian/Kali:`sudo dpkg -i snaffler-ng_*.deb`。
可选扩展:
```
pip install snaffler-ng[socks] # SOCKS proxy support
pip install snaffler-ng[web] # live web dashboard
pip install snaffler-ng[7z,rar] # 7z/RAR archive peeking
```
## 快速入门
```
# 完整域发现 — 查找计算机、解析 DNS、枚举共享、扫描所有内容
snaffler -u USER -p PASS -d DOMAIN.LOCAL
# 使用 ccache 的 Kerberos
snaffler -k --use-kcache -d DOMAIN.LOCAL --dc-host CORP-DC02
# 扫描特定 UNC 路径
snaffler -u USER -p PASS --unc //10.0.0.5/Share --unc //10.0.0.6/Data
# 扫描特定计算机 (启用共享发现)
snaffler -u USER -p PASS --computer 10.0.0.5 --computer 10.0.0.6
# 本地文件系统 (无需认证)
snaffler --local-fs /mnt/share
# FTP 服务器 (匿名)
snaffler --ftp ftp://10.0.0.5
```

## 目标模式
### 域发现 (`-d`)
查询 AD 获取计算机 + DFS 命名空间,解析 DNS,探测 445 端口,枚举共享,然后扫描:
```
snaffler -u USER -p PASS -d DOMAIN.LOCAL
snaffler -u USER -p PASS -d DOMAIN.LOCAL --max-hosts 50 # cap at 50 hosts
snaffler -u USER -p PASS -d DOMAIN.LOCAL --shares-only # enumerate shares without scanning
```
### 计算机列表 (`--computer` / `--computer-file`)
跳过 LDAP 发现,定位特定主机:
```
snaffler -u USER -p PASS --computer 10.0.0.5 --computer 10.0.0.6
snaffler -u USER -p PASS --computer-file targets.txt
```
### UNC 路径 (`--unc`)
跳过共享发现,直接扫描特定路径:
```
snaffler -u USER -p PASS --unc //10.0.0.5/Share --unc //10.0.0.6/IT
```
### 来自 NetExec 的管道输入 (`--stdin`)
```
nxc smb 10.0.0.0/24 -u user -p pass --shares | snaffler -u user -p pass --stdin
```
### FTP 服务器 (`--ftp` / `--ftp-file`)
同样的分类引擎,包含所有 106 条规则,支持内容扫描、恢复和下载:
```
snaffler --ftp ftp://10.0.0.5 # anonymous
snaffler --ftp ftp://10.0.0.5/Data -u ftpuser -p ftppass # with creds + subpath
snaffler --ftp ftp://10.0.0.5:2121 --ftp-tls # custom port + TLS
snaffler --ftp-file ftp_targets.txt -u ftpuser -p ftppass # load from file
```
支持裸主机名:`--ftp 10.0.0.5` 会被视为 `ftp://10.0.0.5`。若未指定 `-u`/`-p`,将尝试匿名登录。
### 本地文件系统 (`--local-fs`)
无网络,无认证 —— 适用于挂载的共享、解压的文件系统或测试规则:
```
snaffler --local-fs /mnt/share
snaffler --local-fs /tmp/extracted --local-fs /home/user/Documents
```
### 重新扫描不可读共享 (`--rescan-unreadable`)
使用新凭据重新测试此前拒绝访问的共享 —— 适用于密码喷洒之后:
```
# 使用低权限凭据进行初始扫描
snaffler -u lowpriv -p 'Password1' -d CORP.LOCAL --state scan.db
# 随后,使用更高权限凭据
snaffler --rescan-unreadable -u highpriv -p 'NewPass!' --state scan.db
```
初始扫描会将所有发现的共享(可读和不可读)存储在状态数据库中。`--rescan-unreadable` 仅加载此前被拒绝的共享,使用当前凭据重新测试,并扫描那些现在已可访问的共享。遵循 `--share`、`--exclude-share` 和 `--exclusions` 过滤器。
## 过滤
```
# 仅扫描特定共享
snaffler ... --share "SYSVOL" --share "IT*"
# 排除共享
snaffler ... --exclude-share "IPC$" --exclude-share "print$"
# 排除路径 (glob,适用于所有模式)
snaffler ... --exclude-path "*/Windows/*" --exclude-path "*/.snapshot/*"
# 限制目录递归深度
snaffler ... --max-depth 5
# 对结果进行 Regex 后过滤 (匹配路径、规则名称、内容)
snaffler ... --match "password|connectionstring"
# 跳过特定主机
snaffler ... --exclusions hosts_to_skip.txt
# 在 N 台主机后停止
snaffler ... --max-hosts 50
```
## 输出
### 格式
三种输出格式:**plain**(默认)、**JSON**、**TSV**。根据 `-o` 文件扩展名自动检测:
```
snaffler ... -o findings.json # JSON
snaffler ... -o findings.tsv # TSV
snaffler ... -o findings.txt # plain
snaffler ... -o out.log -t json # explicit override
```
### 恢复
扫描状态通过 SQLite (`snaffler.db`) 跟踪。当数据库存在时,扫描会自动恢复:
```
snaffler -u USER -p PASS -d DOMAIN.LOCAL # creates snaffler.db
# 被中断?重新运行相同的命令 — 从中断处继续
snaffler -u USER -p PASS -d DOMAIN.LOCAL # resumes
snaffler ... --state /tmp/scan1.db # custom DB path
snaffler ... --fresh # ignore existing state
```
### 查询结果
```
snaffler results # plain text summary
snaffler results -f json # JSON
snaffler results -f html > report.html # self-contained HTML report
snaffler results -b 2 # Red+ severity only
snaffler results -s /path/to/snaffler.db # custom DB path
```
### Web 仪表盘
用于监控扫描进度和发现结果的实时浏览器仪表盘:
```
snaffler ... --web --web-port 8080
```
需要安装 `pip install snaffler-ng[web]`。
### 归档文件查看
无需解压即可扫描 ZIP、7z 和 RAR 归档内的文件名:
```
pip install snaffler-ng[7z,rar] # ZIP works out of the box
```
## 认证与网络
| 标志 | 描述 |
|------|-------------|
| `-u` / `-p` | NTLM 用户名/密码 |
| `--hash` | NTLM Pass-the-Hash |
| `-k` | Kerberos 认证 |
| `--use-kcache` | 通过现有 ccache 进行 Kerberos 认证 (`KRB5CCNAME`) |
| `--socks` | SOCKS 代理 pivoting (`socks5://127.0.0.1:1080`) |
| `--nameserver` / `--ns` | 自定义 DNS 服务器(使用 TCP,支持通过 SOCKS) |
| `--dc-host` | 域控制器主机名或 IP |
| `--stealth` | OPSEC 模式:填充 LDAP 查询以绕过 IDS 特征检测 |
```
# SOCKS + 通过隧道的自定义 DNS
snaffler -u USER -p PASS -d DOMAIN.LOCAL \
--socks socks5://127.0.0.1:1080 --ns 192.168.201.11 --dc-host 192.168.201.11
```
### 运行时热键
扫描期间,按 `d` 开启 DEBUG 输出,按 `i` 切回 INFO。
## 库 API
### 遍历目录
```
from snaffler import Snaffler
for finding in Snaffler().walk("/mnt/share"):
print(f"[{finding.triage.label}] {finding.file_path}")
if finding.match:
print(f" matched: {finding.match}")
```
### 两阶段分类(C2 集成)
最小化 beacon 流量 —— 大多数文件在阶段 1 即被跳过(仅元数据,零 I/O):
```
from snaffler import Snaffler
from snaffler.api import FileCheckStatus
s = Snaffler()
# 阶段 1:仅 metadata — 即时,无文件读取
check = s.check_file(path, size=4096, mtime_epoch=1700000000.0)
if check.status == FileCheckStatus.NEEDS_CONTENT:
# Phase 2: only download + classify when needed
result = s.scan_content(file_bytes, prior=check)
elif check.status == FileCheckStatus.MATCHED:
result = check.result # matched on filename alone (e.g. ntds.dit)
```
### 自定义传输(鸭子类型)
插入任何传输层 —— 无需 ABC(抽象基类),只需实现 `walk_directory` 和 `read`:
```
class BeaconWalker:
def walk_directory(self, path, on_file=None, on_dir=None, cancel=None):
for entry in beacon.ls(path):
if entry.is_dir:
if on_dir: on_dir(entry.path)
elif on_file:
on_file(entry.path, entry.size, entry.mtime)
return [e.path for e in beacon.ls(path) if e.is_dir]
class BeaconReader:
def read(self, path, max_bytes=None):
return beacon.download(path, max_bytes)
s = Snaffler(walker=BeaconWalker(), reader=BeaconReader())
for finding in s.walk("C:\\Users"):
beacon.report(finding.file_path, finding.triage.label)
```
### 构造函数参数
| 参数 | 默认值 | 描述 |
|-----------|---------|-------------|
| `walker` | `LocalTreeWalker()` | 目录列表提供者 |
| `reader` | `LocalFileAccessor()` | 文件内容读取器 |
| `rule_dir` | `None` | 自定义 TOML 规则目录 |
| `min_interest` | `0` | 最低严重级别(0=全部,3=仅 Black) |
| `max_read_bytes` | `2MB` | 内容扫描字节限制 |
| `match_context_bytes` | `200` | 正则匹配周围的上下文字节 |
| `cert_passwords` | 内置列表 | 尝试用于 PKCS12 证书的密码 |
| `exclude_unc` | `None` | 用于跳过目录的 Glob 模式 |
| `match_filter` | `None` | 对发现结果进行正则后过滤 |
| `max_depth` | `None` | 最大目录递归深度 |
标签:Active Directory, C2集成, FTP扫描, HTTP, Impacket, PE 加载器, Plaso, Python, SMB, Snaffler移植, SOCKS代理, StruQ, 代码生成, 共享文件夹扫描, 凭证发现, 敏感数据检测, 文件系统审计, 无后门, 渗透测试工具, 网络安全审计, 逆向工具, 错误配置检测