check-machine-in-the-middle

Icinga/Nagios and stand-alone check scripts for the heuristic testing of machine-in-the-middle situations.

Introduction

This directory contains check scripts to monitor unexpected infrastructure changes with the aim of finding potential Machine-in-the-Middle situations. These scripts are Nagios/Icinga scripts. They can be run as probes from your monitoring station or as stand-alone scripts.

The following approaches are implemented:

  • check_traceroute.py: This script implements a traceroute for IPv4 or IPv6 using TCP to a destination host and port(s). It establishes a TCP connection using increasing TTL values and traces the hops towards the destination host:port. The script compares the last N hops from the current route against a list of expected routes. You can pass these expected last N hosts via command line or via a configuration file. You can specify multiple ports. It is expected that the route to any port follows the expected last hops.

  • check_jarm.py: Salesforces once implemented a fingerprinting for SSL/TLS servers, known as JARM_. To fingerprint a SSL/TLS server, the tool sends several SSL/TLS Client Hello messages and looks at the corresponding Server Hello messages to fingerprint the server implementation. The results are then hashed. This check script allows you to compare a server's fingerprint against an expected fingerprint.

The entire story and background is explained in our blog post at: https://www.pentagrid.ch/en/blog/domain-verification-bypass-prevention-caa-accounturi/

Preventive is better than reactive!

Installation

check_traceroute.py

  • To install dependencies on a Debian-style Linux, run:

::

apt install python3-scapy python3-seccomp python3-cap-ng
  • There is also a requirements.txt if you prefer this approach, but I would recommend using the libaries from your package system.

  • Install the script check_traceroute.py on your monitoring station, for example under /usr/local/bin/check_traceroute.py and ensure the script has proper file permissions:

::

FILE=/usr/local/bin/check_traceroute.py chown root:nagios $FILE chmod 750 $FILE

  • For Icinga: The tool needs a raw socket, which requires elevated privileges. Setting Linux Capabilities on a Python script does not work. So we could use capsh as a wrapper, but it requires elevated privileges as well and need to be allowed in sudo as well. Therefore, we allow the ''nagios'' user to run the check_traceroute.py script with ''sudo'' and without password (to compensate the risk a bit, we use SECCOMP and drop capabilities within the script):

::

FILE=/etc/sudoers.d/icinga_check_traceroute echo 'nagios ALL=(root) NOPASSWD:/usr/local/bin/check_traceroute.py' > $FILE chown root:root $FILE chmod 440 $FILE

  • If you want to use the script from Icinga, define the check command. Depending on your setup, edit for example /etc/icinga2/conf.d/commands_check_traceroute.conf:

::

object CheckCommand "traceroute" {
  import "plugin-check-command"

  command = [ "sudo", "/usr/local/bin/check_traceroute.py", "--last-hops-config", "/usr/local/etc/check_traceroute.conf" ]
  # You may add:
  #   "--disable-seccomp",
  #   "--disable-cap-dropping",

  arguments = {
    "--target" = "$traceroute_target$"
    "--port" = {
      value = "$traceroute_ports$"
      repeat_key = true
    }
  }
}

check_jarm.py

  • To install dependencies on Debian-style Linux, run:

::

apt install python3-scapy
  • Install the script check_jarm.py on your monitoring station, for example under /usr/local/bin/check_jarm.py and ensure the script has proper file permissions:

::

FILE=/usr/local/bin/check_jarm.py chown root:nagios $FILE chmod 750 $FILE

  • For Icinga: The tool needs a raw socket, which requires elevated privileges and using Linux Capabilities is not that easy. Therefore, we allow the ''nagios'' user to run this specific script with ''sudo'' and without password:

::

FILE=/etc/sudoers.d/icinga_check_jarm echo 'nagios ALL=(root) NOPASSWD:/usr/local/bin/check_jarm.py' > $FILE chown root:root $FILE chmod 440 $FILE

  • If you want to use the script from Icinga, define the check command. Depending on your setup, edit for example /etc/icinga2/conf.d/commands_check_jarm.conf:

::

object CheckCommand "jarm" {
  import "plugin-check-command"

  command = [ "/usr/local/bin/check_jarm.py",
          "--hostname", "$jarm_hostname$",
          "--target", "$jarm_target$",
          "--port", "$jarm_port$",
          "--expected-hash", "$jarm_expected_hash$" ]

  if (vars.jarm_socks5_host) {
    command += [ "--socks5-host", "$jarm_socks5_host$"]
  }
  if (vars.jarm_socks5_port) {
    command += [ "--socks5-port", "$jarm_socks5_port$" ]
  }

}

Configuration

check_traceroute.py

  • If you want to use the script from Icinga, add a configuration file for Icinga, for example /etc/icinga2/conf.d/services_traceroute.conf. The target is specified via an IPv4 or IPv6 address, so it will work in DNS round-robin environments.

::

object Service "traceroute-www.example.org" {
  import "generic-service-internet"
  host_name = "www.example.org"
  check_command = "traceroute"

  vars.traceroute_target = "192.168.23.42"
  vars.traceroute_ports = "80 443"
}
  • Run ''mtr'', ''traceroute'', ''traceroute6'' or another tool to determine the last hops of your systems.

  • If you want to use a config file for the expected routes, adjust the example from last_hops.conf.sample and store it in the file system of your test station, for example as /usr/local/etc/check_traceroute.conf and fix file permissions:

::

FILE=/usr/local/etc/check_traceroute.conf chown root:root $FILE chmod 640 $FILE

check_jarm.py

  • If you want to use the script from Icinga, add a configuration file for Icinga, for example /etc/icinga2/conf.d/services_traceroute.conf. The target is specified via an IPv4 or IPv6 address, so it will work in DNS round-robin environments.

::

object Service "jarm-www.example.org" {
  import "generic-service-internet"
  host_name = "www.example.org"
  check_command = "jarm"

  vars.jarm_hostname = "aspecificvhost.example.org"
  vars.jarm_target = "192.168.23.42"
  vars.jarm_port = "443"
  vars.jarm_expected_hash = "28d28d28d00028d00042d42d000000d2e61cae37a95f75ef00cafe1337ca523"
  # you could set a SOCKS5 proxy as well
  # vars.jarm_socks5_host = "localhost"
  # vars.jarm_socks5_port = "8080"
}
  • To determine JARM hashes, run for example the following command. Here, the target is specified as IP address to also connect to a specific address if you use round-robin addresses or similar things. The hostname is used for SNI. We assume that the hash is from your server and not already from a MITM.

::

/usr/local/bin/check_jarm.py --target 45.10.26.156 --hostname www.pentagrid.ch --port 443 --show JARM: 28d28d28d00028d00042d42d000000d2e61cae37a985f75ecafb81b33ca523

Copyright and Licence

check_traceroute.py was developed by Martin Schobert and published under a 3-clause BSD licence.

check_jarm.py was developed by Martin Schobert and published under a 3-clause BSD licence. It is derived from the jarm.py_ script, which was developed by John Althouse, Andrew Smart, RJ Nunaly, Mike Brady and Caleb Yu and which is copyrighted by salesforce.com, inc and published under a BSD 3-Clause license as well.

Please read the the license header in the corresponding files for further details.