本帖最后由 oldmoon 于 2020-8-2 15:14 编辑
冰蝎,从入门到魔改(上)
作者:队员编号049 转载自:酒仙桥六号部队
原文链接:https://mp.weixin.qq.com/s/hbBKQRaMg-b68lJ9k8Rh4A
0X01 什么是冰蝎?
"冰蝎"是一个动态二进制加密网站管理客户端。在实战中,第一代webshell管理工具"菜刀"的流量特征非常明显,很容易就被安全设备检测到。基于流量加密的webshell变得越来越多,"冰蝎"在此应运而生。
"冰蝎"客户端基于JAVA,所以可以跨平台使用,最新版本为v2.0.1,兼容性较之前的版本有较大提升。主要功能为:基本信息、命令执行、虚拟终端、文件管理、Socks代理、反弹shell、数据库管理、自定义代码等,功能非常强大。
0X02 加密原理
我们以PHP版本为例,"冰蝎"在服务端支持open_ssl时,使用AES加密算法,密钥长度16位,也可称为AES-16。此在软件及硬件(英特尔处理器的AES指令集包含六条指令)上都能快速地加解密,内存需求低,非常适合流量加密。
加密流程大致如下图所示:
首先客户端以Get形式发起带密码的请求。 服务端产生随机密钥,将密钥写入Session并将密钥返回客户端。 客户端获取密钥后,将payload用AES算法加密,用POST形式发送请求。 服务端收到请求,用Session中的密钥解密请求的Body部分,之后执行Payload,将直接结果返回到客户端。 客户端获取返回结果,显示到UI界面上。 我们看到在图中,"冰蝎"在执行Payload之后的返回,并没有显示加密,这点我们可以从自带的webshell中看出。 这个问题需要解密一下"冰蝎"的流量,才能知道答案。
0X03 通信过程
我们用wireshark来抓包看下"冰蝎"通信过程:
从抓包结果上粗略来看,加密效果是不错的,全程基本没有可读的执行代码。 我们用服务端返回的密钥,对客户端发送的报文内容进行解密。 解密结果为如下代码: 我们发现核心内容只是一个简单的JSON格式的success的返回,但是会将结果使用AES包装一层加密,所以我们看到webshell中没有加密,而流量却是加密的。
0x04 时过境迁
攻防技术一直都在不断发展的,要想保证攻防的持续有效,就需要不断地更新自我。"冰蝎"的最新版本v2.0.1,在发布于2019.2之后就没有进行过更新。而各大厂商的检测系统及WAF均已经对其特征进行分析并加入规则。
各路分析其流量规则的文章也层出不穷。 原版"冰蝎"已经不能满足攻防对战的要求了,这时我们需要自己动手。
0X05 魔改准备 首先用JD-GUI等反编译工具,反编译JAR包获得源码。可以从中可以看到UI文件引入的包名看到,"冰蝎"使用了SWT框架作为UI。
既然这样我们直接用Eclipse安装WindowsBuilder,来直接创建SWT项目。 安装WindowsBuilder 在Eclipse的Marketplace里搜索WindowsBuilder,点击Install即可安装。 之后我们直接创建基于SWT项目,即可避免因swt包缺失导致的报错问题。 我们将反编译之后的源码和JAR包导入项目,在通过搜索源码和修复报错(会有一大波报错等待你修复,可以多种反编译工具对比结果来修改)等方式尝试将源码跑起来。 最终我们终于成功跑起来了反编译之后的代码。 可以看到项目结构比较简单清晰,主要逻辑都在net包下,Main.java为程序入口。这里简单介绍下各个模块代码的作用: 出于对原作者的瑞思拜,不会放出任何项目文件。
0X06 特征擦除
经过对网上多篇对"冰蝎"特征的资料参考,总结出几条特征并将其特征给予修改擦除。以PHP版本为例,其他语言版本异曲同工。1.密钥交换时的URL参数首当其冲的就是密钥交换时的参数,用GET请求方式,默认webshell的密码为pass,并且参数值为3位随机数字。 从webshell上看,参数值的随机数字并没有任何实际作用: 客户端代码上看也只是随机数: 我们来看下一般对此情况的检测规则: - <font size="4" face="宋体">\.(php|jsp|asp|aspx)\?(\w){1,10}=\d{2,3} HTTP/1.1</font>
复制代码该规则可以匹配1-10位密码的webshell,并且参数值为2-3位的数字。 修改思路: 增加随机数量的随机参数和随机值(随机值不为全数字),并且密码参数不能固定为第一个。 修改后的效果: 2.header中的Content-Type默认在header中的Content-type字段,在一般情况下的GET形式访问是没有该字段的,只有POST形式的访问才会有。但"冰蝎"不论是GET形式还是POST形式的访问均包含此字段。此处露出了较大破绽,而且该字段的大小写有点问题,所以基于这个规则基本可以秒杀。 我们来看下这块相关的的代码: ShellService代表一个Shell服务,在其构造函数中31行判断了,如果类型是php则在header中加入 Content-type 头。但在35行的getKeyAndCookie向服务端发送GET请求获取密钥时,也将此header头带入其中,所以发送GET请求包时也会携带此参数。 修改思路: GET形式访问时在header中去掉此字段,POST形式访问时将值改为 Content-Type值改为"text/html; charset=utf-8"以规避安全检测(值也可以不改)。 修改后的效果: GET请求: POST请求: 3.header中的User-AgentUser-Agent是指用户代理,会包含浏览器和操作系统等信息标志。在"冰蝎"的早期版本存在User-Agent特例化问题,最新版本已经解决了这个问题。解决方案是:每个shell连接会从17个内置的UA里随机选择一个。 来看下这部分的JAVA代码: 可以看到是随机从常量Constants.userAgents中取了一个值。 这块的问题是UA包含的浏览器版本比较旧,比如:Chrome/14.0.835.163是2011年发布的版本,Firefox/6.0也是2011年的版本。这种浏览器基本很少人使用,所以特征较为明显,可以作为规则参考。 修改思路: 使用较新版本的常见浏览器UA来替换内置的旧的UA常量。 修改后的效果: 2020年发布的Firefox 75.0: 2019年11月发布的Chrome 78.0.3904.108: 4.header中的Accept在请求header中的Accept字段默认会是一个比较奇怪的值,此值在GET形式和POST形式的请求中均存在。而在正常的浏览器或其他设备访问的报文中Accept的值不会是这样的,所以此处也可以作为一个强力有效的规则检测依据。 GET请求: POST请求: 此处产生的原因是JAVA的HTTPURLConnection库("冰蝎"使用的HTTP通信库)在没有设置Accept值时会自动设置该值作为默认值,而源码中默认并没有对Accept进行处理。 修改思路: 修改请求header中的Accept的值。 修改后的效果: GET请求: POST请求: 5.二次密钥获取在"冰蝎"的默认流量中,会有两次通过GET形式的请求获取密钥的过程,这点比较奇怪。 此处也可作为一个检测点。 我们来看下代码实现: 这一步是将密钥存入rawkey_1变量中。 再次获取的密钥存到rawkey_2变量中,之后rawkey_1和rawkey_2进行了异或操作,通过异或结果来判断,从而结束循环条件,最多尝试获取10次密钥。实话说这块代码没太看出来作用,实际是大部分情况2次就OK了,3次获取密钥的情况都不太多。个人感觉这块是为了校验获取到的密钥是否可用以及控制获取密钥的次数。 修改思路: 删掉多次获取密钥的过程,可以改成一次获取密钥。或者直接把密钥写到webshell里,省去获取密钥的过程。 修改后的效果: 6.response中返回密钥在获取密钥时,密钥返回是直接以16位字符的形式返回到客户端。这时会有比较大的破绽,我们来看下常用的检测规则: - <font size="4" face="宋体">\r\n\r\n[a-z0-9]{16}[ DISCUZ_CODE_1 ]lt;/font>
复制代码和
- <font size="4" face="宋体">Content-Length: 16</font>
复制代码检测内容是:以两个\r\n完整换行加上16位字母小写+数字组合为结尾,再配合Content-Length: 16 为规则一起检测。 我们来看下客户端代码对于密钥的匹配规则: 源码只匹配了16位的字母a-f大小写+数字,hah~ 这是因为啥呢??? 原因在"冰蝎"默认自带的webshell里: 因为webshell 生成的密码算法为md5,md5输出结果显示是16进制,所以只有0-9a-f。 修改思路: GET形式访问时,可以加入一些混淆的返回内容,或者将密钥变型。 修改后的效果: 可以先从视觉效果上隐藏起来: 流量侧: 这里只是简单的加了一些内容作为演示,实战时可以根据情况混淆。 7.header中的Cookie因为"冰蝎"默认自带的webshell中的key在将密钥返回客户端后,会将密钥保存在Session中。而SessionId在第一次客户端请求时作为Cookie发送给了客户端,所以Cookie也是作为我们一个重要检查点。 Cookie中的问题是"path=/"这部分。在访问服务器时,服务端将Cookie以Set-Cookie的response头中的形式返回,其中Path是该Cookie的应用路径。 举个例子: Cookie1; Path=/ Cookie2; Path=/admin/ 当浏览器访问网站 "/" 路径时,只会携带Cookie1。当访问 "/admin/" 路径时,会同时携带Cookie1和Cookie2。 在正常浏览器访问下,path是不会作为Cookie本身的一部分发送到服务端的。 来看下客户端代码: 此处将服务端返回的Cookie所有字符都在客户端存储起来,当客户端发送请求时全部将这些字符作为Cookie发送出去。 修改思路: 将发送请求中Cookie的Path字段去掉。 修改后的效果:
0X07 总结
在实际检测中,单一的规则检测对"冰蝎"的误报率会比较高,一些比较明显的特征相互结合使用,会有事半功倍的效果。通过魔改程序也只能在一定时间内绕过安全设备的检测。真正想要持续有效必须不断更新,不断学习,在这攻防的浪潮中砥砺前行。 安全路漫漫,与君共勉。 |