1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +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-*-
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.
[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-type <type> Specify proxy type ("http", "socks4", "socks5")
--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-cert Specify SSL certificate file (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>
</listitem>
</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>
</refsect1>
@@ -817,7 +849,9 @@
<listitem>
<para>Completely disable hostname resolution across all Ncat options,
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>
</varlistentry>

View File

@@ -438,6 +438,15 @@ static int do_proxy_http(void)
size_t len;
int sd, code;
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);
if (sd == -1) {
@@ -445,12 +454,35 @@ static int do_proxy_http(void)
return -1;
}
request = NULL;
status_line = NULL;
header = NULL;
if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) {
/* 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);
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. */
request = http_connect_request(o.target,o.portno, &n);
request = http_connect_request(target, o.portno, &n);
if (send(sd, request, n, 0) < 0) {
loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno()));
goto bail;
@@ -501,7 +533,7 @@ static int do_proxy_http(void)
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) {
loguser("Error building Proxy-Authorization header.\n");
http_challenge_free(&challenge);
@@ -569,9 +601,18 @@ bail:
static int do_proxy_socks4(void)
{
struct socket_buffer stateful_buf;
struct socks4_data socks4msg;
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);
if (sd == -1) {
@@ -591,46 +632,40 @@ static int do_proxy_socks4(void)
socks4msg.type = SOCKS_CONNECT;
socks4msg.port = htons(o.portno);
switch(getaddrfamily(o.target)) {
case 1: // IPv4 address family
socks4msg.address = inet_addr(o.target);
if (strlen(username) >= sizeof(socks4msg.data)) {
loguser("Error: username is too long.\n");
close(sd);
return -1;
}
strcpy(socks4msg.data, username);
datalen = strlen(username) + 1;
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");
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;
case -1: // fqdn
socks4msg.address = inet_addr("0.0.0.1");
if (strlen(o.target) > SOCKS_BUFF_SIZE-2) {
loguser("Error: host name is too long.\n");
close(sd);
return -1;
}
if (o.proxy_auth){
if (strlen(o.target)+strlen(o.proxy_auth) > SOCKS_BUFF_SIZE-2) {
loguser("Error: host name and username are too long.\n");
close(sd);
return -1;
}
Strncpy(socks4msg.data,o.proxy_auth,sizeof(socks4msg.data));
len += strlen(o.proxy_auth);
}
memcpy(socks4msg.data+(len-8), o.target, strlen(o.target));
len += strlen(o.target)+1;
}
if (o.verbose)
loguser("Host %s will be resolved by the proxy.\n", o.target);
socks4msg.address = inet_addr("0.0.0.1");
if (datalen + strlen(o.target) >= sizeof(socks4msg.data)) {
loguser("Error: host name is too long.\n");
close(sd);
return -1;
}
strcpy(socks4msg.data + datalen, o.target);
datalen += strlen(o.target) + 1;
} else {
/* addr is now populated with sockaddr_in */
socks4msg.address = addr.in.sin_addr.s_addr;
if (o.verbose && getaddrfamily(o.target) == -1)
loguser("Host %s locally resolved to %s.\n", o.target,
inet_ntoa(addr.in.sin_addr));
}
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()));
close(sd);
return -1;
@@ -659,11 +694,8 @@ static int do_proxy_socks4(void)
*/
static int do_proxy_socks5(void)
{
struct socket_buffer stateful_buf;
struct socks5_connect socks5msg;
uint32_t inetaddr;
char inet6addr[16];
uint16_t proxyport = htons(o.portno);
char socksbuf[8];
int sd;
@@ -672,6 +704,11 @@ static int do_proxy_socks5(void)
struct socks5_auth socks5auth;
char *uptr, *pptr;
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);
if (sd == -1) {
@@ -814,38 +851,47 @@ static int do_proxy_socks5(void)
socks5msg2.cmd = SOCKS_CONNECT;
socks5msg2.rsv = 0;
switch(getaddrfamily(o.target)) {
case 1: // IPv4 address family
socks5msg2.atyp = SOCKS5_ATYP_IPv4;
inetaddr = inet_addr(o.target);
memcpy(socks5msg2.dst, &inetaddr, 4);
dstlen = 4;
break;
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;
targetlen=strlen(o.target);
if (targetlen > SOCKS5_DST_MAXLEN){
loguser("Error: hostname length exceeds %d.\n", SOCKS5_DST_MAXLEN);
close(sd);
return -1;
}
dstlen = 0;
socks5msg2.dst[dstlen++] = targetlen;
memcpy(socks5msg2.dst + dstlen, o.target, targetlen);
dstlen += targetlen;
break;
default: // this shall not happen
ncat_assert(0);
if (proxyresolve(o.target, 0, &addr.storage, &sslen, o.af)) {
/* 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);
socks5msg2.atyp = SOCKS5_ATYP_NAME;
targetlen=strlen(o.target);
if (targetlen > SOCKS5_DST_MAXLEN){
loguser("Error: hostname length exceeds %d.\n", SOCKS5_DST_MAXLEN);
close(sd);
return -1;
}
dstlen = 0;
socks5msg2.dst[dstlen++] = targetlen;
memcpy(socks5msg2.dst + dstlen, o.target, 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;
case AF_INET6:
socks5msg2.atyp = SOCKS5_ATYP_IPv6;
addrbuf = &addr.in6.sin6_addr;
addrlen = 16;
break;
default:
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);

View File

@@ -207,6 +207,7 @@ void options_init(void)
o.proxy_auth = NULL;
o.proxytype = NULL;
o.proxyaddr = NULL;
o.proxydns = PROXYDNS_REMOTE;
o.zerobyte = 0;
#ifdef HAVE_OPENSSL
@@ -297,6 +298,32 @@ int resolve(const char *hostname, unsigned short port,
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
all results into a linked list.
The rest of the behavior is same as resolve(). */

View File

@@ -160,6 +160,10 @@ enum exec_mode {
EXEC_LUA,
};
/* Proxy DNS resolution options (mask bits) */
#define PROXYDNS_LOCAL 1
#define PROXYDNS_REMOTE 2
struct options {
unsigned short portno;
@@ -211,6 +215,7 @@ struct options {
char *proxy_auth;
char *proxytype;
char *proxyaddr;
int proxydns;
int ssl;
char *sslcert;
@@ -242,6 +247,18 @@ void options_init(void);
int resolve(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 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
all results into a linked list.
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-type", required_argument, NULL, 0},
{"proxy-auth", required_argument, NULL, 0},
{"proxy-dns", required_argument, NULL, 0},
{"nsock-engine", required_argument, NULL, 0},
{"test", no_argument, NULL, 0},
{"ssl", no_argument, &o.ssl, 1},
@@ -490,6 +491,17 @@ int main(int argc, char *argv[])
if (o.proxy_auth)
bye("You can't specify more than one --proxy-auth.");
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) {
if (nsock_set_default_engine(optarg) < 0)
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-type <type> Specify proxy type (\"http\", \"socks4\", \"socks5\")\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
" --ssl Connect or listen with SSL\n"