Skip to content

Target Management

Targets represent discovered hosts across your engagement -- machines identified through Nmap imports, beacon check-ins, manual entry, or batch generation from CIDR ranges. The target inventory gives operators a unified view of every machine in scope, whether compromised or not.


Architecture

Targets are stored in PostgreSQL and exposed through a REST API. They can be created individually, in bulk from IP ranges, or imported directly from Nmap XML scan output. The system uses upsert semantics -- if a target with the same IP already exists, metadata fields are updated while operator-set notes are preserved.

graph LR
    subgraph Sources
        A[Manual Entry]
        B[Batch Create<br/>IP / CIDR / Range]
        C[Nmap XML Import]
        D[Beacon Check-in]
    end

    subgraph Backend
        E[Target Handler]
        F[Nmap Importer]
        G[targets table]
        H[services table]
    end

    A --> E
    B --> E
    C --> F
    D --> E
    E --> G
    F --> G
    F --> H

Target Model

Each target record contains the following fields:

Field Type Description
id UUID Unique identifier (auto-generated)
ip string IPv4 address (unique constraint -- used for upsert matching)
hostname string DNS hostname or NetBIOS name
netbios_name string NetBIOS computer name
os string Operating system (e.g., "Windows 10", "Ubuntu 22.04")
arch string CPU architecture (e.g., "amd64", "x86")
note string Operator-set note (preserved across upserts)
source string Discovery source: manual, nmap, beacon
last_seen timestamp Last time this target was observed
discovered_at timestamp When the target was first discovered
created_at timestamp Record creation time
updated_at timestamp Last record modification time

Upsert behavior

When a target is created with an IP that already exists, the system updates hostname, os, arch, netbios_name, source, and last_seen -- but only if the new values are non-empty. The note field is never overwritten on conflict, ensuring operator annotations are preserved across scan imports and beacon check-ins.


Creating Targets

Single Target

Create a single target by specifying at minimum an IP address. All other fields are optional.

curl -s -X POST https://stentor.app/api/v1/targets \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ip": "10.10.10.21",
    "hostname": "WS01.lab.local",
    "netbios_name": "WS01",
    "os": "Windows 10 Pro",
    "arch": "amd64",
    "note": "Developer workstation",
    "source": "manual"
  }' | jq
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "ip": "10.10.10.21",
  "hostname": "WS01.lab.local",
  "netbios_name": "WS01",
  "os": "Windows 10 Pro",
  "arch": "amd64",
  "note": "Developer workstation",
  "source": "manual",
  "last_seen": "2026-02-21T12:00:00Z",
  "discovered_at": "2026-02-21T12:00:00Z",
  "created_at": "2026-02-21T12:00:00Z",
  "updated_at": "2026-02-21T12:00:00Z"
}

Request fields:

Field Type Required Description
ip string Yes IPv4 address of the target
hostname string No DNS hostname
netbios_name string No NetBIOS computer name
os string No Operating system
arch string No CPU architecture
note string No Operator note
source string No Discovery source (defaults to manual)

Automatic deduplication

If a target with the same IP already exists, the record is updated rather than duplicated. This makes it safe to re-import scan results without creating duplicate entries.


Batch Create

Create multiple targets at once from a single IP address, a CIDR block, or an IP range. Useful for populating the target inventory from scope lists before scanning begins.

curl -s -X POST https://stentor.app/api/v1/targets/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "10.10.10.50",
    "os": "Windows Server 2022",
    "note": "File server"
  }' | jq
curl -s -X POST https://stentor.app/api/v1/targets/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "10.10.10.0/24",
    "note": "Lab subnet"
  }' | jq
curl -s -X POST https://stentor.app/api/v1/targets/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "10.10.10.1-50",
    "hostname": "",
    "os": "",
    "note": "Scope range"
  }' | jq
{
  "created": 50,
  "total": 50
}

Request fields:

Field Type Required Description
address string Yes Single IP, CIDR (e.g., 10.0.0.0/24), or range (e.g., 10.0.0.1-50 or 10.0.0.1-10.0.0.50)
hostname string No Hostname applied to all targets in the batch
os string No OS applied to all targets
note string No Note applied to all targets

Address format examples:

Format Example Description
Single IP 192.168.1.100 Creates one target
CIDR 192.168.1.0/24 Expands to 254 IPs (skips network and broadcast for /24+)
Short range 192.168.1.1-50 Expands to 50 IPs (last octet range)
Full range 192.168.1.1-192.168.1.50 Expands to 50 IPs (explicit start and end)

Expansion limits

Batch creation is limited to 1024 IPs per request. CIDR blocks larger than /22 are rejected. Use multiple requests for larger scopes.


