source: trunk/bin/bletchley-clonecertchain @ 126

Last change on this file since 126 was 87, checked in by tim, 9 years ago

moved general purpose SSL/TLS and certificate functions to a library module
minor improvements to error reporting

  • Property svn:executable set to *
File size: 4.4 KB
Line 
1#!/usr/bin/env python3
2#-*- mode: Python;-*-
3#
4# Requires Python 3+
5
6
7'''
8An experimental script which attempts to clone a server certificate's entire
9certificate chain, ideally altering only the keys and signatures along the
10way.
11
12This is useful in a few man-in-the-middle attack situations, including:
13- You swap out certificates on a user and the manually inspect the certificate
14  properties before accepting them.  Identical properties are more convincing.
15
16- A product includes special-purpose certificate properties that are validated
17  with custom procedures (e.g. client user name, product serial number, ...). 
18  If these properties are validated but the certificate's CA isn't, then cloning
19  the full set of certificate properties is essential to bypass the
20  authentication.
21
22Currently, this script is somewhat limited and buggy, but will hopefully
23improve over time.  Patches welcome!
24
25
26Copyright (C) 2014,2016 Blindspot Security LLC
27Author: Timothy D. Morgan
28
29 This program is free software: you can redistribute it and/or modify
30 it under the terms of the GNU Lesser General Public License, version 3,
31 as published by the Free Software Foundation.
32
33 This program is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36 GNU General Public License for more details.
37
38 You should have received a copy of the GNU General Public License
39 along with this program.  If not, see <http://www.gnu.org/licenses/>.
40'''
41
42import sys
43import argparse
44import traceback
45import socket
46from bletchley import ssltls
47try:
48    import OpenSSL
49    from OpenSSL import SSL
50except:
51    sys.stderr.write('ERROR: Could not locate pyOpenSSL module.  Under Debian-based systems, try:\n')
52    sys.stderr.write('       # apt-get install python3-openssl\n')
53    sys.stderr.write('NOTE: pyOpenSSL version 0.14 or later is required!\n')
54    sys.exit(2)
55
56
57parser = argparse.ArgumentParser(
58    description="An experimental script which attempts to clone an SSL server's"
59    " entire certificate chain, ideally altering only the keys and signatures"
60    " along the way.  The script prints results to stdout, starting with a PKCS7 (PEM)"
61    " key (the fake server private key) followed by the newly forged certificate"
62    " chain, also in PEM format.  (The new intermediate and root private keys are"
63    " not currently printed, but will likely be somehow available in a future"
64    " version.)")
65
66parser.add_argument('host', nargs=1, default=None,
67                    help='IP address or host name of server')
68parser.add_argument('port', nargs='?', type=int, default=443,
69                    help='TCP port number of SSL service (default: 443)')
70parser.add_argument(
71    '--p12', dest='p12_filename', type=str, required=False, default=None,
72    help='If specified, a PKCS12 file will be written with the generated certificates'
73    ' and server key (in addition to normal PKCS7 output).  NOTE: the file specified'
74    ' will be overwritten without prompting if it already exists.')
75parser.add_argument(
76    '--p12password', dest='p12_password', type=str, required=False, default='bletchley',
77    help='If specified along with the --p12 argument, the PKCS12 file will use this password'
78    ' to encrypt the server private key.  (Otherwise, the password "bletchley" is used).')
79options = parser.parse_args()
80
81#print("REAL CHAIN:")
82connection = ssltls.ConnectSSLTLS(options.host[0],options.port)
83chain = ssltls.fetchCertificateChain(connection)
84#for c in chain:
85#    print(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, c).decode('utf-8'))
86
87#chain = normalizeCertificateChain(chain)
88#for c in chain:
89#    print(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, c).decode('utf-8'))
90
91#print("FAKE KEY AND CHAIN:")
92if not chain:
93    sys.stderr.write("ERROR: Could not retrieve server certificate\n\n")
94    sys.exit(2)
95
96fake_key, fake_chain = ssltls.genFakeCertificateChain(chain)
97print(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, fake_key).decode('utf-8'))
98for c in fake_chain:
99    print(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, c).decode('utf-8'))
100
101if options.p12_filename:
102    p12_file = open(options.p12_filename, 'w+b')
103
104    p12 = OpenSSL.crypto.PKCS12()
105    p12.set_ca_certificates(fake_chain[1:])
106    p12.set_privatekey(fake_key)
107    p12.set_certificate(fake_chain[0])
108
109    p12_file.write(p12.export(passphrase=options.p12_password.encode('utf-8')))
110    p12_file.close()
Note: See TracBrowser for help on using the repository browser.