blog.kanbach.org

IT-Security and stuff

Network Security Implications of Host Models

This blog post is about a concept in network stacks called “Host Model” and its implications on network security.

It is important for both, the offensive and the defensive side to know the differences between the host model paradigms and to be aware of the defaults that are used in common operating systems.

In this blog post the following 2 hosts will be considered:

Host A

eth0
IPv4 address: 192.168.100.1/24
MAC: 00:0C:29:6D:57:01

eth1
IPv4 Address: 10.0.0.10/24
MAC: 00:0C:29:6D:57:10
Open Port: 443/TCP

Host B

eth0
IPv4 address: 192.168.100.5/24
MAC: 00:0C:29:6D:57:05

Default Gateway: 192.168.100.1

Host A has 2 externally accessible network interfaces, eth0 and eth1 which are configured to be in different subnets. Host B has only 1 externally accessible network interface, which is eth0. It is configured with an IP address that is located in the same subnet as eth0 on Host A.

Weak Host Model

IPv4 or IPv4 stacks that implement the weak host model would accept incoming packets even if the destination IP address of the packet is different from the IP address of the receiving network interface. Also it would allow the system to send packets from an interface, that has a different IP address than the source IP address of the packet being sent. When I first read about this, it seemed to be counterintuitive for me. The important aspect to understand is, that the receiving or sending entity in the weak host model is the host - NOT the interface. As long as the destination or source IP address of a packet is present on a host, regardless on which interface it is configured, it will be accepted.

Let's take a look at a practical example, starting with a simple TCP-SYN packet:

Before sending the TCP packet, it's necessary to provide Host B with a default gateway, pointing to 192.168.100.1.

user@host-b: $ sudo ip r add default via 192.168.100.1

Setting the gateway ensures that all packets will be sent to 192.168.100.1 / 00:0c:29:6d:57:01.

To prepare a nice target for the TCP port, a python HTTP server will be launched. It is configured to listen on TCP port 8080:

user@host-a: $ python -m SimpleHTTPServer 8080

Now let's try to scan this IP address by running nmap on Host B:

user@host-b: $ nmap -n -Pn 10.0.0.10

