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)
