记一次修改版冰蝎简单逆向
0x00 前言
起因是一个小伙伴在小交流♂群里发了一个修改版冰蝎,可以逃避流量检测,欧,棒呀,再瞅一眼,发现没有服务端😳…又试了原版的服务端,报连接失败…😡岂可休!
于是以摸出服务端为目的的简单逆向开始了…
一开始用jd-gui看了一会儿也没瞅出个啥名堂来,搁置了
A few days later…
偶然在先知社区上看到个文章从ClassLoader到冰蝎Java篇,结合冰蝎作者的文章,然后对冰蝎的通信过程窥见了一斑,于是重新摸了摸代码,用了一天多时间(太菜了),把修改版的Jsp服务端写出来了。后续又整了PHP和ASPX端的,比较简单~
0x01 初窥
先看看原版的,读过上面两篇文章,再去看JSP服务端就比较好理解了,展开了一下代码:
1 | <%import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %> |
所以服务端只做了很简单的一件事,一句话描述就是:把客户端传入的类数据解密加载然后实例化调用其中的equals方法。
至于返回结果结构什么的都不用管,加载进来的类里面会自行处理的。
看一眼流量
修改jsp直接打印解密后的字节,看第一个流量包
1 | out.println(new String(bts)); |
可以定位到一个Echo类,在Jd-gui里探索一下,发现这种payload类都是由Utils.getData方法组装塞进一些属性变成字节数组的,当前这个Echo类在ShellService类中echo方法使用,塞进其中的content参数并转换成字节数组
发送请求则是Utils.requestAndParse方法
而echo方法由doConnect调用,来测试连接shell能不能正常通信的
Echo应该是冰蝎中最简单的payload类了,看看Encrypt方法和重写的equals方法
1 | private byte[] Encrypt(byte[] bs) throws Exception { |
equals方法就是每个payload干坏事的入口方法,这里就是把客户端传过来的content加密原样返回。conten的内容应该是客户端生成塞到类里的。
搞懂了原版的通信逻辑后去看看修改版的
0x02 再探
讲道理流程应该差不多,那先抓个包
可以看到请求流量长的不太一样,分行,而且和传统base64长的不太一样,所以解码失败了
得去找找客户端请求部分发生了什么
找到刚才ShellService.echo方法(可以看到修改版回包加密key其实也是客户端传入的,原版是取session里的)
跟入SystemUtils.requestAndParse
继续跟入sendPostRequestBinary
已经看到修改的东西了
就是把base64后的字符串做了一下替换,=
替换成*
,/
替换成_
,+
替换成#
,然后每188字符换行
既然如此,服务端反着来就行了,拿出我捉急的撸码水平给他改一下
1 | String str=""; |
试一下
emmmm,还是连接失败,为啥子捏,但回了个200,说明解密应该还是成功的,打印一下解密后字节看看
说明解密流程没问题
再去客户端代码找找问题,一顿乱翻之后,偶吼,发现payload类都改了,看看Echo类
修改版没有重写equals方法,是自己定义了个w方法,传入两个obj,HttpServletRequest和HttpServletResponse
那我这样子行不行呢?
1 | Object obj=clazz.newInstance(); |
答案是不行滴,因为Object没有w方法,向上转型调用不到子类新定义的方法
所以想到了反射的形式去调用
1 | Object obj=clazz.newInstance(); |
成了,nice!
看一眼流量
不错,回包改成了json格式,值加密的形式,与正常加密APP的流量很相似。
最后又看了下Asp.net和php的,都没有新函数,只要把字符串替换改好就能通了。
0xFF 小结
自己逆向一开始比较混乱,需要把找到的东西一点一点串起来才形成正确的程序逻辑,写文章时调整了一些顺序便于理解。
还好之前刚好看过Java反射的知识,不然最后估计还得挣扎好一会儿,客户端还有许多东西值得去研究,之后有空了再看吧。
本文暂时禁止转载