From 8f84863a23300e51cb2bbd63c767150154b8c572 Mon Sep 17 00:00:00 2001 From: d33tah Date: Wed, 24 Jul 2013 13:58:15 +0000 Subject: [PATCH] add NCAT_LOCAL_ADDR, NCAT_LOCAL_PORT, NCAT_REMOTE_ADDR, NCAR_REMOTE_PORT, NCAT_REMOTE_ADDR environment variables set in all --*-exec child processes. (this is a merge of ncat-env-conninfo as of r31516) --- ncat/Makefile.in | 6 +-- ncat/docs/ncat.xml | 28 +++++++++- ncat/ncat_core.c | 42 +++++++++++++++ ncat/ncat_core.h | 3 ++ ncat/ncat_exec_win.c | 24 +++++++++ ncat/ncat_posix.c | 7 +++ ncat/scripts/log_ips.sh | 10 ++++ ncat/scripts/p0fme.py | 97 ++++++++++++++++++++++++++++++++++ ncat/scripts/whatismyip.lua | 4 ++ ncat/test/ncat-test.pl | 39 ++++++++++++++ ncat/test/test-environment.lua | 9 ++++ ncat/test/test-environment.sh | 11 ++++ 12 files changed, 276 insertions(+), 4 deletions(-) create mode 100644 ncat/scripts/log_ips.sh create mode 100755 ncat/scripts/p0fme.py create mode 100644 ncat/scripts/whatismyip.lua create mode 100644 ncat/test/test-environment.lua create mode 100755 ncat/test/test-environment.sh diff --git a/ncat/Makefile.in b/ncat/Makefile.in index 3de9fdcc9..fcb2a1697 100644 --- a/ncat/Makefile.in +++ b/ncat/Makefile.in @@ -135,16 +135,16 @@ config.h: $(SHELL) ./config.status; \ fi -test/addrset: test/addrset.o ncat_core.o sys_wrap.o util.o $(LUA_OBJS) +test/addrset: test/addrset.o ncat_core.o sys_wrap.o util.o ncat_posix.o $(LUA_OBJS) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS) $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) -test/test-uri: test/test-uri.o base64.o http.o ncat_core.o sys_wrap.o util.o $(LUA_OBJS) +test/test-uri: test/test-uri.o base64.o http.o ncat_core.o sys_wrap.o util.o ncat_posix.o $(LUA_OBJS) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS) $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) test/test-cmdline-split: test/test-cmdline-split.o ncat_posix.o ncat_core.o sys_wrap.o util.o $(LUA_OBJS) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS) $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) -test/test-wildcard: test/test-wildcard.o ncat_core.o ncat_ssl.o sys_wrap.o util.o $(LUA_OBJS) +test/test-wildcard: test/test-wildcard.o ncat_core.o ncat_ssl.o sys_wrap.o util.o ncat_posix.o $(LUA_OBJS) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS) $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) .PHONY: uninstall all clean distclean diff --git a/ncat/docs/ncat.xml b/ncat/docs/ncat.xml index e04f44b34..2ed699183 100644 --- a/ncat/docs/ncat.xml +++ b/ncat/docs/ncat.xml @@ -462,6 +462,30 @@ accept a maximum, definable, number of simultaneous connections controlled by the option. By default this is set to 100 (60 on Windows). + + --exec adds the following variables to the child's environment: + + + NCAT_REMOTE_ADDR - the remote address - in connect mode, + it's the one we're connecting to, in listen mode - it's the one + that connected to us, + + NCAT_REMOTE_PORT - the remote port number, where "remote" + means the same as above, + + NCAT_LOCAL_ADDR - our local address used for + establishing/receiving the connection, + + NCAT_LOCAL_PORT - our local port number, + + NCAT_PROTO - the protocol name - TCP, UDP or SCTP, + + + + + Currently the address fields are numeric IP addresses. In IPv6 + mode, the address might be not expanded. + @@ -476,7 +500,9 @@ Same as , except it tries to execute the command via /bin/sh. This means you don't have to specify the full path for the command, and shell facilities - like environment variables are available. + like environment variables are available. Ncat also sets a few + special environment variables (see for details). + diff --git a/ncat/ncat_core.c b/ncat/ncat_core.c index 49235d49e..8e9cb9f4e 100644 --- a/ncat/ncat_core.c +++ b/ncat/ncat_core.c @@ -511,3 +511,45 @@ static int ncat_hexdump(int logfd, const char *data, int len) return 1; } + +void setup_environment(struct fdinfo *info) +{ + union sockaddr_u su; + char ip[INET6_ADDRSTRLEN]; + char port[16]; + socklen_t alen = sizeof(su); + + if (getpeername(info->fd, &su.sockaddr, &alen) != 0) { + bye("getpeername failed: %s", socket_strerror(socket_errno())); + } + if (getnameinfo((struct sockaddr *)&su, alen, ip, sizeof(ip), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + setenv_portable("NCAT_REMOTE_ADDR", ip); + setenv_portable("NCAT_REMOTE_PORT", port); + } else { + bye("getnameinfo failed: %s", socket_strerror(socket_errno())); + } + + if (getsockname(info->fd, (struct sockaddr *)&su, &alen) < 0) { + bye("getsockname failed: %s", socket_strerror(socket_errno())); + } + if (getnameinfo((struct sockaddr *)&su, alen, ip, sizeof(ip), + port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + setenv_portable("NCAT_LOCAL_ADDR", ip); + setenv_portable("NCAT_LOCAL_PORT", port); + } else { + bye("getnameinfo failed: %s", socket_strerror(socket_errno())); + } + + switch(o.proto) { + case IPPROTO_TCP: + setenv_portable("NCAT_PROTO", "TCP"); + break; + case IPPROTO_SCTP: + setenv_portable("NCAT_PROTO", "SCTP"); + break; + case IPPROTO_UDP: + setenv_portable("NCAT_PROTO", "UDP"); + break; + } +} diff --git a/ncat/ncat_core.h b/ncat/ncat_core.h index 58e4f6a82..a7a79bba6 100644 --- a/ncat/ncat_core.h +++ b/ncat/ncat_core.h @@ -230,3 +230,6 @@ extern int ncat_hostaccess(char *matchaddr, char *filename, char *remoteip); /* Make it so that line endings read from a console are always \n (not \r\n). Defined in ncat_posix.c and ncat_win.c. */ extern void set_lf_mode(void); + +extern int setenv_portable(const char *name, const char *value); +extern void setup_environment(struct fdinfo *fdinfo); diff --git a/ncat/ncat_exec_win.c b/ncat/ncat_exec_win.c index 8c04933e5..dfaf04507 100644 --- a/ncat/ncat_exec_win.c +++ b/ncat/ncat_exec_win.c @@ -195,6 +195,28 @@ extern void set_pseudo_sigchld_handler(void (*handler)(void)) ncat_assert(rc != 0); } +int setenv_portable(const char *name, const char *value) +{ + BOOL ret = SetEnvironmentVariable(name, value); + if (ret == 0) { + DWORD last_error = GetLastError(); + switch (last_error) { + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + default: + if (o.debug) + logdebug("SetEnvironmentVariable: GetLastError returned %d\n", last_error); + errno = EINVAL; + break; + } + } + return ret != 0; +} + /* Run a command and redirect its input and output handles to a pair of anonymous pipes. The process handle and pipe handles are returned in the info struct. Returns the PID of the new process, or -1 on error. */ @@ -207,6 +229,8 @@ static int run_command_redirected(char *cmdexec, struct subprocess_info *info) STARTUPINFO si; PROCESS_INFORMATION pi; + setup_environment(&info->fdn); + /* Make the pipe handles inheritable. */ sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; diff --git a/ncat/ncat_posix.c b/ncat/ncat_posix.c index 314776d7c..9f47b2530 100644 --- a/ncat/ncat_posix.c +++ b/ncat/ncat_posix.c @@ -192,6 +192,8 @@ void netexec(struct fdinfo *info, char *cmdexec) Dup2(child_stdin[0], STDIN_FILENO); Dup2(child_stdout[1], STDOUT_FILENO); + setup_environment(info); + switch (o.execmode) { char **cmdargs; @@ -389,3 +391,8 @@ int ssl_load_default_ca_certs(SSL_CTX *ctx) return 0; } #endif + +int setenv_portable(const char *name, const char *value) +{ + return setenv(name, value, 1); +} diff --git a/ncat/scripts/log_ips.sh b/ncat/scripts/log_ips.sh new file mode 100644 index 000000000..8b0f0e505 --- /dev/null +++ b/ncat/scripts/log_ips.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +LOGFILE="log_ips.log" + +MSG="[`date`] Incoming connection from $NCAT_REMOTE_ADDR:$NCAT_REMOTE_PORT" + +echo $MSG >&2 +echo $MSG >> $LOGFILE + +echo "Yeah, hi." diff --git a/ncat/scripts/p0fme.py b/ncat/scripts/p0fme.py new file mode 100755 index 000000000..5f4d42ee3 --- /dev/null +++ b/ncat/scripts/p0fme.py @@ -0,0 +1,97 @@ +#!/usr/bin/python + +from __future__ import print_function # logging, python2-only. + +""" +A script that reads data generated by p0f -f p0f.log, looking for all entries +about an IP read from NCAT_REMOTE_ADDR environment variable. Then it prints out +all the information it has found. To try it out, run "p0f -i any -o p0f.log" +and ncat -l -k --sh-exec "python p0fme.py". + +Script tested under Python versions 2.7 and 3.3. +""" + +P0F_LOG_FILE = "p0f.log" + +import datetime # logging +import sys # logging +import os # environ +import time # sleeping to wait for data +import sys # to flush STDOUT + + +def expand_ipv6(ip): + """ + Expands short IPv6 address like ::1 into an expanded form without trailing + zeros. Copied from: + + http://svn.python.org/projects/python/tags/r31b1/Lib/ipaddr.py + + (Py3 standard library; added some modifications to match p0f's output data) + """ + + new_ip = [] + hextet = ip.split('::') + sep = len(hextet[0].split(':')) + len(hextet[1].split(':')) + new_ip = hextet[0].split(':') + + for _ in range(8 - sep): + new_ip.append('0') + new_ip += hextet[1].split(':') + + # Now need to make sure every hextet is 4 lower case characters. + # If a hextet is < 4 characters, we've got missing leading 0's. + ret_ip = [] + for hextet in new_ip: + if hextet == '': + hextet = '0' + ret_ip.append(hextet.lower()) + return ':'.join(ret_ip) + + +def split_by_equals(str_): + ret = str_.split('=') + return ret[0], ''.join(ret[1:]) + +if __name__ == "__main__": + try: + ip = os.environ['NCAT_REMOTE_ADDR'] + if os.environ['NCAT_PROTO'] != 'TCP': + sys.exit("ERROR: This script works for TCP servers only!") + except KeyError: + sys.exit("ERROR: This script has to be run from inside of Ncat.") + print("[%s] Got a request from %s" % ( + datetime.datetime.now().isoformat(' '), ip), file=sys.stderr) + + print("Hold on, I'm collecting data on you...") + sys.stdout.flush() + time.sleep(3.0) + + if ':' in ip: # We need to expand IPv6 addresses in a specific way. + ip = expand_ipv6(ip) + result = {} + + # Reading the log backward will give us more recent results. + for line in reversed(open(P0F_LOG_FILE).readlines()): + + without_date = line.split('] ') + if without_date == ['\n']: + continue + without_date = ''.join(without_date[1:]) + + # Create a key-value dictionary out of the '|'-separated substrings. + properties = dict(map(split_by_equals, without_date.split('|'))) + + if not properties['cli'].startswith(ip): + continue # Not the IP we're looking for, check next one. + + for key in properties: + if not key in result or result[key] == '???': + result[key] = properties[key] + + if not result: + print("Got nothing on you. Try again and I will, though.") + + # Now that we've finished, print out the results. + for key in sorted(result): + print("%s: %s" % (key, result[key].rstrip())) diff --git a/ncat/scripts/whatismyip.lua b/ncat/scripts/whatismyip.lua new file mode 100644 index 000000000..f9409724d --- /dev/null +++ b/ncat/scripts/whatismyip.lua @@ -0,0 +1,4 @@ +--A "what is my IP" service code. Since most web browsers put up with servers +--not sending proper HTTP headers, you can simply query the service with it. + +print(os.getenv("NCAT_REMOTE_ADDR")) diff --git a/ncat/test/ncat-test.pl b/ncat/test/ncat-test.pl index e3b1f0a49..4662ed198 100755 --- a/ncat/test/ncat-test.pl +++ b/ncat/test/ncat-test.pl @@ -381,6 +381,16 @@ sub max_conns_test_tcp_ssl { max_conns_test_multi(["tcp", "tcp ssl"], @_); } +sub match_ncat_environment { + $_ = shift; + return /NCAT_REMOTE_ADDR=.+\n + NCAT_REMOTE_PORT=.+\n + NCAT_LOCAL_ADDR=.+\n + NCAT_LOCAL_PORT=.+\n + NCAT_PROTO=.+ + /x; +} + # Ignore broken pipe signals that result when trying to read from a terminated # client. $SIG{PIPE} = "IGNORE"; @@ -1084,6 +1094,35 @@ server_client_test_all "--lua-exec", $resp eq "ABC\n" or die "Client received " . d($resp) . ", not " . d("ABC\n"); }; +# Test environment variables being set for --exec, --sh-exec and --lua-exec. + +server_client_test_all "--exec, environment variables", +["--exec", "/bin/sh test-environment.sh"], [], sub { + syswrite($c_in, "abc\n"); + my $resp = timeout_read($c_out) or die "Read timeout"; + match_ncat_environment($resp) or die "Client received " . d($resp) . "."; +}; + +server_client_test_all "--sh-exec, environment variables", +["--sh-exec", "sh test-environment.sh"], [], sub { + syswrite($c_in, "abc\n"); + my $resp = timeout_read($c_out) or die "Read timeout"; + match_ncat_environment($resp) or die "Client received " . d($resp) . "."; +}; + +proxy_test "--exec through proxy, environment variables", +[], [], ["--exec", "/bin/sh test-environment.sh"], sub { + my $resp = timeout_read($s_out) or die "Read timeout"; + match_ncat_environment($resp) or die "Client received " . d($resp) . "."; +}; + +server_client_test_all "--lua-exec, environment variables", +["--lua-exec", "test-environment.lua"], [], sub { + syswrite($c_in, "abc\n"); + my $resp = timeout_read($c_out) or die "Read timeout"; + match_ncat_environment($resp) or die "Client received " . d($resp) . "."; +}; + # Do a syswrite and then a delay to force separate reads in the subprocess. sub delaywrite { my ($handle, $data) = @_; diff --git a/ncat/test/test-environment.lua b/ncat/test/test-environment.lua new file mode 100644 index 000000000..24b597be1 --- /dev/null +++ b/ncat/test/test-environment.lua @@ -0,0 +1,9 @@ +#!/usr/bin/lua + +--Print the following NCAT_* variables and their values: +envs = {'REMOTE_ADDR', 'REMOTE_PORT', 'LOCAL_ADDR', 'LOCAL_PORT', 'PROTO'} + +for _,v in pairs(envs) do + v = 'NCAT_' .. v + print(("%s=%s"):format(v, os.getenv(v))) +end diff --git a/ncat/test/test-environment.sh b/ncat/test/test-environment.sh new file mode 100755 index 000000000..e78a2209a --- /dev/null +++ b/ncat/test/test-environment.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Print the contents of all environment variables set by Ncat. + +echo "NCAT_REMOTE_ADDR=$NCAT_REMOTE_ADDR" +echo "NCAT_REMOTE_PORT=$NCAT_REMOTE_PORT" + +echo "NCAT_LOCAL_ADDR=$NCAT_LOCAL_ADDR" +echo "NCAT_LOCAL_PORT=$NCAT_LOCAL_PORT" + +echo "NCAT_PROTO=$NCAT_PROTO"