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"