Starting Nmap 7.60 ( https://nmap.org ) at 2019-11-29 01:05 CET
Nmap scan report for 10.0.0.10
Host is up (0.000051s latency).
Not shown: 999 closed ports
PORT		STATE	SERVICE
8080/tcp	open	http-proxy

Nmap done: 1 IP address (1 host up) scanned 1.46 seconds

As you can see we got a response from 10.0.0.10 although it is located on a different subnet than eth0 on Host B. IP forwarding is not necessary at this stage, since in the weak model an address belongs to the whole host rather than to a single network interface.

Strong Host Model

Stacks that implement the strong host model would stop the system from responding to incoming packets, if the destination IP address does not match the IP address of the receiving interface. Also the host would only be allowed to send packets if source IP address of the packet is equal to the address of the egress network interface.

For the next example, the environment changes a little bit: Host A is no longer running Ubuntu Server 18.10 but gets a fresh installation of HardenedBSD with the same IPv4 configuration we've seen before. HardenedBSD implements the Strong Host Model by default. Like in the first example we'll scan the IP address using a TCP SYN-Scan.

Host B will keep its default gateway and a python server will be started on the HardenedBSD box.

Time to start a port scan of Host A:

user@host-b: $ nmap -n -Pn 10.0.0.10

Starting Nmap 7.60 ( https://nmap.org ) at 2019-11-29 01:13 CET
Nmap scan report for 10.0.0.10
Host is up.
All 1000 scanned ports on 10.0.0.10 are filtered

Nmap done: 1 IP address (1 host up) scanned 201.27 seconds

This time the kernel of Host A drops the packet, because its destination IP address is different from the IP address of the interface on which the packet is received.

Behaviour on Layer 2

Although Strong Host Model and Weak Host Model relate to IPv4/IPv6 stacks, a similar mechanism exists on Layer 2.

What happens if Host B sends an ARP packet, asking for the hardware address associated with 10.0.0.100? Well that depends on the target operating systems. Let's assume, that Host A is running Ubuntu Server 18.10 with a default configuration.

Example of an arp-scan against Host A:

$ sudo arp-scan -l
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
192.168.100.1	00:0c:29:6d:57:01	VMware, Inc.

$ sudo arp-scan 10.0.0.0/24
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
10.0.0.10	00:0c:29:6d:57:01	VMware, Inc.

The first scan sends ARP requests to all IP addresses within the subnet of eth0 on Host B (192.168.100.0/24). As a result of this scan, the IP address 192.168.100.1 appears, which is the address of eth0 on Host A. The second scan defines a different IP range: 10.0.0.0/24. Although this network is located on a different subnet than eth0 on Host B, the scan results show an answer from IP address 10.0.0.10, which is the address of eth1 on Host A. You might notice that the MAC address next to the IP address is not the actual MAC address of eth1, but the one of eth0. This is because eth0 responds to the ARP request on behalf of eth1.

This behaviour can be controlled by setting the kernel parameter net.ipv4.conf.<all|ifname>.arp_ignore to 1. Please note that this parameter might not be available on every operating system and can mainly be found on Linux.

Edge Case: Localnet

With weak host model and cross-interface ARP responses in mind, you might wonder if other hosts within the same subnet would be able to access services bound to localhost on your machine. This is not possible by default, since packets from or to localnet (127.0.0.0/8) coming from an unexpected source IP address are flagged as “martian packets” dropped by default. If you would like to log these kinds of packets, you could execute the following sysctl command on Linux-based machines:

sysctl -w net.ipv4.conf.<all|interface>.log_martians=1 However there is a kernel parameter on Linux-based operating systems that would allow the kernel to route packets destined to localhost, coming from an external source. This parameter is called net.ipv4.conf.<all|interface>.route_localnet and defaults to 0. Setting this value to 1 poses a significant security risk, since services listening on 127.0.0.1 might be accessible from the subnets of other network interfaces that are configured on that host. A description of how to spot hosts having this setting enabled is part of the Scanning Techniques section.

Security Considerations

An initial thought might be that the weak host model could be considered as a security weakness. Actually the weak host model is the default on most modern operating systems and securing the infrastructure should take these defaults into account. Network interfaces being placed in different subnets should not be regarded as a security boundary. Sensitive management interfaces that are listening on interfaces like vboxnet0, docker0 or similar virtual interfaces wouldn't prevent attackers from accessing these services. Instead it is important to have proper firewall rules in place that would prevent unauthorised access, or to configure sensitive services to listen on localhost only. Furthermore the strong host model would break core routing functionality and is not an option for devices that act as a router.

Scanning Techniques

In order to scan your subnets to find other internal networks, different strategies can be followed. Finding other networks by performing an ARP scan is the quickest way, but might miss hosts that disabled cross-interface ARP responses.

A good start is to scan all available private network segments:

$ arp-scan -l (Scans your local subnet) $ arp-scan 192.168.0.0/16 $ arp-scan 172.16.0.0/12 $ arp-scan 10.0.0.0/8

And don't forget the IPv4 link-local range, which is automatically picked if automatic configuration methods like DHCP fail:

$ arp-scan 169.254.0.0/16

A last quick (but unlikely) example would cover the case of route_localnet being set to 1:

$ arp-scan 127.0.0.1

Scanning for open TCP or UDP ports on unknown other networks requires a bit more effort, especially if it shouldn't take too long. In a scenario where IP forwarding is not enabled, each host within the known subnet needs to be targeted separately. The reason for this is, that TCP or UDP packets needs to be sent directly to the MAC addresses of each host.

Let's assume again that Host A has two network interfaces, eth0 (10.0.0.10/24) and eth1 (192.168.100.1/24). Our attacker host is Host B, which only has 1 interface, eth0 (192.168.100.2/24). Based on earlier arp-scans (arp-scan -l) we already know that 192.168.100.1 exists, and that it belongs to our subnet. After some ARP Scans of other private IP ranges no additional IP addresses showed up. This could either mean that no other interfaces were available or that cross-interface ARP responses were disabled. To find out more about Host A, let's start a port scan of the IP address that's within our own subnet:

user@host-b: $ nmap -n 192.168.100.1

Starting Nmap 7.60 ( https://nmap.org ) at 2019-11-30 11:32 CET
Nmap scan report for 192.168.100.1
Host is up (0.000050s latency).
Not shown: 999 closed ports
PORT                STATE   SERVICE
22/tcp          open        ssh

As you can see the scan revealed an open SSH port on 192.168.100.1. At this stage we could make the assumption that if SSH is listening on 192.168.100.1, it might listen on other network interfaces on that host as well, especially if SSH is configured to listen on 0.0.0.0. To speed further scans up, we will limit the port to TCP/22 and scan another subnet. But wait .. Before we do that, keep in mind that we need a route to the imaginary target network first:

user@host-b: $ ip r add 172.16.0.0/12 via 192.168.100.1 user@host-b: $ nmap -n -p22 -T5 -Pn --open 172.16.0.0/12

This scan doesn't return any hosts and open ports. From the whitebox perspective we know why: None of the interfaces was assigned an IP address within that subnet. Now let's scan another network, but before we need to add another route:

user@host-b: $ ip r add 10.0.0.0/16 via 192.168.100.1
user@host-b: $ nmap -n -p22 -T5 -Pn --open 10.0.0.0/8

Starting Nmap 7.60 ( https://nmap.org ) at 2019-11-30 11:35 CET
Nmap scan report for 10.0.0.10
Host is up (0.000051s latency).
Not shown: 999 closed ports
PORT		STATE	SERVICE
22/tcp	    open	ssh 

This time we got an answer from 10.0.0.10, a network segment that we did not know before. Now we know that Host A has a second network interface, reachable via 10.0.0.10. With that knowledge we could run a full port scan against 10.0.0.10, which might reveal further open ports.

This strategy can be adapted to other hosts, but keep in mind that you have to change routes each time you change your scan target.

Of course you could also try to achieve the same by sending ICMP echo requests. However many systems tend to block ICMP, and TCP or UDP might be a better choice.

OS Defaults

When I started observing the behaviour of different operating systems, I created some virtual machines to research what the defaults are. While Linux mostly follows the weak host model and also permits cross-interface ARP responses, *BSD-based systems tend to block cross-interface ARP responses, but follow the weak host model for their IP stacks. For Windows it's a different story: Since Windows Vista, Microsoft decided to implement the strong host model by default. Did you expect that?

OS Host Model Toggle Host Model
Ubuntu Server 18.10 Weak Routing table and rules for each interface necessary
Arch Linux Weak Routing table and rules for each interface necessary
CentOS 7 Weak Routing table and rules for each interface necessary
Solaris 11 Weak ndd -set /dev/ip ip_strict_dst_multihoming <0|1>
ndd -set /dev/ip ip_strict_src_multihoming <0|1>
FreeBSD 12.0 Weak sysctl -w net.inet.ip.check_interface=<0|1>
NetBSD 8.1 Weak sysctl -w net.inet.ip.checkinterface=<0|1>
HardenedBSD 12.1 Strong sysctl -w net.inet.ip.check_interface=<0|1>
OpenBSD 6.5 Weak Reversed Path Source Validation can be set using “pf”
Windows 7 Strong Netsh interface IPv4 set interface “<name>” WeakHostSend=<enabled|disabled>
Netsh interface IPv4 set interface “<name>” WeakHostReceive=<enabled|disabled>
Windows 10 Strong Netsh interface IPv4 set interface “<name>” WeakHostSend=<enabled|disabled>
Netsh interface IPv4 set interface “<name>” WeakHostReceive=<enabled|disabled>

Like with the Host Model, also the ARP behaviour can be tweaked on some operating systems. For some I couldn't find an according setting, if you know any, please let me know:

OS Cross-Interface ARP allowed Toggle ARP Behaviour
Ubuntu Server 18.10 Yes sysctl -w net.ipv4.conf.<all|interface>.arp_ignore=<0|1>
Arch Linux Yes sysctl -w net.ipv4.conf.<all|interface>.arp_ignore=<0|1>
CentOS 7 Yes sysctl -w net.ipv4.conf.<all|interface>.arp_ignore=<0|1>
Solaris 11 Nod No setting
FreeBSD 12.0 No No setting
NetBSD 8.1 No No setting
HardenedBSD 12.1 No No setting
OpenBSD 6.5 No No setting
Windows 7 No Netsh interface IPv4 set interface “<name>” WeakHostSend=<enabled|disabled>
Netsh interface IPv4 set interface “<name>” WeakHostReceive=<enabled|disabled>
Windows 10 No Netsh interface IPv4 set interface “<name>” WeakHostSend=<enabled|disabled>
Netsh interface IPv4 set interface “<name>” WeakHostReceive=<enabled|disabled>

Conclusion

The host model implementation is a core concept in network stacks of operating systems. Although documentation could be a bit better, it can be safely assumed that most Linux, and *BSD derivates are following the weak host model, while Windows Vista and newer implement the strong host model. From a security perspective it's important to keep in mind that packet filtering is still necessary even if network interfaces seem to be unreachable. For security researchers it's important to keep these principles in mind to enhance network scanning and discovery.

Cheers