API Guide

The Horizon3.ai API provides programmatic access to a subset of functionality available through the Horizon3.ai portal. At a a high level, this API allows for:

  • scheduling an autonomous pentest
  • monitoring the status of a pentest while it is running
  • retrieving a pentest report after it’s complete

The API can be used for a variety of use cases such as periodically scheduling assessments of your environment or kicking off a pentest as part of a continuous integration build pipeline.

  • Pre-Requisites
    • API Access
    • Client Infrastructure
      • Host/VM Requirements
      • Outbound Network Access
      • Inbound Network Access
      • Container Privileges
    • Create a Pentest Template
  • API Specification
    • Authentication
    • Scheduling a Pentest
    • Launching a Pentest
    • Monitoring the Status of a Pentest
      • Pentest Life Cycle
      • Querying the Pentest State
    • Reporting
  • Example Script

Pre-Requisites

Prior to using the Horizon3.ai API, make sure to complete the following steps:

  • Getting an API Key
  • Setting up a host/VM to host the Horizon3.ai Docker container, called NodeZero
  • Creating a pentest template

These steps are described in greater detail below.

API Access

An API key is required to access the API. The key is tied to an existing user account with Org Admin privileges in the Horizon3.ai portal.

At this time, API keys are manually provisioned by Horizon3.ai. If you don’t have an API key, please contact your Horizon3.ai representative to acquire one.

In addition, if you have an API key and need to revoke it and/or generate a new one, please contact your Horizon3.ai representative.

Client Infrastructure

Running a Horizon3.ai pentest requires running a Docker container, called NodeZero, on a Linux VM or host. The NodeZero container receives instructions from the Horizon3.ai cloud in order carry out the pentest.

The NodeZero container may open ports on the host/VM in order to conduct certain types of attacks. The container automatically shuts itself down after the pentest is complete.

A NodeZero container is used only once per pentest. Once a pentest is complete, the NodeZero container can be removed.

api_img_01.png

Host/VM Requirements

  • Linux:
    • Ubuntu >= 16.04
    • Redhat >= 7
  • 2 CPUs
  • 4GB RAM
  • 10GB free disk space
  • Docker >= v17.0.7

Outbound Network Access

  • Uninterrupted network access to the following endpoints during the entire pentest
    • cognito-identity.us-east-2.amazonaws.com (over HTTPS port 443)
    • cognito-idp.us-east-2.amazonaws.com (over HTTPS port 443)
    • us-east-2.queue.amazonaws.com (over HTTPS port 443)
    • *.s3.us-east-2.amazonaws.com (over HTTPS port 443)
    • interact.h3ai.io (over HTTP port 80) – Note no sensitive information of any kind is transmitted over this channel

It is possible to run NodeZero through a proxy if necessary. Contact your Horizon3.ai representative for more details.

Inbound Network Access

The following ports should be opened on the NodeZero host/VM to allow traffic in:

  • TCP 21, 23, 25, 53, 80, 88, 110, 135, 139, 143, 389, 443, 445, 587, 1433, 3306, 3389, 5900, 8080, 8443, 8888
  • UDP 69

This is required on the NodeZero host. This does not pertain to perimeter firewalls.

Container Privileges

The NodeZero container runs with the following elevated privileges:

  • root user inside the container
  • –network=host privileges
  • –cap-add=SYS_ADMIN
  • –security-opt apparmor=unconfined

These privileges are required to carry out certain types of automated attacks.

Create a Pentest Template

Prior to scheduling an pentest, the user to whom the API key belongs must first create an pentest template in the Horizon3.ai portal. A pentest template describes the scope that the client wants to scan and the pentest configuration. The scope consists of IP addresses and subnets that the client wants to scan. The configuration describes which attack behaviors are turned on or off during the pentest.


API Specification

The API is hosted at api.horizon3ai.com. It provides endpoints for authentication, scheduling a pentest, monitoring the status of a pentest, and reporting. The current API version is v1, and all endpoints in the v1 API are accessible at api.horizon3ai.com/v1.