Nmap XML Import

Import targets and services directly from Nmap XML output. The importer parses <host>, <address>, <hostname>, <os>, and <port> elements from the XML and creates target and service records accordingly.

curl -s -X POST https://stentor.app/api/v1/targets/import/nmap \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/xml" \
  --data-binary @scan-results.xml | jq
{
  "targets_created": 12,
  "targets_updated": 3,
  "services_created": 47,
  "errors": 0
}

Import behavior:

  • Only hosts with state="up" are imported
  • The first IPv4 (or IPv6) address found on each host is used as the target IP
  • The first <hostname> element is used as the hostname
  • The highest-accuracy OS match from <osmatch> is used as the OS name
  • Only ports with state="open" are imported as services
  • Service banners are assembled from product, version, and extrainfo attributes
  • All imported targets have source set to nmap
  • Existing targets (matched by IP) are updated; notes are preserved

Nmap scan command

For best results, run Nmap with OS detection and service version scanning:

nmap -sV -O -oX scan-results.xml 10.10.10.0/24

File size limit

The maximum accepted XML payload is 50 MB. For very large scans, split the results into multiple files.


Bulk OS Update

Update the operating system field on multiple targets at once. Useful after identifying OS versions through post-exploitation or when correcting Nmap OS fingerprinting results.

curl -s -X PUT https://stentor.app/api/v1/targets/bulk-os \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "target_ids": [
      "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "os": "Windows Server 2022"
  }' | jq
{
  "updated": 2
}

Request fields:

Field Type Required Description
target_ids UUID[] Yes Array of target UUIDs to update
os string Yes New OS value to set

Listing Targets

Retrieve all targets, ordered by discovery time (newest first).

curl -s https://stentor.app/api/v1/targets \
  -H "Authorization: Bearer $TOKEN" | jq
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "ip": "10.10.10.21",
    "hostname": "WS01.lab.local",
    "netbios_name": "WS01",
    "os": "Windows 10 Pro",
    "arch": "amd64",
    "note": "Developer workstation",
    "source": "nmap",
    "last_seen": "2026-02-21T12:00:00Z",
    "discovered_at": "2026-02-21T11:45:00Z",
    "created_at": "2026-02-21T11:45:00Z",
    "updated_at": "2026-02-21T12:00:00Z"
  }
]

Get Single Target

Retrieve a specific target by its UUID.

curl -s https://stentor.app/api/v1/targets/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer $TOKEN" | jq

Updating Notes

Update the operator note on a specific target. Notes are free-text fields for tagging targets with engagement context.

curl -s -X PUT https://stentor.app/api/v1/targets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/note \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "note": "Domain admin logged in here - high value target"
  }' | jq
{
  "status": "updated"
}

Deleting Targets

Remove a target by its UUID. Returns 204 No Content on success.

curl -s -X DELETE https://stentor.app/api/v1/targets/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"

API Reference

Method Endpoint Description
GET /api/v1/targets List all targets
GET /api/v1/targets/:id Get a single target
POST /api/v1/targets Create or upsert a target
POST /api/v1/targets/batch Batch create from IP/CIDR/range
POST /api/v1/targets/import/nmap Import from Nmap XML
PUT /api/v1/targets/bulk-os Bulk update OS field
PUT /api/v1/targets/:id/note Update target note
DELETE /api/v1/targets/:id Delete a target

Workflow Example

A typical target management workflow during an engagement:

  1. Scope import -- Batch create targets from the client-provided scope using CIDR notation.
  2. Nmap scan -- Run network discovery and import results via the Nmap XML endpoint.
  3. Beacon enrichment -- As beacons check in, target records are automatically updated with hostname, OS, and architecture from the implant.
  4. Operator notes -- Tag high-value targets (domain controllers, admin workstations) with notes for prioritization.
  5. OS correction -- Use bulk OS update after confirming versions through post-exploitation.
sequenceDiagram
    participant Op as Operator
    participant API as Stentor API
    participant DB as PostgreSQL

    Op->>API: POST /targets/batch (scope CIDR)
    API->>DB: Upsert 254 targets
    API-->>Op: {created: 254}

    Op->>API: POST /targets/import/nmap (scan XML)
    API->>DB: Upsert targets + services
    API-->>Op: {targets_created: 12, services_created: 47}

    Note over API,DB: Beacon checks in from 10.10.10.21
    API->>DB: Upsert target (hostname, OS from beacon)

    Op->>API: PUT /targets/:id/note
    API->>DB: Update note (preserved on future upserts)
    API-->>Op: {status: "updated"}