冰蝎学习笔记
这篇文章记录冰蝎的学习,先大致了解一下原理,然后一点一点补充学习(其实原理什么的没有像学习之前感觉的那么难,但把所有的功能都实现,一点一点迭代,还是很强的。后面牛的是内存马那部分,再然后一步一步走到真正的无文件内存马)
冰蝎交互流程
冰蝎 v2.0.1 及之前使用的是动态密钥协商机制
冰蝎 v2.0.1 及之前使用的是动态密钥协商机制,使用POST发送payload之前一般会有两段包含16位字符串的明文报文。最后一个报文中的16位字符串为密钥。
简单画了一下交互图
Behinder_v3.0 Beta 1 及之后使用的是预共享密钥
Behinder_v3.0 Beta 1 及之后使用的是预共享密钥,密钥为md5(“rebeyond”)[0:16],即e45e329feb5d925b。服务端直接硬编码进去,客户端根据用户设置的密码获取密钥md5(“rebeyond”)[0:16],所以如果要修改默认密钥就要手动修改马中的硬编码密钥。
进行全程无明文交互,waf比较难以防御。修改密钥之后,waf基本不可能通过解密进行拦截处理。
同时Behinder_v3.0 Beta 1 增强了内网穿透功能。
为什么使用equals进行触发
这里为什么使用equals进行触发,具体原因可以参考https://xz.aliyun.com/t/2744#toc-7
简单来说就是:最开始通过重写Object类的toString方法来作为我们的Payload执行入口。
1 | public class Payload { |
这样的好处是我们可以取到Payload的返回值并输出到页面,但是缺点也很明显:在toString方法内部没办法访问Request、Response、Seesion等servlet相关对象。所以需要找一个带有入参的方法,并且能把Request、Response、Seesion等servlet相关对象传递进去。
为什么选择equals方法:重新翻看了一下Object类的方法列表,可以看到equals方法完美符合我们的要求:有入参,而且入参是Object类,在Java世界中,Object类是所有类的基类,所以我们可以传递任何类型的对象进去。
equals的参数设置:但是equals方法只接受一个参数,怎么把Request、Response、Seesion等servlet相关对象传进去。通过对servlet的9个对象分析发现,只要传递pageContext进去,便可以间接获取Request、Response、Seesion等对象,如HttpServletRequest request=(HttpServletRequest) pageContext.getRequest();
另外,如果想要顺利的在equals中调用Request、Response、Seesion这几个对象,还需要考虑一个问题,那就是ClassLoader的问题。JVM是通过ClassLoader+类路径来标识一个类的唯一性的。我们通过调用自定义ClassLoader来defineClass出来的类与Request、Response、Seesion这些类的ClassLoader不是同一个,所以在equals中访问这些类会出现java.lang.ClassNotFoundException异常。
解决方法就是复写ClassLoader的如下构造函数,传递一个指定的ClassLoader实例进去:
1 | protected ClassLoader(ClassLoader parent) |
版本更新
同时Behinder_v3.0 Beta X 增加了很多模块,模块中的功能也不断在迭代,比如内网穿透、反弹shell、内存马等等
Behinder_v4.0 取消硬编码通信协议,传输协议完全自定义,并支持即时在线校验测试,这样就弥补了Behinder_v3.0中修改密钥很麻烦的问题。密钥支持xor、xor_base64(先base64解码,在与密钥xor)、aes、images、json(将所有信息层层封装成msg的值,每一层的value都用base64加密)
在《冰蝎v4.0传输协议详解》一文中作者提到:对于aes加解密方式,由于默认使用的是aes128的算法,会导致密文长度恒是16的整数倍,流量设备可能通过这个特征来对冰蝎做流量识别。
为了解决这个问题,可以自定义一个加解密函数,即在客户端生成密文后增加了一个魔法尾巴
1 | //省略之前生成密文encrypted的代码 |
这里能根据密钥判断出魔法尾巴的长度。在解密的时候直接截去魔法尾巴,然后在解密就可以了。这样就避免由于密文长度而被识别出来的问题了。
1 | String k="e45e329feb5d925b"; |
同时Behinder_v4.0新增支持Java Agent无文件落地注入内存马;新增平行世界模块,可对目标内网资产进行管理;新增主机扫描、端口扫描、服务识别等模块
内存马
冰蝎v3.0 注入内存马流程
使用的是self attach技术。入口点是net.rebeyond.behinder.ui.controller.MainController#injectMemShell
在net.rebeyond.behinder.ui.controller.MainController#injectMemShell中,osType代表操作系统类型,用于获取对应的agent jar,所有的jar包保存在net/rebeyond/behinder/resource/tools中。
shellService.uploadFile()方法:
通过Utils.getResourceData方法获取对于agent jar的字节码,然后通过shellService.uploadFile方法上传至受害端(对于Linux是/tmp/六位随机字符)shellService.loadJar()方法:loadJar()方法将net.rebeyond.behinder.payload.java.Loader传到受害端Loader实现的功能是通过URLClassLoader将上传的jar包加载到内存中。
受害端实例化Loader后会执行Loader.equals方法,执行Loader逻辑。(
loadJar的意义是确保injectMemshell()能loadAgent到/tmp/下的jar包?)shellService.injectMemshell()方法:injectMemshell()方法将net.rebeyond.behinder.payload.java.MemShell传到受害端MemShell.equals方法首先设置了allowAttachSelf并调用了doAgentShell写入内存马。
设置allowAttachSelf的原因时 rebeyond师傅在Java内存攻击技术漫谈中讲过,在JDK9以上,jdk.attach.allowAttachSelf默认为false,也就是无法Attach,所以才需要设置为true。
调用doAgentShell,反射获取attach和loadAgent方法并执行attach的是自身PID,因为这是将MemShell文件传到受害端,受害端执行的equals方法loadAgent的是/tmp/xxxxxx目录下的jar包
(doAgentShell注入到受害端执行,相当于AttachAgent;agentmain和transform在jar包中,相当于AgentMain和MyTransformer)
如果是linux则删除生成的临时文件,最后删除上传的jar包。

下图是冰蝎自带windows和linux类型的agent jar包的一点区别

Reference
项目地址https://github.com/rebeyond/Behinder
版本更新信息https://github.com/rebeyond/Behinder/releases
学习文章:
利用动态二进制加密实现新型一句话木马之客户端篇
利用动态二进制加密实现新型一句话木马之Java篇
冰蝎v2.0.1核心部分源码浅析
冰蝎v3.0操作使用手册
Java内存攻击技术漫谈
冰蝎v4.0传输协议详解
论如何优雅的注入Java Agent内存马
