From d4ed35bba83f0591784a1a8ac0fc8a8ab096c0bb Mon Sep 17 00:00:00 2001 From: Marc <34656315+MarcT512@users.noreply.github.com> Date: Mon, 3 Jun 2019 17:02:39 +0100 Subject: [PATCH 1/3] Fix use after free of ssl object. --- sslscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sslscan.c b/sslscan.c index a5353932..edcd1cc3 100644 --- a/sslscan.c +++ b/sslscan.c @@ -1574,8 +1574,8 @@ int testCipher(struct sslCheckOptions *options, const SSL_METHOD *sslMethod) } else if (cipherStatus != 1) { - SSL_free(ssl); printf_verbose("SSL_get_error(ssl, cipherStatus) said: %d\n", SSL_get_error(ssl, cipherStatus)); + SSL_free(ssl); return false; } From d9422795d72415fe5065f76cb735278b1ba57ccc Mon Sep 17 00:00:00 2001 From: Marc <34656315+MarcT512@users.noreply.github.com> Date: Tue, 4 Jun 2019 18:17:18 +0100 Subject: [PATCH 2/3] RFC: Additional SSL error reporting in verbose mode. Add a function to convert SSL error codes to a string. In addition, get the underlying error from OpenSSL and display it. Before: [...] Accepted TLSv1.0 128 bits AES128-SHA SSL_get_error(ssl, cipherStatus) said: 1 After: [...] Accepted TLSv1.0 128 bits AES128-SHA SSL_get_error(ssl, cipherStatus) returned: 1 (SSL_ERROR_SSL) [sslscan.c:testCipher@1584]:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number --- sslscan.c | 42 +++++++++++++++++++++++++++++++++++++++++- sslscan.h | 1 + 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sslscan.c b/sslscan.c index edcd1cc3..4c1cb4d0 100644 --- a/sslscan.c +++ b/sslscan.c @@ -1574,7 +1574,18 @@ int testCipher(struct sslCheckOptions *options, const SSL_METHOD *sslMethod) } else if (cipherStatus != 1) { - printf_verbose("SSL_get_error(ssl, cipherStatus) said: %d\n", SSL_get_error(ssl, cipherStatus)); + tempInt = SSL_get_error(ssl, cipherStatus); + printf_verbose("SSL_get_error(ssl, cipherStatus) returned: %d (%s)\n", tempInt, SSL_ERR_to_string(tempInt)); + + // I'd rather use ERR_print_errors(BIO) instead of this loop, but it needs a BIO for stdout/stderr which we + // don't have yet. + while (ERR_peek_error() > 0) + { + printf_verbose("[%s:%s@%d]:%s\n", __FILE__, __func__, __LINE__, ERR_error_string(ERR_peek_error(), NULL)); + // Dequeue the error, since we only peeked at it. Can't put this in the line above or we'll loop + // forever when not in verbose mode. + ERR_get_error(); + } SSL_free(ssl); return false; } @@ -3421,6 +3432,35 @@ int testHost(struct sslCheckOptions *options) return status; } +// Return a string description of an SSL error. +// It would be nice if there were a standard function for this... +const char *SSL_ERR_to_string (int sslerr) +{ + switch (sslerr) + { + // Values taken from openssl/ssl.h + case SSL_ERROR_NONE: + return "SSL_ERROR_NONE"; + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + case SSL_ERROR_WANT_READ: + return "SSL_ERROR_WANT_READ"; + case SSL_ERROR_WANT_WRITE: + return "SSL_ERROR_WANT_WRITE"; + case SSL_ERROR_WANT_X509_LOOKUP: + return "SSL_ERROR_WANT_X509_LOOKUP"; + case SSL_ERROR_SYSCALL: + return "SSL_ERROR_SYSCALL"; + case SSL_ERROR_ZERO_RETURN: + return "SSL_ERROR_ZERO_RETURN"; + case SSL_ERROR_WANT_CONNECT: + return "SSL_ERROR_WANT_CONNECT"; + case SSL_ERROR_WANT_ACCEPT: + return "SSL_ERROR_WANT_ACCEPT"; + default: + return "SSL_ERROR_UNKNOWN"; + } +} int main(int argc, char *argv[]) { diff --git a/sslscan.h b/sslscan.h index 0f37fe23..be9a63ee 100644 --- a/sslscan.h +++ b/sslscan.h @@ -193,6 +193,7 @@ void readLine(FILE *, char *, int); ssize_t sendString(int, const char[]); int readOrLogAndClose(int, void *, size_t, const struct sslCheckOptions *); const char *printableSslMethod(const SSL_METHOD *); +const char *SSL_ERR_to_string (int sslerr); static int password_callback(char *, int, int, void *); int ssl_print_tmp_key(struct sslCheckOptions *, SSL *s); static int ocsp_resp_cb(SSL *s, void *arg); From 465ed4ed1436f5e293f19f03f22b97c49a219d4b Mon Sep 17 00:00:00 2001 From: Marc <34656315+MarcT512@users.noreply.github.com> Date: Wed, 10 Jul 2019 23:09:09 +0100 Subject: [PATCH 3/3] RFC: Experimental support for servers which require a client certificate RFC: Experimental support for servers which require a client certificate (Fixes #119). Fix: Typo s/response/respond in "Some servers will fail to response to SSLv3 ciphers over STARTTLS" Fix: Logic error prevents show trusted CAs running with checkCertificate == true. RFC patch to enable scanning of servers which require a client certificate. How: Allow tests to continue in the event the SSL_connect() fails with certain "acceptable" errors . These are: SSL alert 40 (Handshake failure) SSL alert 46 (Certificate Unknown) SSL alert 42 (Bad Certificate) Testing is encouraged. Unfortunately I cannot provide any public test cases. --- sslscan.c | 59 +++++++++++++++++++++++++++++++++++++++++++------------ sslscan.h | 1 + 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/sslscan.c b/sslscan.c index 1790071d..f67fb549 100644 --- a/sslscan.c +++ b/sslscan.c @@ -1013,6 +1013,10 @@ int testFallback(struct sslCheckOptions *options, const SSL_METHOD *sslMethod) // Connect SSL over socket connStatus = SSL_connect(ssl); + + // We can't check for "acceptable" errors for "client cert required" here, since SSL Alert 40 (Handshake failure) + // can be reported for either client cert required, or downgrade failure. So it's not possible to distinguish + // between them. if (connStatus) { if (!downgraded) @@ -1062,8 +1066,8 @@ int testFallback(struct sslCheckOptions *options, const SSL_METHOD *sslMethod) } else { - printf("%sConnection failed%s - unable to determine TLS Fallback SCSV support\n\n", - COL_YELLOW, RESET); + printf("%sConnection failed%s - unable to determine TLS Fallback SCSV support%s.\n\n", + COL_YELLOW, RESET, acceptableError() ? " - Server requires a client cert":""); status = false; } } @@ -1198,7 +1202,7 @@ int testRenegotiation(struct sslCheckOptions *options, const SSL_METHOD *sslMeth } - if (cipherStatus == 1) + if (cipherStatus == 1 || acceptableError()); { #if ( OPENSSL_VERSION_NUMBER > 0x009080cfL ) @@ -1564,13 +1568,16 @@ int testCipher(struct sslCheckOptions *options, const SSL_METHOD *sslMethod) // Connect SSL over socket cipherStatus = SSL_connect(ssl); - sslCipherPointer = SSL_get_current_cipher(ssl); - cipherbits = SSL_CIPHER_get_bits(sslCipherPointer, NULL); - if (cipherStatus == 0) { - SSL_free(ssl); - return false; + // An "acceptable" error is eg "client certificate required". We can still produce a list of supported ciphers. + if (acceptableError()) + cipherStatus = 1; + else + { + SSL_free(ssl); + return false; + } } else if (cipherStatus != 1) { @@ -1590,6 +1597,15 @@ int testCipher(struct sslCheckOptions *options, const SSL_METHOD *sslMethod) return false; } + sslCipherPointer = SSL_get_current_cipher(ssl); + // If sslCipherPointer is NULL, SSL_CIPHER_get_bits() will segfault + if (sslCipherPointer == NULL) + { + SSL_free(ssl); + return false; + } + cipherbits = SSL_CIPHER_get_bits(sslCipherPointer, NULL); + cipherid = SSL_CIPHER_get_id(sslCipherPointer); cipherid = cipherid & 0x00ffffff; // remove first byte which is the version (0x03 for TLSv1/SSLv3) @@ -1879,7 +1895,7 @@ int checkCertificate(struct sslCheckOptions *options, const SSL_METHOD *sslMetho // Connect SSL over socket cipherStatus = SSL_connect(ssl); - if (cipherStatus == 1) + if (cipherStatus == 1 || acceptableError()) { // Setup BIO's if (!xml_to_stdout) { @@ -2299,7 +2315,7 @@ int ocspRequest(struct sslCheckOptions *options) // Connect SSL over socket cipherStatus = SSL_connect(ssl); - if (cipherStatus == 1) + if (cipherStatus == 1 || acceptableError()) { // Setup BIO's if (!xml_to_stdout) { @@ -2568,7 +2584,7 @@ int showCertificate(struct sslCheckOptions *options) // Connect SSL over socket cipherStatus = SSL_connect(ssl); - if (cipherStatus == 1) + if (cipherStatus == 1 || acceptableError()) { // Setup BIO's if (!xml_to_stdout) { @@ -3011,7 +3027,7 @@ int showTrustedCAs(struct sslCheckOptions *options) // Connect SSL over socket cipherStatus = SSL_connect(ssl); - if (cipherStatus >= 0) + if (cipherStatus >= 0 || acceptableError()) { // Setup BIO's if (!xml_to_stdout) { @@ -3213,7 +3229,7 @@ int testHost(struct sslCheckOptions *options) // Verbose warning about STARTTLS and SSLv3 if (options->sslVersion == ssl_v3 || options->sslVersion == ssl_all) { - printf_verbose("Some servers will fail to response to SSLv3 ciphers over STARTTLS\nIf your scan hangs, try using the --tlsall option\n\n"); + printf_verbose("Some servers will fail to respond to SSLv3 ciphers over STARTTLS\nIf your scan hangs, try using the --tlsall option\n\n"); } printf("Testing SSL server %s%s%s on port %s%d%s using SNI name %s%s%s\n\n", COL_GREEN, options->host, RESET, @@ -3421,6 +3437,9 @@ int testHost(struct sslCheckOptions *options) if (status != false) status = checkCertificateProtocol(options, SSLv2_client_method()); #endif + // Reset status to true, otherwise show trusted CAs below can never run + // with the default checkCertificate == true + status = true; } // Print client auth trusted CAs @@ -3466,6 +3485,20 @@ const char *SSL_ERR_to_string (int sslerr) } } +// Define a list of "acceptable" SSL errors which might indicate a client certificate is required, and continue anyway. +static inline int acceptableError (void) +{ + // Error 0x14094410 = SSL Alert 40 = Handshake Failure, probably because Server requested client Cert, which we'll treat as a "success". + // Error 0x14094416 = SSL Alert 46 = Certificate Unknown, treat as "success". + // Error 0x14094412 = SSL Alert 42 = Bad Certificate - probably means Server requested client Cert. Will also send a list of Acceptable client certificate CA names. + // SSL Alerts documented here: https://tools.ietf.org/html/rfc5246#section-7.2 + + if (ERR_peek_error() == 0x14094410L || ERR_peek_error() == 0x14094416L || ERR_peek_error() == 0x14094412L) + return true; + + return false; +} + int main(int argc, char *argv[]) { // Variables... diff --git a/sslscan.h b/sslscan.h index be9a63ee..289d6853 100644 --- a/sslscan.h +++ b/sslscan.h @@ -198,6 +198,7 @@ static int password_callback(char *, int, int, void *); int ssl_print_tmp_key(struct sslCheckOptions *, SSL *s); static int ocsp_resp_cb(SSL *s, void *arg); int ocsp_certid_print(BIO *bp, OCSP_CERTID *a, int indent); +static inline int acceptableError (void); int tcpConnect(struct sslCheckOptions *);