📄 nessus_tcp_scanner.c
字号:
/* * Copyright (C) 2004 Michel Arboi <mikhail@nessus.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation * * 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <includes.h>#ifdef LINUX#include <netinet/tcp.h>#include <netinet/ip.h>#endif#include <limits.h>#include <math.h>#if defined STANDALONE && defined __STDC__#include <stdarg.h>#endif#ifndef ipci#define ipci() #define ipcd()#define ipcc() ( 0 == 1 )#define cct(x,y) ( 1 == 1 )#endif#if ! defined FD_SETSIZE || FD_SETSIZE > 1024#define GRAB_MAX_SOCK 1024#else#define GRAB_MAX_SOCK FD_SETSIZE#endif#if ! defined FD_SETSIZE || FD_SETSIZE > 32#define GRAB_MIN_SOCK 32#else#define GRAB_MIN_SOCK FD_SETSIZE#warn "FD_SETSIZE is lower than 32"#endif#if ! defined FD_SETSIZE || FD_SETSIZE > 128#define GRAB_MAX_SOCK_SAFE 128#else#define GRAB_MAX_SOCK_SAFE FD_SETSIZE#warn "FD_SETSIZE is lower than 128"#endif#define MAX_PASS_NB 16#ifndef MAXINT#define MAXINT 0x7fffffffL#endif/* MA 2006-09-29 * As far as I know... * RANDOMIZE_PORTS does not have any bad side effect. * (we do not really "randomize" ports, we use an increment > 1) * RST_RATE_LIMIT may trigger wrongly on a slow link. * DETECT_FIREWALL may overload a slow link */#define RANDOMIZE_PORTS_TXT "Scan ports in random order"#define RST_RATE_LIMIT_TXT "Detect RST rate limitation"#define DETECT_FIREWALL_TXT "Detect firewall"#define NET_CONGESTION_TXT "Network congestion detection"#define RANDOMIZE_PORTS_OPT (1 << 0)#define RST_RATE_LIMIT_OPT (1 << 1)#define DETECT_FIREWALL_OPT (1 << 2)#define NET_CONGESTION_OPT (1 << 3)#if ! defined STANDALONEPlugExport int plugin_init(struct arglist * desc){ plug_set_id(desc, 10335); plug_set_version(desc, "$Revision: 1.65 $"); plug_set_name(desc, "Nessus TCP scanner", NULL); plug_set_summary(desc, "Look for open TCP ports & services banners", NULL); plug_set_description(desc, "\This plugin is a classical TCP port scanner\n\It shall be reasonably quick even against a firewalled target.\n\\n\Once a TCP connection is open, it grabs any available banner\n\for the service identification plugins\n\\n\Note that TCP scanners are more intrusive than \n\SYN (half open) scanners\", NULL); plug_set_copyright(desc, "(C) 2004 Michel Arboi <mikhail@nessus.org>", NULL); plug_set_category(desc, ACT_SCANNER); plug_set_family(desc, "Scanners de ports", "francais"); plug_set_family(desc, "Port scanners", NULL); add_plugin_preference(desc, RANDOMIZE_PORTS_TXT, PREF_CHECKBOX, "yes"); add_plugin_preference(desc, RST_RATE_LIMIT_TXT, PREF_CHECKBOX, "yes"); add_plugin_preference(desc, DETECT_FIREWALL_TXT, PREF_CHECKBOX, "yes");#ifdef TCP_INFO add_plugin_preference(desc, NET_CONGESTION_TXT, PREF_CHECKBOX, "yes");#endif plug_set_dep(desc, "ping_host.nasl"); return(0);}#endiftypedef struct { int fd; struct timeval tictac; /* open time */ unsigned short port; unsigned char state;} grab_socket_t;#define DIFFTV(t1,t2) (t1.tv_sec - t2.tv_sec + (t1.tv_usec - t2.tv_usec) / 1000000)#define DIFFTVu(t1,t2) ((t1.tv_sec - t2.tv_sec) * 1000000.0 + (t1.tv_usec - t2.tv_usec))#define GRAB_SOCKET_UNUSED 0#define GRAB_SOCKET_OPENING 1#define GRAB_SOCKET_OPEN 2#define GRAB_PORT_UNKNOWN 0#define GRAB_PORT_CLOSED 1#define GRAB_PORT_OPEN 2#define GRAB_PORT_SILENT 3#define GRAB_PORT_REJECTED 4#define GRAB_PORT_NOT_TESTED 254#define GRAB_PORT_TESTING 255/* Increase if you want verbose logs * This value is overwritten by the NESSUS_TCP_SCANNER_DEBUG environment * variable, if it is set */static int debug_level = 0;/* * RTT is always estimated (at least, the maximum is remembered) * If you want to enable the "statistics", define COMPUTE_RTT and link * the plugin with libm (we need sqrt) */#define COMPUTE_RTT/* Linux re-sends a SYN packet after 3 s. On some other OSs, this value * can be configured. e.g. http://support.microsoft.com/kb/223450/EN-US/ * Anyway, I don't think that we can really have a RTT bigger than 2 s * I set 2.5 s just in case... * MA 2006-09-29 * a random value is added to the maximum RTT to get the timeout * This value is 100 ms at most. * In ANY case, the total timeout MUST be inferior to the retransmission * interval. Otherwise, performances will collapse against some firewalled * machines. */#if defined TCP_INFO#define MAX_SANE_RTT (2900 * 1000) /* 2.9 s */#else#define MAX_SANE_RTT (2500*1000) /* micro-seconds => 2.5 s */#endifstatic int#ifdef __GNUC____attribute__((format(printf, 3, 4)))#endifdebug_printf(const void *pia, /* may be NULL */ int debug, const char *fmt, ...){ char buf[256]; int i; va_list ap; if (debug < 0 || debug > debug_level) return 0; va_start (ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); buf[sizeof(buf)-1] = '\0'; va_end (ap); /* Remove the \n at the end */ for (i = 0; i < sizeof(buf); i ++) if (buf[i] == '\0') break; if (i > 0 && buf[i-1] == '\n') buf[i-1] = '\0'; if (pia == NULL) fprintf(stderr, "nessus_tcp_scanner[%d]: %s\n", getpid(), buf); else fprintf(stderr, "nessus_tcp_scanner(%s)[%d]: %s\n", pia ? inet_ntoa(*(const struct in_addr*) pia):"ipv6", getpid(), buf); return 0;} /* * On Linux, is_sane_rtt checks if there have been retransmits or not. * It also returns congestion detected by the kernel. * On other systems where TCP_INFO is not available, or if the getsockopt call * failed, it only checks if rtt < MAX_SANE_RTT */static intis_sane_rtt(int rtt, int s, int *congestion){#if defined TCP_INFO struct tcp_info ti; int e; socklen_t len = sizeof(ti); e = getsockopt(s, IPPROTO_TCP, TCP_INFO, &ti, &len); if (e >= 0 && len == sizeof(ti)) { debug_printf(NULL, 3, "tcpi_retransmits=0x%x tcpi_unacked=%u tcpi_ca_state=0x%x tcpi_rtt=%u tcpi_rttvar=%u x=%d\n", (unsigned) ti.tcpi_retransmits, ti.tcpi_unacked, (unsigned) ti.tcpi_ca_state, ti.tcpi_rtt, ti.tcpi_rttvar, rtt); if (congestion != NULL) *congestion += (ti.tcpi_ca_state & (1 << TCP_CA_CWR)) != 0; return ti.tcpi_retransmits == 0; } if (e < 0) { if (errno != EBADF && debug_level > 0) perror("nessus_tcp_scanner->getsockopt(TCP_INFO)"); } else debug_printf(NULL, 1, "getsockopt(TCP_INFO)=%d != %d\n", len, sizeof(ti));#endif return rtt < MAX_SANE_RTT;}/* * If SO_LINGER was not set, my_socket_close calls shutdown to try to speed * up the close process */static intmy_socket_close(int s){#ifndef SO_LINGER if (shutdown(s, 2) < 0) if (debug_level > 0) perror("nessus_tcp_scanner->shutdown");#endif if ( s >= 0 ) ipcd(); return close(s);}/* Just a glue function to avoid ugly #ifdef in the main code */static intset_so_linger(int s){#ifdef SO_LINGER struct linger l; l.l_onoff = 0; l.l_linger = 0; if (setsockopt(s, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) { perror("nessus_tcp_scanner->setsockopt(SO_LINGER)"); return -1; }#endif return 0;}static intset_nonblock(int s){ int x; if ((x = fcntl(s, F_GETFL)) < 0) { perror("nessus_tcp_scanner->fcntl(F_GETFL)"); return -1; } if (fcntl(s, F_SETFL, x | O_NONBLOCK) < 0) { perror("nessus_tcp_scanner->fcntl(F_SETFL)"); return -1; } return 0;}/* * Linux can set IP TOS field from userland. This is probably not very useful, * as IP TOS (RFC791) is obsoleted by RFC2474, but some QoS systems may * still sort packets according to this field. * RFC3168 deprecates IPTOS_MINCOST, as it conflicts with * the "ECN capable" flags */static intset_ip_tos(int s){ int x;#if defined LINUX && defined IPTOS_RELIABILITY x = IPTOS_RELIABILITY; if (setsockopt(s, SOL_IP, IP_TOS, &x, sizeof(x)) < 0) { perror("nessus_tcp_scanner->setsockopt(IP_TOS,IPTOS_RELIABILITY"); return -1; }#endif return 0;}static int std_port(int port){ const char *name; extern char* nessus_get_svc_name(); if (port < 1 || port > 65535) return 0; name = nessus_get_svc_name(port, NULL); if (name == NULL || strcmp(name, "unknown") == 0) return 0; debug_printf(NULL, 3, "std_port(%d)=%s\n", port, name != NULL ? name : "(null)"); return 1;}/* * double_check_std_ports resets silent (filtered) standard ports to * "to be tested" state. * (standard ports = those from nessus-services, which are included in the * "default" port range) * Return value: * Number of filtered or unknown ports; and number of "rejected" ports in an * auxilliary pointer */static intdouble_check_std_ports(unsigned char* ports_states, int *rejected){ int port, tbd_nb = 0; if (rejected != NULL) (*rejected) = 0; for (port = 1; port <= 65535; port ++) if (ports_states[port] == GRAB_PORT_UNKNOWN) { debug_printf(NULL, 0, "bug in double_check_std_ports! Unknown port %d status\n", port); tbd_nb ++; } else if (std_port(port)) { if (ports_states[port] == GRAB_PORT_SILENT) { ports_states[port] = GRAB_PORT_UNKNOWN; tbd_nb ++; } else if (ports_states[port] == GRAB_PORT_REJECTED) { if (rejected != NULL) (*rejected) ++; } } debug_printf(NULL, 1, "double_check_std_ports found %d dropped standard ports\n", tbd_nb); return tbd_nb;}static intget_ping_rtt(const struct in_addr *pia /* for debug only, may be NULL */, struct arglist *desc){ char *k; int type = 0, ping_rtt = 0; k = plug_get_key(desc, "/tmp/ping/RTT", &type); if (type == ARG_STRING && k != NULL) ping_rtt = atoi(k); else if (type == ARG_INT) ping_rtt = (int) k; else if (type >= 0) debug_printf(pia, 0, "unknown key type %d\n", type); if (ping_rtt < 0 || ping_rtt > MAX_SANE_RTT) ping_rtt = 0; else debug_printf(pia, 1, "ping_rtt=%g s\n", ping_rtt / 1e6); return ping_rtt;}static intget_port_range(const char *portrange, unsigned char *ports_states /* Will be set */){ int i, n, untested_ports_nb = 0; unsigned short *nums = (unsigned short*)getpts((char*)portrange, &n); if (nums == NULL ) { debug_printf(NULL, 0, "nessus_tcp_scanner: getpts returns NULL. Fix your Nessus installation!\n"); return -1; } for (i = 0; i < n; i ++) { ports_states[nums[i]] = GRAB_PORT_UNKNOWN; untested_ports_nb ++; } /* Do *not* free nums */ return untested_ports_nb;}/* * Double-check pass will disrupt our counters * Rather that saving old variables & restoring them on some branches of code, * it is safer to recount everything. * We need to go through the whole array of ports to reset the status of * silent (filtered) ports to unknown/"to be tested", so this function call * is not expensive */static voidrecount_ports(unsigned char *ports_states, int retest, int *open, int *closed, int *dropped, int *rejected, int *untested){ int i; *dropped = *rejected = *open = *closed = *untested = 0; for (i = 1; i <= 65535; i ++) switch(ports_states[i]) { case GRAB_PORT_SILENT: if (retest) { (*untested) ++; ports_states[i] = GRAB_PORT_UNKNOWN; } else (*dropped) ++; break; case GRAB_PORT_REJECTED: (*rejected) ++; break; case GRAB_PORT_OPEN: (*open) ++; break; case GRAB_PORT_CLOSED: (*closed) ++; break; case GRAB_PORT_UNKNOWN: (*untested) ++; break; }}/* * The main function */static intbanner_grab(const struct in_addr *pia, #ifdef IPV6_SUPPORT const struct sockaddr_in6 * pia6, #endif const char* portrange,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -