/*************************************************************************** * ncat_connect.c -- Ncat connect mode. * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2015 Insecure.Com LLC. Nmap is * * also a registered trademark of Insecure.Com LLC. This program is free * * software; you may redistribute and/or modify it under the terms of the * * GNU General Public License as published by the Free Software * * Foundation; Version 2 ("GPL"), BUT ONLY WITH ALL OF THE CLARIFICATIONS * * AND EXCEPTIONS DESCRIBED HEREIN. This guarantees your right to use, * * modify, and redistribute this software under certain conditions. If * * you wish to embed Nmap technology into proprietary software, we sell * * alternative licenses (contact sales@nmap.com). Dozens of software * * vendors already license Nmap technology such as host discovery, port * * scanning, OS detection, version detection, and the Nmap Scripting * * Engine. * * * * Note that the GPL places important restrictions on "derivative works", * * yet it does not provide a detailed definition of that term. To avoid * * misunderstandings, we interpret that term as broadly as copyright law * * allows. For example, we consider an application to constitute a * * derivative work for the purpose of this license if it does any of the * * following with any software or content covered by this license * * ("Covered Software"): * * * * o Integrates source code from Covered Software. * * * * o Reads or includes copyrighted data files, such as Nmap's nmap-os-db * * or nmap-service-probes. * * * * o Is designed specifically to execute Covered Software and parse the * * results (as opposed to typical shell or execution-menu apps, which will * * execute anything you tell them to). * * * * o Includes Covered Software in a proprietary executable installer. The * * installers produced by InstallShield are an example of this. Including * * Nmap with other software in compressed or archival form does not * * trigger this provision, provided appropriate open source decompression * * or de-archiving software is widely available for no charge. For the * * purposes of this license, an installer is considered to include Covered * * Software even if it actually retrieves a copy of Covered Software from * * another source during runtime (such as by downloading it from the * * Internet). * * * * o Links (statically or dynamically) to a library which does any of the * * above. * * * * o Executes a helper program, module, or script to do any of the above. * * * * This list is not exclusive, but is meant to clarify our interpretation * * of derived works with some common examples. Other people may interpret * * the plain GPL differently, so we consider this a special exception to * * the GPL that we apply to Covered Software. Works which meet any of * * these conditions must conform to all of the terms of this license, * * particularly including the GPL Section 3 requirements of providing * * source code and allowing free redistribution of the work as a whole. * * * * As another special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. * * * * Any redistribution of Covered Software, including any derived works, * * must obey and carry forward all of the terms of this license, including * * obeying all GPL rules and restrictions. For example, source code of * * the whole work must be provided and free redistribution must be * * allowed. All GPL references to "this License", are to be treated as * * including the terms and conditions of this license text as well. * * * * Because this license imposes special exceptions to the GPL, Covered * * Work may not be combined (even as part of a larger work) with plain GPL * * software. The terms, conditions, and exceptions of this license must * * be included as well. This license is incompatible with some other open * * source licenses as well. In some cases we can relicense portions of * * Nmap or grant special permissions to use it in other open source * * software. Please contact fyodor@nmap.org with any such requests. * * Similarly, we don't incorporate incompatible open source software into * * Covered Software without special permission from the copyright holders. * * * * If you have any questions about the licensing restrictions on using * * Nmap in other works, are happy to help. As mentioned above, we also * * offer alternative license to integrate Nmap into proprietary * * applications and appliances. These contracts have been sold to dozens * * of software vendors, and generally include a perpetual license as well * * as providing for priority support and updates. They also fund the * * continued development of Nmap. Please email sales@nmap.com for further * * information. * * * * If you have received a written license agreement or contract for * * Covered Software stating terms other than these, you may choose to use * * and redistribute Covered Software under those terms instead of these. * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes. * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to the dev@nmap.org mailing list for possible incorporation into the * * main distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, or checking them into the Nmap * * source code repository, it is understood (unless you specify otherwise) * * that you are offering the Nmap Project (Insecure.Com LLC) the * * unlimited, non-exclusive right to reuse, modify, and relicense the * * code. Nmap will always be available Open Source, but this is important * * because the inability to relicense code has caused devastating problems * * for other Free Software projects (such as KDE and NASM). We also * * occasionally relicense the code to third parties as discussed above. * * If you wish to specify special license conditions of your * * contributions, just say so when you send them. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Nmap * * license file for more details (it's in a COPYING file included with * * Nmap, and also available from https://svn.nmap.org/nmap/COPYING) * * * ***************************************************************************/ /* $Id: ncat_connect.c 34756 2015-06-27 08:21:53Z henri $ */ #include "nsock.h" #include "ncat.h" #include "util.h" #include "sys_wrap.h" #include "nbase.h" #include "http.h" #ifndef WIN32 #include #include #endif #include #include #include #ifdef HAVE_OPENSSL #include #include #endif #ifdef WIN32 /* Define missing constant for shutdown(2). * See: * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740481%28v=vs.85%29.aspx */ #define SHUT_WR SD_SEND #endif struct conn_state { nsock_iod sock_nsi; nsock_iod stdin_nsi; nsock_event_id idle_timer_event_id; int crlf_state; }; static struct conn_state cs = { NULL, NULL, 0, 0 }; static void connect_handler(nsock_pool nsp, nsock_event evt, void *data); static void post_connect(nsock_pool nsp, nsock_iod iod); static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data); static void read_socket_handler(nsock_pool nsp, nsock_event evt, void *data); static void write_socket_handler(nsock_pool nsp, nsock_event evt, void *data); static void idle_timer_handler(nsock_pool nsp, nsock_event evt, void *data); static void refresh_idle_timer(nsock_pool nsp); #ifdef HAVE_OPENSSL /* This callback is called for every certificate in a chain. ok is true if OpenSSL's internal verification has verified the certificate. We don't change anything about the verification, we only need access to the certificates to provide diagnostics. */ static int verify_callback(int ok, X509_STORE_CTX *store) { X509 *cert = X509_STORE_CTX_get_current_cert(store); int err = X509_STORE_CTX_get_error(store); /* Print the subject, issuer, and fingerprint depending on the verbosity level. */ if ((!ok && o.verbose) || o.debug > 1) { char digest_buf[SHA1_STRING_LENGTH + 1]; char *fp; loguser("Subject: "); X509_NAME_print_ex_fp(stderr, X509_get_subject_name(cert), 0, XN_FLAG_COMPAT); loguser_noprefix("\n"); loguser("Issuer: "); X509_NAME_print_ex_fp(stderr, X509_get_issuer_name(cert), 0, XN_FLAG_COMPAT); loguser_noprefix("\n"); fp = ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)); ncat_assert(fp == digest_buf); loguser("SHA-1 fingerprint: %s\n", digest_buf); } if (!ok && o.verbose) { loguser("Certificate verification failed (%s).\n", X509_verify_cert_error_string(err)); } return ok; } static void set_ssl_ctx_options(SSL_CTX *ctx) { if (o.ssltrustfile == NULL) { ssl_load_default_ca_certs(ctx); } else { if (o.debug) logdebug("Using trusted CA certificates from %s.\n", o.ssltrustfile); if (SSL_CTX_load_verify_locations(ctx, o.ssltrustfile, NULL) != 1) { bye("Could not load trusted certificates from %s.\n%s", o.ssltrustfile, ERR_error_string(ERR_get_error(), NULL)); } } if (o.sslverify) { SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); } else { /* Still check verification status and report it */ SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback); if (o.ssl && o.debug) logdebug("Not doing certificate verification.\n"); } if (o.sslcert != NULL && o.sslkey != NULL) { if (SSL_CTX_use_certificate_file(ctx, o.sslcert, SSL_FILETYPE_PEM) != 1) bye("SSL_CTX_use_certificate_file(): %s.", ERR_error_string(ERR_get_error(), NULL)); if (SSL_CTX_use_PrivateKey_file(ctx, o.sslkey, SSL_FILETYPE_PEM) != 1) bye("SSL_CTX_use_Privatekey_file(): %s.", ERR_error_string(ERR_get_error(), NULL)); } else { if ((o.sslcert == NULL) != (o.sslkey == NULL)) bye("The --ssl-key and --ssl-cert options must be used together."); } } #endif /* Depending on verbosity, print a message that a connection was established. */ static void connect_report(nsock_iod nsi) { union sockaddr_u peer; zmem(&peer, sizeof(peer.storage)); nsock_iod_get_communication_info(nsi, NULL, NULL, NULL, &peer.sockaddr, sizeof(peer.storage)); if (o.verbose) { #ifdef HAVE_OPENSSL if (nsock_iod_check_ssl(nsi)) { X509 *cert; X509_NAME *subject; char digest_buf[SHA1_STRING_LENGTH + 1]; char *fp; loguser("SSL connection to %s:%hu.", inet_socktop(&peer), nsock_iod_get_peerport(nsi)); cert = SSL_get_peer_certificate((SSL *)nsock_iod_get_ssl(nsi)); ncat_assert(cert != NULL); subject = X509_get_subject_name(cert); if (subject != NULL) { char buf[256]; int n; n = X509_NAME_get_text_by_NID(subject, NID_organizationName, buf, sizeof(buf)); if (n >= 0 && n <= sizeof(buf) - 1) loguser_noprefix(" %s", buf); } loguser_noprefix("\n"); fp = ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)); ncat_assert(fp == digest_buf); loguser("SHA-1 fingerprint: %s\n", digest_buf); } else { #if HAVE_SYS_UN_H if (peer.sockaddr.sa_family == AF_UNIX) loguser("Connected to %s.\n", peer.un.sun_path); else #endif loguser("Connected to %s:%hu.\n", inet_socktop(&peer), nsock_iod_get_peerport(nsi)); } #else #if HAVE_SYS_UN_H if (peer.sockaddr.sa_family == AF_UNIX) loguser("Connected to %s.\n", peer.un.sun_path); else #endif loguser("Connected to %s:%hu.\n", inet_socktop(&peer), nsock_iod_get_peerport(nsi)); #endif } } int ncat_connect(void) { nsock_pool mypool; int rc; /* Unless explicitly asked not to do so, ncat uses the * fallback nsock engine to maximize compatibility between * operating systems and the different use cases. */ if (!o.nsock_engine) nsock_set_default_engine("select"); /* Create an nsock pool */ if ((mypool = nsock_pool_new(NULL)) == NULL) bye("Failed to create nsock_pool."); if (o.proxy_chain_str) { if (o.verbose) loguser("Enabling proxy chain...\n"); if (nsock_proxychain_new(o.proxy_chain_str, &o.proxy_chain, NULL) < 0) bye("Failed to create a proxy chain.\n"); nsock_pool_set_proxychain(mypool, o.proxy_chain); } if (o.debug >= 6) nsock_set_loglevel(NSOCK_LOG_DBG_ALL); else if (o.debug >= 3) nsock_set_loglevel(NSOCK_LOG_DBG); else if (o.debug >= 1) nsock_set_loglevel(NSOCK_LOG_INFO); else nsock_set_loglevel(NSOCK_LOG_ERROR); /* Allow connections to broadcast addresses. */ nsock_pool_set_broadcast(mypool, 1); #ifdef HAVE_OPENSSL set_ssl_ctx_options((SSL_CTX *) nsock_pool_ssl_init(mypool, 0)); #endif cs.sock_nsi = nsock_iod_new(mypool, NULL); if (cs.sock_nsi == NULL) bye("Failed to create nsock_iod."); if (nsock_iod_set_hostname(cs.sock_nsi, o.target) == -1) bye("Failed to set hostname on iod."); #if HAVE_SYS_UN_H /* For DGRAM UNIX socket we have to use source socket */ if (o.af == AF_UNIX && o.proto == IPPROTO_UDP) { if (srcaddr.storage.ss_family != AF_UNIX) { char *tmp_name = NULL; /* If no source socket was specified, we have to create temporary one. */ if ((tmp_name = tempnam(NULL, "ncat.")) == NULL) bye("Failed to create name for temporary DGRAM source Unix domain socket (tempnam)."); srcaddr.un.sun_family = AF_UNIX; strncpy(srcaddr.un.sun_path, tmp_name, sizeof(srcaddr.un.sun_path)); free (tmp_name); } nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage, SUN_LEN((struct sockaddr_un *)&srcaddr.storage)); if (o.verbose) loguser("[%s] used as source DGRAM Unix domain socket.\n", srcaddr.un.sun_path); } else #endif switch (srcaddr.storage.ss_family) { case AF_UNSPEC: break; case AF_INET: nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage, sizeof(srcaddr.in)); break; #ifdef AF_INET6 case AF_INET6: nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage, sizeof(srcaddr.in6)); break; #endif #if HAVE_SYS_UN_H case AF_UNIX: nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage, SUN_LEN((struct sockaddr_un *)&srcaddr.storage)); break; #endif default: nsock_iod_set_localaddr(cs.sock_nsi, &srcaddr.storage, sizeof(srcaddr.storage)); break; } if (o.numsrcrtes) { unsigned char *ipopts = NULL; size_t ipoptslen = 0; if (o.af != AF_INET) bye("Sorry, -g can only currently be used with IPv4."); ipopts = buildsrcrte(targetss.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen); nsock_iod_set_ipoptions(cs.sock_nsi, ipopts, ipoptslen); free(ipopts); /* Nsock has its own copy */ } #if HAVE_SYS_UN_H if (o.af == AF_UNIX) { if (o.proto == IPPROTO_UDP) { nsock_connect_unixsock_datagram(mypool, cs.sock_nsi, connect_handler, NULL, &targetss.sockaddr, SUN_LEN((struct sockaddr_un *)&targetss.sockaddr)); } else { nsock_connect_unixsock_stream(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, &targetss.sockaddr, SUN_LEN((struct sockaddr_un *)&targetss.sockaddr)); } } else #endif if (o.proto == IPPROTO_UDP) { nsock_connect_udp(mypool, cs.sock_nsi, connect_handler, NULL, &targetss.sockaddr, targetsslen, inet_port(&targetss)); } #ifdef HAVE_OPENSSL else if (o.proto == IPPROTO_SCTP && o.ssl) { nsock_connect_ssl(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, &targetss.sockaddr, targetsslen, IPPROTO_SCTP, inet_port(&targetss), NULL); } #endif else if (o.proto == IPPROTO_SCTP) { nsock_connect_sctp(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, &targetss.sockaddr, targetsslen, inet_port(&targetss)); } #ifdef HAVE_OPENSSL else if (o.ssl) { nsock_connect_ssl(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, &targetss.sockaddr, targetsslen, IPPROTO_TCP, inet_port(&targetss), NULL); } #endif else if (o.proxy_chain_str && !strncmp(o.proxy_chain_str, "socks4a", 7)) { nsock_connect_tcp_socks4a(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, o.proxy_chain_str + 7, inet_port(&targetss)); } else { nsock_connect_tcp(mypool, cs.sock_nsi, connect_handler, o.conntimeout, NULL, &targetss.sockaddr, targetsslen, inet_port(&targetss)); } /* connect */ rc = nsock_loop(mypool, -1); if (o.verbose) { struct timeval end_time; double time; gettimeofday(&end_time, NULL); time = TIMEVAL_MSEC_SUBTRACT(end_time, start_time) / 1000.0; loguser("%lu bytes sent, %lu bytes received in %.2f seconds.\n", nsock_iod_get_write_count(cs.sock_nsi), nsock_iod_get_read_count(cs.sock_nsi), time); } #if HAVE_SYS_UN_H if (o.af == AF_UNIX && o.proto == IPPROTO_UDP) { if (o.verbose) loguser("Deleting source DGRAM Unix domain socket. [%s]\n", srcaddr.un.sun_path); unlink(srcaddr.un.sun_path); } #endif nsock_pool_delete(mypool); return rc == NSOCK_LOOP_ERROR ? 1 : 0; } static void connect_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); ncat_assert(type == NSE_TYPE_CONNECT || type == NSE_TYPE_CONNECT_SSL); if (status == NSE_STATUS_ERROR) { loguser("%s.\n", socket_strerror(nse_errorcode(evt))); exit(1); } else if (status == NSE_STATUS_TIMEOUT) { loguser("%s.\n", socket_strerror(ETIMEDOUT)); exit(1); } else if (status == NSE_STATUS_PROXYERROR) { /* Should provide a better idea of what went wrong, this status * is returned for a lot of errors when using a proxy. */ bye("Unable to connect: Proxy Error (possible closed port or malformed packet)"); } else { ncat_assert(status == NSE_STATUS_SUCCESS); } #ifdef HAVE_OPENSSL if (nsock_iod_check_ssl(cs.sock_nsi)) { /* Check the domain name. ssl_post_connect_check prints an error message if appropriate. */ if (!ssl_post_connect_check((SSL *)nsock_iod_get_ssl(cs.sock_nsi), o.target)) bye("Certificate verification error."); } #endif connect_report(cs.sock_nsi); /* Create IOD for nsp->stdin */ if ((cs.stdin_nsi = nsock_iod_new2(nsp, 0, NULL)) == NULL) bye("Failed to create stdin nsiod."); post_connect(nsp, nse_iod(evt)); } /* Handle --exec if appropriate, otherwise start the initial read events and set the idle timeout. */ static void post_connect(nsock_pool nsp, nsock_iod iod) { /* Command to execute. */ if (o.cmdexec) { struct fdinfo info; info.fd = nsock_iod_get_sd(iod); #ifdef HAVE_OPENSSL info.ssl = (SSL *)nsock_iod_get_ssl(iod); #endif /* Convert Nsock's non-blocking socket to an ordinary blocking one. It's possible for a program to write fast enough that it will get an EAGAIN on write on a non-blocking socket. */ block_socket(info.fd); netexec(&info, o.cmdexec); } /* Start the initial reads. */ if (!o.sendonly) nsock_read(nsp, cs.sock_nsi, read_socket_handler, -1, NULL); if (!o.recvonly) nsock_readbytes(nsp, cs.stdin_nsi, read_stdin_handler, -1, NULL, 0); /* The --idle-timeout option says to exit after a certain period of inactivity. We start a timer here and reset it on every read event; see refresh_idle_timer. */ if (o.idletimeout > 0) { cs.idle_timer_event_id = nsock_timer_create(nsp, idle_timer_handler, o.idletimeout, NULL); } } static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); char *buf, *tmp = NULL; int nbytes; ncat_assert(type == NSE_TYPE_READ); if (status == NSE_STATUS_EOF) { shutdown(nsock_iod_get_sd(cs.sock_nsi), SHUT_WR); /* In --send-only mode or non-TCP mode, exit after EOF on stdin. */ if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.sendonly)) nsock_loop_quit(nsp); return; } else if (status == NSE_STATUS_ERROR) { loguser("%s.\n", socket_strerror(nse_errorcode(evt))); exit(1); } else if (status == NSE_STATUS_TIMEOUT) { loguser("%s.\n", socket_strerror(ETIMEDOUT)); exit(1); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { ncat_assert(status == NSE_STATUS_SUCCESS); } buf = nse_readbuf(evt, &nbytes); /* read from stdin */ if (o.linedelay) ncat_delay_timer(o.linedelay); if (o.crlf) { if (fix_line_endings(buf, &nbytes, &tmp, &cs.crlf_state)) buf = tmp; } nsock_write(nsp, cs.sock_nsi, write_socket_handler, -1, NULL, buf, nbytes); ncat_log_send(buf, nbytes); if (tmp) free(tmp); refresh_idle_timer(nsp); } static void read_socket_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); char *buf; int nbytes; ncat_assert(type == NSE_TYPE_READ); if (status == NSE_STATUS_EOF) { Close(STDOUT_FILENO); /* In --recv-only mode or non-TCP mode, exit after EOF on the socket. */ if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.recvonly)) nsock_loop_quit(nsp); return; } else if (status == NSE_STATUS_ERROR) { loguser("%s.\n", socket_strerror(nse_errorcode(evt))); exit(1); } else if (status == NSE_STATUS_TIMEOUT) { loguser("%s.\n", socket_strerror(ETIMEDOUT)); exit(1); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { ncat_assert(status == NSE_STATUS_SUCCESS); } buf = nse_readbuf(evt, &nbytes); if (o.linedelay) ncat_delay_timer(o.linedelay); if (o.telnet) dotelnet(nsock_iod_get_sd(nse_iod(evt)), (unsigned char *) buf, nbytes); /* Write socket data to stdout */ Write(STDOUT_FILENO, buf, nbytes); ncat_log_recv(buf, nbytes); nsock_readbytes(nsp, cs.sock_nsi, read_socket_handler, -1, NULL, 0); refresh_idle_timer(nsp); } static void write_socket_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); ncat_assert(type == NSE_TYPE_WRITE); if (status == NSE_STATUS_ERROR) { loguser("%s.\n", socket_strerror(nse_errorcode(evt))); exit(1); } else if (status == NSE_STATUS_TIMEOUT) { loguser("%s.\n", socket_strerror(ETIMEDOUT)); exit(1); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { ncat_assert(status == NSE_STATUS_SUCCESS); } /* The write to the socket was successful. Allow reading more from stdin now. */ nsock_readbytes(nsp, cs.stdin_nsi, read_stdin_handler, -1, NULL, 0); } static void idle_timer_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); ncat_assert(type == NSE_TYPE_TIMER); if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) return; ncat_assert(status == NSE_STATUS_SUCCESS); loguser("Idle timeout expired (%d ms).\n", o.idletimeout); exit(1); } static void refresh_idle_timer(nsock_pool nsp) { if (o.idletimeout <= 0) return; nsock_event_cancel(nsp, cs.idle_timer_event_id, 0); cs.idle_timer_event_id = nsock_timer_create(nsp, idle_timer_handler, o.idletimeout, NULL); }