libressl / openbsd

Source code pulled from OpenBSD for LibreSSL - this includes most of the library and supporting code. The place to contribute to this code is via the OpenBSD CVS tree. Please mail patches to tech@openbsd.org, instead of submitting pull requests, since this tree is often rebased.
230 stars 92 forks source link

stack-buffer-overflow(max 5byte) in print_bin() when indent is specified as 124 or more #145

Closed masanore closed 10 months ago

masanore commented 10 months ago

First of all, this is a stack buffer overflow, but it is quite difficult to exploit because the fixed value ' ' (0x20) is written up to 5 bytes. Also, since it is necessary to specify 124 or more for indent, the range of influence is small. I simply found it by chance, so I'd like to report it. (Coverity discovered this by chance when I added a variable to print_bin() locally)

The symptom is that when calling ECPKParameters_print() with an explicit curve ecparam which has seed and with indent 124 or higher, a stack-buffer-overflow of up to 5 bytes occurs.

The same symptoms also occur with APIs that internally call ECPKParameters_print(), such as EVP_PKEY_print_params().

The cause is in print_bin of eck_prn.c. In line 342, the memset of size off+4 is performed from the position of str[1], so the variable off must be less than sizeof(str) - 4 -1, that is, less than 123. However, line 330 checks that off is less than or equal to 128 (=sizeof(str)). Therefore, if a value of 124 or more is specified for the argument off, a stack buffer overflow of up to 5 bytes will occur. https://github.com/libressl/openbsd/blob/17c60e5b8955deb5786d3f1577b5532478186585/src/lib/libcrypto/ec/eck_prn.c#L325-L342

It seems that the value to be checked needs to be appropriate. For example, as below.

diff --git a/src/lib/libcrypto/ec/eck_prn.c b/src/lib/libcrypto/ec/eck_prn.c
index 6e89bfa73..cdf19eb8f 100644
--- a/src/lib/libcrypto/ec/eck_prn.c
+++ b/src/lib/libcrypto/ec/eck_prn.c
@@ -327,8 +327,8 @@ print_bin(BIO *fp, const char *name, const unsigned char *buf,
        if (buf == NULL)
                return 1;
        if (off) {
-               if (off > 128)
-                       off = 128;
+               if (off > sizeof(str) - 4 - 1)
+                       off = sizeof(str) - 4 - 1;
                memset(str, ' ', off);
                if (BIO_write(fp, str, off) <= 0)
                        return 0;

print_bin is called when displaying the seed in ecpk_print_explicit_parameters(). Therefore, the influence is limited to when the parameter has a seed. https://github.com/libressl/openbsd/blob/17c60e5b8955deb5786d3f1577b5532478186585/src/lib/libcrypto/ec/eck_prn.c#L293

ecpk_print_explicit_parameters() is called when ECPKParameters_print() passes an EC_GROUP without asn1_flag set. And asn1_flag is 0 when it is an explicit curve. Therefore, the effect is limited to explicit curves only. https://github.com/libressl/openbsd/blob/17c60e5b8955deb5786d3f1577b5532478186585/src/lib/libcrypto/ec/eck_prn.c#L313-L316

How to reproduce

The quickest way to create parameters is to specify explicit flag with a named curve as shown below. Please note that you need to choose a curve that has a seed parameter. $ openssl ecparam -param_enc explicit -name secp384r1 -genkey -out explicit.key -outform PEM

If you pass the parameters created in this way to ECPKParameters_print() with an indent of 124 or higher, it will be reproduced. The reproduction program is as follows.

main.c

#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/ec.h>
#include <openssl/pem.h>

#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/ec.h>
#include <openssl/pem.h>

// created with "openssl ecparam  -param_enc explicit -name secp384r1 -outform PEM"
// Need to choose a curve which has a seed
static char *ecparam_pem=
"-----BEGIN EC PARAMETERS-----\n"
"MIIBVwIBATA8BgcqhkjOPQEBAjEA////////////////////////////////////\n"
"//////7/////AAAAAAAAAAD/////MHsEMP//////////////////////////////\n"
"///////////+/////wAAAAAAAAAA/////AQwszEvp+I+5+SYjgVr4/gtGRgdnG7+\n"
"gUESAxQIj1ATh1rGVjmNii7RnSqFyO3T7CrvAxUAozWSaqMZonodAIlqZ3OkgnrN\n"
"rHMEYQSqh8oivosFN46xxx7zIK10bh07Younm5hZ90HgglQqOFUC8l2/VSlsOlRe\n"
"OHJ2Crc2F95KliYsb12emL+Sktwp+PQdvSiaFHzp2jETtfC4wApgsc4dfoGdekMd\n"
"fJDqDl8CMQD////////////////////////////////HY02B9Dct31gaDbJIsKd6\n"
"7OwZaszFKXMCAQE=\n"
"-----END EC PARAMETERS-----\n";

#define INDENT  124

int main(int argc, char**argv)
{
    BIO *mem = NULL, *out = NULL;
    EC_GROUP *group = NULL;

    // for ECPKParameters_print's output
    out = BIO_new(BIO_s_file());
    if (out == NULL)
        goto err;
    BIO_set_fp(out, stdout, BIO_NOCLOSE);

    // load pem to bio
    mem = BIO_new_mem_buf(ecparam_pem, -1);
    if (mem == NULL)
        goto err;

    // create group from bio
    group = PEM_read_bio_ECPKParameters(mem, NULL, NULL, NULL);
    if (group == NULL)
        goto err;

    // if INDENT >= 124, stack buffer overflow(max 5byte) occur in print_bin()
    ECPKParameters_print(out, group, INDENT);

 err:
    BIO_free(mem);
    BIO_free_all(out);
    EC_GROUP_free(group);

}

I statically linked libressl's libcrypto.a and executed it with the following command. $ gcc main.c crypto/.libs/libcrypto.a -lpthread $ ./a.out

backtrace

$ ./a.out
                                                                                                                           Field Type: prime-field
                                                                                                                           Prime:
                                                                                                                               00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                                                                                                                               ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                                                                                                                               ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:
                                                                                                                               ff:ff:ff:ff
                                                                                                                           A:
                                                                                                                               00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                                                                                                                               ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                                                                                                                               ff:ff:fe:ff:ff:ff:ff:00:00:00:00:00:00:00:00:
                                                                                                                               ff:ff:ff:fc
                                                                                                                           B:
                                                                                                                               00:b3:31:2f:a7:e2:3e:e7:e4:98:8e:05:6b:e3:f8:
                                                                                                                               2d:19:18:1d:9c:6e:fe:81:41:12:03:14:08:8f:50:
                                                                                                                               13:87:5a:c6:56:39:8d:8a:2e:d1:9d:2a:85:c8:ed:
                                                                                                                               d3:ec:2a:ef
                                                                                                                           Generator (uncompressed):
                                                                                                                               04:aa:87:ca:22:be:8b:05:37:8e:b1:c7:1e:f3:20:
                                                                                                                               ad:74:6e:1d:3b:62:8b:a7:9b:98:59:f7:41:e0:82:
                                                                                                                               54:2a:38:55:02:f2:5d:bf:55:29:6c:3a:54:5e:38:
                                                                                                                               72:76:0a:b7:36:17:de:4a:96:26:2c:6f:5d:9e:98:
                                                                                                                               bf:92:92:dc:29:f8:f4:1d:bd:28:9a:14:7c:e9:da:
                                                                                                                               31:13:b5:f0:b8:c0:0a:60:b1:ce:1d:7e:81:9d:7a:
                                                                                                                               43:1d:7c:90:ea:0e:5f
                                                                                                                           Order:
                                                                                                                               00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                                                                                                                               ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:c7:63:4d:81:f4:
                                                                                                                               37:2d:df:58:1a:0d:b2:48:b0:a7:7a:ec:ec:19:6a:
                                                                                                                               cc:c5:29:73
                                                                                                                           Cofactor:  1 (0x1)
*** buffer overflow detected ***: ./a.out terminated
Aborted (core dumped)
...
(gdb) bt
#0  0x00007f5a32794acf in raise () from /lib64/libc.so.6
#1  0x00007f5a32767ea5 in abort () from /lib64/libc.so.6
#2  0x00007f5a327d5cd7 in __libc_message () from /lib64/libc.so.6
#3  0x00007f5a328849c5 in __fortify_fail_abort () from /lib64/libc.so.6
#4  0x00007f5a328849f7 in __fortify_fail () from /lib64/libc.so.6
#5  0x00007f5a32882a96 in __chk_fail () from /lib64/libc.so.6
#6  0x00000000004063aa in memset (__len=128, __ch=32, __dest=0x7ffe5409ca71)
    at /usr/include/bits/string_fortified.h:74
#7  print_bin (name=0x4c80b1 "Seed:", off=124, len=20,
    buf=0x1449710 "\243\065\222j\243\031\242z\035", fp=0x14372a0) at ec/eck_prn.c:342
#8  ecpk_print_explicit_parameters (off=124, group=<optimized out>, bp=0x14372a0)
    at ec/eck_prn.c:293
#9  ECPKParameters_print (bp=0x14372a0, group=<optimized out>, off=<optimized out>)
    at ec/eck_prn.c:316
#10 0x0000000000401e82 in main ()

LibreSSL version

This issue is reproducible on LibreSSL master.

OS

RockyLinux8

(In addition, there is just one more bug, so I will write another issue immediately after this.)