Nmap Development mailing list archives
Vuls of Nmap and OpenSSL cause Ncat Crush
From: <Mitsuaki_Shiraishi () Dell com>
Date: Mon, 30 Mar 2015 01:23:50 +0000
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/
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)