相信大家都知道,沙箱识别是老生常谈的话题了,目前大部分的识别方案都是基于样本侧去完成的。
例如常规方式,包括硬件检查(CPU核心数、输入输出设备、内存)、鼠标移动检查、进程名、系统服务、开机时长等,都不能直观准确地识别出目标进行流量交互的服务器是否是沙箱环境。
举个例子,之前看到有师傅使用鼠标移动检查的方式去识别目标是否是沙箱虚拟机环境。 那么问题来了,这种方式在钓鱼的场景下,我们虽然知道目标是PC客户端,有人使用这台电脑,但是对于目标是服务器场景的情况下,这种方法就不适用了。
运维人员并不会时刻都在每台服务器前面操作,所以我们需要一种更加优雅的识别方式。
当然,沙箱是快照还原,时间一般都存在问题。并且由于进行sleep加速,意味着这时候在样本侧进行延迟执行操作会被沙箱反调。
一旦样本被反调了,那么其样本就身处异常环境下,这时候延迟几秒后获取本地时间,就能够识别出异常。
这当然也是一种很好的反调试手段。但是,上述这些操作都是在样本侧完成的,需要定制化脚本实现功能,出现问题后进行排查等等都会比较麻烦。
本文将深入浅出地讲解基于流量侧对沙箱请求流量进行识别的方法,这种方法也能更易部署且有效识别,从而针对性的反制沙箱分析流量。
1 TLS JA3指纹
正式讲解流量侧识别云沙箱技术之前,我们先简述一下TLS JA3(S)指纹的基本概念。
JA3为客户端与服务器之间的加密通信提供了识别度更高的指纹,通过TLS指纹来识别恶意客户端和服务器之间的TLS协商,从而实现关联恶意客户端的效果。
该指纹使用MD5加密,易于在任何平台上生成,目前广泛应用于威胁情报。例如,在某些沙箱的样本分析报告中,可以看到以此佐证不同样本之间的关联性的样例。
如果可以掌握C2服务器与恶意客户端的JA3(S),即使加密流量且不知道C2服务器的IP地址或域名,我们仍然可以通过TLS指纹来识别恶意客户端和服务器之间的TLS协商。
相信看到这里大家就能想到,这也正是对付域前置、反向代理、云函数等流量转发隐匿手段的一种措施,通过沙箱执行样本识别与C2之间通信的TLS协商并生成JA3(S)指纹,以此应用于威胁情报从而实现辅助溯源的技术手段。
JA3通过对客户端发送的ClientHello数据包中的以下字段收集字节的十进制值: SSL 版本、接受的密码、扩展列表、椭圆曲线、椭圆曲线格式。
然后它将这些值按顺序连接在一起,使用“,”分隔每个字段,使用“-”分隔每个字段中的每个值。
示例:
771,39578-4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21,56026-29-23-24,0
MD5编码:
9ef1ac1938995d826ebe3b9e13d9f83a
如上示例,最终得到并应用的JA3指纹即:
9ef1ac1938995d826ebe3b9e13d9f83a
之前文章提到的JARM与JA3(S)都是TLS指纹,那么他们的区别是什么呢?
一是JARM指纹是主动扫描并生成指纹的,类似FUZZ的效果;
二是JA3(S)是基于客户端与服务端流量交互识别并生成的指纹。
2 基于流量的云沙箱识别
上面简述了JA3(S)指纹的概念,这里应用到识别沙箱流量,也是类似的原理。我们需要一个基础设施可以监控识别上线主机和C2服务器之间的TLS协商,从而生成请求主机的JA3指纹。
这里我们以RedGuard举例。
通过上图不难看出,RedGuard充当着C2服务器与上线主机流量交互的代理主机,所有的流量都会经由它转发到C2服务器上,那么在这个过程中,我们基于流量侧生成并识别JA3指纹的想法就可以实现了,在不修改后端C2设施源码的基础上,赋予了生成识别JA3指纹的功能。
在云沙箱的立场上,通过监控样本与C2服务器之间流量交互,生成JA3(S)指纹,识别恶意客户端,从而进行关联。
而我们逆向思考,同样作为C2前置的流量控制设施,我们也可以进行这样的操作获取客户端请求的JA3指纹,通过对不同沙箱环境的调试,获取这些JA3指纹形成指纹库,从而形成基础拦截策略。
在测试某厂商沙箱环境时发现,其请求交互的出口IP虽然数量不大,但是通过IP识别沙箱并不准确,并且这是很容易改变的特征,但是其在多种不同配置的相同系统环境下JA3指纹是唯一的。
设想在分阶段木马交互的过程中,加载器会首先拉取远程地址的shellcode,那么在流量识别到请求符合JA3指纹库的云沙箱特征时,就会进行拦截后续请求。那么无法获取shellcode,不能完成整个加载过程,沙箱自然不能对其完整的分析。
如果环境是无阶段的木马,那么沙箱分析同样无法最终上线 到C2服务器上,相信大家都有睡一觉起来,C2上挂了一大堆超时已久的沙箱记录的经历吧。
当然理想状态下,我们可以对不同沙箱环境进行识别,这主要也是依赖于指纹库的可靠性。
3 识别网络空间测绘扫描
在测试的过程中,指纹库添加GO语言请求库的JA3指纹后,监测RedGuard请求流量情况。可以看到,大部分的请求触发了JA3指纹库特征的基础拦截。
这里我猜测其测绘产品在该扫描中,底层语言是以GO语言实现的大部分扫描任务。通过一条链路,不同底层语言组成的扫描逻辑最终完成了整个扫描任务,这也就解释了部分测绘产品的扫描为什么触发了GO语言请求库的JA3指纹拦截特征。
当然,触发拦截规则的请求都会被重定向到指定URL站点。
4 后记
JA3(S)指纹当然是可以更改的,但是会很大程度上提高成本,同样,修改基础特征无法对其造成影响。如果准备用劫持的方式去伪造JA3指纹,并不一定是可行的,OpenSSL会校验extension。如果和自己发出的不一致,则会报错:
OpenSSL: error:141B30D9:SSL routines:tls_collect_extensions:unsolicited extension
伪造JA3指纹可以看以下两个项目:
"https://github.com/CUCyber/ja3transport"
"https://github.com/lwthiker/curl-impersonate"
通常自主定制化的恶意软件会自己去实现TLS,这种情况下JA3指纹可以唯一的指向它。但是现在研发一般都会用第三方的库,不管是诸如Python的官方模块还是win下的组件,如果是这种情况,那么JA3就会重复,误报率很高。
当然应用到我们的流量控制设施,其实不需要考虑这些固定组件的问题,因为并不会有正常的封装组件的请求,多数也是上面提到某些语言编写的扫描流量。反而以此对这些语言请求的JA3指纹封装进指纹库,也能起到防止扫描的效果。