At a high level, to run a pentest, the following sequence of steps should be followed:

  1. Authenticate using an API key.
  2. Schedule a pentest using the pentest template you configured earlier.
  3. Download a NodeZero launch script.
  4. Run the launch script on the NodeZero Linux host to launch the pentest. This is done outside of the API.
  5. While the pentest is running, its state can be monitored using the API. The pentest is complete when its state is “done.”
  6. Download a pentest report.

api_img_02.png

These steps are described in further detail below.

Authentication

A client first authenticates by using its API key against the auth endpoint. The auth API validates the API key and returns a JSON Web Token (JWT) that clients use to invoke other APIs, as described below.

The JWT expires after an hour. Clients must retrieve a new JWT from the auth endpoint after an hour.

Example Request


curl \
  -X POST https://api.horizon3ai.com/v1/auth \
  -H "Content-Type: application/json" \
  -d @- <<HERE 
{
    "key": "<API key>"
}
HERE

Example response


{"token": "<token>"}

Status Codes

  • 200: Success: The response contains the JWT in the token field.
  • 400: Bad Request: Missing Content-Type header
  • 401: Unauthorized: Malformed request payload or unauthorized API key
  • 5xx: Internal Server Error

Scheduling a Pentest

This section assumes that the user to whom the API key belongs has created a pentest template as described in the Pre-Requisites section above.

Once a pentest template is defined, the client can issue an API call against the graphql endpoint to schedule a pentest, passing in the name of the pentest template and, optionally, the name of the pentest. If the op_name is not set in the API call, it will be defaulted to the name defined inside the pentest template.

A successful response contains an op_id, which should be used for subsequent API calls related to this pentest. It also contains a download URL, the nodezero_script_url, for a script that downloads and runs NodeZero.

Example Request

curl \
  -X POST https://api.horizon3ai.com/v1/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" -d @- <<HERE
{
    "query": "
        mutation {
            schedule_op_template(
                op_template_name:\"<my op template name>\"
                op_name:\"<my op name>\"
            ) {
                op {
                    op_id
                    nodezero_script_url
                }
            }
        }
    "
}
HERE

Example Response

{
    "data": {
        "schedule_op_template": {
            "op": {
                "op_id": "e11b0a7c-fd24-4d48-b4c9-de8220f99847",
                "nodezero_script_url": "<nodezero script URL>"
            }
        }
    }
}

Status Codes

  • 200: Success. The response contains the op_id and 'nodezero_script_url. The op_id should be used in subsequent API calls related to this pentest.
  • 400: Bad Request: Malformed request, unknown or missing op template name. The errors field inside the response will contain a descriptive message.
  • 401: Unauthorized: Invalid or expired JWT
  • 5xx: Internal Server Error

Launching a Pentest

The above step to schedule a pentest returns the URL to a NodeZero launch script. The launch script is a bash script that should be run on the NodeZero host. The script validates NodeZero system requirements, downloads the NodeZero Docker image, and runs the NodeZero container. This script URL is short-lived and valid up to 12 hours.

After a pentest is scheduled, the Horizon3.ai service begins the process of provisioning cloud infrastructure to support the pentest. This process can take a few minutes. After the cloud infrastructure is fully provisioned, it connects with the NodeZero conainer to begin the pentest.

After a pentest is scheduled, the launch script URL can also be directly retrieved using the query below against the graphql endpoint.

Example Request

curl \
 -X POST https://api.horizon3ai.com/v1/graphql \
 -H "Content-Type: application/json" \
 -H "Authorization: Bearer <token>" -d @- <<HERE
{
   "query": "
                 query {
                             op(op_id:\"e11b0a7c-fd24-4d48-b4c9-de8220f99847\") {
                                    nodezero_script_url
                             }
                   }
       "
}
HERE

Example Response

{
     "data": {
             "op": {
                   "nodezero_script_url": "<link>"
               }
       }
}

Status Codes

  • 200: Success. The nodezero_script_url field in the response will contain a link to download the script using an HTTPS GET.
  • 400: Bad Request: Malformed request. The errors field inside the response will contain a descriptive message.
  • 401: Unauthorized: Invalid or expired JWT
  • 403: Forbidden: op_id in the GET request is invalid or not authorized for access. The errors field inside the response will contain a descriptive message.
  • 5xx: Internal Server Error

