Lucene search

K
openvasCopyright (C) 2021 Greenbone Networks GmbHOPENVAS:1361412562310147205
HistoryNov 24, 2021 - 12:00 a.m.

IP Forwarding Enabled - Active Check

2021-11-2400:00:00
Copyright (C) 2021 Greenbone Networks GmbH
plugins.openvas.org
4

7.2 High

AI Score

Confidence

Low

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.003 Low

EPSS

Percentile

71.6%

Checks if the remote host has IP forwarding enabled.

# Copyright (C) 2021 Greenbone Networks GmbH
# Some text descriptions might be excerpted from (a) referenced
# source(s), and are Copyright (C) by the respective right holder(s).
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

if (description)
{
  script_oid("1.3.6.1.4.1.25623.1.0.147205");
  script_version("2021-12-03T08:27:06+0000");
  script_tag(name:"last_modification", value:"2021-12-03 08:27:06 +0000 (Fri, 03 Dec 2021)");
  script_tag(name:"creation_date", value:"2021-11-24 07:35:11 +0000 (Wed, 24 Nov 2021)");
  # nb: CVE linked below has a severity but is very broad, using a severity would cause too many
  # false positives as we're not able to determine if a system has IP Forwarding enabled on purpose
  # (e.g. a Router/Firewall) or not. This needs to be inspected manually.
  script_tag(name:"cvss_base", value:"0.0");
  script_tag(name:"cvss_base_vector", value:"AV:A/AC:L/Au:N/C:N/I:N/A:N");

  script_cve_id("CVE-1999-0511");

  script_tag(name:"qod_type", value:"remote_analysis");

  script_name("IP Forwarding Enabled - Active Check");

  script_category(ACT_GATHER_INFO);

  script_copyright("Copyright (C) 2021 Greenbone Networks GmbH");
  script_family("General");
  script_dependencies("global_settings.nasl", "smtp_settings.nasl"); # nb: The setting for get_3rdparty_domain() is currently located in this VT.
  script_mandatory_keys("keys/islocalnet");
  script_exclude_keys("keys/islocalhost");

  script_tag(name:"summary", value:"Checks if the remote host has IP forwarding enabled.");

  script_tag(name:"vuldetect", value:"Sends a crafted Local Link Layer (LLL) frame and checks the
  response.");

  exit(0);
}

include("misc_func.inc");
include("string_hex_func.inc");
include("version_func.inc");
include("dump.inc");
include("network_func.inc");
include("byte_func.inc");
include("smtp_func.inc");

# nb: Available since GOS 21.04.10 / openvas-scanner 21.04.4
if (!defined_func("get_local_mac_address_from_ip") ||
    !defined_func("send_arp_request") ||
    !defined_func("forge_frame") ||
    !defined_func("send_frame") ||
    !defined_func("dump_frame"))
  exit(0);

if (islocalhost())
  exit(0);

if (!islocalnet())
  exit(0);

debug = 0;
src_ip = this_host();
dst_ip = get_host_ip();
ext_host = get_3rdparty_domain();
ext_ips = resolve_hostname_to_multiple_ips(hostname: ext_host);

# nb: Loop over the list (e.g. if IPv4 and IPv6 IPs are returned) and pick out the first IP matching
# the used IP version.
foreach _ext_ip (ext_ips) {
  if (TARGET_IS_IPV6()) {
    if (":" >< _ext_ip) {
      ext_ip = _ext_ip;
      break;
    }
  } else {
    if (":" >!< _ext_ip) {
      ext_ip = _ext_ip;
      break;
    }
  }
}

# nb: Just a fallback if anything has gone wrong (e.g. we're scanning an IPv4 target but the
# as external defined host is only reachable via IPv6).
if (!ext_ip)
  ext_ip = resolve_host_name(hostname: ext_host);

src_mac = get_local_mac_address_from_ip(src_ip);
if (isnull(src_mac)) {
  if (debug) display("DEBUG: get_local_mac_address_from_ip() call failed for IP '", src_ip, "'");
  exit(0);
}

dst_mac = send_arp_request(host: dst_ip);
if (isnull(dst_mac)) {
  if (debug) display("DEBUG: send_arp_request() call failed for IP '", dst_ip, "'");
  exit(0);
}

if (debug) display("DEBUG:\nSource IP:       ", src_ip, "\nSource MAC:      ", src_mac, "\nDestination IP:  ", dst_ip, "\nDestination MAC: ", dst_mac, "\nExternal IP:     ", ext_ip, " (", ext_host, ")");

split = split(src_mac, sep: ":", keep:FALSE);
for (i = 0; i < max_index(split); i++)
  src_mac_raw += hex2raw(s: split[i]);

split = split(dst_mac, sep: ":", keep:FALSE);
for (i = 0; i < max_index(split); i++)
  dst_mac_raw += hex2raw(s: split[i]);

function create_packet(src_ip, ext_ip, ip_proto, src_mac_raw, debug) {

  local_var src_ip, ext_ip, ip_proto, src_mac_raw, debug;
  local_var ret_arry, vt_strings, data, src_port, dst_port, dst_ip, IPV6_VERSION, ip_packet, tcp_packet, filter, ether_proto;
  local_var ICMP6_ECHO_REQ_TYPE, ICMP6_ECHO_RES_TYPE, ICMP_ID, ICMP_ID_REVERSE, icmp_packet;
  local_var ICMP_ECHO_REQ_TYPE, ICMP_ECHO_RES_TYPE;

  ret_arry = make_array();
  vt_strings = get_vt_strings();
  data = vt_strings["default_rand"];
  src_port = (rand() + 1024) % 65535;
  dst_port = (rand() + 1024) % 65535;

  # nb: On TCP we're setting src_ip = dst_ip, on ICMP we're trying to sent an ICMP ECHO REQUEST to
  # an external system.
  if (ip_proto == IPPROTO_TCP)
    dst_ip = src_ip;
  else
    dst_ip = ext_ip;

  if (TARGET_IS_IPV6()) {

    # nb: Previous versions had calculated a wrong IPv6 flow label
    # (see https://github.com/greenbone/openvas/pull/545) which
    # requires this workaround here.
    # TODO: Remove once all GOS/GVM versions < 20.8.0 are EOL.
    if (version_is_less(version: OPENVAS_VERSION, test_version: "20.8.0"))
      IPV6_VERSION = 0x60;
    else
      IPV6_VERSION = 6;

    # TODO: Use forge_ip_v6_packet once all GOS/GVM versions < 20.8.0 are EOL.
    ip_packet = forge_ipv6_packet(ip6_v    : IPV6_VERSION,
                                  ip6_p    : ip_proto,
                                  ip6_src  : src_ip,
                                  ip6_dst  : dst_ip,
                                  # nb: Those are the defaults from (see note in function description on the reason):
                                  # https://github.com/greenbone/openvas-scanner/blob/v21.4.2/nasl/nasl_packet_forgery_v6.c#L112-L126
                                  ip6_tc   : 0,
                                  ip6_fl   : 0,
                                  ip6_hlim : 64);

    if (!ip_packet) {
      if (debug) display("DEBUG: forge_ipv6_packet() call failed.");
      return NULL;
    }

    if (debug) { display("---[ Crafted IPv6 packet ]---"); dump_ipv6_packet(ip_packet); }

    if (ip_proto == IPPROTO_TCP) {

      tcp_packet = forge_tcp_v6_packet(ip6           : ip_packet,
                                       data          : data,
                                       th_sport      : src_port,
                                       th_dport      : dst_port,
                                       # nb: Those are the defaults from (see note in function description on the reason):
                                       # https://github.com/greenbone/openvas-scanner/blob/v21.4.2/nasl/nasl_packet_forgery_v6.c#L520-L538
                                       th_seq        : rand(),
                                       th_ack        : 0,
                                       th_x2         : 0,
                                       th_off        : 5,
                                       th_flags      : 0,
                                       th_win        : 0,
                                       th_urp        : 0,
                                       update_ip_len : TRUE);

      if (!tcp_packet) {
        if (debug) display("DEBUG: forge_tcp_v6_packet() call failed.");
        return NULL;
      }

      ret_arry["packet"] = tcp_packet;

      if (debug) { display("---[ Crafted TCP packet ]---"); dump_tcp_v6_packet(tcp_packet); }

      filter = string("ether dst ", hexstr(src_mac_raw), " and dst port ", dst_port);
      ret_arry["filter"] = filter;

    } else {

      ICMP6_ECHO_REQ_TYPE = 128;
      ICMP6_ECHO_RES_TYPE = 129;
      ICMP_ID = rand() % 65536;

      # nb: See description on the filter below
      set_byte_order(BYTE_ORDER_LITTLE_ENDIAN);
      ICMP_ID_REVERSE = mkdword(ICMP_ID);
      # nb: Reset to the default of byte_func.inc
      set_byte_order(BYTE_ORDER_BIG_ENDIAN);
      ICMP_ID_REVERSE = getword(blob: ICMP_ID_REVERSE);

      icmp_packet = forge_icmp_v6_packet(ip6       : ip_packet,
                                         icmp_type : ICMP6_ECHO_REQ_TYPE,
                                         icmp_code : 0,
                                         icmp_seq  : 1,
                                         icmp_id   : ICMP_ID,
                                         data      : data);

      if (!icmp_packet) {
        if (debug) display("DEBUG: forge_icmp_v6_packet() call failed.");
        return NULL;
      }

      ret_arry["packet"] = icmp_packet;

      if (debug) { display("---[ Crafted ICMPv6 packet ]---"); dump_icmp_v6_packet(icmp_packet); }

      # nb: Some older pcap versions seems do not support the following syntax:
      # filter = string("ether dst ", hexstr(src_mac_raw), " and icmp6[0] = ", ICMP6_ECHO_RES_TYPE, " and icmp6[4:2] = ", ICMP_ID);
      # and are throwing a "pcap_compile : IPv6 upper-layer protocol is not supported by proto[x]" message so we're parsing the type and id directly
      # from the IPv6 payload to work around this.
      # Note that icmp6[4:2] seems to be able to handle Big vs. Little Endian where ip6[44:2] isn't so we're using "ICMP_ID_REVERSE" and "ICMP_ID" here "just to be safe".
      filter = string("ether dst ", hexstr(src_mac_raw), " and ip6[40] = ", ICMP6_ECHO_RES_TYPE, " and ( ip6[44:2] = ", ICMP_ID, " or ip6[44:2] = ", ICMP_ID_REVERSE, " )");
      ret_arry["filter"] = filter;

    }

    ether_proto = get_ethertype_proto_value(proto: "ipv6");
    ret_arry["ether_proto"] = ether_proto;
    return ret_arry;
  } else {

    ip_packet = forge_ip_packet(ip_v   : 4,
                                ip_hl  : 5,
                                ip_tos : 0,
                                ip_len : 20,
                                ip_id  : rand() % 65535,
                                ip_p   : ip_proto,
                                ip_ttl : 255,
                                ip_off : 0,
                                ip_src : src_ip,
                                ip_dst : dst_ip);

    if (!ip_packet) {
      if (debug) display("DEBUG: forge_ip_packet() call failed.");
      return NULL;
    }

    if (debug) { display("---[ Crafted IPv4 packet ]---"); dump_ip_packet(ip_packet); }

    if (ip_proto == IPPROTO_TCP) {

      tcp_packet = forge_tcp_packet(ip       : ip_packet,
                                    th_ack   : 0,
                                    th_dport : dst_port,
                                    th_flags : TH_SYN,
                                    th_sport : src_port,
                                    th_x2    : 0,
                                    th_off   : 5,
                                    th_win   : 1024,
                                    th_urp   : 0,
                                    data     : data);

      if (!tcp_packet) {
        if (debug) display("DEBUG: forge_tcp_packet() call failed.");
        return NULL;
      }

      ret_arry["packet"] = tcp_packet;

      if (debug) { display("---[ Crafted TCP packet ]---"); dump_tcp_packet(tcp_packet); }

      filter = string("ether dst ", hexstr(src_mac_raw), " and dst port ", dst_port);
      ret_arry["filter"] = filter;

    } else {

      ICMP_ECHO_REQ_TYPE = 8;
      ICMP_ECHO_RES_TYPE = 0;
      ICMP_ID = rand() % 65536;

      icmp_packet = forge_icmp_packet(icmp_type : ICMP_ECHO_REQ_TYPE,
                                      icmp_code : 0,
                                      icmp_seq  : 1,
                                      icmp_id   : ICMP_ID,
                                      data      : data,
                                      ip        : ip_packet);

      if (!icmp_packet) {
        if (debug) display("DEBUG: forge_icmp_packet() call failed.");
        return NULL;
      }

      ret_arry["packet"] = icmp_packet;

      if (debug) { display("---[ Crafted ICMP packet ]---"); dump_icmp_packet(icmp_packet); }

      filter = string("ether dst ", hexstr(src_mac_raw), " and icmp[0] = ", ICMP_ECHO_RES_TYPE, " and icmp[4:2] = ", ICMP_ID);
      ret_arry["filter"] = filter;

    }

    ether_proto = get_ethertype_proto_value(proto: "ipv4");
    ret_arry["ether_proto"] = ether_proto;
    return ret_arry;
  }
}

if (TARGET_IS_IPV6()) {
  # nb: This variable was added in GOS/GVM 20.8.0, see:
  # https://github.com/greenbone/openvas/pull/549
  # TODO: Remove once all GOS/GVM versions < 20.8.0 are EOL.
  if (!IPPROTO_ICMPV6)
    IPPROTO_ICMPV6 = 58;
  ip_protos = make_list(make_list(IPPROTO_TCP, IPPROTO_ICMPV6));
} else {
  ip_protos = make_list(make_list(IPPROTO_TCP, IPPROTO_ICMP));
}

# nb: Some systems (have seen this on a Palo Alto PA-220 doesn't forward the crafted TCP request but
# using an ICMP ECHO request sent to an external system works so we're checking both cases just to be sure.
foreach ip_proto (ip_protos) {

  packet_info = create_packet(src_ip: src_ip, ext_ip: ext_ip, ip_proto: ip_proto, src_mac_raw: src_mac_raw, debug: debug);
  if (!packet_info)
    continue;

  payload = packet_info["packet"];
  ether_proto = packet_info["ether_proto"];

  frame = forge_frame(src_haddr: src_mac_raw, dst_haddr: dst_mac_raw, ether_proto: ether_proto, payload: payload);
  if (!frame) {
    if (debug) display("DEBUG: forge_frame() call failed.");
    continue;
  }

  if (debug) { display("---[ Crafted Ethernet Frame ]---"); dump_frame(frame: frame); }

  filter = packet_info["filter"];

  if (debug) display("---[ Used PCAP filter ]---", "\n", filter);

  recv = send_frame(frame: frame, pcap_filter: filter, timeout: 10);

  if (!recv) {
    if (debug) display("DEBUG: No answer received with send_frame().");
    continue;
  } else {
    if (debug) { display("---[ Received Ethernet Frame ]---"); dump_frame(frame: recv); }
  }

  if (recv) {
    if (ip_proto == IPPROTO_TCP)
      report_proto = "a TCP";
    else
      report_proto = "an ICMP";
    report = "It was possible to route " + report_proto + " packet through the target host and received an answer which means IP forwarding is enabled.";
    log_message(port: 0, data: report);
    exit(0);
  }
}

exit(0);

7.2 High

AI Score

Confidence

Low

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.003 Low

EPSS

Percentile

71.6%

Related for OPENVAS:1361412562310147205