Aggregator
给老徐的书《软件自动化测试实战解析-基于Python3》写的序
watchAD的调试及使用
鹅厂4045天,老总监我毕业了
ProxyLogon 僅僅只是冰山一角,一個針對 Microsoft Exchange Server 的全新攻擊面!
Microsoft Exchange Server 作為當今世界上最常見的郵件解決方案,已經幾乎是企業以及政府每日工作與維繫安全不可或缺的一部分!在今年一月,我們回報了一系列的 Exchange Server 漏洞給 Microsoft,並且將這個漏洞它命名為 ProxyLogon,相信如果您有在關注業界新聞,一定也聽過這個名字!ProxyLogon 也許是 Exchange 歷史上最嚴重、影響力也最大的一個漏洞!
隨著更深入的從架構層去研究 ProxyLogon,我們發現它不僅僅只是一個漏洞,而是一整個新的、沒有人提過的攻擊面可讓駭客或安全研究員去挖掘更多的漏洞。因此我們專注深入研究這個攻擊面,並從中發現了至少八個漏洞,這些漏洞涵蓋了伺服器端、客戶端,甚至密碼學漏洞,我們並將這些漏洞組合成了三個攻擊鏈:
- ProxyLogon: 最知名、影響力也最大的 Exchange 攻擊鏈
- ProxyOracle: 一個可以還原任意 Exchange 使用者明文密碼的攻擊鏈
- ProxyShell: 我們在 Pwn2Own 2021 上展示打掉 Exchange 的攻擊鏈
所有我們找到的漏洞都是邏輯漏洞,這代表相較於記憶體毀損類型的漏洞,這些漏洞更容易被重現以及利用,我們也將成果發表至 Black Hat USA 及 DEFCON 上,也同時獲得了 2021 Pwnie Awards 年度 Best Server-Side Bug 獎項,如果你有興趣的話可以從這邊下載會議的投影片!
- ProxyLogon is Just the Tip of the Iceberg: A New Attack Surface on Microsoft Exchange Server! [投影片] [影片]
本次提及的漏洞皆經過負責任的漏洞接露程序回報給微軟、並獲得修復,您可以從下面這張圖查看詳細的漏洞編號及回報時間表。
Report Time Name CVE Patch Time CAS[1] Reported By Jan 05, 2021 ProxyLogon CVE-2021-26855 Mar 02, 2021 Yes Orange Tsai, Volexity and MSTIC Jan 05, 2021 ProxyLogon CVE-2021-27065 Mar 02, 2021 - Orange Tsai, Volexity and MSTIC Jan 17, 2021 ProxyOracle CVE-2021-31196 Jul 13, 2021 Yes Orange Tsai Jan 17, 2021 ProxyOracle CVE-2021-31195 May 11, 2021 - Orange Tsai Apr 02, 2021 ProxyShell[2] CVE-2021-34473 Apr 13, 2021 Yes Orange Tsai working with ZDI Apr 02, 2021 ProxyShell[2] CVE-2021-34523 Apr 13, 2021 Yes Orange Tsai working with ZDI Apr 02, 2021 ProxyShell[2] CVE-2021-31207 May 11, 2021 - Orange Tsai working with ZDI Jun 02, 2021 - - - Yes Orange Tsai Jun 02, 2021 - CVE-2021-33768 Jul 13, 2021 - Orange Tsai and Dlive[1] Bugs relate to this new attack surface direclty
[2] Pwn2Own 2021 bugs
更詳盡的技術細節我們已陸續公布,後續連結會持續更新於本文,敬請期待:
PHP 源码阅读笔记:编译与调试 PHP
【招聘】Go安全开发工程师
PHP反序列化漏洞浅入浅出
老徐自述:《软件自动化测试实战解析_基于Python3编程语言》
Shadow Credentials简单利用流水帐
Fraud Scenarios in the Buy Now, Pay Later Ecosystem
A New Attack Surface on MS Exchange Part 1 - ProxyLogon!
The series of A New Attack Surface on MS Exchange:
- A New Attack Surface on MS Exchange Part 1 - ProxyLogon!
- A New Attack Surface on MS Exchange Part 2 - ProxyOracle!
- A New Attack Surface on MS Exchange Part 3 - ProxyShell!
- A New Attack Surface on MS Exchange Part 4 - ProxyRelay!
Microsoft Exchange, as one of the most common email solutions in the world, has become part of the daily operation and security connection for governments and enterprises. This January, we reported a series of vulnerabilities of Exchange Server to Microsoft and named it as ProxyLogon. ProxyLogon might be the most severe and impactful vulnerability in the Exchange history ever. If you were paying attention to the industry news, you must have heard it.
While looking into ProxyLogon from the architectural level, we found it is not just a vulnerability, but an attack surface that is totally new and no one has ever mentioned before. This attack surface could lead the hackers or security researchers to more vulnerabilities. Therefore, we decided to focus on this attack surface and eventually found at least 8 vulnerabilities. These vulnerabilities cover from server side, client side, and even crypto bugs. We chained these vulnerabilities into 3 attacks:
- ProxyLogon: The most well-known and impactful Exchange exploit chain
- ProxyOracle: The attack which could recover any password in plaintext format of Exchange users
- ProxyShell: The exploit chain we demonstrated at Pwn2Own 2021 to take over Exchange and earn $200,000 bounty
I would like to highlight that all vulnerabilities we unveiled here are logic bugs, which means they could be reproduced and exploited more easily than any memory corruption bugs. We have presented our research at Black Hat USA and DEFCON, and won the Best Server-Side bug of Pwnie Awards 2021. You can check our presentation materials here:
- ProxyLogon is Just the Tip of the Iceberg: A New Attack Surface on Microsoft Exchange Server! [Slides] [Video]
By understanding the basics of this new attack surface, you won’t be surprised why we can pop out 0days easily!
IntroI would like to state that all the vulnerabilities mentioned have been reported via the responsible vulnerability disclosure process and patched by Microsoft. You could find more detail of the CVEs and the report timeline from the following table.
Report Time Name CVE Patch Time CAS[1] Reported By Jan 05, 2021 ProxyLogon CVE-2021-26855 Mar 02, 2021 Yes Orange Tsai, Volexity and MSTIC Jan 05, 2021 ProxyLogon CVE-2021-27065 Mar 02, 2021 - Orange Tsai, Volexity and MSTIC Jan 17, 2021 ProxyOracle CVE-2021-31196 Jul 13, 2021 Yes Orange Tsai Jan 17, 2021 ProxyOracle CVE-2021-31195 May 11, 2021 - Orange Tsai Apr 02, 2021 ProxyShell[2] CVE-2021-34473 Apr 13, 2021 Yes Orange Tsai working with ZDI Apr 02, 2021 ProxyShell[2] CVE-2021-34523 Apr 13, 2021 Yes Orange Tsai working with ZDI Apr 02, 2021 ProxyShell[2] CVE-2021-31207 May 11, 2021 - Orange Tsai working with ZDI Jun 02, 2021 - - - Yes Orange Tsai Jun 02, 2021 - CVE-2021-33768 Jul 13, 2021 - Orange Tsai and Dlive[1] Bugs relate to this new attack surface direclty
[2] Pwn2Own 2021 bugs
Why did Exchange Server become a hot topic? From my point of view, the whole ProxyLogon attack surface is actually located at an early stage of Exchange request processing. For instance, if the entrance of Exchange is 0, and 100 is the core business logic, ProxyLogon is somewhere around 10. Again, since the vulnerability is located at the beginning place, I believe anyone who has reviewed the security of Exchange carefully would spot the attack surface. This was also why I tweeted my worry about bug collision after reporting to Microsoft. The vulnerability was so impactful, yet it’s a simple one and located at such an early stage.
You all know what happened next, Volexity found that an APT group was leveraging the same SSRF (CVE-2021-26855) to access users’ emails in early January 2021 and reported to Microsoft. Microsoft also released the urgent patches in March. From the public information released afterwards, we found that even though they used the same SSRF, the APT group was exploiting it in a very different way from us. We completed the ProxyLogon attack chain through CVE-2021-27065, while the APT group used EWS and two unknown vulnerabilities in their attack. This has convinced us that there is a bug collision on the SSRF vulnerability.
Image from Microsoft Blog
Regarding the ProxyLogon PoC we reported to MSRC appeared in the wild in late February, we were as curious as everyone after eliminating the possibility of leakage from our side through a thorough investigation. With a clearer timeline appearing and more discussion occurring, it seems like this is not the first time that something like this happened to Microsoft. Maybe you would be interested in learning some interesting stories from here.
Why targeting on Exchange Server?Mail server is a highly valuable asset that holds the most confidential secrets and corporate data. In other words, controlling a mail server means controlling the lifeline of a company. As the most common-use email solution, Exchange Server has been the top target for hackers for a long time. Based on our research, there are more than four hundred thousands Exchange Servers exposed on the Internet. Each server represents a company, and you can imagine how horrible it is while a severe vulnerability appeared in Exchange Server.
Normally, I will review the existing papers and bugs before starting a research. Among the whole Exchange history, is there any interesting case? Of course. Although most vulnerabilities are based on known attack vectors, such as the deserialization or bad input validation, there are still several bugs that are worth mentioning.
The most specialThe most special one is the arsenal from Equation Group in 2017. It’s the only practical and public pre-auth RCE in the Exchange history. Unfortunately, the arsenal only works on an ancient Exchange Server 2003. If the arsenal leak happened earlier, it could end up with another nuclear-level crisis.
The most interestingThe most interesting one is CVE-2018-8581 disclosed by someone who cooperated with ZDI. Though it was simply an SSRF, with the feature, it could be combined with NTLM Relay, the attacker could turn a boring SSRF into something really fancy. For instance, it could directly control the whole Domain Controller through a low privilege account.
The most surprisingThe most surprising one is CVE-2020-0688, which was also disclosed by someone working with ZDI. The root cause of this bug is due to a hard-coded cryptographic key in Microsoft Exchange. With this hard-coded key, an attacker with low privilege can take over the whole Exchange Server. And as you can see, even in 2020, a silly, hard-coded cryptographic key could still be found in an essential software like Exchange. This indicated that Exchange is lacking security reviews, which also inspired me to dig more into the Exchange security.
Where is the new attack surfaceExchange is a very sophisticated application. Since 2000, Exchange has released a new version every 3 years. Whenever Exchange releases a new version, the architecture changes a lot and becomes different. The changes of architecture and iterations make it difficult to upgrade an Exchange Server. In order to ensure the compatibility between the new architecture and old ones, several design debts were incurred to Exchange Server and led to the new attack surface we found.
Where did we focus at Microsoft Exchange? We focused on the Client Access Service, CAS. CAS is a fundamental component of Exchange. Back to the version 2000/2003, CAS was an independent Frontend Server in charge of all the Frontend web rendering logics. After several renaming, integrating, and version differences, CAS has been downgraded to a service under the Mailbox Role. The official documentation from Microsoft indicates that:
Mailbox servers contain the Client Access services that accept client connections for all protocols. These frontend services are responsible for routing or proxying connections to the corresponding backend services on a Mailbox server
From the narrative you could realize the importance of CAS, and you could imagine how critical it is when bugs are found in such infrastructure. CAS was where we focused on, and where the attack surface appeared.
The CAS architectureCAS is the fundamental component in charge of accepting all the connections from the client side, no matter if it’s HTTP, POP3, IMAP or SMTP, and proxies the connections to the corresponding Backend Service. As a Web Security researcher, I focused on the Web implementation of CAS.
The CAS web is built on Microsoft IIS. As you can see, there are two websites inside the IIS. The “Default Website” is the Frontend we mentioned before, and the “Exchange Backend” is where the business logic is. After looking into the configuration carefully, we notice that the Frontend is binding with ports 80 and 443, and the Backend is listening on ports 81 and 444. All the ports are binding with 0.0.0.0, which means anyone could access the Frontend and Backend of Exchange directly. Wouldn’t it be dangerous? Please keep this question in mind and we will answer that later.
Exchange implements the logic of Frontend and Backend via IIS module. There are several modules in Frontend and Backend to complete different tasks, such as the filter, validation, and logging. The Frontend must contain a Proxy Module. The Proxy Module picks up the HTTP request from the client side and adds some internal settings, then forwards the request to the Backend. As for the Backend, all the applications include the Rehydration Module, which is in charge of parsing Frontend requests, populating the client information back, and continuing to process the business logic. Later we will be elaborating how Proxy Module and Rehydration Module work.
Frontend Proxy ModuleProxy Module chooses a handler based on the current ApplicationPath to process the HTTP request from the client side. For instance, visiting /EWS will use EwsProxyRequestHandler, as for /OWA will trigger OwaProxyRequestHandler. All the handlers in Exchange inherit the class from ProxyRequestHandler and implement its core logic, such as how to deal with the HTTP request from the user, which URL from Backend to proxy to, and how to synchronize the information with the Backend. The class is also the most centric part of the whole Proxy Module, we will separate ProxyRequestHandler into 3 sections:
Frontend Reqeust SectionThe Request section will parse the HTTP request from the client and determine which cookie and header could be proxied to the Backend. Frontend and Backend relied on HTTP Headers to synchronize information and proxy internal status. Therefore, Exchange has defined a blacklist to avoid some internal Headers being misused.
HttpProxy\ProxyRequestHandler.cs
protected virtual bool ShouldCopyHeaderToServerRequest(string headerName) { return !string.Equals(headerName, "X-CommonAccessToken", OrdinalIgnoreCase) && !string.Equals(headerName, "X-IsFromCafe", OrdinalIgnoreCase) && !string.Equals(headerName, "X-SourceCafeServer", OrdinalIgnoreCase) && !string.Equals(headerName, "msExchProxyUri", OrdinalIgnoreCase) && !string.Equals(headerName, "X-MSExchangeActivityCtx", OrdinalIgnoreCase) && !string.Equals(headerName, "return-client-request-id", OrdinalIgnoreCase) && !string.Equals(headerName, "X-Forwarded-For", OrdinalIgnoreCase) && (!headerName.StartsWith("X-Backend-Diag-", OrdinalIgnoreCase) || this.ClientRequest.GetHttpRequestBase().IsProbeRequest()); }In the last stage of Request, Proxy Module will call the method AddProtocolSpecificHeadersToServerRequest implemented by the handler to add the information to be communicated with the Backend in the HTTP header. This section will also serialize the information from the current login user and put it in a new HTTP header X-CommonAccessToken, which will be forwarded to the Backend later.
For instance, If I log into Outlook Web Access (OWA) with the name Orange, the X-CommonAccessToken that Frontend proxy to Backend will be:
Frontend Proxy SectionThe Proxy Section first uses the GetTargetBackendServerURL method to calculate which Backend URL should the HTTP request be forwarded to. Then initialize a new HTTP Client request with the method CreateServerRequest.
HttpProxy\ProxyRequestHandler.cs
protected HttpWebRequest CreateServerRequest(Uri targetUrl) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(targetUrl); if (!HttpProxySettings.UseDefaultWebProxy.Value) { httpWebRequest.Proxy = NullWebProxy.Instance; } httpWebRequest.ServicePoint.ConnectionLimit = HttpProxySettings.ServicePointConnectionLimit.Value; httpWebRequest.Method = this.ClientRequest.HttpMethod; httpWebRequest.Headers["X-FE-ClientIP"] = ClientEndpointResolver.GetClientIP(SharedHttpContextWrapper.GetWrapper(this.HttpContext)); httpWebRequest.Headers["X-Forwarded-For"] = ClientEndpointResolver.GetClientProxyChainIPs(SharedHttpContextWrapper.GetWrapper(this.HttpContext)); httpWebRequest.Headers["X-Forwarded-Port"] = ClientEndpointResolver.GetClientPort(SharedHttpContextWrapper.GetWrapper(this.HttpContext)); httpWebRequest.Headers["X-MS-EdgeIP"] = Utilities.GetEdgeServerIpAsProxyHeader(SharedHttpContextWrapper.GetWrapper(this.HttpContext).Request); // ... return httpWebRequest; }Exchange will also generate a Kerberos ticket via the HTTP Service-Class of the Backend and put it in the Authorization header. This header is designed to prevent anonymous users from accessing the Backend directly. With the Kerberos Ticket, the Backend could validate the access from the Frontend.
HttpProxy\ProxyRequestHandler.cs
if (this.ProxyKerberosAuthentication) { serverRequest.ConnectionGroupName = this.ClientRequest.UserHostAddress + ":" + GccUtils.GetClientPort(SharedHttpContextWrapper.GetWrapper(this.HttpContext)); } else if (this.AuthBehavior.AuthState == AuthState.BackEndFullAuth || this. ShouldBackendRequestBeAnonymous() || (HttpProxySettings.TestBackEndSupportEnabled.Value && !string.IsNullOrEmpty(this.ClientRequest.Headers["TestBackEndUrl"]))) { serverRequest.ConnectionGroupName = "Unauthenticated"; } else { serverRequest.Headers["Authorization"] = KerberosUtilities.GenerateKerberosAuthHeader( serverRequest.Address.Host, this.TraceContext, ref this.authenticationContext, ref this.kerberosChallenge); }HttpProxy\KerberosUtilities.cs
internal static string GenerateKerberosAuthHeader(string host, int traceContext, ref AuthenticationContext authenticationContext, ref string kerberosChallenge) { byte[] array = null; byte[] bytes = null; // ... authenticationContext = new AuthenticationContext(); string text = "HTTP/" + host; authenticationContext.InitializeForOutboundNegotiate(AuthenticationMechanism.Kerberos, text, null, null); SecurityStatus securityStatus = authenticationContext.NegotiateSecurityContext(inputBuffer, out bytes); // ... string @string = Encoding.ASCII.GetString(bytes); return "Negotiate " + @string; }Therefore, a Client request proxied to the Backend will be added with several HTTP Headers for internal use. The two most essential Headers are X-CommonAccessToken, which indicates the mail users’ log in identity, and Kerberos Ticket, which represents legal access from the Frontend.
Frontend Response SectionThe last is the section of Response. It receives the response from the Backend and decides which headers or cookies are allowed to be sent back to the Frontend.
Backend Rehydration ModuleNow let’s move on and check how the Backend processes the request from the Frontend. The Backend first uses the method IsAuthenticated to check whether the incoming request is authenticated. Then the Backend will verify whether the request is equipped with an extended right called ms-Exch-EPI-Token-Serialization. With the default setting, only Exchange Machine Account would have such authorization. This is also why the Kerberos Ticket generated by the Frontend could pass the checkpoint but you can’t access the Backend directly with a low authorized account.
After passing the check, Exchange will restore the login identity used in the Frontend, through deserializing the header X-CommonAccessToken back to the original Access Token, and then put it in the httpContext object to progress to the business logic in the Backend.
Authentication\BackendRehydrationModule.cs
private void OnAuthenticateRequest(object source, EventArgs args) { if (httpContext.Request.IsAuthenticated) { this.ProcessRequest(httpContext); } } private void ProcessRequest(HttpContext httpContext) { CommonAccessToken token; if (this.TryGetCommonAccessToken(httpContext, out token)) { // ... } } private bool TryGetCommonAccessToken(HttpContext httpContext, out CommonAccessToken token) { string text = httpContext.Request.Headers["X-CommonAccessToken"]; if (string.IsNullOrEmpty(text)) { return false; } bool flag; try { flag = this.IsTokenSerializationAllowed(httpContext.User.Identity as WindowsIdentity); } finally { httpContext.Items["BEValidateCATRightsLatency"] = stopwatch.ElapsedMilliseconds - elapsedMilliseconds; } token = CommonAccessToken.Deserialize(text); httpContext.Items["Item-CommonAccessToken"] = token; //... } private bool IsTokenSerializationAllowed(WindowsIdentity windowsIdentity) { flag2 = LocalServer.AllowsTokenSerializationBy(clientSecurityContext); return flag2; } private static bool AllowsTokenSerializationBy(ClientSecurityContext clientContext) { return LocalServer.HasExtendedRightOnServer(clientContext, WellKnownGuid.TokenSerializationRightGuid); // ms-Exch-EPI-Token-Serialization } The attack surfaceAfter a brief introduction to the architecture of CAS, we now realize that CAS is just a well-written HTTP Proxy (or Client), and we know that implementing Proxy isn’t easy. So I was wondering:
Could I use a single HTTP request to access different contexts in Frontend and Backend respectively to cause some confusion?
If we could do that, maaaaaybe I could bypass some Frontend restrictions to access arbitrary Backends and abuse some internal API. Or, we can confuse the context to leverage the inconsistency of the definition of dangerous HTTP headers between the Frontend and Backend to do further interesting attacks.
With these thoughts in mind, let’s start hunting!
The ProxyLogonThe first exploit is the ProxyLogon. As introduced before, this may be the most severe vulnerability in the Exchange history ever. ProxyLogon is chained with 2 bugs:
- CVE-2021-26855 - Pre-auth SSRF leads to Authentication Bypass
- CVE-2021-27065 - Post-auth Arbitrary-File-Write leads to RCE
There are more than 20 handlers corresponding to different application paths in the Frontend. While reviewing the implementations, we found the method GetTargetBackEndServerUrl, which is responsible for calculating the Backend URL in the static resource handler, assigns the Backend target by cookies directly.
Now you figure out how simple this vulnerability is after learning the architecture!
HttpProxy\ProxyRequestHandler.cs
protected virtual Uri GetTargetBackEndServerUrl() { this.LogElapsedTime("E_TargetBEUrl"); Uri result; try { UrlAnchorMailbox urlAnchorMailbox = this.AnchoredRoutingTarget.AnchorMailbox as UrlAnchorMailbox; if (urlAnchorMailbox != null) { result = urlAnchorMailbox.Url; } else { UriBuilder clientUrlForProxy = this.GetClientUrlForProxy(); clientUrlForProxy.Scheme = Uri.UriSchemeHttps; clientUrlForProxy.Host = this.AnchoredRoutingTarget.BackEndServer.Fqdn; clientUrlForProxy.Port = 444; if (this.AnchoredRoutingTarget.BackEndServer.Version < Server.E15MinVersion) { this.ProxyToDownLevel = true; RequestDetailsLoggerBase<RequestDetailsLogger>.SafeAppendGenericInfo(this.Logger, "ProxyToDownLevel", true); clientUrlForProxy.Port = 443; } result = clientUrlForProxy.Uri; } } finally { this.LogElapsedTime("L_TargetBEUrl"); } return result; }From the code snippet, you can see the property BackEndServer.Fqdn of AnchoredRoutingTarget is assigned from the cookie directly.
HttpProxy\OwaResourceProxyRequestHandler.cs
protected override AnchorMailbox ResolveAnchorMailbox() { HttpCookie httpCookie = base.ClientRequest.Cookies["X-AnonResource-Backend"]; if (httpCookie != null) { this.savedBackendServer = httpCookie.Value; } if (!string.IsNullOrEmpty(this.savedBackendServer)) { base.Logger.Set(3, "X-AnonResource-Backend-Cookie"); if (ExTraceGlobals.VerboseTracer.IsTraceEnabled(1)) { ExTraceGlobals.VerboseTracer.TraceDebug<HttpCookie, int>((long)this.GetHashCode(), "[OwaResourceProxyRequestHandler::ResolveAnchorMailbox]: AnonResourceBackend cookie used: {0}; context {1}.", httpCookie, base.TraceContext); } return new ServerInfoAnchorMailbox(BackEndServer.FromString(this.savedBackendServer), this); } return new AnonymousAnchorMailbox(this); }Though we can only control the Host part of the URL, but hang on, isn’t manipulating a URL Parser exactly what I am good at? Exchange builds the Backend URL by built-in UriBuilder. However, since C# didn’t verify the Host, so we can enclose the whole URL with some special characters to access arbitrary servers and ports.
https://[foo]@example.com:443/path#]:444/owa/auth/x.js
So far we have a super SSRF that can control almost all the HTTP requests and get all the replies. The most impressive thing is that the Frontend of Exchange will generate a Kerberos Ticket for us, which means even when we are attacking a protected and domain-joined HTTP service, we can still hack with the authentication of Exchange Machine Account.
So, what is the root cause of this arbitrary Backend assignment? As mentioned, the Exchange Server changes its architecture while releasing new versions. It might have different functions in different versions even with the same component under the same name. Microsoft has put great effort into ensuring the architectural capability between new and old versions. This cookie is a quick solution and the design debt of Exchange making the Frontend in the new architecture could identify where the old Backend is.
CVE-2021-27065 - Post-auth Arbitrary-File-WriteThanks to the super SSRF allowing us to access the Backend without restriction. The next is to find a RCE bug to chain together. Here we leverage a Backend internal API /proxyLogon.ecp to become the admin. The API is also the reason why we called it ProxyLogon.
Because we leverage the Frontend handler of static resources to access the ECExchange Control Panel (ECP) Backend, the header msExchLogonMailbox, which is a special HTTP header in the ECP Backend, will not be blocked by the Frontend. By leveraging this minor inconsistency, we can specify ourselves as the SYSTEM user and generate a valid ECP session with the internal API.
With the inconsistency between the Frontend and Backend, we can access all the functions on ECP by Header forgery and internal Backend API abuse. Next, we have to find an RCE bug on the ECP interface to chain them together. The ECP wraps the Exchange PowerShell commands as an abstract interface by /ecp/DDI/DDIService.svc. The DDIService defines several PowerShell executing pipelines by XAML so that it can be accessed by Web. While verifying the DDI implementation, we found the tag of WriteFileActivity did not check the file path properly and led to an arbitrary-file-write.
DDIService\WriteFileActivity.cs
public override RunResult Run(DataRow input, DataTable dataTable, DataObjectStore store, Type codeBehind, Workflow.UpdateTableDelegate updateTableDelegate) { DataRow dataRow = dataTable.Rows[0]; string value = (string)input[this.InputVariable]; string path = (string)input[this.OutputFileNameVariable]; RunResult runResult = new RunResult(); try { runResult.ErrorOccur = true; using (StreamWriter streamWriter = new StreamWriter(File.Open(path, FileMode.CreateNew))) { streamWriter.WriteLine(value); } runResult.ErrorOccur = false; } // ... }There are several paths to trigger the vulnerability of arbitrary-file-write. Here we use ResetOABVirtualDirectory.xaml as an example and write the result of Set-OABVirtualDirectory to the webroot to be our Webshell.
Now we have a working pre-auth RCE exploit chain. An unauthenticated attacker can execute arbitrary commands on Microsoft Exchange Server through an exposed 443 port. Here is an demonstration video:
EpilogueAs the first blog of this series, ProxyLogon perfectly shows how severe this attack surface could be. We will have more examples to come. Stay tuned!
A New Attack Surface on MS Exchange Part 2 - ProxyOracle!
Hi, this is the part 2 of the New MS Exchange Attack Surface. Because this article refers to several architecture introductions and attack surface concepts in the previous article, you could find the first piece here:
This time, we will be introducing ProxyOracle. Compared with ProxyLogon, ProxyOracle is an interesting exploit with a different approach. By simply leading a user to visit a malicious link, ProxyOracle allows an attacker to recover the user’s password in plaintext format completely. ProxyOracle consists of two vulnerabilities:
- CVE-2021-31195 - Reflected Cross-Site Scripting
- CVE-2021-31196 - Padding Oracle Attack on Exchange Cookies Parsing
So where is ProxyOracle? Based on the CAS architecture we introduced before, the Frontend of CAS will first serialize the User Identity to a string and put it in the header of X-CommonAccessToken. The header will be merged into the client’s HTTP request and sent to the Backend later. Once the Backend receives, it deserializes the header back to the original User Identity in Frontend.
We now know how the Frontend and Backend synchronize the User Identity. The next is to explain how the Frontend knows who you are and processes your credentials. The Outlook Web Access (OWA) uses a fancy interface to handle the whole login mechanism, which is called Form-Based Authentication (FBA). The FBA is a special IIS module that inherits the ProxyModule and is responsible for executing the transformation between the credentials and cookies before entering the proxy logic.
The FBA MechanismHTTP is a stateless protocol. To keep your login state, FBA saves the username and password in cookies. Every time you visit the OWA, Exchange will parse the cookies, retrieve the credential and try to log in with that. If the login succeed, Exchange will serialize your User Identity into a string, put it into the header of X-CommonAccessToken, and forward it to the Backend
HttpProxy\FbaModule.cs
protected override void OnBeginRequestInternal(HttpApplication httpApplication) { httpApplication.Context.Items["AuthType"] = "FBA"; if (!this.HandleFbaAuthFormPost(httpApplication)) { try { this.ParseCadataCookies(httpApplication); } catch (MissingSslCertificateException) { NameValueCollection nameValueCollection = new NameValueCollection(); nameValueCollection.Add("CafeError", ErrorFE.FEErrorCodes.SSLCertificateProblem.ToString()); throw new HttpException(302, AspNetHelper.GetCafeErrorPageRedirectUrl(httpApplication.Context, nameValueCollection)); } } base.OnBeginRequestInternal(httpApplication); }All the cookies are encrypted to ensure even if an attacker can hijack the HTTP request, he/she still couldn’t get your credential in plaintext format. FBA leverages 5 special cookies to accomplish the whole de/encryption process:
- cadata - The encrypted username and password
- cadataTTL - The Time-To-Live timestamp
- cadataKey - The KEY for encryption
- cadataIV - The IV for encryption
- cadataSig - The signature to prevent tampering
The encryption logic will first generate two 16 bytes random strings as the IV and KEY for the current session. The username and password will then be encoded with Base64, encrypted by the algorithm AES and sent back to the client within cookies. Meanwhile, the IV and KEY will be sent to the user, too. To prevent the client from decrypting the credential by the known IV and KEY directly, Exchange will once again use the algorithm RSA to encrypt the IV and KEY via its SSL certificate private key before sending out!
Here is a Pseudo Code for the encryption logic:
@key = GetServerSSLCert().GetPrivateKey() cadataSig = RSA(@key).Encrypt("Fba Rocks!") cadataIV = RSA(@key).Encrypt(GetRandomBytes(16)) cadataKey = RSA(@key).Encrypt(GetRandomBytes(16)) @timestamp = GetCurrentTimestamp() cadataTTL = AES_CBC(cadataKey, cadataIV).Encrypt(@timestamp) @blob = "Basic " + ToBase64String(UserName + ":" + Password) cadata = AES_CBC(cadataKey, cadataIV).Encrypt(@blob)The Exchange takes CBC as its padding mode. If you are familiar with Cryptography, you might be wondering whether the CBC mode here is vulnerable to the Padding Oracle Attack? Bingo! As a matter of fact, Padding Oracle Attack is still existing in such essential software like Exchange in 2021!
CVE-2021-31196 - The Padding OracleWhen there is something wrong with the FBA, Exchange attaches an error code and redirects the HTTP request back to the original login page. So where is the Oracle? In the cookie decryption, Exchange uses an exception to catch the Padding Error, and because of the exception, the program returned immediately so that error code number is 0, which means None:
Location: /OWA/logon.aspx?url=…&reason=0
In contrast with the Padding Error, if the decryption is good, Exchange will continue the authentication process and try to login with the corrupted username and password. At this moment, the result must be a failure and the error code number is 2, which represents InvalidCredntials:
Location: /OWA/logon.aspx?url=…&reason=2
The diagram looks like:
With the difference, we now have an Oracle to identify whether the decryption process is successful or not.
HttpProxy\FbaModule.cs
private void ParseCadataCookies(HttpApplication httpApplication) { HttpContext context = httpApplication.Context; HttpRequest request = context.Request; HttpResponse response = context.Response; string text = request.Cookies["cadata"].Value; string text2 = request.Cookies["cadataKey"].Value; string text3 = request.Cookies["cadataIV"].Value; string text4 = request.Cookies["cadataSig"].Value; string text5 = request.Cookies["cadataTTL"].Value; // ... RSACryptoServiceProvider rsacryptoServiceProvider = (x509Certificate.PrivateKey as RSACryptoServiceProvider); byte[] array = null; byte[] array2 = null; byte[] rgb2 = Convert.FromBase64String(text2); byte[] rgb3 = Convert.FromBase64String(text3); array = rsacryptoServiceProvider.Decrypt(rgb2, true); array2 = rsacryptoServiceProvider.Decrypt(rgb3, true); // ... using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider()) { aesCryptoServiceProvider.Key = array; aesCryptoServiceProvider.IV = array2; using (ICryptoTransform cryptoTransform2 = aesCryptoServiceProvider.CreateDecryptor()) { byte[] bytes2 = null; try { byte[] array5 = Convert.FromBase64String(text); bytes2 = cryptoTransform2.TransformFinalBlock(array5, 0, array5.Length); } catch (CryptographicException ex8) { if (ExTraceGlobals.VerboseTracer.IsTraceEnabled(1)) { ExTraceGlobals.VerboseTracer.TraceDebug<CryptographicException>((long)this.GetHashCode(), "[FbaModule::ParseCadataCookies] Received CryptographicException {0} transforming auth", ex8); } httpApplication.Response.AppendToLog("&CryptoError=PossibleSSLCertrolloverMismatch"); return; } catch (FormatException ex9) { if (ExTraceGlobals.VerboseTracer.IsTraceEnabled(1)) { ExTraceGlobals.VerboseTracer.TraceDebug<FormatException>((long)this.GetHashCode(), "[FbaModule::ParseCadataCookies] Received FormatException {0} decoding caData auth", ex9); } httpApplication.Response.AppendToLog("&DecodeError=InvalidCaDataAuthCookie"); return; } string @string = Encoding.Unicode.GetString(bytes2); request.Headers["Authorization"] = @string; } } }It should be noted that since the IV is encrypted with the SSL certificate private key, we can’t recover the first block of the ciphertext through XOR. But it wouldn’t cause any problem for us because the C# internally processes the strings as UTF-16, so the first 12 bytes of the ciphertext must be B\x00a\x00s\x00i\x00c\x00 \x00. With one more Base64 encoding applied, we will only lose the first 1.5 bytes in the username field.
(16−6×2) ÷ 2 × (3/4) = 1.5
The ExploitAs of now, we have a Padding Oracle that allows us to decrypt any user’s cookie. BUT, how can we get the client cookies? Here we find another vulnerability to chain them together.
XSS to Steal Client CookiesWe discover an XSS (CVE-2021-31195) in the CAS Frontend (Yeah, CAS again) to chain together, the root cause of this XSS is relatively easy: Exchange forgets to sanitize the data before printing it out so that we can use the \ to escape from the JSON format and inject arbitrary JavaScript code.
https://exchange/owa/auth/frowny.aspx ?app=people &et=ServerError &esrc=MasterPage &te=\ &refurl=}}};alert(document.domain)//But here comes another question: all the sensitive cookies are protected by the HttpOnly flag, which makes us unable to access the cookies by JavaScript. WHAT SHOULD WE DO?
Bypass the HttpOnlyAs we could execute arbitrary JavaScript on browsers, why don’t we just insert the SSRF cookie we used in ProxyLogon? Once we add this cookie and assign the Backend target value as our malicious server, Exchange will become a proxy between the victims and us. We can then take over all the client’s HTTP static resources and get the protected HttpOnly cookies!
By chaining bugs together, we have an elegant exploit that can steal any user’s cookies by just sending him/her a malicious link. What’s noteworthy is that the XSS here is only helping us to steal the cookie, which means all the decryption processes wouldn’t require any authentication and user interaction. Even if the user closes the browser, it wouldn’t affect our Padding Oracle Attack!
Here is the demonstration video showing how we recover the victim’s password:
HTTP/2 Request Smuggling
ITG18: Operational Security Errors Continue to Plague Sizable Iranian Threat Group
邮箱信息收集
施耐德充电桩漏洞挖掘之旅
大家好,我是BaCde,今天来说一说2020年底针对施耐德充电桩的漏洞挖掘过程。此次挖掘最终实现了通过远程无需用户交互场景下实现Root权限shell获取(一键远程Rootshell获取)。官方已经于今年7月份公布漏洞补丁以及相应的CVE编号。
0x01 为什么选择施耐德?作为车联网安全研究来说,充电桩作为车联网必要组成部分,具备实际的研究价值与意义。而面临如此多的品牌,选择哪个目标作为研究对象是面临的第一个问题。为了能够更快的实现我选择了几个衡量指标,包括官方有响应中心、固件可下载、市面上可以买到、互联网上有暴露的目标。分别对应合法性、静态分析、动态测试、漏洞可产生实际的影响。
根据指标通过网络上去收集信息,最终将目标锁定在施耐德。同时,施耐德也在CVE官方的CNA列表中,报送的漏洞可以获得CVE编号。
0x02 目标设定确定了要研究的对象,接下来就要确定一下我们要实现什么样的效果。这可以使得在分析过程中保持聚焦,不偏离方向。目标设定如下:
- 远程获取设备Root权限
- 无需登录,无需交互
根据上述设定最直接的方式就是寻找远程命令执行漏洞,即要RCE类型漏洞。
0x03 信息收集一切准备就绪,开始我们的漏洞挖掘之旅。