本篇文章是《冰蝎,从入门到魔改》的续篇。有小伙伴读过上一篇文章后联系笔者,说只介绍了 PHP 版本的特征擦除,希望可以获知其它语言版本的特征擦除思路和方法。本篇首先简单介绍一下上一篇文章中的通用流量特征点及擦除后的效果,再着重介绍在对"冰蝎"JSP 版和 ASP 版的魔改中碰到的问题及流量监测/规避的方法思路。
本章节简单的介绍一下 PHP、JSP、ASP 版本"冰蝎"的通用特征,具体原理和修改思路可以参考上一篇文章:《冰蝎,从入门到魔改》
特征:
密钥交换式 Webshell 默认密码为 pass,并且参数值为 3 位随机数字。
原版:
修改版:
特征:
GET 形式访问会携带 Content-type 字段,并且内容固定。
原版:
修改版:
GET请求:
POST请求:
特征:
内置 17 个较旧 的User-Agent。
原版:
修改版:
2020 年发布的 Firefox 75.0:
2019 年 11 月发布的 Chrome 78.0.3904.108:
特征:
Accept 字段为固定非常见值。
原版:
GET请求:
POST请求:
修改版:
GET请求:
POST请求:
特征:
至少两次的 GET 形式获取密钥的过程。
原版:
修改版:
特征:
密钥返回是直接以 16 位字符的形式返回到客户端,Content-Length 固定为 16。
原版:
修改版:
直接访问:
流量侧:
特征:
Cookie 中携带非常规的 Path 参数内容。
原版:
修改后的效果:
首先看下 JSP 版的 Webshell 代码,大体逻辑如下:
可以看到将客户端发来的字节码转为类并实例化,之后调用了 equals 函数。 equals 函数默认为 Object 的函数,是比较两个对象的内存地址,在 JAVA 代码中非常常见的函数,所以整个 Webshell 看起来人畜无害。
我们看下客户端中的相关代码,首先通过 getData 函数来获取发送的数据:
getParamedClass 函数为将类转为字节码的关键函数:
被转成字节码的 Cmd 类,其中 cmd 参数为执行命令的字符串:
可以看到,Cmd 类中将 equals 函数重写了,内部中调用了 RunCMD 。而 RunCMD 实际就是使用 Runtime.getRuntime().exec 执行系统命令,并将输出返回。
JSP版本连接的时候,客户端的请求包中的 Content-Type 为 application/octet-stream ,意思是客户端传输的为字节流。如果未有此相关业务,可作为一个较明显的监测特征。
修改思路:
POST形式访问时将值改为 Content-Type 值改为 "text/html; charset=utf-8" 或者 "text/x-www-form-urlencoded; charset=utf-8" 以规避安全检测。
看Webshell中的代码是直接读取了一整行的数据,所以改成其它类型也是没关系的。
修改后的效果:
POST请求:
什么是RASP?
RASP 英文为 Runtime application self-protection,即实时应用自我保护。它是一种新型应用安全保护技术,它将保护程序像疫苗一样注入到应用程序和应用程序融为一体,能实时检测和阻断安全攻击,使应用程序具备自我保护能力,当应用程序遇到特定漏洞和攻击时不需要人工干预就可以进行自动重新配置应对新的攻击。
此理念的众多产品,其中比较有名的开源项目叫做 OpenRASP 。 OpenRASP 是可以监测冰蝎后门的,不论 Webshell 如何免杀变形,OpenRASP 基于命令执行的调用堆栈来识别冰蝎:
hah~ 这里提到了只要客户端代码不变,就可以检测到,但是我们既然是魔改就肯定会改代码的~
先来看下"冰蝎"连接 Webshell 后运行命令 whoami 的结果,在部署好 OpenRASP 后运行,可以在 tomcatroot/rasp/logs/alarm/alarm.log 文件中查到告警日志:
我们可以看到,OpenRASP监测到了调用堆栈,冰蝎识别出了命令"cmd.exe /c whoami" 。
修改思路:
网上能查到的规避方案是修改包名,将 net.rebeyond.behinder 这三层包名修改或去掉。但是我们要知其然还要知其所以然。
在更深入的了解 OpenRASP 的检测功能后我们发现,OpenRASP 的检测逻辑部分是由 JavaScript 语言实现的,原因是为了避免多平台上的重新实现。官方对此也有所说明:
官方的检测逻辑在 /rasp/plugins/official.js 中,我们来查看这个文件并找出了检测调用堆栈的部分:
可以看到多种检测堆栈关键字的漏洞,如:fastjson 反序列化、ElasticSearch Groovy 的 RCE 等。在该文件第866行我们找到了"冰蝎"的检测关键字:
关键字检测精确到了函数名 RunCMD。
既然如此我们没有必要大张旗鼓地修改包名(还要修改调用资源的路径,非常麻烦),我们只需要修改 RunCMD 函数的名称就可以规避 OpenRASP 的检测。
修改后的效果:
我们修改好函数名后重新编译,并将服务器中的 alarm.log 告警日志清空后重启。
客户端执行 whoami:
服务端 OpenRASP 无法检测到该条命令执行,告警日志为空:
在 ASP 版本的 Webshell 有个小坑。
我们直接使用魔改之后的版本进行连接会报错:
我们抓包看下:
服务端返回状态码 500,服务器内部错误。
其实这个坑点是在密钥交换的 GET 请求中,判断密码参数(默认为 pass 的字段)是否存在。
代码如下:
在原版本中 pass 的值为随机 3 位数字,在 C# 语法中数字可以作为If判断的条件(0 为 False,其它数字为 True)。但是在我们的魔改版本中 pass 的值为了规避监测设置为了随机字符串。 C# 中字符串类型无法作为 If 的判断条件,会报类型不匹配的错误:
我们需要将 Webshell 稍微改下,判断 pass 的值不为空字符串即可解决此问题。
运行结果:
客户端连接:
ASP 版本的此问题跟 JSP 版本相同,都是在连接的时候,客户端的请求包中的 Content-Type 为 application/octet-stream ,可参考 JSP版本的修改思路。
修改后的效果:
POST请求:
在笔者编写此篇文章之际,已经听闻到有"冰蝎"即将更新的消息。在这 HW 来临之际进行更新,可以预见到攻防两方都需要对此做好准备。祝各位参与 HW 的读者:HW 顺利 !