Nmap Development mailing list archives
RE: Vuls of Nmap and OpenSSL cause Ncat Crush
From: <Mitsuaki_Shiraishi () Dell com>
Date: Tue, 31 Mar 2015 14:20:10 +0000
Hello, Thank you for providing information. I did not check the development version but only checked stable release. The report you mentioned seems to be same issue I reported. And the fix might work correctly. Thank you. ========================================= Mitsuaki Shiraishi Mitsuaki_Shiraishi () dell com ----------------------------------------- Dell SecureWorks - CISSP, CISA, GCIH - Information Security Specialist - Software Design & Development Engineer ========================================= From: dev [mailto:dev-bounces () nmap org] On Behalf Of Daniel Miller Sent: Monday, March 30, 2015 9:50 PM To: Shiraishi, Mitsuaki Cc: Nmap-dev; openssl-security () openssl org Subject: Re: Vuls of Nmap and OpenSSL cause Ncat Crush Mitsuaki, Thank you for this very detailed bug report. If I am not mistaken, this issue was previously discovered and fixed in r33743 [1] in October 2014. The fix has not been released, but is in our development version in SVN. Here's the commit message: Fix Ncat crash on concurrent ssl connections Reported on debian bugtracker here: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=724580 We can't remove an fdinfo from client_fdlist and still expect to access the fdinfo via a pointer we got from get_fdinfo(&client_fdlist) since rm_fd() modifies the data at the address pointed to. So instead of removing it from the list and then adding it right back, we just don't remove it in the first place. I'm not sure what we can do about the OpenSSL issue; I do see you have sent this report to them as well. Can you see any way we can be more careful to validate the parameters we pass to SSL_accept? Dan [1] https://github.com/nmap/nmap/commit/3b6ea5a9e5cdc2d40f2761b1cdef2fa99d0c1daa On Sun, Mar 29, 2015 at 8:23 PM, <Mitsuaki_Shiraishi () dell com> wrote: Hello, Nmap Developers & OpenSSL Developers, I found two vulnerabilities which cause segmentation fault, the one is in Nmap and another is in OpenSSL. ============================================ Summary ============================================ * Both of Nmap 6.47 and OpenSSL 1.0.2a has a vulnerability which results ncat listener crush * Nmap 6.47 has a race condition / use-after-free vulnerability in ncat listener with ssl support which triggers OpenSSL's bug * OpenSSL 1.0.2a does not check user input data which causes segmentation fault * Attacker can crush ncat listener remotely by exploiting these vulns * At the worst scenario, remote code execution might be possible. [Affected Version] * Nmap 6.47 source compiled version (latest version at 2015/03/30) * Nmap 6.47 installed Kali, 3.14-kali1-amd64 * Nmap 6.47 installed Debian 7, Debian 3.2.65-1+deb7u2 i686 * OpenSSL 1.0.2a source compiled version (latest version at 2015/03/30) * OpenSSL 1.0.1e 11 Feb 2013 for Debian 7, Debian 3.2.65-1+deb7u2 i686 * OpenSSL 1.0.1e 11 Feb 2013 for Kali, 3.14-kali1-amd64 * OpenSSL 1.0.1e-fips 11 Feb 2013 for CentOS, 3.10.0-123.20.1.el7.x86_64 [Exploit Example] # ncat -nvv -l -p 443 -k --ssl # cd /usr/share/nmap/scripts # nmap -nvv -r -Pn -sS -p 443 --script=ssl* 127.0.0.1 [Output] # ncat -nvv -l -p 443 -k --ssl Ncat: Version 6.47 ( http://nmap.org/ncat ) Ncat: Generating a temporary 1024-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one. Ncat: SHA-1 fingerprint: FAFA 8414 F23D 86FC 0555 8D51 3CFA 2B28 3972 36AA Ncat: Listening on :::443 Ncat: Listening on 0.0.0.0:443 Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46276. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46277. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46278. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46279. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46280. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46281. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46282. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46283. Ncat: Failed SSL connection from 127.0.0.1: error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol Ncat: Failed SSL connection from 127.0.0.1: error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46284. Ncat: Failed SSL connection from 127.0.0.1: error:00000000:lib(0):func(0):reason(0) Ncat: Failed SSL connection from 127.0.0.1: error:00000000:lib(0):func(0):reason(0) Ncat: Failed SSL connection from 127.0.0.1: error:00000000:lib(0):func(0):reason(0) Ncat: Failed SSL connection from 127.0.0.1: error:00000000:lib(0):func(0):reason(0) Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46285. Ncat: Connection from 127.0.0.1. Ncat: Connection from 127.0.0.1:46286. Segmentation fault ======================================================== A. Nmap race condition / use-after-free vulnerability ======================================================== [A-1] Condition The vulnerability exposed if events occurs following order below: * ncat listens with "-k" and "--ssl" * connection-1 starts ssl-handshake * connection-2 starts ssl-handshake * connection-1 complete ssl-handshake * connection-2 is terminated with ssl_failed * connection-3 starts ssl-handshake using same file descriptor as connection-2 [A-2] Cause ncat_listen_stream() in ncat/ncat_listen.c 352 fdi = get_fdinfo(&client_fdlist, i); 353 ncat_assert(fdi != NULL); 354 switch (ssl_handshake(fdi)) { 355 case NCAT_SSL_HANDSHAKE_COMPLETED: 356 /* Clear from sslpending_fds once ssl is established */ 357 FD_CLR(i, &sslpending_fds); 358 rm_fd(&client_fdlist, i); 359 post_handle_connection(*fdi); 360 break; rm_fd() in ncat/util.c 630 fdl->fds[x] = fdl->fds[last - 1]; 631 632 fdl->nfds--; post_handle_connection() in ncat/ncat_listen.c 531 FD_SET(sinfo.fd, &master_readfds); 532 /* add it to our list of fds for maintaining maxfd */ 533 if (add_fdinfo(&client_fdlist, &sinfo) < 0) 534 bye("add_fdinfo() failed."); When NCAT_SSL_HANDSHAKE_COMPLETED, ncat removes current fdinfo from client_fdlist by calling rm_fd(), then pass fdi to post_handle_connection(). (image of client_fdlist) client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 6, ssl=80e3a78 /* COMPLETED */ client_fdlist: idx=5, fd 7, ssl=810edd0 client_fdlist: idx=6, fd 8, ssl=81212e0 But in rm_fd(), removing fdinfo is done by below: * copy last fdinfo to current position * decrement array index Therefore, after returned from rm_fd(), line 358 in ncat/ncat_listen.c, fdinfo pointed by fdi is the latest created, not NCAT_SSL_HANDSHAKE_COMPLETED. (image of client_fdlist) client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 8, ssl=81212e0 /* Moved from last */ client_fdlist: idx=5, fd 7, ssl=810edd0 At next line(359), post_handle_connection() will add target fdinfo(again, the latest created fdinfo, not NCAT_SSL_HANDSHAKE_COMPLETED) to client_fdlist, resulting duplicates the last created fdinfo. If the last one is in the way to ssl-handshake, two entries point to one SSL object. (image of client_fdlist) client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 8, ssl=81212e0 client_fdlist: idx=5, fd 7, ssl=810edd0 client_fdlist: idx=6, fd 8, ssl=81212e0 /* Added by post_handle_connection() */ When latest fdinfo is closed with NCAT_SSL_HANDSHAKE_FAILED, SSL_free() and rm_fd() are called. the first fdlist related to latest fdinfo is removed, but duplicated one remains in client_fdlist. ncat_listen_stream() in ncat/ncat_listen.c 367 case NCAT_SSL_HANDSHAKE_FAILED: 368 default: 369 SSL_free(fdi->ssl); 370 Close(fdi->fd); 371 FD_CLR(i, &sslpending_fds); 372 FD_CLR(i, &master_readfds); 373 rm_fd(&client_fdlist, i); Image of client_fdlist: before rm_fd() is called client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 8, ssl=81212e0 /* NCAT_SSL_HANDSHAKE_FAILED */ client_fdlist: idx=5, fd 9, ssl=0 client_fdlist: idx=6, fd 8, ssl=81212e0 /* Duplicated */ Image of client_fdlist: after rm_fd() is called client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 8, ssl=81212e0 /* Deleted, but still remains */ client_fdlist: idx=5, fd 9, ssl=0 Ncat keep accepting new connection. When new connection is established and same fd with duplicated one is assigned, ncat grubs duplicated fdlist which contains SSL_free()ed SSL object. 348 /* Is this an ssl socket pending a handshake? If so handle it. */ 349 if (o.ssl && FD_ISSET(i, &sslpending_fds)) { 350 FD_CLR(i, &master_readfds); 351 FD_CLR(i, &master_writefds); 352 fdi = get_fdinfo(&client_fdlist, i); 353 ncat_assert(fdi != NULL); 354 switch (ssl_handshake(fdi)) { 355 case NCAT_SSL_HANDSHAKE_COMPLETED: Image of client_fdlist: after get_fdinfo() is called with fd=8 client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 8, ssl=81212e0 /* obtained */ client_fdlist: idx=5, fd 9, ssl=80fcec0 client_fdlist: idx=6, fd 7, ssl=0 client_fdlist: idx=7, fd 8, ssl=0 Ncat does not call new_ssl() because sinfo->ssl is not NULL. ssl_handshake() in ncat/ncat_ssl.c 622 /* Initialize the socket too if it isn't. */ 623 if (!sinfo->ssl) 624 sinfo->ssl = new_ssl(sinfo->fd); 625 626 ret = SSL_accept(sinfo->ssl); Calling SSL_accept() triggers use-after-free condition in OpenSSL. It results segmentation fault in OpenSSL. Ncat: Connection from 172.16.223.131. Ncat: Connection from 172.16.223.131:60634. Segmentation fault [A-3] Remediation * post_handle_connection() should be called before rm_fd() is called. ncat_listen_stream() in ncat/ncat_listen.c 355 case NCAT_SSL_HANDSHAKE_COMPLETED: 356 /* Clear from sslpending_fds once ssl is established */ 357 FD_CLR(i, &sslpending_fds); 358 post_handle_connection(*fdi); // current fdinfo is copied 359 rm_fd(&client_fdlist, i); // remove current fdinfo 360 break; client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 6, ssl=80e3a78 /* COMPLETED */ client_fdlist: idx=5, fd 7, ssl=810edd0 client_fdlist: idx=6, fd 8, ssl=81212e0 client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 6, ssl=80e3a78 /* COMPLETED */ client_fdlist: idx=5, fd 7, ssl=810edd0 client_fdlist: idx=6, fd 8, ssl=81212e0 client_fdlist: idx=7, fd 6, ssl=80e3a78 /* Added first */ client_fdlist: idx=0, fd 3, ssl=0 client_fdlist: idx=1, fd 4, ssl=0 client_fdlist: idx=2, fd 0, ssl=0 client_fdlist: idx=3, fd 5, ssl=80e33b8 client_fdlist: idx=4, fd 6, ssl=80e3a78 /* Removed */ client_fdlist: idx=5, fd 7, ssl=810edd0 client_fdlist: idx=6, fd 8, ssl=81212e0 ======================================================== B. OpenSSL input validation error ======================================================== [B-1] Condition * SSL object contains malicious "method" value when SSL_acccept() is called [b-2] Cause * In some situation such as ncat bug described above, SSL object may have invalid/malicious member. * SSL object has a member named "method", which is a pointer to struct ssl_method_st. * Struct ssl_method_st contains a function pointer "ssl_accept". * When SSL_accept() is called, it calls method->ssl_accept(). * SSL_accept() does not check the value of "method". * This causes segmentation fault if the "method" is invalid. * (Attacker may put specially crafted "method" which contains malicious function pointer. It seems to be possible to execute arbitrary code.) --- Code --- ssl/ssl_lib.c 984 int SSL_accept(SSL *s) 985 { 986 if (s->handshake_func == 0) 987 /* Not properly initialized yet */ 988 SSL_set_accept_state(s); 989 990 return (s->method->ssl_accept(s)); 991 } ssl/ssl.h 437 /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ 438 struct ssl_method_st { 439 int version; 440 int (*ssl_new) (SSL *s); 441 void (*ssl_clear) (SSL *s); 442 void (*ssl_free) (SSL *s); 443 int (*ssl_accept) (SSL *s); /* called by s->method->ssl_accept(s) in SSL_accept() */ 444 int (*ssl_connect) (SSL *s); --- PoC --- ssl = new_ssl(fd); modify_ssl(ssl); /* set ssl->method to 0x1234567 */ ret = SSL_accept(ssl); --- Input invalid SSL object --- ssl->version = 0xa0b0c0d /* malicious value 1 */ ssl->type = 0xa0b0c0d /* malicious value 2 */ ssl->method = 0x1234567 /* malicious value 3 causes segfault */ ssl->rbio = 0x8290c50 ssl->wbio = 0x8290c50 --- Debugger --- Program received signal SIGSEGV, Segmentation fault. 0x080752b8 in SSL_accept () (gdb) info registers eax 0x1234567 19088743 /* the value of ssl->method */ ecx 0xb7f9e36c -1208360084 edx 0x0 0 ebx 0x82910b8 136908984 esp 0xbffff4b0 0xbffff4b0 ebp 0x1 0x1 esi 0x8291d20 136912160 edi 0x0 0 eip 0x80752b8 0x80752b8 <SSL_accept+56> eflags 0x210246 [ PF ZF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) disas SSL_accept Dump of assembler code for function SSL_accept: 0x08075280 <+0>: push %ebx 0x08075281 <+1>: sub $0x18,%esp 0x08075284 <+4>: mov 0x20(%esp),%ebx 0x08075288 <+8>: mov 0x20(%ebx),%eax 0x0807528b <+11>: test %eax,%eax 0x0807528d <+13>: je 0x80752a0 <SSL_accept+32> 0x0807528f <+15>: mov 0x8(%ebx),%eax 0x08075292 <+18>: mov %ebx,0x20(%esp) 0x08075296 <+22>: mov 0x10(%eax),%eax 0x08075299 <+25>: add $0x18,%esp 0x0807529c <+28>: pop %ebx 0x0807529d <+29>: jmp *%eax 0x0807529f <+31>: nop 0x080752a0 <+32>: mov 0x8(%ebx),%eax 0x080752a3 <+35>: movl $0x1,0x24(%ebx) 0x080752aa <+42>: movl $0x0,0x30(%ebx) 0x080752b1 <+49>: movl $0x6000,0x34(%ebx) => 0x080752b8 <+56>: mov 0x10(%eax),%eax 0x080752bb <+59>: mov %eax,0x20(%ebx) 0x080752be <+62>: mov 0x80(%ebx),%eax 0x080752c4 <+68>: test %eax,%eax 0x080752c6 <+70>: je 0x80752e8 <SSL_accept+104> [B-3] Remediation * SSL_accept() should validate the value of ssl->method (and values of other members) before use it * SSL_accept() should return 0 or <0 https://www.openssl.org/docs/ssl/SSL_accept.html ========================= Mitsuaki Shiraishi Mitsuaki_Shiraishi () dell com ----------------------------------------- Dell SecureWorks - CISSP, CISA, GCIH - Information Security Specialist - Software Design & Development Engineer ========================= _______________________________________________ Sent through the dev mailing list https://nmap.org/mailman/listinfo/dev Archived at http://seclists.org/nmap-dev/ _______________________________________________ Sent through the dev mailing list https://nmap.org/mailman/listinfo/dev Archived at http://seclists.org/nmap-dev/
Current thread:
- Vuls of Nmap and OpenSSL cause Ncat Crush Mitsuaki_Shiraishi (Mar 29)
- Re: Vuls of Nmap and OpenSSL cause Ncat Crush Daniel Miller (Mar 30)
- RE: Vuls of Nmap and OpenSSL cause Ncat Crush Mitsuaki_Shiraishi (Mar 31)
- RE: Vuls of Nmap and OpenSSL cause Ncat Crush Salz, Rich (Mar 30)
- Re: Vuls of Nmap and OpenSSL cause Ncat Crush Daniel Miller (Mar 30)