冰蝎学习笔记
这篇文章记录冰蝎的学习,先大致了解一下原理,然后一点一点补充学习(其实原理什么的没有像学习之前感觉的那么难,但把所有的功能都实现,一点一点迭代,还是很强的。后面牛的是内存马那部分,再然后一步一步走到真正的无文件内存马)
冰蝎交互流程
冰蝎 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内存马