{"id":377,"date":"2013-04-21T19:51:59","date_gmt":"2013-04-22T00:51:59","guid":{"rendered":"http:\/\/www.rajatswarup.com\/blog\/?p=377"},"modified":"2013-04-21T19:51:59","modified_gmt":"2013-04-22T00:51:59","slug":"plaidctf-2013-crypto-250-compression-writeup","status":"publish","type":"post","link":"https:\/\/www.rajatswarup.com\/blog\/2013\/04\/21\/plaidctf-2013-crypto-250-compression-writeup\/","title":{"rendered":"PlaidCTF 2013 &#8211; Crypto 250 Compression Writeup"},"content":{"rendered":"<p>On the recently concluded PlaidCTF (which was an awesome competition) by PPP there was a problem.\u00a0 Here it goes:<\/p>\n<blockquote><p><b>Question<\/b>: We managed to get the source code for an encryption service running at 54.234.224.216:4433.<\/p><\/blockquote>\n<p>I have listed the python source provided below:<\/p>\n<pre>#!\/usr\/bin\/python\r\nimport os\r\nimport struct\r\nimport SocketServer\r\nimport zlib\r\nfrom Crypto.Cipher import AES\r\nfrom Crypto.Util import Counter\r\n\r\n# Not the real keys!\r\nENCRYPT_KEY = '0000000000000000000000000000000000000000000000000000000000000000'.decode('hex')\r\n# Determine this key.\r\n# Character set: lowercase letters and underscore\r\nPROBLEM_KEY = 'XXXXXXXXXXXXXXXXXXXX'\r\n\r\ndef encrypt(data, ctr):\r\n    aes = AES.new(ENCRYPT_KEY, AES.MODE_CTR, counter=ctr)\r\n    return aes.encrypt(zlib.compress(data))\r\n\r\nclass ProblemHandler(SocketServer.StreamRequestHandler):\r\n    def handle(self):\r\n        nonce = os.urandom(8)\r\n        self.wfile.write(nonce)\r\n        ctr = Counter.new(64, prefix=nonce)\r\n        while True:\r\n            data = self.rfile.read(4)\r\n            if not data:\r\n                break\r\n\r\n            try:\r\n                length = struct.unpack('I', data)[0]\r\n                if length &gt; (1&lt;&lt;20):\r\n                    break\r\n                data = self.rfile.read(length)\r\n                data += PROBLEM_KEY\r\n                ciphertext = encrypt(data, ctr)\r\n                self.wfile.write(struct.pack('I', len(ciphertext)))\r\n                self.wfile.write(ciphertext)\r\n            except:\r\n                break\r\n\r\nclass ReusableTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):\r\n    allow_reuse_address = True\r\n\r\nif __name__ == '__main__':\r\n    HOST = '0.0.0.0'\r\n    PORT = 4433\r\n    SocketServer.TCPServer.allow_reuse_address = True\r\n    server = ReusableTCPServer((HOST, PORT), ProblemHandler)\r\n    server.serve_forever()<\/pre>\n<p>The key on this challenge is to see that the stream encryption is being done on the <b>compressed input<\/b>. In the source provided, if the user input is similar to the secret value in the PROBLEM_DATA variable then the zlib.compress() function would show a reduced length ciphertext. This is somewhat (and I use the term loosely) similar to the CRIME vulnerability. The <a href=\"http:\/\/tools.ietf.org\/html\/rfc3686#section-2.1\">AES Counter mode RFC<\/a> has the implementation details of the cipher.  So I wrote the following script.  <\/p>\n<pre>import socket\r\nimport sys\r\nfrom itertools import *\r\nimport struct\r\ndef display(msg,numbytes):\r\n\t#print >>sys.stderr, 'received \"%s\"' % msg\r\n\t#print >>sys.stderr, 'bytes \"%d\"' % numbytes\r\n\tprint >>sys.stderr, 'bytes %d ' % numbytes + msg.encode('hex')\r\n# Create a TCP\/IP socket\r\nsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\r\n# Connect the socket to the port where the server is listening\r\nserver_address = ('54.234.224.216', 4433)\r\nprint >>sys.stderr, 'connecting to %s port %s' % server_address\r\nsock.connect(server_address)\r\n#mesage len = 20 lowercase and underscore letters\r\ntry:\r\n\tamount_received = 0\r\n\tnonce = sock.recv(8)\r\n\tamount_received += len(nonce)\r\n\t# Send data\r\n\t#strng = 'crime_some'\r\n\t#minciphlen = 1000\r\n\t#strng = 'crimes_pays'\r\n\t#strng = 'so_'\r\n\t#strng = 'crime_some_times_pays'\r\n\t#strng = 'somet_'\r\n\tstrng = 'cr'\r\n\tminchar = ''\r\n\tciphlen = 1000\r\n\tsampleset = 'hijklmnopqrstuvwxyz_abdefgc'\r\n\t#while True:\r\n\tstrng = strng + minchar\t\r\n\tminciphlen = ciphlen\r\n\tminchar = ''\r\n\tfor s in map(\"\".join,permutations(sampleset,1)):\r\n\t\t#message = nonce +  (strng + s)*10  #'\\x00'*11 + s\r\n\t\tmessage = strng + s\r\n\t\tdatalen = struct.pack('I',len(message))  # datalen = '\\xe4\\x00\\x00\\x00'\r\n\t\tsock.sendall(datalen)\r\n\t\t#print >>sys.stderr, 'sending '+ message\r\n\t\tsock.sendall(message)\r\n\t\t#print >>sys.stderr, 'message sent'\r\n\t\tamount_received = 0\r\n\t\t# Look for the response\r\n\t\tdata = sock.recv(4)\r\n\t\tamount_received += len(data)\r\n\t\tciphlen = struct.unpack('I', data)[0]\r\n\t\t#print >>sys.stderr, message + ' ' \r\n\t\tamount_received = 0\r\n\t\tif ciphlen <= minciphlen:\r\n\t\t\tminciphlen = ciphlen\r\n\t\t\tminchar = s\r\n\t\t\tprint str(ciphlen) + ' It is ' + strng + minchar\r\n\t\tdata = sock.recv(ciphlen)\r\n\t\t#display(data,ciphlen)\t\t\r\nfinally:\r\n    print >>sys.stderr, 'closing socket'\r\n    sock.close()<\/pre>\n<p>When you connect to the service it provides you the nonce, so I prepended the nonce to the plaintext. The above script shows the plaintext and the length of the cipher text.  To start off with this, you start with a string of length 1, and see which is the smallest length response, that gives your first character.  Then in the <\/p>\n<pre>strng<\/pre>\n<p> variable above, you add that character and run again, and the lowest length ciphertext tells you the next character and so on.  I noticed that sometimes the output had a few characters with the lowest length.   So I tried each of them and ended up with the following flag: <\/p>\n<pre>crime_sometimes_pays <\/pre>\n","protected":false},"excerpt":{"rendered":"<p>On the recently concluded PlaidCTF (which was an awesome competition) by PPP there was a problem.\u00a0 Here it goes: Question: We managed to get the source code for an encryption service running at 54.234.224.216:4433. I have listed the python source provided below: #!\/usr\/bin\/python import os import struct import SocketServer import zlib from Crypto.Cipher import AES [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[195,196],"tags":[222,456],"class_list":["post-377","post","type-post","status-publish","format-standard","hentry","category-crypto","category-programming","tag-ctf","tag-plaidctf"],"_links":{"self":[{"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/posts\/377","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/comments?post=377"}],"version-history":[{"count":1,"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/posts\/377\/revisions"}],"predecessor-version":[{"id":378,"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/posts\/377\/revisions\/378"}],"wp:attachment":[{"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/media?parent=377"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/categories?post=377"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rajatswarup.com\/blog\/wp-json\/wp\/v2\/tags?post=377"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}