0% found this document useful (0 votes)
17 views121 pages

Siphashdos Appsec12 Slides

The document discusses hash-flooding denial of service attacks against systems using hash tables. It analyzes the MurmurHash3 algorithm and demonstrates how to generate multicollisions to force worst-case insertion times and consume server resources. The attack remains viable against other popular hash functions like CityHash64 as well.

Uploaded by

mewax37732
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views121 pages

Siphashdos Appsec12 Slides

The document discusses hash-flooding denial of service attacks against systems using hash tables. It analyzes the MurmurHash3 algorithm and demonstrates how to generate multicollisions to force worst-case insertion times and consume server resources. The attack remains viable against other popular hash functions like CityHash64 as well.

Uploaded by

mewax37732
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Hash-flooding DoS reloaded:

attacks and defenses


Jean-Philippe Aumasson, Kudelski Group
Daniel J. Bernstein, University of Illinois at Chicago
Martin Boßlet, freelancer
Hash-flooding DoS reloaded:
attacks and defenses
Jean-Philippe Aumasson, Kudelski Group
Daniel J. Bernstein, University of Illinois at Chicago
Martin Boßlet, freelancer
Jean-Philippe
Cryptography expert at the Kudelski Group
Applied crypto researcher
[Link] @aumasson

Martin
Independent SW engineer and security expert
Ruby core dev team member
[Link] @_emboss_
Hash-flooding DoS reloaded:
attacks and defenses
Denial-of-Service (DoS) attacks

“Attempt to make a machine or network


resource unavailable to its intended users.”
Wikipedia
Popular DoS techniques are distributed
HTTP or TCP SYN flood… (DDoS)
More subtle techniques exploit properties
of TCP-congestion-avoidance algorithms…
Hash-flooding DoS reloaded:
attacks and defenses
Hash tables used in many applications to
maintain an association between objects

Example: Python dictionaries


d={} # empty table
d[12345]=0xc # insertion
d[‘astring’]=‘foo’ # insertion
d[(‘a’,‘tuple’)]=0 # insertion
print d[‘a string’] # lookup
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n) operations on average
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n) operations on average
0 1 2
12345: 0xc

d[12345]=0xc, hash(12345)=1
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n) operations on average
0 1 2
‘astring’: ‘foo’ 12345: 0xc

d[‘astring’]=‘foo’ , hash(‘astring’)=0
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n) operations on average
0 1 2
‘astring’: ‘foo’ 12345: 0xc (‘a’,’tuple’): 0

d[(‘a’,’tuple’)=0; hash((‘a’,’tuple’))=2
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n2) operations in the worst case
0 1 2
12345: 0xc

d[12345]=0xc, hash(12345)=1
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n2) operations in the worst case
0 1 2
12345: 0xc

‘astring’: ‘foo’

d[‘astring’]=‘foo’ , hash(‘astring’)=0
If the table is about as large as the
number of elements to be stored (=n),
insertion or lookup of n elements takes
O(n2) operations in the worst case
0 1 2
12345: 0xc

‘astring’: ‘foo’

(‘a’, ‘tuple’): ‘foo’

