Skip to content

Crash with --cert-status on HTTP/3 connections #14049

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Fullmetal5 opened this issue Jun 28, 2024 · 1 comment
Closed

Crash with --cert-status on HTTP/3 connections #14049

Fullmetal5 opened this issue Jun 28, 2024 · 1 comment
Assignees
Labels
HTTP/3 h3 or quic related

Comments

@Fullmetal5
Copy link
Contributor

Fullmetal5 commented Jun 28, 2024

I did this

curl --cert-status --http3-only 'https://example.com/'
Segmentation fault (core dumped)
Thread 1 "curl" received signal SIGSEGV, Segmentation fault.
0x00007ffff7f2fbbd in verifystatus (cf=0x55555559d790, data=0x5555555a1db0) at /usr/src/debug/curl/curl/lib/vtls/openssl.c:2386
2386      len = SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status);                                                                                                                                                                                                           
(gdb) bt
#0  0x00007ffff7f2fbbd in verifystatus (cf=0x55555559d790, data=0x5555555a1db0) at /usr/src/debug/curl/curl/lib/vtls/openssl.c:2386
#1  Curl_oss_check_peer_cert (cf=cf@entry=0x55555559d790, data=data@entry=0x5555555a1db0, octx=0x5555555a18e8, peer=0x5555555a18c0) at /usr/src/debug/curl/curl/lib/vtls/openssl.c:4699
#2  0x00007ffff7f319df in Curl_vquic_tls_verify_peer (ctx=<optimized out>, cf=0x55555559d790, data=0x5555555a1db0, peer=<optimized out>) at /usr/src/debug/curl/curl/lib/vquic/vquic-tls.c:315
#3  cf_osslq_verify_peer (cf=0x55555559d790, data=0x5555555a1db0) at /usr/src/debug/curl/curl/lib/vquic/curl_osslq.c:475
#4  cf_osslq_connect (cf=0x55555559d790, data=0x5555555a1db0, blocking=<optimized out>, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/vquic/curl_osslq.c:1636
#5  0x00007ffff7ec6172 in Curl_conn_cf_connect (cf=<optimized out>, data=0x5555555a1db0, blocking=false, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cfilters.c:307
#6  Curl_conn_cf_connect (cf=<optimized out>, data=<optimized out>, blocking=false, done=<optimized out>) at /usr/src/debug/curl/curl/lib/cfilters.c:302
#7  baller_connect (cf=<optimized out>, data=<optimized out>, baller=0x55555559c250, now=0x7fffffffdb30, connected=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/connect.c:562
#8  is_connected (cf=0x55555559d760, data=0x5555555a1db0, connected=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/connect.c:619
#9  cf_he_connect (cf=0x55555559d760, data=0x5555555a1db0, blocking=<optimized out>, done=<optimized out>) at /usr/src/debug/curl/curl/lib/connect.c:904
#10 0x00007ffff7ec7100 in Curl_conn_cf_connect (cf=<optimized out>, data=0x5555555a1db0, blocking=false, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cfilters.c:307
#11 Curl_conn_cf_connect (cf=<optimized out>, data=0x5555555a1db0, blocking=false, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cfilters.c:302
#12 cf_setup_connect (cf=0x55555559d6e0, data=0x5555555a1db0, blocking=false, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/connect.c:1199
#13 0x00007ffff7eb47be in Curl_conn_cf_connect (cf=<optimized out>, data=0x5555555a1db0, blocking=false, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cfilters.c:307
#14 Curl_conn_cf_connect (cf=<optimized out>, data=0x5555555a1db0, blocking=false, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cfilters.c:302
#15 cf_hc_baller_connect (b=<optimized out>, cf=0x55555559b1a0, data=0x5555555a1db0, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cf-https-connect.c:135
#16 cf_hc_connect (cf=0x55555559b1a0, data=0x5555555a1db0, blocking=<optimized out>, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cf-https-connect.c:273
#17 0x00007ffff7ebe1b6 in Curl_conn_connect (data=0x5555555a1db0, sockindex=<optimized out>, blocking=<optimized out>, done=0x7fffffffdce4) at /usr/src/debug/curl/curl/lib/cfilters.c:353
#18 0x00007ffff7eff870 in multi_runsingle (multi=multi@entry=0x5555555998c0, nowp=nowp@entry=0x7fffffffdd90, data=data@entry=0x5555555a1db0) at /usr/src/debug/curl/curl/lib/multi.c:2128
#19 0x00007ffff7f01f3d in curl_multi_perform (multi=0x5555555998c0, running_handles=0x7fffffffdf38) at /usr/src/debug/curl/curl/lib/multi.c:2760
#20 0x00007ffff7ed061c in easy_transfer (multi=<optimized out>) at /usr/src/debug/curl/curl/lib/easy.c:675
#21 easy_perform (events=false, data=0x5555555a1db0) at /usr/src/debug/curl/curl/lib/easy.c:769
#22 curl_easy_perform (data=0x5555555a1db0) at /usr/src/debug/curl/curl/lib/easy.c:788
#23 0x0000555555558ea7 in serial_transfers (global=0x7fffffffe150, share=0x55555559a630) at /usr/src/debug/curl/curl/src/tool_operate.c:2498
#24 run_all_transfers (global=0x7fffffffe150, share=0x55555559a630, result=CURLE_OK) at /usr/src/debug/curl/curl/src/tool_operate.c:2685
#25 operate (global=0x7fffffffe150, argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/curl/curl/src/tool_operate.c:2801
#26 main (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/curl/curl/src/tool_main.c:273

