Introduction

Meerkat Scan makes it really easy to do two things:

  1. Identify the infrastructure your organization exposes to the internet
  2. Automate arbitrary scans or assertions against your exposed infrastructure

The workflow when using Meerkat Scan goes something like this: You supply basic information about your organization's infrastructure, such as domains and IP addresses, and Meerkat Scan will attempt to identify all the listening services your organization exposes to the internet. You can then use scan modules to implement completely custom scans and checks against your infrastructure in Python.

Dashboard

Scan Results

The Scan Results tab is where information about your organization's most recently observed attack surface is displayed.

Scan Options

The Scan Options tab is where you supply information about your organization as a starting point for Meerkat Scan to perform its analysis of your infrastructure. See the Network Scans, Subdomain Enumeration, and Cloud Integration sections below for more information.

Alert Settings

The Alert Settings tab is where you can configure your alerting preferences. See the Alerts section below for more information.

Scan Modules

The scan modules tab is where you can create and configure scan modules. See the Scan Modules section below for more information.

Network Scans

Network scans can be enabled or disabled using the toggles under "Scan Types" on the Scan Options tab. Full scans cover all 65,535 TCP and UDP ports, and take about a day to complete. Quick scans cover the top 1,000 most common TCP and UDP ports, and take about 20 minutes to complete.

Hosts protected by network intrusion prevention technology that obfuscates which ports are open (for example, by pretending that a very large number of ports are open) may not show any open ports.

IP Address Limits

If the number of IPs associated with your infrastructure exceeds the capacity for account, scans will continue but only for a subset of IPs that fits within your account's capacity.

Subdomain Enumeration

Subdomain enumeration is performed for each of your domains on a daily basis. One-off subdomain enumerations also occur when you add new domains to your list of domains in the Scan Options tab. At the moment, only one one-off enumeration is allowed every 30 minutes, so it is recommended to add new domains all at once for the fastest results.

Cloud Integration

Meerkat Scan can pull IP addresses from your cloud environment and include them in scans. To enable this, add your cloud provider account's API key to the Cloud API Keys section of the Scan Options tab. The uploaded keys must have a certain set of permissions, and also adhere to the principle of least privilege by not having any additional permissions. For your security, keys which do not meet these requirements are immediately deleted from our system. API keys are stored on our servers encrypted according to cryptographic best practices.

Currently, only AWS EC2 instances are supported. If you'd like to see support for a particular cloud environment or use case prioritized, please let us know!

API Key Permission Requirements

AWS

Create your AWS API key associated with an IAM user with the following permissions and permissions boundary.

Permissions Policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:GetPolicyVersion",
                "iam:GetPolicy",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeRegions",
                "iam:GetUser"
            ],
            "Resource": "*"
        }
    ]
        }

Permissions Boundary

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:GetPolicyVersion",
                "ec2:DescribeNetworkInterfaces",
                "iam:GetPolicy",
                "ec2:DescribeRegions",
                "iam:GetUser"
            ],
            "Resource": "*"
        }
    ]
}

Alerts

Currently, Meerkat Scan supports email alerts for activity related to your internet-accessible infrastructure.

Alerts are sent whenever:

  • A new port opens
  • An open port closes
  • A new subdomain is discovered
  • A scan module returns failing results

By default, alerts will not be sent for ports opening or closing when Meerkat Scan detects that this activity is an artifact of a domain switching IP addresses. This option can be disabled in the Alert Settings tab.

Scan Modules

Scan modules are arbitrary, user-written Python 3 functions that are called once for each service exposed by your infrastructure and are defined as follows:

def scan_module(ip, port, domain=None, info=''):
    # Your logic here
    # return True if the service passes your check
    # return False if the service fails your check

The function must be named "scan_module", and take four arguments. Since not every service has an associated domain or info, these arguments may take the value None or the empty string, respectively.

Scan modules should return true if a service passes your check or scan, and should return false if a service fails your check or scan.

A "service" in this context consists of an IP, port, domain, and service info. For example, if your infrastructure exposes the following open ports as displayed in the Scan Results tab:

IP

Port

Protocol

Info

Source(s)

1.1.1.1

80

TCP

http

a.example.com, b.example.com

2.2.2.2

443

TCP

https

c.example.com

3.3.3.3

22

TCP

Then your scan module will be called four times as follows:

scan_module(ip='1.1.1.1', port=80, domain='a.example.com', info='http')
scan_module(ip='1.1.1.1', port=80, domain='b.example.com', info='http')
scan_module(ip='2.2.2.2', port=443, domain='c.example.com', info='https')
scan_module(ip='3.3.3.3', port=22, domain=None, info='')

Each domain is considered its own service, even when it shares an IP and port with other domains.

Example 1

The following scan module checks that no non-web ports are listening:

def scan_module(ip, port, domain, info):
    web_ports = [80, 443]

    if port not in web_ports:
        return False

    return True

Note that this scan module does not actually perform any further scans against your infrastructure, it simply analyzes the results already obtained from the network scans. A service listening on port 22 would fail this check.

Example 2

Similarly, this scan module checks that only a certain set of hosts expose SSH:

def scan_module(ip, port, domain, info):
    allowed_hosts = [
        'bastion1.example.com',
        'bastion2.example.com'
    ]

    if port == 22 or 'ssh' in info.lower():
        if domain not in allowed_hosts:
            return False

    return True

Example 3

Lastly, this scan module goes a bit further and actually issues a request to check that the '/admin' path on a web application returns 401:

import requests

def scan_module(ip, port, domain, info):
    if domain is None:
        return True

    status = requests.get(
        'https://' + domain + '/admin', timeout=2
    ).status_code

    if status != 401:
        print(f"Domain {domain} returned status {status}")
        return False

    return True
      

Note that the output from the calls to print will be available in the scan module results. The scan module also implements a check to make sure the passed-in service has a domain before attempting to issue a request to it. Alternatively, the scan module configuration could be adjusted so that it only runs for a certain set of domains.