攻击者用SOCKS代理隧道在环境里横跳、远程执行代码,而防御方大多还在靠静态指标或进程-端口基线来硬猜。SpecterOps开源的概念验证工具ProxyWatch换了个思路:直接盯着进程的行为模式打分,再用本地机器学习持续校准,帮你从端点侧揪出C2会话、代理隧道和信标行为。

换个视角看检测

干防守的,有时候得站在攻击者的鞋子里想问题。攻击者的战术、技术和流程(TTP)是被什么逼出来的?是那些真正好使的手段,以及一旦失效就要被迫换招的现实。随着网络边界越收越紧,EDR越来越精,攻击者越来越喜欢玩代理跳板。拿下了一台机器,就把它当成网络内部的出口节点,所有攻击工具都放在自己本机跑,流量经由那台肉鸡转发到目标网络,EDR上连个新进程都看不到。

SOCKS协议就是干这个的——在客户端和服务器之间插一个代理,让通信看上去都是从代理发起的。Cobalt Strike、Mythic、Sliver这些C2框架,全都内置了把Agent变成SOCKS代理的功能。打开SOCKS之后,攻击者用proxychains几乎能把任何工具——哪怕原本不支持代理的工具——的流量导进目标网络。

代理隧道一旦搭好,底层的工作方式其实很朴实:

  1. 攻击者本地工具经proxychains跟肉鸡建一条socket连接

  2. 肉鸡再跟目标服务器(比如域控)建另一条socket连接,开需要的TCP端口

  3. 在两个socket之间倒腾字节流就行了。TCP本来就是按顺序传字节的,所以上层协议根本不用管底下是不是代理

SOCKS4a能让客户端用域名指定目标,对付虚拟主机特别有用。SOCKS5不兼容4,但支持域名、IPv6,还能加认证。跟VPN不一样,SOCKS代理不用重写流量,完全可以在用户态搞定,不需要root或内核权限。简单,就是它最大的优点,所以C2框架和黑客工具几乎人手一个。

但你得知道,搞代理不一定非得用恶意工具。SSH的端口转发功能是运维最常用的隧道手段之一,plink这类工具把SSH转发能力打包成单一功能,socat这类能把两个socket怼到一起的工具也能干。就连cloudflared、ngrok这种为了让本地服务通过NAT暴露到公网的开发者工具,也被攻击者拿来做内网持久化的后门通道。

那些APT们是怎么玩代理的

MITRE ATT&CK里记录了国家级别的APT组织使用代理执行的案例。Mandiant披露过一例,某个APT团伙用自定义工具,通过一个独特的HTTP头迫使目标端点去连接一个中介服务器,这个HTTP头标志着代理连接的开始,并建立SOCKS5 TCP会话。

另一个案例里,攻击者用SOCKS代理把受害者云端邮件服务的数据往外拖。他们把流量导进一台已经沦陷的内部服务器,于是云端邮箱到攻击者机器之间的连接被内网的肉鸡给盖住了。

也不是所有团伙都自己造隧道,用现成开源工具的一大把。Sekoia报道过一个团伙在事件中用了改名后的chisel项目。Cybereason详细描述了Sliver(含SOCKS功能)被多个威胁团伙使用。DFIR Reports也记录过一个案例,攻击者安装Cloudflare Tunnels,让服务器开启了RDP远程桌面。

红队视角:代理执行为何吃香

像Cobalt Strike的Beacon这类C2 agent,通常有基础核心功能,以及加载执行后渗透工具的能力。经典的“fork-and-run”模式是:为了保护Beacon不被可能崩溃的代码搞挂,Cobalt Strike会新起一个进程,把代码注入这个牺牲品进程,然后通过命名管道把输出传回来。坏处是,这套动作被EDR盯得死死的。EDR会监控进程创建、父子进程关系、常见网络连接和文件创建模式,一旦出现异常子进程就会告警。于是红队学精了,转而用内联执行——直接在植入进程内部执行代码,不fork新进程。

