2.ASP.NET Route型内存马

1.关于Route机制

绝大部分WEB框架里都有的路由机制,在Global.asax里也可以看到一个RegisterRoutes()方法的调用,看名字就知道是注册Routes用的

2.ASP.NET Route型内存马

注意到这里使用一个MapRoute()方法去添加Route,可以指定默认要使用的控制器、Action

2.ASP.NET Route型内存马

看起来直接调用这个MapRoute()方法就很有戏,然而不行,因为这个方法来自RouteCollectionExtensions类,而这个类依然需要依赖Mvc

2.ASP.NET Route型内存马

不过,有一个MapRoute()方法的实现很有意思

2.ASP.NET Route型内存马

这个MapRoute,实际上是调用RouteCollection的Add()方法,而RouteCollection来自System.Web.Routing,这就是个Framework都有的命名空间了,因此不只局限于MVC

2.ASP.NET Route型内存马

2.ASP.NET Route型内存马

注意Add()方法的实现,传入两个参数,一个参数是name,另一个参数要求传入实现RouteBase的对象。这里的name要求不为空,且不能在_namedMap中重复出现,都通过之后,把Route实现类加入到Collection中。

So,我们现在知道可以用这个Add()方法去注册和添加Route,那么如何实现RouteBase就成了一个问题。


2.实现恶意RouteBase用于注入内存马

看一下RouteBase抽象类,注意到我们要实现两个方法,分别是GetRouteData和GetVirtualPath

2.ASP.NET Route型内存马

当我们访问到某个已有的Route规则时,实际上会触发这个Route实现的GetRouteData或者GetVirtualPath方法,因此只要在这俩方法的其中之一实现恶意代码,就可以做出一个Route类型的内存马了。这里还有个问题就是要怎么拿到request和response相关的对象,无论是JAVA还是.NET内存马,这个都是相当关键的问题。第一个方法就是靠反射获取:

HttpContextWrapper实现了HttpContextBase,其中有一个_context字段:

2.ASP.NET Route型内存马

因此可以反射获取这个字段,然后通过其中的Request和Response属性拿到我们想要的东西。

2.ASP.NET Route型内存马

第二种方法是直接调用HttpContext的Current属性,然后老样子去调用Request和Response属性

2.ASP.NET Route型内存马

第三种方法是调用HttpContextBase的Request和Response属性

2.ASP.NET Route型内存马

总之,我们可以实现下面这样的Route类:

来自:https://exp10it.io/2024/02/asp.net-%E5%86%85%E5%AD%98%E9%A9%AC/#route

2.ASP.NET Route型内存马

2.ASP.NET Route型内存马

这里我们是在GetRouteData()方法里实现的恶意代码,当然也可以实现在GetVirtualPath()方法里:

2.ASP.NET Route型内存马

这里有一点,GetRouteData()的优先级比GetVirtualPath()高,因此优先选择实现前者。但是不管怎么实现,注意在最后必须调用一个response.End()。

总之,通过上面这些信息,可以做出一个恶意RouteBase实现类。


3.注册恶意RouteBase实现类并调整优先级

这里用Add方法去注册就行,还有一个比较关键的问题还是优先级,因为RouteCollection里也有别的元素,我们要怎么确保优先调用我们的恶意RouteBase实现类呢?这个其实好解决,因为RouteCollection也是一种Collection,可以通过Insert()方法将指定元素插入到指定的索引位置,只要插入到最初始的位置就可以保证优先级最高。

2.ASP.NET Route型内存马

因此实际上可以通过

RouteCollection.Insert(0,恶意RouteBase实现类())

进行注册,后我们可以写出内存马注入器:

来自:https://exp10it.io/2024/02/asp.net-%E5%86%85%E5%AD%98%E9%A9%AC/#route-1

2.ASP.NET Route型内存马

关键代码其实就这两行,EvialRouteBase()就是恶意RouteBase实现类,上面已经学习过要怎么构造了

运行完这个注入器后,需要触发已有的Route规则,然后加入cmd传参执行命令即可:

2.ASP.NET Route型内存马

有一个缺点,就是必须要依赖已有的Route规则。


4.实现恶意Route用于注入内存马

另一种实现恶意Route的思路,还是先回到前文学习过的MapRoute()

2.ASP.NET Route型内存马

注意这个Add()进去的route变量,实际上是一个Route类的对象,这也是一个实现了RouteBase的类,来自System.Web.Routing。

2.ASP.NET Route型内存马

所以实际上我们也可以做一个Route类的对象。其构造函数要求接受一个url形参(实际上就是Route所匹配的uri),还有一个routeHandler形参,类型为IRouteHandler,看看IRouteHandler接口:

2.ASP.NET Route型内存马

要求我们实现GetHttpHandler()方法,我们可以在其中实现我们的恶意代码。然后我们注意到,其返回值类型是IHttpHandler,这个我们熟悉呀,之前学习ashx的时候,不就是要实现这个方法吗?它的ProcessRequest()方法就是用来处理Web请求的。所以上面这边的设计思路应该就是当匹配到特定的URI(Route规则)时,将请求交给指定的Handler进行处理。因此实际上我们也可以做一个IHttpHandler的实现类,在它的ProcessRequest()方法中实现恶意代码,然后经由GetHttpHandler()进行返回,也可以实现一个内存马。所以有两种思路。

先来看第一种,在GetHttpHandler()里实现恶意代码:

2.ASP.NET Route型内存马

2.ASP.NET Route型内存马

然后利用上面的代码进行注入,这里有一个好处就是Route的构造方法允许我们传入自己的Route规则,当访问的URI以evil开头,就会触发后续恶意代码,这里就不需要借助已有的Route规则。然后我们还是把做好的route添加到第一位。

注入成功后,通过evil开头的uri去触发我们添加的恶意路由规则,然后还是通过cmd参数执行命令:

2.ASP.NET Route型内存马

当然上述那样的实现会有一个问题,单纯访问以evil开头的uri时,会看到下面这样的报错:

2.ASP.NET Route型内存马

我们前面提到过这是因为GetHttpHandler()要求返回IHttpHandler,而上面的实现返回的是null,解决方法也很简单,写一个TestHttpHandler,实现IHttpHandler,不进行任何操作,然后GetHttpHandler()返回TestHttpHandler类的对象即可

2.ASP.NET Route型内存马

此时直接访问就不会有报错了,只有白屏

2.ASP.NET Route型内存马

这就引出第二种实现方式,那就是GetHttpHandler()里面什么都不做,返回一个恶意的IHttpHandler实现类,在其ProcessRequest()方法里实现恶意代码,具体实现如下:

2.ASP.NET Route型内存马

2.ASP.NET Route型内存马

5.结语

Route型内存马就比较通用了,不必依赖MVC环境。本文主要学习了四种实现Route型内存马的方法,首先学习的两种都是通过直接实现RouteBase类,在Route生命流程中必定会触发的方法中实现恶意代码。接着我们又学习了通过实现Route类去注入恶意代码。不过最终都是要把恶意的Route放到RouteCollection里去的,获取这玩意遍历一遍有没有可以Route就很好排查出来了。


6.参考

https://yzddmr6.com/posts/asp-net-memory-shell-router/https://exp10it.io/2024/02/asp.net-%E5%86%85%E5%AD%98%E9%A9%AC/#route-1

原文始发于微信公众号(HW专项行动小组):2.ASP.NET Route型内存马

版权声明:admin 发表于 2024年11月17日 下午2:04。
转载请注明:2.ASP.NET Route型内存马 | CTF导航

相关文章