Monitoring the Status of an Pentest

Pentest Life Cycle

It’s helpful to understand the life cycle of a pentest. A pentest always starts off in an initial state of “scheduled”, and it proceeds until it reaches a terminal state of either “Done”, “Canceled”, or “Error.” In between the initial state and a terminal state, there are other states that a pentest can assume. These are described in the table below, roughly in the order that the pentest proceeds.

api_img_03.png

State Description
scheduled This is the first state a pentest enters, Immediately after an API client or end-user schedules a pentest.
provisioning The Horizon3.ai cloud service is actively setting up infrastructure to support the pentest.
ready The pentest is ready to begin. The Horizon3.ai cloud service is waiting for the NodeZero container to start.
running The NodeZero container has connected to the Horizon3.ai cloud service, and the pentest is in progress.
complete The Horizon3.ai service has finished executing all attack sequences against the client environment via NodeZero.
destroying The Horizon3.ai service is actively tearing down infrastructure that was provisioned for the pentest.
post-processing The Horizon3.ai service is processing the results of the pentest and preparing a pentest report.
done The pentest report is available in the portal or via the API.
canceled An end-user has manually canceled the pentest from the Horizon3.ai portal. A pentest can only be canceled while it is in the “ready” or “running” state. After it is canceled, the pentest moves into the “complete” state and ends at “canceled” instead of “done.” The pentest report for everything that was collected prior to cancellation is available in the portal or via the API.
error Unspecified error

Querying the Pentest State

The following graphql query against the graphql API endpoint can be used to retrieve the state of a pentest.

A successful response contains the status of the pentest in the op_state field.

Example Request

curl \
  -X POST https://api.horizon3ai.com/v1/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" -d @- <<HERE 
{
    "query": "
        query { 
            op(op_id:\"e11b0a7c-fd24-4d48-b4c9-de8220f99847\") {
                op_state
            }
        }
    "
}
HERE

Example Response

{
    "data": {
        "op": {
            "op_state": "done"
        }
    }
}

Status

  • 200: Success. The response will contains the pentest status in the op_state field.
  • 400: Bad Request: Malformed request. The errors field inside the response will contain a descriptive message.
  • 401: Unauthorized: Invalid or expired JWT
  • 403: Forbidden: op_id in the GET request is invalid or not authorized for access. The errors field inside the response will contain a descriptive message.
  • 5xx: Internal Server Error

Reporting

After a pentest reaches the “done” or “canceled” state, a client can download a summary report using the following graphql query against the graphql API endpoint. The endpoint returns a set of summary statistics containing the number of weaknesses found, credentials discovered, and hosts scanned.

Example Request

curl \
  -X POST https://api.horizon3ai.com/v1/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" -d @- <<HERE 
{
    "query": "
        query { 
            op(op_id:\"e11b0a7c-fd24-4d48-b4c9-de8220f99847\") {
                op_name
                op_state
                scheduled_timestamp_iso
                launched_timestamp_iso
                completed_timestamp_iso
                duration_hms
                in_scope_hosts_count
                confirmed_weaknesses_count
                confirmed_credentials_count
            }
        }
    "
}
HERE

Example Response

{
    "data": {
        "op": {
            "completed_timestamp_iso": "2020-11-13T23:23:43",
            "duration_hms": "00:21:45",
            "in_scope_hosts_count": 10,
            "launched_timestamp_iso": "2020-11-13T23:01:59",
            "op_name": "my op name",
            "op_state": "done",
            "confirmed_credentials_count": 11,
            "confirmed_weaknesses_count": 7,
            "scheduled_timestamp_iso": "2020-11-13T22:14:17"
        }
    }
}

Status

  • 200: Success. The response contains summary data for the pentest. If the pentest was never launched or is not yet done, certain fields inside the response may have null values.
  • 400: Bad Request: Malformed request. The errors field inside the response will contain a descriptive message.
  • 401: Unauthorized: Invalid or expired JWT
  • 403: Forbidden: op_id in the GET request is invalid or not authorized for access. The errors field inside the response will contain a descriptive message.
  • 5xx: Internal Server Error

Example Script

