# PicoCTF 2k13 - Broken CBC


# cat cbc_server.py
#!/usr/bin/env python
import os
from Crypto.Cipher import AES
import SocketServer
import threading
import time

#actual key and iv go here...
key = "00000000000000000000000000000000".decode("hex")
iv  = "00000000000000000000000000000000".decode("hex")

def pkcs(msg):
  print msg.encode("hex")
  padding_length = ord(msg[-1])
  padding = msg[-padding_length:]
  print padding.encode("hex")
  if (padding != (chr(padding_length)*padding_length)):
    print (chr(padding_length)*padding_length).encode("hex")
    return None
  return msg[:-padding_length]

def decrypt(cipher,enc):
  print enc,((len(enc) % 16) != 0)
  dec = ""
  if ((len(enc) % 16) != 0):
    return (False,"Error: cipher length must be a multiple of 16\n")
  dec = cipher.decrypt(enc)
  msg = pkcs(dec)
  if msg is None:
    return (False,"Error: incorrect padding\n")
  return (True,msg)

def process(cmd):
  # Message is like HERE_IS_COMMAND:cmd
  # eg "HERE_IS_COMMAND:help"
  
  cmd = cmd[16:] #ignore the COMMAND: part, it's all the same anyhow
  
  if (cmd == "help"):
    return "Commands:\n\thelp - this\n\tflag - prints out the flag\n\tnyan - prints out a nyan cat\n"
  if (cmd == "flag"):
    return "key: XXX TRY TO READ ME XXX"
  if (cmd == "nyan"):
    return """
+      o     +              o   
    +             o     +       +
o          +
    o  +           +        +
+        o     o       +        o
-_-_-_-_-_-_-_,------,      o 
_-_-_-_-_-_-_-|   /\_/\  
-_-_-_-_-_-_-~|__( ^ .^)  +     +  
_-_-_-_-_-_-_-""  ""      
+      o         o   +       o
    +         +
o        o         o      o     +
    o           +
+      +     o        o      +    
"""
  return "Invalid command. See help for a list of commands\n"

class threadedserver(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

class incoming(SocketServer.BaseRequestHandler):
  def handle(self):
    cur_thread = threading.current_thread()
    welcome = """
Enter your encrypted command:
"""
    self.request.send(welcome)
    while True:
      m = self.request.recv(1024)
      cipher = AES.new(key, AES.MODE_CBC, iv)
      success,cmd = decrypt(cipher,m[:-1]) #discard newline
      if (success):
        self.request.send(process(cmd))
      else:
        self.request.send(cmd)

server = threadedserver(("0.0.0.0", 4567), incoming)
server.timeout = 4
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()

server_thread.join()
# cat padding_oracle.py 
#!/usr/bin/python

import socket
from Crypto.Cipher import AES

cipher = []
key = []
plain = list('HERE_IS_COMMAND:flag' + ('\x0c' * 12))
count = 0

def oracle_padding(cipher, result = False):
 s = socket.create_connection(('localhost',4567))
 s.recv(1024)
 data = ''
 for i in xrange(0,32):
  data = data + chr(cipher[i])
 #print str(data)
 s.sendall(data+"\n")
 recv = s.recv(1024)
 #print recv
 if "Error" in recv:
  return False
 else:
  if result:
   print
   print recv
  return True

def print_array(a, t, n):
 result = ''
 for i in xrange(0, n):
  result += '%02x' % a[i]
 print t + ' = ' + result

for i in range(32):
 cipher.append(0)
 key.append(0)


for i in range(16):
 for j in range(256):
  count += 1
  cipher[15 - i] = j
  result = oracle_padding(cipher)
  if result:
   key[15 - i] = (i + 1) ^ j
   print '[' + str(i) + ']'
   print_array(cipher, 'c', 16)
   print_array(key, 'k', 16)
   for z in range(i + 1):
    cipher[15 - z] = (i + 2) ^ key[15 - z]
   break

for i in range(16):
 cipher[i] = key[i] ^ ord(plain[16 + i])
print

print_array(cipher, 'solution', 32)
 
oracle_padding(cipher, True)
print 'Tries = ' + str(count)
# ./padding_oracle.py 
[0]
c = 0000000000000000000000000000003b
k = 0000000000000000000000000000003a
[1]
c = 0000000000000000000000000000ee38
k = 0000000000000000000000000000ec3a
[2]
c = 00000000000000000000000000daef39
k = 00000000000000000000000000d9ec3a
[3]
c = 000000000000000000000000fbdde83e
k = 000000000000000000000000ffd9ec3a
[4]
c = 000000000000000000000012fadce93f
k = 000000000000000000000017ffd9ec3a
[5]
c = 000000000000000000007111f9dfea3c
k = 000000000000000000007717ffd9ec3a
[6]
c = 0000000000000000005f7010f8deeb3d
k = 000000000000000000587717ffd9ec3a
[7]
c = 000000000000000071507f1ff7d1e432
k = 000000000000000079587717ffd9ec3a
[8]
c = 000000000000003470517e1ef6d0e533
k = 000000000000003d79587717ffd9ec3a
[9]
c = 000000000000283773527d1df5d3e630
k = 000000000000223d79587717ffd9ec3a
[10]
c = 0000000000be293672537c1cf4d2e731
k = 0000000000b5223d79587717ffd9ec3a
[11]
c = 000000001db92e3175547b1bf3d5e036
k = 0000000011b5223d79587717ffd9ec3a
[12]
c = 0000001d1cb82f3074557a1af2d4e137
k = 0000001011b5223d79587717ffd9ec3a
[13]
c = 0000011e1fbb2c3377567919f1d7e234
k = 00000f1011b5223d79587717ffd9ec3a
[14]
c = 0000001f1eba2d3276577818f0d6e335
k = 000f0f1011b5223d79587717ffd9ec3a
[15]
c = 041f1f0001a5322d69486707efc9fc2a
k = 140f0f1011b5223d79587717ffd9ec3a

solution = 72636e771db92e3175547b1bf3d5e03600000000000000000000000000000000

key: XXX TRY TO READ ME XXX
Tries = 1466

1 comment:

Blogger said...

If you want your ex-girlfriend or ex-boyfriend to come crawling back to you on their knees (no matter why you broke up) you must watch this video
right away...

(VIDEO) Want your ex CRAWLING back to you...?