d[(‘a’,’tuple’)=0; hash((‘a’,’tuple’))=2
Hash flooding:
Send to a server many inputs with a
same hash (a multicollision) so as to
enforce worst-case insert time
send 2MB of POST data consisting of
200.000 colliding 10B strings

≈ [Link] string comparisons


(at least 10s on a 2GHz machine…)
Previous work

Crosby, Wallach. Denial of Service via Algorithmic


Complexity Attacks, USENIX Security 2003
-> attack formalized and applied to Perl, Squid, etc.

Klink, Wälde. Efficient Denial of Service Attacks on


Web Application Platforms. CCC 28c3
-> application to PHP, Java, Python, Ruby, etc.
Previous work

Crosby, Wallach. Denial of Service via Algorithmic


Complexity Attacks, USENIX Security 2003
-> attack formalized and applied to Perl, Squid, etc.

Klink, Wälde. Efficient Denial of Service Attacks on


Web Application Platforms. CCC 28c3
-> application to PHP, Java, Python, Ruby, etc.
Patches released consisting of a
stronger hash with randomization
(to make colliding values impossible to find)
MurmurHash2
“used in code by Google, Microsoft,
Yahoo, and many others”
[Link]

CRuby, JRuby
MurmurHash3

“successor to MurmurHash2”

Oracle’s Java SE, Rubinius


Hash-flooding DoS reloaded:
attacks and defenses
1. Theory
MurmurHash3 core

Processes the input per blocks of 4 bytes

for (i=0;i<nblocks;i++) {
uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ;
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593;

h1 ^= k1;
h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) {
uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ;
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593;

h1 ^= k1;
h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) { i=0


uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ; inject difference D1
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593; diff in k1:0x00040000

h1 ^= k1;
h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) { i=0


uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ; inject difference D1
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593; diff in k1:0x00040000

h1 ^= k1; diff in h1 0x00040000


h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) { i=0


uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ; inject difference D1
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593; diff in k1:0x00040000

h1 ^= k1; diff in h1 0x00040000


h1 = ROTL32 ( h1 ,13); 0x80000000
h1 = h1 *5+0 xe6546b64;} 0x80000000
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) { i=1


uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ; inject difference D2
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593;

h1 ^= k1;
h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) { i=1


uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ; inject difference D2
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593; diff in k1:0x80000000

h1 ^= k1;
h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Differential cryptanalysis strategy
1/ introduce a difference in the state h1 via the input k1
2/ cancel this difference with a second well chosen difference

for (i=0;i<nblocks;i++) { i=1


uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ; inject difference D2
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593; diff in k1:0x80000000
diff in h1: 0x80000000 ^ 0x80000000 = 0
h1 ^= k1;
h1 = ROTL32 ( h1 ,13); COLLISION!
h1 = h1 *5+0 xe6546b64;}
M1 M2

32-bit

32-bit h1=f(X)=H
h1=X

M1^D1 M2^D2

32-bit

32-bit
h1=f(X^D3^D3)=H
h1=X^D3

2 colliding 8-byte inputs


M1 M2 M3 M4 M5 M6

collision collision collision

Chain collisions => multicollisions


n
8n bytes => 2 colliding inputs
A multicollision works for any seed

=> “Universal” multicollisions


h1=seed;
for (i=0;i<nblocks;i++) {
uint32_t k1 = getblock(blocks, i);
k1 *= 0xcc9e2d51 ;
k1 = ROTL32(k1 ,15);
k1 *= 0x1b873593;
// transform of k1 independent of the seed!
h1 ^= k1;
h1 = ROTL32 ( h1 ,13);
h1 = h1 *5+0 xe6546b64;}
Even simpler for MurmurHash2
Consequence:
Systems using MurmurHash2/3 remain
vulnerable to hash-flooding
Other hash attacked
Even weaker than MurmurHash2…
Also vulnerable to hash flooding
CityHash64( BU9[85WWp/ HASH!, 16 ) = b82e7612e6933d2f
CityHash64( 8{YDLn;d.2 HASH!, 16 ) = b82e7612e6933d2f
CityHash64( d+nkK&t?yr HASH!, 16 ) = b82e7612e6933d2f
CityHash64( {A.#v5i]V{ HASH!, 16 ) = b82e7612e6933d2f
CityHash64( FBC=/\hJeA!HASH!, 16 ) = b82e7612e6933d2f
CityHash64( $03$=K1.-H!HASH!, 16 ) = b82e7612e6933d2f
CityHash64( 3o'L'Piw\\!HASH!, 16 ) = b82e7612e6933d2f
CityHash64( duDu%qaUS@"HASH!, 16 ) = b82e7612e6933d2f
CityHash64( IZVo|0S=BX"HASH!, 16 ) = b82e7612e6933d2f
CityHash64( X2V|P=<u,=#HASH!, 16 ) = b82e7612e6933d2f
CityHash64( 9<%45yG]qG#HASH!, 16 ) = b82e7612e6933d2f
CityHash64( 6?4O:'<Vho#HASH!, 16 ) = b82e7612e6933d2f
CityHash64( 2u 2}7g^>3$HASH!, 16 ) = b82e7612e6933d2f
CityHash64( kqwnZH=cKG$HASH!, 16 ) = b82e7612e6933d2f
CityHash64( Nl+:rtvw}K$HASH!, 16 ) = b82e7612e6933d2f
CityHash64( s/pI!<5u*]$HASH!, 16 ) = b82e7612e6933d2f
CityHash64( f|P~n*<xPc$HASH!, 16 ) = b82e7612e6933d2f
CityHash64( Cj7TCG|G}}$HASH!, 16 ) = b82e7612e6933d2f
CityHash64( a4$>Jf3PF'%HASH!, 16 ) = b82e7612e6933d2f
2. Practice
Breaking Murmur:
We‘ve got the recipe –
Now all we need is the (hash) cake
Where are hashes used?
Internally vs. Externally
Parser symbol tables
Method lookup tables
Attributes / Instance variables
IP Addresses
Transaction IDs
Database Indexing
Session IDs
HTTP Headers
JSON Representation
URL-encoded POST form data
Deduplication (HashSet)
A* search algorithm
Dictionaries

=> Where aren’t they used?
Can’t we use something different?
We could,

but amortized constant time is just too sexy


Possible real-life attacks
Attack internal use?
Elegant, but low impact
Need a high-profile target
Web Application
Example #1
Rails
First:
Attacking MurmurHash in Ruby
Straight-forward with a few quirks
Apply the recipe
Demo
Should work with Rails
out of the box, no?
Unfortunately, no
Demo
def POST

@env["[Link].form_hash"] = parse_query(form_vars)

end
def parse_query(qs)

Utils.parse_nested_query(qs)

end
def parse_nested_query(qs, d = nil)

params = [Link]

(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|

k, v = [Link]('=', 2).map { |s| unescape(s) }


normalize_params(params, k, v)

end

return params.to_params_hash
end
def unescape(s, encoding = Encoding::UTF_8)

URI.decode_www_form_component(s, encoding)

end
def self.decode_www_form_component(str, enc=Encoding::UTF_8)

raise ArgumentError, "invalid %-encoding (#{str})"


unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str

[Link](/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)

end
/\A[^%]*(?:%\h\h[^%]*)*\z/
???
Catches invalid % encodings
(e.g. %ZV, %%1 instead of %2F)
def parse_nested_query(qs, d = nil)

params = [Link]

(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|

k, v = [Link]('=', 2).map { |s| unescape(s) }


normalize_params(params, k, v)

end

return params.to_params_hash
end
def normalize_params(params, name, v = nil)

name =~ %r(\A[\[\]]*([^\[\]]+)\]*)

k = $1 || ''


end
%r(\A[\[\]]*([^\[\]]+)\]*)
???
helps transform [[]] to []
idea:
pre-generate matching values
create random values
passing the regular expressions
that should do it, right?
Demo
def parse_nested_query(qs, d = nil)

params = [Link]

(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|

k, v = [Link]('=', 2).map { |s| unescape(s) }


normalize_params(params, k, v)

end

return params.to_params_hash
end
class KeySpaceConstrainedParams

def []=(key, value)

@size += [Link] if key && !@[Link]?(key)

raise RangeError, 'exceeded available parameter key space‘


if @size > @limit

@params[key] = value

end

end
What now? Rails is safe?
Remember:

Hashes are used everywhere


So if
application/x-www-form-urlencoded
doesn't work, how about
application/json
?
Again, with the encoding...
Fast-forward...
Demo
Conclusion

Patchwork is not helping


too many places
code bloat
yet another loophole will be found
Fix it
at the
root
Example #2
Java
String(byte[] bytes)
public String(byte bytes[], int offset, int length,
Charset charset) {

char[] v = [Link](charset, bytes, offset, length);

}
Tough nut to crack
What now? Java is safe?
String(char[] value)
public String(char value[]) {

int size = [Link];


[Link] = 0;
[Link] = size;
[Link] = [Link](value, size);

}
No decoding!
Substitute byte[] operations
with equivalent operations
on char[]
Demo
Disclosure
Oracle (Java): Sep 11
CRuby, JRuby, Rubinius: Aug 30
Hash-flooding DoS reloaded:
attacks and defenses
SipHash: a fast short-input PRF

New crypto algorithm to fix hash-flooding:


• Rigorous security requirements and analysis
• Speed competitive with that of weak hashes

Peer-reviewed research paper (A., Bernstein).


published at DIAC 2012, INDOCRYPT 2012
SipHash initialization
256-bit state v0 v1 v2 v3
128-bit key k0 k1

v0 = k0 ⊕ 736f6d6570736575
v1 = k1 ⊕ 646f72616e646f6d
v2 = k0 ⊕ 6c7967656e657261
v3 = k1 ⊕ 7465646279746573
SipHash initialization
256-bit state v0 v1 v2 v3
128-bit key k0 k1

v0 = k0 ⊕ “somepseu”
v1 = k1 ⊕ “dorandom”
v2 = k0 ⊕ “lygenera”
v3 = k1 ⊕ “tedbytes”
SipHash compression
Message parsed as 64-bit words m0, m1, …

v3 ⊕= m0
c iterations of SipRound
v0 ⊕= m0
SipHash compression
Message parsed as 64-bit words m0, m1, …

v3 ⊕= m1
c iterations of SipRound
v0 ⊕= m1
SipHash compression
Message parsed as 64-bit words m0, m1, …

v3 ⊕= m2
c iterations of SipRound
v0 ⊕= m2
SipHash compression
Message parsed as 64-bit words m0, m1, …

Etc.
SipRound
SipHash finalization

v2 ⊕= 255
d iterations of SipRound
Return v0 ⊕ v1 ⊕ v2 ⊕ v3
SipHash-2-4 hashing 15 bytes
Family SipHash-c-d
Fast proposal: SipHash-2-4
Conservative proposal: SipHash-4-8

Weaker versions for cryptanalysis:


SipHash-1-0, SipHash-2-0, etc.
SipHash-1-1, SipHash-2-1, etc.
Etc.
Proof of simplicity
June 20: paper published online
June 28: 18 third-party implementations

C (Floodyberry, Boßlet, Neves); C# (Haynes)


Cryptol (Lazar); Erlang, Javascript, PHP (Denis)
Go (Chestnykh); Haskell (Hanquez)
Java, Ruby (Boßlet); Lisp (Brown); Perl6 (Julin)
Who is using SipHash?

[Link] [Link]

Soon?
Take home message

• DoS is doable with only small data/bandwidth

• Java- and Ruby-based web applications


vulnerable to DoS (and maybe others…)

• SipHash offers both security and performance

Contact us if you need to check your application


Hash-flooding DoS reloaded:
attacks and defenses

THANK YOU!

You might also like