iklobato/avai
GitHub: iklobato/avai
avai 是一个无需 agent 和 SIEM 的主机安全遥测收集器,结合 17 个威胁情报源和 LLM 对 macOS/Linux 端点发现进行自动化威胁研判,并通过单页 Web 仪表盘呈现结果。
Stars: 15 | Forks: 2
avai
[](https://pypi.org/project/avai-monitor/)
[](https://hub.docker.com/r/iklob1/avai)
[](LICENSE)
[](https://getavai.com)
`avai` 会在 macOS 上为你主机的 26 个角落(在 Linux 上为 21 个)生成快照 ——
包括进程、USB、持久化、文件完整性、浏览器扩展、exec 事件 —— 并利用多达
**17 个威胁情报源**(VirusTotal、MalwareBazaar、URLhaus、CISA KEV、Shodan、
AbuseIPDB、OSV、NVD 等)来丰富每一个新发现,然后让 Claude 级别的 LLM 告诉你哪些是
值得关注的内容。判定结果会以
**恶意** / **可疑** / **未知** / **良性** 的形式返回,并附带
MITRE 对齐的分类、置信度以及单行修复建议。
- 无需 agent 契约,无需 SIEM,无需云端控制平面。
- 基于内容哈希去重 —— 同一个制品(artifact)绝对不会被发送给 LLM 两次。
- 在 LLM 背后有 17 个即插即用的威胁情报源 —— 请参阅 [`.env.example`](.env.example);缺失的密钥会直接禁用对应源。
- 位于 `:8765` 的只读 Flask + HTMX + Chart.js 仪表盘。
- 自带密钥(`ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN`),或者切换到任何 litellm 支持的提供商。
→ 营销网站与截图:
→ 源码:
## 截图
位于 `:8765` 的只读 Flask + HTMX + Chart.js 仪表盘。每个面板都是
从监控器写入的同一个 SQLite 快照中渲染出来的 —— 没有独立的控制平面。
### 仪表盘 —— 概览
一目了然的系统健康度:存储的运行次数、最新周期中的收集器(包含任何
故障)、自上次运行以来的判定结果,以及判定总量环形图
(恶意 / 可疑 / 未知 / 良性)。macOS 的 **系统完整性** 面板
会显示 FileVault、防火墙、Gatekeeper 和远程访问开关;**收集器
错误** 显示了失败的内容(例如 TCC 权限问题);12 小时的图表则追踪了
判定结果随时间的变化。下方的发现表格会流式加载活动的、非良性的
结果。

### 发现、收集器与运行记录
发现表格可以按状态、判定结果、收集器和类别进行过滤。
在它下方,**每个收集器的行数** 显示了每个收集器在
最近一次运行中提取了多少数据,而 **最近的运行** 列出了运行历史,包含成功/失败计数以及
回溯时间窗口。

### 发现详情
展开任意发现,可以查看 LLM 的 **推理过程**、具体的 **修复**
步骤,以及判定背后的确切 **收集数据** —— 对于一个进程
这意味着 pid/ppid、完整的 `cmdline`、运行中的用户/uid、状态、用于
去重的内容哈希,以及首次判定时间与最后发现时间。

### 网络流
tcpdump 聚合器按目的地对流量进行分组,以便分类器能够对其进行推理:
这里到一个异常高端口的 IPv6 连接被标记为
**可疑**,被视为潜在的 C2 信标,而 CDN、mDNS 和 LAN 流量则返回
**良性** —— 每一项都带有一句“原因”解释。

### 网络流 —— 富化后
同一个视图按目的地进行了富化,显示了所属的 **进程**、ASN/地理位置、
流量大小,以及每个判定结果的理由。

### 另一台主机上的同一个仪表盘
在不同主机/周期上运行 —— 这里有 61 次运行记录和 3,426 个判定结果 ——
并显示了可疑的 AirWatch/MDM 持久化项供审查。

## 功能
**主机遥测 —— macOS 上有 26 个收集器,Linux 上有 21 个**,对恶意软件
隐藏、驻留和回传的每个位置进行快照:
- **进程与执行** —— 正在运行的进程,以及触发时的 `execve` 执行事件。
- **网络** —— 活动连接、监听端口、tcpdump **流聚合器**
(按目的地分组,与拥有它的进程绑定)、DNS 查询、网络接口、Wi-Fi 状态。
- **持久化** —— 启动项(LaunchDaemons/Agents、systemd 单元、cron),
内核与系统扩展、MDM/配置描述文件、已安装的应用。
- **访问与身份** —— 认证事件(统一日志 / `journalctl`)、SSH
`authorized_keys`、TCC 隐私授权(摄像头/麦克风/位置/屏幕/全盘),
权限配置、setuid 二进制文件。
- **完整性与安全姿态** —— 系统完整性(FileVault、防火墙、Gatekeeper、SIP、
SELinux/AppArmor/ufw 等)、文件完整性(passwd/shadow/sudoers/SSH/dotfiles),
`/etc/hosts`、隔离事件、挂载点。
- **硬件** —— USB 和蓝牙设备。
- **浏览器** —— 已安装的浏览器扩展。
**LLM 威胁分类器** —— 一个 Claude 级别的模型会将每个新制品
标记为 **恶意 / 可疑 / 未知 / 良性**,并附带 **MITRE 对齐的分类**、
**置信度** 和 **单行修复建议**。请自带您的密钥
(`ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN`)或使用任何 litellm 支持的提供商。
**每个判定背后有 17 个威胁情报源** —— 指标(哈希、IP、域名、
URL、CVE、软件包、OS 版本)在模型看到它们 *之前* 就已被富化:
VirusTotal · MalwareBazaar · URLhaus · ThreatFox · Feodo Tracker · AbuseIPDB ·
GreyNoise · Shodan InternetDB · CISA KEV · NVD · OSV · GitHub Advisory ·
CIRCL hashlookup · crt.sh · PhishTank · Google Safe Browsing · endoflife.date
(外加 IP 地理位置)。无需密钥的源始终运行;需要密钥的源会在您添加
密钥后启用,而缺失密钥会直接禁用该源。结果会被缓存在
SQLite 中,并带有每个源的 TTL。
**只读仪表盘**(位于 `:8765` 的 Flask + HTMX + Chart.js)—— 判定总量环形图、
macOS 系统完整性面板、收集器错误、12 小时判定图表,以及
一个在各个部分都具备搜索 / 过滤 / 排序 / 分页功能的发现表格。展开任意
发现即可查看模型的推理过程、修复方案和原始收集数据。
每 30–60 秒自动刷新,并在出现新的
恶意/可疑发现时弹出 Toast **+ 音频警报**。
**旨在不打扰您的方式构建**
- **一条 `docker run` 命令** —— 一个镜像同时运行监控器 *和* 仪表盘(通过 supervisord),因此开箱即用。
- **无需 agent 契约,无需 SIEM,无需云端控制平面** —— 它直接在您的主机上运行。
- **基于内容哈希去重** —— 同一个制品绝对不会被发送给 LLM 两次。
- **仅仅是一个 SQLite 文件** —— 将仪表盘指向任何操作系统上的任何 `avai.db` 即可。
- **也支持原生安装**:`pip install avai-monitor`。
完整参考见下文:[收集了什么](#whats-collected-one-line-summary) ·
[仪表盘](#dashboard) · [威胁情报富化](#threat-intel-enrichment)。
## 为什么选择 avai —— 优势
- **给出答案,而非日志。** 每个发现都会以简明易懂的文字返回,附带判定结果、
置信度、MITRE 分类和具体的修复方法 —— 无需查询语言,无需分流电子表格。
- **零基础设施。** 一个容器。无需 SIEM,无需注册 agent,无需运行或付费的
控制平面。
- **默认私密。** 一切都在您的机器上运行;您自带模型
密钥,并且只有 *新* 的发现才会被发送出去 —— 用于您选择的威胁情报查询或 LLM 调用。
- **运行成本低。** 内容哈希去重意味着每个制品仅被判定一次 —— 一台繁忙的
主机并不意味着高昂的账单 —— 而且缓存的情报命中会完全跳过网络。
- **真正的广度。** 26 个主机攻击面 × 17 个情报源支持一个判定 ——
拥有 EDR 般的广度,却不需要 agent。
- **跨平台。** macOS 和 Linux 使用同一款工具。
- **开放且属于您。** 采用 MIT 许可证,可审计,且与模型无关 —— 只需一个环境变量即可切换到任何
litellm 提供商。
- **可安全指向生产环境。** 收集器仅执行 *读取* 操作;仪表盘是只读的。
- **可移植的历史记录。** 整个状态就是一个单一的 SQLite 文件 —— 在服务器上扫描,
在笔记本电脑上查看,存档快照,或随时间进行对比。
## 一个镜像,两种角色
| 运行方式 | 命令 | 适用场景 |
|---|---|---|
| 监控器 + 仪表盘(默认) | `docker run -p 8765:8765 -v "$PWD":/data iklob1/avai` | 任意主机 —— 在 supervisord 下同时运行两者;位于 :8765 的仪表盘由容器内的监控器提供数据 |
| 完整的主机可见性 | `docker run --pid=host --network=host ... iklob1/avai` | **Linux 主机** —— 容器内的监控器可以查看到真实的宿主机 |
| 单一角色 | `docker run ... iklob1/avai avai dashboard`(或 `avai monitor`) | 仅运行一种角色(例如针对现有 `avai.db` 的仪表盘,或仅运行监控器的容器) |
镜像的默认 `CMD` 指令通过 supervisord 将两个角色运行在一起(它们
共享 `/data/avai.db` —— 监控器负责写入,仪表盘实时读取 ——
如果其中一个退出,两者都会自动重启)。可以覆盖该命令以
运行单一角色。LLM 判定器在运行时仍然需要凭据
(`-e CLAUDE_CODE_OAUTH_TOKEN=…` / `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`);
如果没有,监控器将回退到 NullJudge(仅收集)。原生
安装同样有效:`pip install avai-monitor`,然后运行 `avai monitor` /
`avai dashboard`。
镜像针对仪表盘的 `/api/notifications/new` 端点携带了 `HEALTHCHECK` —— 首次
启动时约 10 秒内状态变为 `starting → healthy`。`docker compose ps` 和 `docker inspect --format
'{{.State.Health.Status}}'` 都会反映出该状态。
## TL;DR —— 60 秒测试,无需 LLM 密钥
在任意主机(macOS 或 Linux)上进行安全的首次运行,无需
特权,无需凭据,无需主机目录绑定挂载。会生成一个填有数据的 DB 和一个
您可以随意查看的绿色仪表盘。
```
mkdir -p ~/.avai && cd ~/.avai
# 用容器视图的一个 snapshot 填充 DB
docker run --rm -v "$PWD":/data iklob1/avai \
avai monitor --once --no-streaming --no-judge --db /data/avai.db
# 运行它
docker run -d --name avai -p 8765:8765 -v "$PWD":/data iklob1/avai
open http://localhost:8765/ # macOS; xdg-open on Linux
```
您会看到大约 14 个收集器的数据行(`processes`、
`network_connections`、`listening_ports`、`network_interfaces`、
`usb_devices`、`launch_items`、`installed_apps`、`mounts`、
`setuid_files` 等)—— 因为上面的运行没有绑定挂载主机状态,所以这些是
从容器本身读取的,而不是来自主机。要获取真实
数据,请跳至下文的 §2 / §3。
使用 `docker stop avai && docker rm avai` 停止运行。
## 1 —— 仅仪表盘(任意主机,包括 macOS)
仪表盘读取由监控器(或由之前的
运行)写入的 SQLite 数据库。它不需要特权,不需要主机
命名空间,不需要 capabilities —— 只需将一个包含
`avai.db` 的目录挂载到 `/data` 即可。
```
mkdir -p ~/.avai && cd ~/.avai
docker run -d \
--name avai-dashboard \
-p 8765:8765 \
-v "$PWD":/data \
iklob1/avai
open http://localhost:8765/
```
如果数据库文件不存在,仪表盘会在启动时
创建一个空的 schema,并且在监控器产生
数据行之前,每个面板都将呈现空白。使用 `docker stop avai-dashboard &&
docker rm avai-dashboard` 停止运行。
### 覆盖端口或 DB 路径
```
docker run --rm -p 9000:9000 \
-v /var/lib/avai:/data \
iklob1/avai \
avai dashboard --host 0.0.0.0 --port 9000 --db /data/custom.db
```
镜像入口点是 `avai`;镜像名称后面的任何内容都会
传递给它。
## 2 —— 监控器:一次性扫描(Linux 主机)
在本地 Linux 主机上进行单个周期。没有流式传输,没有 LLM 判定 ——
这是一个快速的冒烟测试,用于验证绑定挂载是否配置正确。
```
mkdir -p ~/.avai && cd ~/.avai
docker run --rm \
--pid=host \
--network=host \
--user 0:0 \
--cap-add SYS_PTRACE --cap-add NET_ADMIN --cap-add NET_RAW --cap-add DAC_READ_SEARCH \
-e HOST_PREFIX=/host \
-v /proc:/host/proc:ro \
-v /sys:/host/sys:ro \
-v /etc:/host/etc:ro \
-v /var/lib/bluetooth:/host/var/lib/bluetooth:ro \
-v /var/lib/dpkg:/host/var/lib/dpkg:ro \
-v /usr/share/applications:/host/usr/share/applications:ro \
-v /lib/systemd:/host/lib/systemd:ro \
-v /usr/lib/systemd:/host/usr/lib/systemd:ro \
-v /run/systemd:/run/systemd:ro \
-v /run/dbus:/run/dbus:ro \
-v /etc/machine-id:/etc/machine-id:ro \
-v /dev/mapper:/dev/mapper:ro \
-v /home:/host/home:ro \
-v /root:/host/root:ro \
-v "$PWD":/data \
iklob1/avai \
avai monitor --once --no-streaming --no-judge --db /data/avai.db
```
当命令退出时,`~/.avai/avai.db` 会包含一条
`collection_runs` 记录以及填充好的收集器表。验证:
```
docker run --rm -v "$PWD":/data iklob1/avai python -c "
import sqlite3
c = sqlite3.connect('/data/avai.db')
for n, in c.execute(\"select name from sqlite_master where type='table'\"):
print(f'{n:<22} {c.execute(f\"select count(*) from {n}\").fetchone()[0]}')"
```
要在 macOS 上进行没有绑定挂载的冒烟测试(没有主机数据,但
可以证明工具链有效),请参阅上文的 §0。
## 3 —— 监控器:持续运行,带 LLM 判定(Linux 主机)
使用与 §2 相同的绑定挂载,但是以分离模式运行,并启用 LLM 判定。
判定器需要一个凭据 —— 可以是 `ANTHROPIC_API_KEY`(标准
Anthropic API),也可以是 `CLAUDE_CODE_OAUTH_TOKEN` —— 并且
默认使用 **Claude Haiku 4.5**(`claude-haiku-4-5-20251001`)。
使用 `--judge-model` 覆盖它,可将 litellm 指向任何其他提供商。
威胁情报富化会自动使用环境变量(`VT_API_KEY`、`ABUSE_CH_AUTH_KEY`、`ABUSEIPDB_API_KEY`、
…)中提供的任何密钥运行。最简单的模式是使用项目本地的 `.env`:
```
cp .env.example .env && vi .env # fill in only the keys you have
docker run -d --env-file .env --name avai-monitor ... iklob1/avai
```
请参阅下文的 **§ 威胁情报富化** 以获取完整的源列表和
每个源的触发条件。
```
mkdir -p ~/.avai && cd ~/.avai
docker run -d --name avai-monitor --restart unless-stopped \
--pid=host --network=host --user 0:0 \
--cap-add SYS_PTRACE --cap-add NET_ADMIN --cap-add NET_RAW --cap-add DAC_READ_SEARCH \
-e HOST_PREFIX=/host \
-e DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket \
-e ANTHROPIC_API_KEY \
-v /proc:/host/proc:ro -v /sys:/host/sys:ro -v /etc:/host/etc:ro \
-v /var/lib/bluetooth:/host/var/lib/bluetooth:ro \
-v /var/lib/dpkg:/host/var/lib/dpkg:ro \
-v /usr/share/applications:/host/usr/share/applications:ro \
-v /lib/systemd:/host/lib/systemd:ro \
-v /usr/lib/systemd:/host/usr/lib/systemd:ro \
-v /var/log/journal:/host/var/log/journal:ro \
-v /var/spool/cron:/host/var/spool/cron:ro \
-v /run/systemd:/run/systemd:ro -v /run/dbus:/run/dbus:ro \
-v /etc/machine-id:/etc/machine-id:ro \
-v /dev/mapper:/dev/mapper:ro \
-v /home:/host/home:ro -v /root:/host/root:ro \
-v "$PWD":/data \
iklob1/avai \
avai monitor --db /data/avai.db --interval 300
docker logs -f avai-monitor # watch the cycle
```
`avai monitor` 中的默认设置:
| 标志 | 默认值 | 效果 |
|---|---|---|
| `--interval` | `300` | 快照周期间的秒数 |
| `--lookback-min` | `6` | 每次运行时获取的日志历史分钟数 |
| `--max-db-mb` | `1024` | 轮转上限(0 表示禁用);在每个周期后,最旧的运行记录会被修剪 + 并执行 `VACUUM` |
| `--judge-model` | `claude-haiku-4-5-20251001` | 任意 litellm 模型 ID |
| `--judge-batch-size` | `20` | 每次 LLM 调用的条目数 |
| `--judge-max-per-collector` | `25` | 每个收集器每个周期判定新条目的上限 |
| `--no-streaming` | (关闭) | 禁用 `auth_events` + `process_exec_events` 跟踪器 |
| `--no-judge` | (关闭) | 运行收集器但不存储判定结果 |
| `--no-enrich` | (关闭) | 跳过整个威胁情报层;收集器 → 直接判定 |
| `--enrich-only NAME` | (所有) | 将链路限制为单个指定的源(可重复);用于调试 |
在 `docker run … iklob1/avai avai monitor …`
命令后追加任意标志即可进行覆盖。完整参考:`docker run --rm iklob1/avai
avai monitor --help`。
## 4 —— 使用 docker-compose 运行两个服务(Linux 主机)
`docker-compose.yml`:
```
x-avai-image: &avai-image
image: iklob1/avai:latest
services:
monitor:
<<: *avai-image
container_name: avai-monitor
command: ["avai","monitor","--db","/data/avai.db","--interval","300"]
user: "0:0"
pid: host
network_mode: host
cap_add: [SYS_PTRACE, NET_ADMIN, NET_RAW, DAC_READ_SEARCH]
# Loads LLM-judge + every threat-intel API key from .env. Copy
# .env.example to .env and fill in only the keys you have.
env_file: [.env]
environment:
- HOST_PREFIX=/host
- DBUS_SYSTEM_BUS_ADDRESS=unix:path=/run/dbus/system_bus_socket
volumes:
- ./data:/data
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /etc:/host/etc:ro
- /var/lib/bluetooth:/host/var/lib/bluetooth:ro
- /var/lib/dpkg:/host/var/lib/dpkg:ro
- /usr/share/applications:/host/usr/share/applications:ro
- /lib/systemd:/host/lib/systemd:ro
- /usr/lib/systemd:/host/usr/lib/systemd:ro
- /var/log/journal:/host/var/log/journal:ro
- /var/spool/cron:/host/var/spool/cron:ro
- /run/systemd:/run/systemd:ro
- /run/dbus:/run/dbus:ro
- /etc/machine-id:/etc/machine-id:ro
- /dev/mapper:/dev/mapper:ro
- /home:/host/home:ro
- /root:/host/root:ro
restart: unless-stopped
dashboard:
<<: *avai-image
container_name: avai-dashboard
# uses the image's default CMD
ports: ["8765:8765"]
volumes: ["./data:/data"]
restart: unless-stopped
```
然后:
```
mkdir -p data
cp .env.example .env && vi .env # fill in the keys you have
docker compose up -d
docker compose logs -f monitor
open http://localhost:8765/
```
## 5 —— 针对现有 DB 运行的仪表盘(任意主机)
如果您已经有一个 `avai.db`(由监控器在不同的
机器上生成,并放入了当前目录等):
```
docker run --rm -p 8765:8765 -v "$PWD":/data iklob1/avai
```
仪表盘会以 `?mode=ro&immutable=1` 模式打开该文件,因此它绝不会
进行写入,也绝不会持有锁 —— 可以安全地指向一个正在由另一个
容器中的监控器写入的实时数据库。
## 6 —— 常用操作命令
```
# 检查内置的 CLI
docker run --rm iklob1/avai avai --help
docker run --rm iklob1/avai avai monitor --help
docker run --rm iklob1/avai avai dashboard --help
docker run --rm iklob1/avai avai --version
# Healthcheck + 状态
docker inspect avai-dashboard --format '{{.State.Health.Status}}' # healthy|unhealthy|starting
docker compose ps # if using compose
docker logs -f avai-monitor # follow monitor cycles
# 实际运行中的 DB rotation — 观察 size cap 生效
docker exec avai-monitor du -h /data/avai.db
# 停止 / 清理
docker compose down # if using compose
docker stop avai-dashboard avai-monitor 2>/dev/null
docker rm avai-dashboard avai-monitor 2>/dev/null
# 清除 database(同时清除 verdicts;monitor 将从头重新判定)
rm -f data/avai.db data/avai.db-wal data/avai.db-shm
# Pull 最新的 image
docker pull iklob1/avai
```
## 7 —— 配方
基础操作之外的实用、可直接复制粘贴的场景。
### 在 Linux 服务器上原生安装(完整的主机可见性)
在真实的 Linux 主机上的容器内,监控器已经可以工作了,但是
监控服务器的最简单方法是原生安装它,让它
直接查看所有内容:
```
pip install avai-monitor # includes the LLM judge (litellm + anthropic)
export ANTHROPIC_API_KEY=sk-ant-... # or CLAUDE_CODE_OAUTH_TOKEN
export ABUSE_CH_AUTH_KEY=... # optional, free — adds 3 sources
sudo -E avai monitor --db /var/lib/avai/avai.db --interval 300 &
avai dashboard --db /var/lib/avai/avai.db --host 0.0.0.0 --port 8765
```
`sudo` 允许收集器读取 root 拥有的状态(`/etc/shadow`、
其他用户的 crontabs、每一个进程)。`-E` 会跨越 sudo 边界保留您的 API 密钥。
### 使用 systemd 保持运行
`/etc/systemd/system/avai.service`:
```
[Unit]
Description=avai host monitor
After=network-online.target
[Service]
Environment=ANTHROPIC_API_KEY=sk-ant-...
Environment=ABUSE_CH_AUTH_KEY=...
ExecStart=/usr/local/bin/avai monitor --db /var/lib/avai/avai.db --interval 300
Restart=always
User=root
[Install]
WantedBy=multi-user.target
```
```
sudo systemctl enable --now avai
journalctl -u avai -f # watch cycles
```
### 直接从命令行读取发现(无需仪表盘)
一切都存放在一个 SQLite 文件中,因此您可以直接查询它 ——
非常适合脚本编写、cron 邮件或没有浏览器的服务器:
```
# 活动的 dangerous + suspicious findings,最新的优先
sqlite3 -box /var/lib/avai/avai.db "
SELECT verdict, collector, substr(reasoning,1,60) AS why
FROM judgements
WHERE verdict IN ('malicious','suspicious')
ORDER BY created_at DESC LIMIT 20;"
# 按 verdict 计数
sqlite3 /var/lib/avai/avai.db \
"SELECT verdict, count(*) FROM judgements GROUP BY verdict;"
# threat-intel 源是怎么说的?
sqlite3 -box /var/lib/avai/avai.db "
SELECT source, verdict_hint, substr(summary,1,70)
FROM enrichment_evidence
WHERE verdict_hint IN ('malicious','suspicious');"
```
### 从 cron 运行一次性扫描(取代常驻 daemon)
```
# /etc/cron.d/avai — 每小时扫描一次,无 streaming
0 * * * * root ANTHROPIC_API_KEY=sk-ant-... \
avai monitor --once --no-streaming --db /var/lib/avai/avai.db
```
### 拆分设置:监控器在服务器上,仪表盘在您的笔记本电脑上
监控器写入 DB;仪表盘仅读取它。同步该文件
(rsync/scp/NFS),就可以在任何地方查看它:
```
# 在 server 上(writer)
avai monitor --db /var/lib/avai/avai.db --interval 300
# pull 到你的笔记本电脑并查看(reader — 任何 OS,无需权限)
scp server:/var/lib/avai/avai.db ./avai.db
docker run --rm -p 8765:8765 -v "$PWD":/data iklob1/avai
```
### 保持较低的 LLM 成本
```
avai monitor \
--judge-model claude-haiku-4-5-20251001 \ # cheapest tier (default)
--judge-max-per-collector 20 \ # cap new items judged per cycle
--judge-batch-size 20 # entries per API call
```
无论如何,在稳定状态下成本几乎为零 —— 只有 *新* 的制品会被
判定,而且威胁情报的判定结果会被缓存,因此安静的主机在
第一个周期过后几乎不会发起 API 调用。
### 开启/关闭富化并调试单个源
```
avai monitor --no-enrich # collectors + judge only
avai monitor --enrich-only cisa_kev # just this source (repeatable)
avai monitor --enrich-only virustotal --enrich-only abuseipdb
```
源名称:`malware_bazaar` `urlhaus` `threatfox` `circl_hashlookup`
`shodan_internetdb` `feodo_tracker` `osv` `cisa_kev` `nvd` `endoflife`
`crtsh` `virustotal` `abuseipdb` `greynoise` `safe_browsing`
`phishtank` `github_advisory`。
### 使用您自己的 LLM 提供商
`--judge-model` 是一个 [litellm](https://docs.litellm.ai/docs/providers)
模型 ID,因此任何支持的提供商都可以使用:
```
avai monitor --judge-model gpt-4o-mini # OpenAI (OPENAI_API_KEY)
avai monitor --judge-model ollama/llama3.1 # local, free, offline
avai monitor --judge-model gemini/gemini-1.5-pro # Google
```
## 收集了什么(一句话总结)
快照收集器(每个周期运行,默认 300 秒):
| 组别 | 来源 |
|---|---|
| 进程 / 网络 | `processes`, `network_connections`, `listening_ports`, `network_interfaces` (psutil) |
| 硬件 | `usb_devices` (/sys/bus/usb), `bluetooth_devices` (/var/lib/bluetooth), `wifi_state` (sysfs + `iw`) |
| 持久化 | `launch_items` (systemd unit files + cron) |
| 文件 | `file_integrity` (passwd / shadow / sudoers / SSH config / dotfiles), `setuid_files`, `mounts` |
| 应用 | `installed_apps` (dpkg-query + XDG `.desktop`), `browser_extensions` |
| 姿态 | `system_integrity` (SELinux / AppArmor / ufw / sshd / vnc / LUKS) |
| 姿态(仅限 macOS) | `tcc_permissions` (摄像头/麦克风/位置/屏幕授权), `quarantine_events`, `mdm_profiles`, `kernel_extensions`, `system_extensions` |
流式收集器(事件发生时即刻捕获):
| 收集器 | 来源 |
|---|---|
| `auth_events` | `journalctl -f` (Linux) / macOS 统一日志 (macOS),过滤至与安全相关的子系统。由唯一的 `(process, subsystem, message)` 模式进行 LLM 判定 —— 无论触发多少次,每个事件模板只判定一次。 |
| `process_exec_events` | `journalctl -f _AUDIT_TYPE_NAME=EXECVE` (需要 auditd `auditctl -a always,exit -F arch=b64 -S execve` 规则) |
对于收集到的每一个实体(根据收集器的“判定字段”上的内容哈希进行去重),LLM 判定器会将其
分类为 `恶意` / `可疑` / `未知` / `良性`,并附带置信度、
MITRE 对齐的分类和单行修复建议。判定结果会被
持久化;同一个制品绝不会发送两次。
## 仪表盘
位于 `:8765` 的 Flask + HTMX 仪表盘在每个表格上都有完整的过滤和分页功能:
- **发现** —— 按判定结果、收集器、类别、状态(活动/已解决)过滤,支持自由文本搜索;可排序的列;可配置的每页显示数量(10/25/50/100)。
- **网络流** —— 按判定结果和 IP/主机/进程搜索进行过滤;提供汇总统计(目的地、流量、恶意数量)。
- **监听端口** —— 按判定结果和绑定范围(所有接口 / 可路由 / 回环)过滤;进程搜索。
- **DNS 查询** —— 按判定结果、解析级别(DoH / 外部 DNS / 本地解析器)、域名搜索进行过滤。
- **持久化** —— SSH 授权密钥、`/etc/hosts` 映射和权限配置,各自具有独立的分页。
- **认证事件** —— 根据唯一的 `(进程, 子系统, 消息)` 模式进行聚合,并带有发生次数和最后发现时间戳。按子系统(TCC、securityd、syspolicy、loginwindow、Authorization)或判定结果过滤。按计数或判定结果的严重程度排序。随着模式的分类完成,会出现 LLM 判定结果。
- **TCC 权限** (macOS) —— 每个应用的摄像头、麦克风、位置、屏幕录制和全盘访问权限的授权/拒绝,并带有 LLM 判定结果和认证状态过滤器。
所有部分都会自动刷新(30–60 秒)。对于新的恶意/可疑判定,会触发 Toast 通知 + 音频警报。
## 威胁情报富化
在每个发现进入 LLM 之前,avai 会提取指标(SHA256、
IPv4、域名、URL、CVE、软件包、操作系统版本),并通过
外部威胁情报 API 运行它们。然后,判定器会
在 prompt 中内联看到原始证据,这极大地收紧了判定结果。
每个源都是可选的。无需密钥的源始终运行。需要密钥的源
只有在设置了下方的环境变量时才会注册 —— 请参阅 [`.env.example`](.env.example)
获取可复制粘贴的模板。
| 来源 | 指标 | 环境变量 | 配额 | 增加的内容 |
|---|---|---|---|---|
| **MalwareBazaar** (abuse.ch) | SHA256/1/MD5 | `ABUSE_CH_AUTH_KEY` | 不限 | 已知的恶意软件家族 |
| **CIRCL hashlookup** (NSRL) | SHA256/1/MD5 | — | 不限 | 已知良好的供应商二进制文件(白名单) |
| **Shodan InternetDB** | IPv4 | — | 1 rps | 开放端口、CVE、标签 |
| **URLhaus** (abuse.ch) | URL, domain | `ABUSE_CH_AUTH_KEY` | 不限 | 恶意软件分发 URL |
| **Feodo Tracker** (abuse.ch) | IPv4 | — | 不限 | 僵尸网络 C2 IP(缓存的 feed) |
| **ThreatFox** (abuse.ch) | IPv4 / domain / URL / hash | `ABUSE_CH_AUTH_KEY` | 不限 | 混合 IOC 搜索 |
| **OSV.dev** | CVE, package | — | 不限 | 开源安全公告 |
| **CISA KEV** | CVE | — | 静态 feed | 正在被利用的 CVE |
| **NVD** | CVE | `NVD_API_KEY` (可选) | 5 → 50 / 30 s | CVSS + 描述 |
| **crt.sh** | domain | — | 适度 | 证书透明度历史 |
| **endoflife.date** | OS version | — | 不限 | 已停止支持的 OS / 运行时 |
| **VirusTotal** | SHA256/1/MD5, URL, domain, IPv4 | `VT_API_KEY` | 4/min, 500/day | 多引擎信誉 |
| **AbuseIPDB** | IPv4 | `ABUSEIPDB_API_KEY` | 1000/day | 滥用置信度评分 |
| **GreyNoise Community** | IPv4 | `GREYNOISE_API_KEY` | 50/day | "这个 IP 只是干扰吗?" |
| **Google Safe Browsing** | URL | `GOOGLE_SAFE_BROWSING_API_KEY` | 10k/day | 钓鱼 / 恶意软件判定 |
| **PhishTank** | URL | `PHHTANK_API_KEY` | 宽松 | 社区钓鱼数据库 |
| **GitHub Advisory** | CVE | `GITHUB_TOKEN` | 高 | 精选的安全公告 + 修复版本 |
每个指标的结果都会被缓存在同一个 SQLite(`enrichment_evidence`
表)中,并带有针对特定源的 TTL(6 小时 – 14 天)。新鲜的缓存命中会完全跳过
网络;该缓存在重启后依然存在。
使用以下命令切换:
```
avai monitor # all enabled sources, default
avai monitor --no-enrich # collectors + judge, no external lookups
avai monitor --enrich-only malware_bazaar # debugging: only this one
```
## 为什么本 README 中不涉及 macOS
监控器依赖于 Linux 原生工具 —— `pid=host` 访问
主机的 `/proc`,位于 `/sys/bus/usb` 的 sysfs,搭配
`auditd` 的 `journalctl`、`systemctl is-active`、`dpkg-query`、用于 LUKS 的 `dmsetup`。
macOS 上的 Docker Desktop 仅公开了其附带的 Linux VM,而
不是 macOS 主机,因此 macOS 上的容器化监控器报告的是该 VM(内容
空洞/无趣),而不是 Mac。仪表盘角色在 macOS Docker 上运行良好 —— 您
只需要从其他地方写入该数据库即可。
如果您想要完整的 macOS 覆盖,请以原生方式安装(`pip install
avai-monitor`)并使用 `sudo` 运行 `avai monitor`。那是另外一条
路径,不在此文档赘述。
## 开发与测试
该测试套件不需要网络,并在几秒钟内运行完毕。仓库的开发版 Python
可能带有插件冲突,因此请在一次性的 venv 中运行它:
```
python3 -m venv /tmp/venv && /tmp/venv/bin/pip install -e . pytest
/tmp/venv/bin/python -m pytest tests/ -q # 320+ unit tests
```
测试覆盖范围涵盖了富化框架和全部 18 个源、
指标提取器、HTTP 客户端(速率限制 / 退避 / 429)、
CLI 调度器、SQLAlchemy 仓库 + 数据库轮转、LLM
判定器的解析、仪表盘端点,以及 Linux 收集器的
文件解析(systemd / cron / `.desktop` / BlueZ)。这些测试的编写目的在于
一旦实现逻辑遭到破坏就会失败 —— 这是通过变异测试验证的,
而不仅仅是基于覆盖率。
无人值守 Docker 冒烟测试(构建镜像,运行 CLI 界面,
一次冷收集器遍历,以及无需密钥的富化注册表检查):
```
tests/local.sh # all phases; exits non-zero on any failure
```
请参阅 [`CHANGELOG.md`](CHANGELOG.md) 获取版本历史。
## 许可证
MIT —— 请参阅 `LICENSE`。标签:AMSI绕过, EDR, PE 加载器, Python, x64dbg, 威胁情报, 威胁检测, 安全遥测, 开发者工具, 无后门, 网络信息收集, 脆弱性评估, 请求拦截, 逆向工具