那用代理执行的好处就更直接了:所有工具在攻击者自己机器上跑,肉鸡只需要当好SOCKS端口的中转站,不用生新进程,不用加载可疑代码,EDR那套基于父子进程的检测模型碰上一堵墙。很多红队认为,一个轻量化的、仅充当代理中间人的核心agent是最优解。

紫队评估中暴露的检测盲区

SpecterOps做紫队服务,不单纯看攻击成没成,而是看客户端的安全方案到底有没有拦住、有没有产生告警、以及即便没告警,遥测数据里是不是留下了痕迹。我们不评价执行方法“隐蔽”与否——这个词太主观了。代理执行在磁盘上几乎不留痕,攻击者工具碰都不碰目标系统,执行后渗透代码也无需引入新进程。但如果你用代理执行去OpenProcess到LSASS抓凭据,目标端点上的检测照样会响。

紫队评估里一个经典场景:操作者通过SOCKS用Impacket的reg.py创建注册表Run键来实现持久化,EDR完全抓不到执行代码的动作。因为代码根本不在目标机上跑,只是通过四层网络协议在C2和目标间传数据。可是如果防守方监控的是注册表Run键修改本身,那就抓得着。也就是说,代理执行躲得过执行层面的监控,但躲不过目标行为的监控。这句话听起来简单,可在实际检测工程里,后者的数据源往往噪音巨大。

常规的SOCKS检测建议是:找不常见的进程-端口配对,比如某个进程跟88/TCP(Kerberos)、389/TCP(LDAP)、135/TCP(RPC)通信了。但你要为每个进程、每个“敏感”端口都建行为基线,这事又累又烦,一不小心就搞出大把例外。再加上代理执行的目标完全没有可预测性,业内又缺专门针对代理隧道的端点工具,所以我们自己造了一个:ProxyWatch。

ProxyWatch:不猜端口,看行为

ProxyWatch起初是拉斯维加斯的一次黑客马拉松项目,想回答一个简单问题:能不能在端点上靠行为特征直接抓SOCKS隧道?市面上没我们想要的工具,那就自己写。大多数已知检测要么依赖网络流量(防火墙、IPS),要么事后分析,很少直接盯住端点的实时活动。

一开始是个简单的Go程序,用几个信号给进程打分,贴个角色标签。三个月后,它已经能识别C2会话、SOCKS代理跳板、以及信标行为——靠的是进程自身的表现,而不是包内容。

ProxyWatch装在端点上,定期采集网络连接、用户上下文和进程信息,做成快照。Linux下直接解析/proc/net下的TCP/UDP/RAW/PACKET文件,通过/proc//fd把socket反查回进程ID。Windows下用GetExtendedTcpTable和GetExtendedUdpTable这类Win32 API拿TCP/UDP表,再用CreateToolhelp32Snapshot抓进程快照,得到命令行、用户上下文、IO、启动时间、可执行文件路径和完整性等级。有了足够数据,评分引擎就开始给每个进程画像。

规则引擎现在有83个行为信号,包括:原始套接字使用、回环地址使用、周期性回调的节奏、抖动幅度、可写路径、非正常流量目的地、命名管道C2模式、父子进程隧道继承等。信号组合打分,决定进程的角色。所有角色与信号映射关系都定义在 proxywatch/internal/shared/roles.go 中。

机器学习:让模型跟着规则长

除了规则,ProxyWatch还跑一个本地ML模型,每个周期学习一次。模型读取每个进程的122个特征,对着规则引擎的输出进行训练。相当于规则当老师,模型跟着自学,不会跑偏。在模型还没达到合格匹配度时,它会在影子模式下运行;一旦跟规则的一致性达标,就接管角色指派。

两者互补:规则从第一天起就能给出可用的基线,ML捕捉那些静态规则弄不出来的信号组合。我们在模型面板上能看到训练进度,还能重置基线、开关ML引擎。

可扩展性与部署模式

