Skip to content
tomlen045Public

About

Outbound beacon sentinel - multi-round lsof sampling, beacon-like persistent talkers float to the top, baseline drift catches first-seen C2 targets. Zero-dep single file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

callhome 📡

外联信标哨兵 —— 哨兵套件的「外联面」一环:porteye 管「谁在听」,suidhunt 管「谁能提权」,authguard 管「谁想进来」,callhome 管「谁在偷偷打电话回家」。

半夜三点你的服务器往外发 TCP 包,你知道是谁吗?callhome 用 lsof 多轮采样,把每个进程的每条外联流按「进程 → 目标 → 出现轮次」聚合,信标样常驻外联自动浮顶:临时目录里的进程在对外通信直接 CRIT(exit 3),公网常驻信标 HIGH,本地链路噪音自动降噪 INFO。

tests beacon-drill deps license

English | 简体中文


解决什么问题

  • 反弹 shell / C2 信标的第一特征是**「进程位置不对 + 目标固定 + 长期在场」**——netstat 看一眼就过去,没人天天盯。callhome 多轮采样聚合,常驻外联按「在场轮次占比」排序,信标自己浮到第一行
  • 传统「看连接」工具把 fe80:: 本地链路、127.0.0.1 回环、推送心跳全端出来报警,狼来了三次你就再也不看了。callhome 分域降噪:本地链路恒 INFO,推送/浏览器类平台常客标注降级,报出来的都是真该看的
  • 进程在 /tmp、/private/var/folders(macOS 临时区)、Downloads、/dev/shm 里还敢对外连——这是比「目标 IP 是谁」更强的信号,callhome 直接 CRIT
  • 昨天还没的外联目标今天出现了 → snapshot 拍基线 + compare 找新增:基线只收「出现 ≥2 轮」的稳定目标,瞬时连接不污染;新增目标按 CRIT/HIGH/MED 分级,退出码直接接监控管道
  • 盯外联不想盯细节 → --json 全量导出,SIEM/日志平台直接吃

30 秒上手

# 多轮采样 + 信标判定 (默认 10 轮 x 3s, 约 30 秒)
python3 callhome.py watch

# 快速看一眼 (2 轮)
python3 callhome.py watch --rounds 2 --interval 1

# 信标阈值放宽: 在场 60% 即算常驻 (抓断续信标)
python3 callhome.py watch --hits 0.6

# 拍外联基线 (稳定目标才入库)
python3 callhome.py snapshot callhome_base.json --rounds 8 --interval 3

# 以后每天 cron 一跑: 只报基线外新增
python3 callhome.py compare callhome_base.json

# 全量盘点含平台心跳类 + JSON 导出
python3 callhome.py watch --show-all --json callhome_flows.json

退出码即结论:0=clean · 1=MED+ · 2=HIGH+ · 3=CRIT。挂 cron/监控管道不用解析文本。

判定矩阵(降不冤枉,升不放过)

场景 判定
临时目录/下载区/内存盘进程 + 任何外联 CRIT (exit 3)
非白名单进程 + 公网目标常驻(≥80% 轮在场) HIGH (exit 2)
基线外新增公网目标(常驻) HIGH (exit 2)
私网目标常驻 / 公网断续外联 / 基线外新增(断续) MED (exit 1)
本地链路 fe80::/127. 回环目标 INFO(降噪)
推送/浏览器类平台常客(Chrome/apsd/identityservicesd…) INFO(标注降级)

白名单是「平台常客」进程名模糊匹配(chrome/apsd/push/mdns…30+ 条),只影响降噪不影响盘点——--show-all 一条不少全部列出。

信标演习(真实自证)

开发时跑过一轮实弹:后台起一个 Python 进程对 93.184.216.34:443 每 0.8s 重连一次——

$ python3 callhome.py compare demo_base.json --rounds 4 --interval 2
基线: 2026-10-11 10:20:49 (54 条)  →  现状: 73 条外联流
------------------------------------------------------------------------
[MED] 93.184.216.34:443  (tcp)
       ↑ Python
       /Library/.../Python.app/Contents/MacOS/Python
       · 基线外新增 (基线 2026-10-11 10:20:49 建立, 54 条)
       · 在场 3/4 轮
------------------------------------------------------------------------
结论: HIGH — 3 条信标样新目标, 建议核对归属

信标进程第一次出现就被拎出:目标、进程、路径、在场轮次,一行不缺。

真机一句话

在本机(开着一堆应用的 macOS)跑 watch --rounds 8:116 条外联流里 CRIT 0 / HIGH 42 / MED 6 / INFO 68——WPS 云服务 8 个目标、企业微信 9 个、ClassIn 4 个、xray 2 个全部在列。这些不是入侵,但你被问「这台机器都在跟谁通信」时,答案 30 秒就有。

一句大实话

callhome 只看「谁在往外连」,看不到连接里传了什么内容——那要抓包(tcpdump/Wireshark)。它定位的是「该看哪条」:先用它把 116 条收到 6 条,再对这 6 条上抓包。另外 watch 模式对「应用正常联网」也会标 HIGH(它不认识你的应用),所以才有 snapshot/compare——基线法才是日常姿势,watch 是第一次盘点和应急用的。

与哨兵套件配合

# 每天一条 cron, 四个哨兵各守一面:
python3 cronguard.py doctor     # 定时任务面
python3 authguard.py check      # 登录面
python3 suidhunt.py --compare suid_baseline.json   # 特权面
python3 callhome.py  compare callhome_base.json    # 外联面

限制

  • 采样靠 lsof -i,只能看到采样瞬间的连接;只连一次就断的闪连可能错过(--rounds 加大窗口可缓解)
  • macOS 上看其他用户进程的完整信息可能需要 sudo;不加 sudo 盘点自己会话内的外联已够用
  • Windows 不支持(lsof 依赖);macOS / Linux 通用

自测

python3 selftest.py   # 22/22 全绿 (离线 fixture, 不依赖真实网络)

MIT License · 零依赖单文件 · Python 3.8+

About

Outbound beacon sentinel - multi-round lsof sampling, beacon-like persistent talkers float to the top, baseline drift catches first-seen C2 targets. Zero-dep single file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages