mosquitto icon indicating copy to clipboard operation
mosquitto copied to clipboard

NULL pointer dereference in tls_mosq.c

Open dqp10515 opened this issue 3 months ago • 4 comments

Version: 28f9147

Description: The function X509_STORE_CTX_get_ex_data() can return NULL, so a NULL check is needed at line 57 in mqtt_protocol/mosquitto/lib/tls_mosq.c.

When X509_STORE_CTX_get_ex_data() returns NULL, the runtime ASAN Log shows:

./08-ssl-bridge.py         
1760012847: The 'port' option is now deprecated and will be removed in a future version. Please use 'listener' instead.
1760012847: Warning: Bridge bridge_test using insecure mode.
1760012847: Warning: Unable to drop privileges to 'mosquitto' because this user does not exist. Trying 'nobody' instead.
1760012847: mosquitto version 2.0.22 starting
1760012847: Config loaded from 08-ssl-bridge.conf.
1760012847: Opening ipv4 listen socket on port 1889.
1760012847: Opening ipv6 listen socket on port 1889.
1760012847: Bridge local.5634ed8d3141.bridge_test doing local SUBSCRIBE on topic bridge/#
1760012847: Connecting bridge bridge_test (127.0.0.1:1888)
1760012847: Bridge 5634ed8d3141.bridge_test sending CONNECT
1760012847: mosquitto version 2.0.22 running
1760012847: New connection from ::1:49066 on port 1889.
1760012847: Client <unknown> closed its connection.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1660457==ERROR: AddressSanitizer: SEGV on unknown address 0x0000000005b8 (pc 0x7fe6c8330bdd bp 0x7ffed89ae290 sp 0x7ffed89ae180 T0)
==1660457==The signal is caused by a READ memory access.
==1660457==Hint: address points to the zero page.
==1660457==WARNING: invalid path to external symbolizer!
==1660457==WARNING: Failed to use and restart external symbolizer!
    #0 0x7fe6c8330bdd  (openssl/libcrypto.so.1.1+0x180bdd)
    #1 0x757b2d  (mosquitto/src/mosquitto+0x757b2d)
    #2 0x7fe6c83be6c9  (openssl/libcrypto.so.1.1+0x20e6c9)
    #3 0x7fe6c83c05cf  (openssl/libcrypto.so.1.1+0x2105cf)
    #4 0x7fe6c83c10e7  (openssl/libcrypto.so.1.1+0x2110e7)
    #5 0x7fe6c814ade7  (openssl/libssl.so.1.1+0x32de7)
    #6 0x7fe6c816f7a8  (openssl/libssl.so.1.1+0x577a8)
    #7 0x7fe6c8171ed4  (openssl/libssl.so.1.1+0x59ed4)
    #8 0x7fe6c816b7ac  (openssl/libssl.so.1.1+0x537ac)
    #9 0x7fe6c813f185  (openssl/libssl.so.1.1+0x27185)
    #10 0x7fe6c8147143  (openssl/libssl.so.1.1+0x2f143)
    #11 0x7fe6c8153d4e  (openssl/libssl.so.1.1+0x3bd4e)
    #12 0x7fe6c8153e56  (openssl/libssl.so.1.1+0x3be56)
    #13 0x6067f6  (mosquitto/src/mosquitto+0x6067f6)
    #14 0x612aec  (mosquitto/src/mosquitto+0x612aec)
    #15 0x5e8e0f  (mosquitto/src/mosquitto+0x5e8e0f)
    #16 0x5e7f0c  (mosquitto/src/mosquitto+0x5e7f0c)
    #17 0x5e4cf3  (mosquitto/src/mosquitto+0x5e4cf3)
    #18 0x5d9ecb  (mosquitto/src/mosquitto+0x5d9ecb)
    #19 0x4ce9c3  (mosquitto/src/mosquitto+0x4ce9c3)
    #20 0x7fe6c7d9d0b2  (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #21 0x41fd8d  (mosquitto/src/mosquitto+0x41fd8d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (openssl/libcrypto.so.1.1+0x180bdd) 
==1660457==ABORTING

dqp10515 avatar Oct 11 '25 02:10 dqp10515

Can you please provide an example of how to reproduce this please?

ralight avatar Oct 11 '25 08:10 ralight

This bug was discovered through fault injection testing on the OpenSSL 1.1 library. I injected faults to force the X509_STORE_CTX_get_ex_data() function to return NULL, which exposed this vulnerability.

Reproduction Steps:

  1. Apply fault injection to make X509_STORE_CTX_get_ex_data() return NULL
  2. Use the existing test case: ./mosquitto/test/broker/08-ssl-bridge.py
  3. The crash will occur.

Root Cause:

At line 57-58 in /mqtt_protocol/mosquitto/lib/tls_mosq.c:

ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());

mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq);  // Crashes here if ssl is NULL

The function X509_STORE_CTX_get_ex_data() can return NULL in the following scenarios:

  1. Invalid index: If SSL_get_ex_data_X509_STORE_CTX_idx() returns an invalid index
  2. Memory allocation failures: In low-memory conditions or when OpenSSL internal structures fail to initialize
  3. Extra data not set: If the SSL connection context was not properly initialized with the required extra data

dqp10515 avatar Oct 11 '25 11:10 dqp10515

Thank you for the detailed explanation.

From my analysis of the openssl code, it is impossible for SSL_get_ex_data_X509_STORE_CTX_idx() to return an invalid index in a certificate verify callback - ssl_verify_cert_chain() would already have exited were that the case. For the out of memory case (which also covers case 3), I haven't checked extensively but from what I've seen so far, the same is true - the verify callback would not be called because an earlier error would have prevented it.

It is important to fix the check, but from what I can see this is almost impossible to trigger in practice.

ralight avatar Oct 11 '25 13:10 ralight

Thank you for the quick fix and analysis.

dqp10515 avatar Oct 13 '25 11:10 dqp10515