ProxyWatch现在能在Linux和Windows上独立运行当监控器,也可以部署成服务端-代理模式,在整个环境里撒Agent。用途上,实验室监控、快速排查、威胁狩猎、摸清出口路径,都行。服务端模式下,Agent在本地做分类,通过gRPC把候选结果推给服务端,你在控制台上能按主机名挑着管理。

AI辅助开发:快是快,坑也多

ProxyWatch的开发用了“氛围编程”(vibe coding),速度明显上去了,但过程没少踩坑。提示词写得太松,模型输出的东西勉强能用却抓不住要点;写得太死板,它又机械地按指令堆砌。好用的办法是:先看输出,再迭代提示词,把已有的代码模式、约束条件喂给它,让它先复述一遍自己的理解,再动手改。

修一个bug却搞出另一个,是家常便饭。减少误报的时候,我们一度发现ProxyWatch不检测信标了。后来摸索出一个有效做法:提示词里把已有检测项和对应的信号都列出来,明确告诉LLM,必须保留现有检测同时增加新的,且不能互相干扰。

重复造轮子也是老大难。LLM喜欢动不动就给你生成个 hasSignal() 这样的辅助函数,好几个文件里各写一套。解决办法很简单:在让模型写新函数之前,先grep一把看是不是已经有人写过了,把清理重构当作固定动作。氛围编程不能替代你对代码的理解,只能让你在已经吃透逻辑的工作上跑得更快。

实战示例:抓SOCKS隧道

下面这个例子,Sliver生成的信标 cheerful_glove.exe 启动了一个SOCKS隧道,跟内部某台机器的多个端口维持着稳定的控制通道。ProxyWatch给这个进程贴上了“control-pivot”角色,触发的信号包括:pivot-multiplex-relay(外部通道多路复用到内部目标)、pivot-non-loopback-internal(连接内部地址)。攻击者用这个隧道跑过nmap端口扫描和netexec的SMB扫描。

抓C2信标:周期回调露了馅

beacon-j.exe 被贴上了“control-channel”角色。ProxyWatch识别出75秒的回调周期,抖动/偏差只有0.44,而且进程一启动就往外连。触发的信号包括:beacon-interval-confirmed(跨睡眠周期的稳定节奏)、beacon-http-channel(所有回调都是HTTPS)、suspicious-exe-path(从桌面测试目录运行)、无签名、不属于任何已知软件包、不在标准厂商路径。当进程在回调间隙安静下来时,睡眠信标升级路径就被触发。一个挂起的SYN_SENT连接还暴露了它在试图连一台可能离线或被过滤的主机。事后确认,这确实是个Sliver信标,配置了60秒回调加抖动。

ProxyHound:把进程关系画出来

BloodHound用有向图展示用户、进程、主机和出站连接的关系,排查起来非常直观。ProxyWatch内置了一个叫ProxyHound的采集器,能收主机、用户、进程和连接的端点数据。下面这张关系图里,用户 DEMO\OPS 在主机 DEMO 上执行了 session*.exe,那进程直接往外连到了主机 LOK

Contour:摸清网络的真实出口

每次行动,几乎都要搞清楚一个问题:到底哪些流量能从里面出得去?Contour模块就是干这个的:它主动探测主机的出口路径,识别代理跳板、域前置、DNS通道、TLS拦截、允许的HTTP方法。防守方用它看的是:“我现有网络控制到底能拦住哪些隧道和外传通道?”进攻方拿到同样的结果,可以直接在确认可用的端口和协议上搭一条客户端/服务端SOCKS隧道,或者利用服务死信箱往外拖数据。每一次扫描反馈还会喂给本地模型和规则引擎,让分类器记住这个网络里哪些端口、协议、服务是已经被验证的出口路径,后续检测就更有底气。

更多信息,访问ProxyWatch的GitHub页面[1]


参考资料

[1] https://github.com/In3x0rabl3/Proxywatch

[2] https://specterops.io/blog/2026/07/09/finding-socks-with-proxywatch/#h-overview