1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-08 05:31:31 +00:00

Ability to control hostname resolution for ncat proxy destinations

Closes #1214, fixes #1230, closes #1439
This commit is contained in:
nnposter
2019-02-23 22:34:13 +00:00
parent 12f1894f97
commit dbed133fc5
7 changed files with 220 additions and 78 deletions

View File

@@ -1,5 +1,9 @@
#Nmap Changelog ($Id$); -*-text-*- #Nmap Changelog ($Id$); -*-text-*-
o [ncat][GH#1214][GH#1230][GH#1439] New ncat option provides control over
whether proxy destinations are resolved by the remote proxy server or
locally, by Ncat itself. See option --proxy-dns. [nnposter]
o [NSE][GH#1478] Updated script ftp-syst to prevent potential endless looping. o [NSE][GH#1478] Updated script ftp-syst to prevent potential endless looping.
[nnposter] [nnposter]

View File

@@ -42,6 +42,7 @@ Options taking a time assume seconds. Append 'ms' for milliseconds,
--proxy <addr[:port]> Specify address of host to proxy through --proxy <addr[:port]> Specify address of host to proxy through
--proxy-type <type> Specify proxy type ("http", "socks4", "socks5") --proxy-type <type> Specify proxy type ("http", "socks4", "socks5")
--proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server --proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server
--proxy-dns <type> Specify where to resolve proxy destination
--ssl Connect or listen with SSL --ssl Connect or listen with SSL
--ssl-cert Specify SSL certificate file (PEM) for listening --ssl-cert Specify SSL certificate file (PEM) for listening
--ssl-key Specify SSL private key (PEM) for listening --ssl-key Specify SSL private key (PEM) for listening

View File

@@ -468,6 +468,38 @@
<option>--proxy-type socks4</option>, it should be a username only.</para> <option>--proxy-type socks4</option>, it should be a username only.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<option>--proxy-dns <replaceable>type</replaceable></option> (Specify where to resolve proxy destination)
<indexterm><primary><option>--proxy-dns</option> (Ncat option)</primary></indexterm>
</term>
<listitem>
<para>In connect mode, it provides control over whether proxy
destination hostnames are resolved by the remote proxy server or
locally, by Ncat itself.
Possible values for <replaceable>type</replaceable> are:</para>
<para><literal>local</literal> - Hostnames are resolved locally on
the Ncat host. Ncat exits with error if the hostname cannot be
resolved.</para>
<para><literal>remote</literal> - Hostnames are passed directly onto
the remote proxy server. This is the default behavior.</para>
<para><literal>both</literal> - Hostname resolution is first
attempted on the Ncat host. Unresolvable hostnames are passed onto
the remote proxy server.</para>
<para><literal>none</literal> - Hostname resolution is completely
disabled. Only a literal IPv4 or IPv6 address can be used as
the proxy destination.</para>
<para>Local hostname resolution generally respects IP version
specified with options <option>-4</option> or <option>-6</option>,
except for SOCKS4, which is incompatible with IPv6.</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
@@ -817,7 +849,9 @@
<listitem> <listitem>
<para>Completely disable hostname resolution across all Ncat options, <para>Completely disable hostname resolution across all Ncat options,
such as the destination, source address, source routing hops, and such as the destination, source address, source routing hops, and
the proxy. All addresses must be specified numerically.</para> the proxy. All addresses must be specified numerically.
(Note that resolution of proxy destinations is controlled separately
via option <option>--proxy-dns</option>.)</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@@ -438,6 +438,15 @@ static int do_proxy_http(void)
size_t len; size_t len;
int sd, code; int sd, code;
int n; int n;
char *target;
union sockaddr_u addr;
size_t sslen;
void *addrbuf;
char addrstr[INET6_ADDRSTRLEN];
request = NULL;
status_line = NULL;
header = NULL;
sd = do_connect(SOCK_STREAM); sd = do_connect(SOCK_STREAM);
if (sd == -1) { if (sd == -1) {
@@ -445,12 +454,35 @@ static int do_proxy_http(void)
return -1; return -1;
} }
request = NULL; if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) {
status_line = NULL; /* target resolution has failed, possibly because it is disabled */
header = NULL; if (!(o.proxydns & PROXYDNS_REMOTE)) {
loguser("Error: Failed to resolve host %s locally.\n", o.target);
goto bail;
}
if (o.verbose)
loguser("Host %s will be resolved by the proxy.\n", o.target);
target = o.target;
} else {
/* addr is now populated with either sockaddr_in or sockaddr_in6 */
switch (addr.sockaddr.sa_family) {
case AF_INET:
addrbuf = &addr.in.sin_addr;
break;
case AF_INET6:
addrbuf = &addr.in6.sin6_addr;
break;
default:
ncat_assert(0);
}
inet_ntop(addr.sockaddr.sa_family, addrbuf, addrstr, sizeof(addrstr));
target = addrstr;
if (o.verbose && getaddrfamily(o.target) == -1)
loguser("Host %s locally resolved to %s.\n", o.target, target);
}
/* First try a request with no authentication. */ /* First try a request with no authentication. */
request = http_connect_request(o.target,o.portno, &n); request = http_connect_request(target, o.portno, &n);
if (send(sd, request, n, 0) < 0) { if (send(sd, request, n, 0) < 0) {
loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno()));
goto bail; goto bail;
@@ -501,7 +533,7 @@ static int do_proxy_http(void)
goto bail; goto bail;
} }
request = http_connect_request_auth(o.target,o.portno, &n, &challenge); request = http_connect_request_auth(target, o.portno, &n, &challenge);
if (request == NULL) { if (request == NULL) {
loguser("Error building Proxy-Authorization header.\n"); loguser("Error building Proxy-Authorization header.\n");
http_challenge_free(&challenge); http_challenge_free(&challenge);
@@ -569,9 +601,18 @@ bail:
static int do_proxy_socks4(void) static int do_proxy_socks4(void)
{ {
struct socket_buffer stateful_buf; struct socket_buffer stateful_buf;
struct socks4_data socks4msg;
char socksbuf[8]; char socksbuf[8];
int sd,len = 9; struct socks4_data socks4msg;
size_t datalen;
char *username = o.proxy_auth != NULL ? o.proxy_auth : "";
union sockaddr_u addr;
size_t sslen;
int sd;
if (getaddrfamily(o.target) == 2) {
loguser("Error: IPv6 addresses are not supported with Socks4.\n");
return -1;
}
sd = do_connect(SOCK_STREAM); sd = do_connect(SOCK_STREAM);
if (sd == -1) { if (sd == -1) {
@@ -591,46 +632,40 @@ static int do_proxy_socks4(void)
socks4msg.type = SOCKS_CONNECT; socks4msg.type = SOCKS_CONNECT;
socks4msg.port = htons(o.portno); socks4msg.port = htons(o.portno);
switch(getaddrfamily(o.target)) { if (strlen(username) >= sizeof(socks4msg.data)) {
case 1: // IPv4 address family loguser("Error: username is too long.\n");
socks4msg.address = inet_addr(o.target);
if (o.proxy_auth){
memcpy(socks4msg.data, o.proxy_auth, strlen(o.proxy_auth));
len += strlen(o.proxy_auth);
}
break;
case 2: // IPv6 address family
loguser("Error: IPv6 addresses are not supported with Socks4.\n");
close(sd); close(sd);
return -1; return -1;
}
strcpy(socks4msg.data, username);
datalen = strlen(username) + 1;
case -1: // fqdn if (proxyresolve(o.target, 0, &addr.storage, &sslen, AF_INET)) {
/* target resolution has failed, possibly because it is disabled */
if (!(o.proxydns & PROXYDNS_REMOTE)) {
loguser("Error: Failed to resolve host %s locally.\n", o.target);
close(sd);
return -1;
}
if (o.verbose)
loguser("Host %s will be resolved by the proxy.\n", o.target);
socks4msg.address = inet_addr("0.0.0.1"); socks4msg.address = inet_addr("0.0.0.1");
if (datalen + strlen(o.target) >= sizeof(socks4msg.data)) {
if (strlen(o.target) > SOCKS_BUFF_SIZE-2) {
loguser("Error: host name is too long.\n"); loguser("Error: host name is too long.\n");
close(sd); close(sd);
return -1; return -1;
} }
strcpy(socks4msg.data + datalen, o.target);
if (o.proxy_auth){ datalen += strlen(o.target) + 1;
if (strlen(o.target)+strlen(o.proxy_auth) > SOCKS_BUFF_SIZE-2) { } else {
loguser("Error: host name and username are too long.\n"); /* addr is now populated with sockaddr_in */
close(sd); socks4msg.address = addr.in.sin_addr.s_addr;
return -1; if (o.verbose && getaddrfamily(o.target) == -1)
} loguser("Host %s locally resolved to %s.\n", o.target,
Strncpy(socks4msg.data,o.proxy_auth,sizeof(socks4msg.data)); inet_ntoa(addr.in.sin_addr));
len += strlen(o.proxy_auth);
}
memcpy(socks4msg.data+(len-8), o.target, strlen(o.target));
len += strlen(o.target)+1;
} }
if (send(sd, (char *) &socks4msg, len, 0) < 0) { if (send(sd, (char *)&socks4msg, offsetof(struct socks4_data, data) + datalen, 0) < 0) {
loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno())); loguser("Error: sending proxy request: %s.\n", socket_strerror(socket_errno()));
close(sd); close(sd);
return -1; return -1;
@@ -659,11 +694,8 @@ static int do_proxy_socks4(void)
*/ */
static int do_proxy_socks5(void) static int do_proxy_socks5(void)
{ {
struct socket_buffer stateful_buf; struct socket_buffer stateful_buf;
struct socks5_connect socks5msg; struct socks5_connect socks5msg;
uint32_t inetaddr;
char inet6addr[16];
uint16_t proxyport = htons(o.portno); uint16_t proxyport = htons(o.portno);
char socksbuf[8]; char socksbuf[8];
int sd; int sd;
@@ -672,6 +704,11 @@ static int do_proxy_socks5(void)
struct socks5_auth socks5auth; struct socks5_auth socks5auth;
char *uptr, *pptr; char *uptr, *pptr;
size_t authlen, ulen, plen; size_t authlen, ulen, plen;
union sockaddr_u addr;
size_t sslen;
void *addrbuf;
size_t addrlen;
char addrstr[INET6_ADDRSTRLEN];
sd = do_connect(SOCK_STREAM); sd = do_connect(SOCK_STREAM);
if (sd == -1) { if (sd == -1) {
@@ -814,23 +851,15 @@ static int do_proxy_socks5(void)
socks5msg2.cmd = SOCKS_CONNECT; socks5msg2.cmd = SOCKS_CONNECT;
socks5msg2.rsv = 0; socks5msg2.rsv = 0;
switch(getaddrfamily(o.target)) { if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) {
/* target resolution has failed, possibly because it is disabled */
case 1: // IPv4 address family if (!(o.proxydns & PROXYDNS_REMOTE)) {
socks5msg2.atyp = SOCKS5_ATYP_IPv4; loguser("Error: Failed to resolve host %s locally.\n", o.target);
inetaddr = inet_addr(o.target); close(sd);
memcpy(socks5msg2.dst, &inetaddr, 4); return -1;
dstlen = 4; }
break; if (o.verbose)
loguser("Host %s will be resolved by the proxy.\n", o.target);
case 2: // IPv6 address family
socks5msg2.atyp = SOCKS5_ATYP_IPv6;
inet_pton(AF_INET6,o.target,&inet6addr);
memcpy(socks5msg2.dst, inet6addr,16);
dstlen = 16;
break;
case -1: // FQDN
socks5msg2.atyp = SOCKS5_ATYP_NAME; socks5msg2.atyp = SOCKS5_ATYP_NAME;
targetlen=strlen(o.target); targetlen=strlen(o.target);
if (targetlen > SOCKS5_DST_MAXLEN){ if (targetlen > SOCKS5_DST_MAXLEN){
@@ -842,11 +871,28 @@ static int do_proxy_socks5(void)
socks5msg2.dst[dstlen++] = targetlen; socks5msg2.dst[dstlen++] = targetlen;
memcpy(socks5msg2.dst + dstlen, o.target, targetlen); memcpy(socks5msg2.dst + dstlen, o.target, targetlen);
dstlen += targetlen; dstlen += targetlen;
} else {
/* addr is now populated with either sockaddr_in or sockaddr_in6 */
switch (addr.sockaddr.sa_family) {
case AF_INET:
socks5msg2.atyp = SOCKS5_ATYP_IPv4;
addrbuf = &addr.in.sin_addr;
addrlen = 4;
break; break;
case AF_INET6:
default: // this shall not happen socks5msg2.atyp = SOCKS5_ATYP_IPv6;
addrbuf = &addr.in6.sin6_addr;
addrlen = 16;
break;
default:
ncat_assert(0); ncat_assert(0);
} }
memcpy(socks5msg2.dst, addrbuf, addrlen);
dstlen = addrlen;
if (o.verbose && getaddrfamily(o.target) == -1)
loguser("Host %s locally resolved to %s.\n", o.target,
inet_ntop(addr.sockaddr.sa_family, addrbuf, addrstr, sizeof(addrstr)));
}
memcpy(socks5msg2.dst + dstlen, &proxyport, 2); memcpy(socks5msg2.dst + dstlen, &proxyport, 2);
dstlen += 2; dstlen += 2;

View File

@@ -207,6 +207,7 @@ void options_init(void)
o.proxy_auth = NULL; o.proxy_auth = NULL;
o.proxytype = NULL; o.proxytype = NULL;
o.proxyaddr = NULL; o.proxyaddr = NULL;
o.proxydns = PROXYDNS_REMOTE;
o.zerobyte = 0; o.zerobyte = 0;
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
@@ -297,6 +298,32 @@ int resolve(const char *hostname, unsigned short port,
return result; return result;
} }
/* Resolves the given hostname or IP address with getaddrinfo, and stores the
first result (if any) in *ss and *sslen. The value of port will be set in the
appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in
which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one
is first depends on the system configuration. Returns 0 on success, or a
getaddrinfo return code (suitable for passing to gai_strerror) on failure.
*ss and *sslen are always defined when this function returns 0.
Resolve the hostname with DNS only if global o.proxydns includes PROXYDNS_LOCAL. */
int proxyresolve(const char *hostname, unsigned short port,
struct sockaddr_storage *ss, size_t *sslen, int af)
{
int flags;
struct sockaddr_list sl;
int result;
flags = 0;
if (!(o.proxydns & PROXYDNS_LOCAL))
flags |= AI_NUMERICHOST;
result = resolve_internal(hostname, port, &sl, af, flags, 0);
*ss = sl.addr.storage;
*sslen = sl.addrlen;
return result;
}
/* Resolves the given hostname or IP address with getaddrinfo, and stores /* Resolves the given hostname or IP address with getaddrinfo, and stores
all results into a linked list. all results into a linked list.
The rest of the behavior is same as resolve(). */ The rest of the behavior is same as resolve(). */

View File

@@ -160,6 +160,10 @@ enum exec_mode {
EXEC_LUA, EXEC_LUA,
}; };
/* Proxy DNS resolution options (mask bits) */
#define PROXYDNS_LOCAL 1
#define PROXYDNS_REMOTE 2
struct options { struct options {
unsigned short portno; unsigned short portno;
@@ -211,6 +215,7 @@ struct options {
char *proxy_auth; char *proxy_auth;
char *proxytype; char *proxytype;
char *proxyaddr; char *proxyaddr;
int proxydns;
int ssl; int ssl;
char *sslcert; char *sslcert;
@@ -242,6 +247,18 @@ void options_init(void);
int resolve(const char *hostname, unsigned short port, int resolve(const char *hostname, unsigned short port,
struct sockaddr_storage *ss, size_t *sslen, int af); struct sockaddr_storage *ss, size_t *sslen, int af);
/* Resolves the given hostname or IP address with getaddrinfo, and stores the
first result (if any) in *ss and *sslen. The value of port will be set in the
appropriate place in *ss; set to 0 if you don't care. af may be AF_UNSPEC, in
which case getaddrinfo may return e.g. both IPv4 and IPv6 results; which one
is first depends on the system configuration. Returns 0 on success, or a
getaddrinfo return code (suitable for passing to gai_strerror) on failure.
*ss and *sslen are always defined when this function returns 0.
Resolve the hostname with DNS only if global o.proxydns includes PROXYDNS_LOCAL. */
int proxyresolve(const char *hostname, unsigned short port,
struct sockaddr_storage *ss, size_t *sslen, int af);
/* Resolves the given hostname or IP address with getaddrinfo, and stores /* Resolves the given hostname or IP address with getaddrinfo, and stores
all results into a linked list. all results into a linked list.
The rest of behavior is same as resolve(). */ The rest of behavior is same as resolve(). */

View File

@@ -327,6 +327,7 @@ int main(int argc, char *argv[])
{"proxy", required_argument, NULL, 0}, {"proxy", required_argument, NULL, 0},
{"proxy-type", required_argument, NULL, 0}, {"proxy-type", required_argument, NULL, 0},
{"proxy-auth", required_argument, NULL, 0}, {"proxy-auth", required_argument, NULL, 0},
{"proxy-dns", required_argument, NULL, 0},
{"nsock-engine", required_argument, NULL, 0}, {"nsock-engine", required_argument, NULL, 0},
{"test", no_argument, NULL, 0}, {"test", no_argument, NULL, 0},
{"ssl", no_argument, &o.ssl, 1}, {"ssl", no_argument, &o.ssl, 1},
@@ -490,6 +491,17 @@ int main(int argc, char *argv[])
if (o.proxy_auth) if (o.proxy_auth)
bye("You can't specify more than one --proxy-auth."); bye("You can't specify more than one --proxy-auth.");
o.proxy_auth = optarg; o.proxy_auth = optarg;
} else if (strcmp(long_options[option_index].name, "proxy-dns") == 0) {
if (strcmp(optarg, "none") == 0)
o.proxydns = 0;
else if (strcmp(optarg, "local") == 0)
o.proxydns = PROXYDNS_LOCAL;
else if (strcmp(optarg, "remote") == 0)
o.proxydns = PROXYDNS_REMOTE;
else if (strcmp(optarg, "both") == 0)
o.proxydns = PROXYDNS_LOCAL | PROXYDNS_REMOTE;
else
bye("Invalid proxy DNS type.");
} else if (strcmp(long_options[option_index].name, "nsock-engine") == 0) { } else if (strcmp(long_options[option_index].name, "nsock-engine") == 0) {
if (nsock_set_default_engine(optarg) < 0) if (nsock_set_default_engine(optarg) < 0)
bye("Unknown or non-available engine: %s.", optarg); bye("Unknown or non-available engine: %s.", optarg);
@@ -647,6 +659,7 @@ int main(int argc, char *argv[])
" --proxy <addr[:port]> Specify address of host to proxy through\n" " --proxy <addr[:port]> Specify address of host to proxy through\n"
" --proxy-type <type> Specify proxy type (\"http\", \"socks4\", \"socks5\")\n" " --proxy-type <type> Specify proxy type (\"http\", \"socks4\", \"socks5\")\n"
" --proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server\n" " --proxy-auth <auth> Authenticate with HTTP or SOCKS proxy server\n"
" --proxy-dns <type> Specify where to resolve proxy destination\n"
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
" --ssl Connect or listen with SSL\n" " --ssl Connect or listen with SSL\n"