The following bash script demonstrates all of the above API actions, from scheduling a pentest to retrieving the pentest report after the pentest is done.

NOTE: This script does not handle error conditions or token renewal if the pentest extends past an hour.

#!/bin/bash
#
# requires: curl, jq
#

# User defined parameters, set in the environment
API_KEY="$API_KEY"
OPERATION_TEMPLATE_NAME="$OP_TEMPLATE"
OPERATION_NAME="$OP_NAME"

# Static
API_URL="https://api.horizon3ai.com"
AUTH_URL="$API_URL"/v1/auth
GQL_URL="$API_URL"/v1/graphql
CONTENT_TYPE_HEADER="Content-Type: application/json"

function authenticate {
    curl -s -S \
	 -X POST "$AUTH_URL" \
	 -H "$CONTENT_TYPE_HEADER" \
	 -d "{\"key\": \"$API_KEY\"}"
}

function schedule_op_template {
    curl -s -S \
	 -X POST "$GQL_URL" \
	 -H "$CONTENT_TYPE_HEADER" \
	 -H "$AUTHZ_HEADER" \
	 -d @- <<EOF 
{
    "query": "
        mutation { 
            schedule_op_template(
                op_template_name:\"$OPERATION_TEMPLATE_NAME\"
                op_name:\"$OPERATION_NAME\"
            ) { 
                op {
                    op_id
                    nodezero_script_url
                }
            }
        }
    "
}
EOF
}

function fetch_op_state {
    curl -s -S \
      -X POST "$GQL_URL" \
      -H "$CONTENT_TYPE_HEADER" \
      -H "$AUTHZ_HEADER" \
      -d @- <<EOF
{
    "query": "
        query { 
            op(op_id:\"$OP_ID\") {
                op_state
            }
        }
    "
}
EOF
}

function fetch_nodezero_script_url {
    curl -s -S \
      -X POST "$GQL_URL" \
      -H "$CONTENT_TYPE_HEADER" \
      -H "$AUTHZ_HEADER" \
      -d @- <<EOF 
{
    "query": "
        query { 
            op(op_id:\"$OP_ID\") {
                nodezero_script_url
            }
        }
    "
}
EOF
}

function fetch_op_report {
    curl -s -S \
      -X POST "$GQL_URL" \
      -H "$CONTENT_TYPE_HEADER" \
      -H "$AUTHZ_HEADER" \
      -d @- <<EOF 
{
    "query": "
        query {
            op(op_id:\"$OP_ID\") {
                op_name
                op_state
                scheduled_timestamp_iso
                launched_timestamp_iso
                completed_timestamp_iso
                duration_hms
                in_scope_hosts_count
                confirmed_weaknesses_count
                confirmed_credentials_count
            }
        }
    "
}
EOF
}

function poll_op_for_status {
    while [ 1 ]; do
        op_state=`fetch_op_state | jq -r .data.op.op_state`
        if [ "$op_state" = "$WAIT_STATE" ]; then
            echo "op_state $op_state == $WAIT_STATE! breaking loop."
            break
        fi
        echo "op_state $op_state != $WAIT_STATE ..."
        sleep 15
    done
}

# Step 1: Authenticate to API to get short-lived session token
TOKEN=`authenticate | jq -r .token`
echo "Authenticated to API"

export AUTHZ_HEADER="Authorization: Bearer $TOKEN"

# Step 2: Schedule a pentest
export OP_ID=`schedule_op_template | jq -r .data.schedule_op_template.op.op_id`
echo "Pentest operation id: $OP_ID"

# Step 3: Get the link to the NodeZero launch script
SCRIPT_URL=`fetch_nodezero_script_url | jq -r .data.op.nodezero_script_url`
echo "Launching NodeZero..."

# Step 4: Launch the NodeZero container
curl "$SCRIPT_URL" | bash &

# Step 5: Wait til the pentest is "done"
export WAIT_STATE="done"
poll_op_for_status
echo "Pentest is done"

# Step 6: Download the op report
REPORT=`fetch_op_report | jq -r .data.op`
echo "Pentest report: $REPORT"

How can NodeZero help you?

Let our experts walk you through a demonstration of NodeZero, so you can see how to put it to work for your company.