The cause of this is actually rather interesting and turns out to be a near miss on a potentially bad security issue.
The root cause is actually a type confusion, I'm fuzzy as to where exactly types are confused because of the amount of indirection and casting of ctx pointers in the connection filters.

In lib/vquic/curl_ngtcp2.c Curl_cf_ngtcp2_create creates a cfilter with the ctx being a struct cf_ngtcp2_ctx*, later in qng_verify_peer this cfilter is passed to Curl_vquic_tls_verify_peer from lib/vquic/vquic-tls.c which appears to be a middle man between all the different TLS libraries. In Curl_vquic_tls_verify_peer the same cfilter is passed to Curl_oss_check_peer_cert which lives in lib/vtls/openssl.c, the problem appears to be that all the functions in lib/vtls/openssl.c expect a struct ssl_connect_data* in their cfilter ctx. So Curl_oss_check_peer_cert eventually calls verifystatus with the cfilter and verifystatus finally casts the ctx to a struct ssl_connect_data*, tries to get connssl->backend, cast it to a struct ossl_ctx* and access octx->ssl and crashes. I tried tracing the calls to figure out if this was a case of the wrong cfilter being passed in from vquic-tls.c (it seems like they can be chained) but eventually gave up and decided someone with more experience will know where to look quicker than I.

As for the actual crash it luckily can only ever be a NULL pointer dereference because the backend field of struct ssl_connect_data is at offset 0x30 and in struct cf_ngtcp2_ctx that happens to land in the padding section of a struct sockaddr_storage (apparently the large amounts of padding is something to do with alignments and from looking at the glibc source the padding is almost 128 bytes, I couldn't find any other libc's that changed this but didn't look very hard). This object was allocated with calloc so this padding space is guaranteed to be zero'd and so this can only every be a NULL pointer deference. Every other QUIC backend used structs that happened to have a struct sockaddr_storage in the same place with the exception of the msh3 one which I didn't investigate farther as it is experimental.

As for where the actual bug is I think it's vquic-tls.c not passing in the right cfilter that these TLS backends are expecting, I traced the other functions that vquic-tls.c calls with a cfilter for the OpenSSL backend and I can't tell if we just got lucky and only the verifystatus function actually used the cfilter's ctx but the rest were also assuming it was holding their internal structs or if openssl.c was supposed to be doing something to the cfilter it was passed to access the right struct but my energy ran out and I decided to just file this bug report instead. :P

I expected the following

No crash.

curl/libcurl version

curl 8.8.0 (x86_64-pc-linux-gnu) libcurl/8.8.0 OpenSSL/3.3.1 zlib/1.3.1 brotli/1.1.0 zstd/1.5.6 libidn2/2.3.7 libpsl/0.21.5 libssh2/1.11.0 nghttp2/1.62.1 nghttp3/1.4.0
Release-Date: 2024-05-22
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd

Also happens on master.

operating system

Linux 6.9.6-arch1-1 #1 SMP PREEMPT_DYNAMIC Fri, 21 Jun 2024 19:49:19 +0000 x86_64 GNU/Linux

@icing icing self-assigned this Jun 28, 2024
icing added a commit to icing/curl that referenced this issue Jun 28, 2024
- fix --cert-status use in ngtcp2+quictls
- add test for --cert-status on all http versions
- refs curl#14049
@icing
Copy link
Contributor

icing commented Jun 28, 2024

Thanks for the report. I made a fix in #14050. Hope this works for you as well.

@bagder bagder closed this as completed in 185a05e Jun 28, 2024
@bagder bagder added the HTTP/3 h3 or quic related label Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
HTTP/3 h3 or quic related
Development

No branches or pull requests

3 participants