Operator-C2 HTTP API

These are the default endpoints for the operator HTTP server. These endpoints can be changed by modifying the server/config/admin/routes.yaml file.

Endpoint Verb Purpose Details
/op/stats GET Gets basic statistics about the C2 example
/op/tasks/list GET Lists currently running or new tasks given by this operator example
/op/tasks/add POST Adds a new task for an implant example
/op/tasks/results/:task_id GET Gets the results of a task example
/op/tasks/delete/:task_id DELETE Deletes the task with the given ID example
/op/implant/config/:implant_id GET Gets the malleable configuration of the implant with the given ID example
/op/implant/config/:implant_id POST Updates the malleable configuration of the implant with the given ID example
/op/implant/list GET Lists all implants example
/op/implant/kill/:id GET Removes the given implant from the database and purges it from the affected system. example
/op/implant/build POST Builds an implant with the given configuration example
/op/auth/token/request GET Used for fetching an operators authentication token example
/op/auth/token/revoke DELETE Revokes the current operator authentication token example
/op/auth/token/status GET Checks the status of the current operator authentication token example
/op/implant/alias/create POST Creates an alias for the given implant example
/op/admin/revoke_access POST Revokes access to the admin panel for the given operator example

Examples

For all requests except /op/auth/token/request, the authentication token should be present in the Authorization header as a bearer token.

All requests can return a 401 status code if the token is invalid or expired. This will have the body:

{
 "msg": "Not authenticated",
 "status": false
}

/op/stats

This endpoint is used to get basic statistics about the C2.

Example response:

  {
    "active_tasks": 0,
    "implants": 1,
    "operators": 1,
    "status": true,
    "total_tasks": 1,
    "uptime": "3:38:16.451002"
  }

/op/tasks/list

This endpoint is used to list all active (i.e. not COMPLETE) tasks that have been created by the operator.

Example response:

{
 "status": true,
 "tasks": [
  {
   "args": {
    "kill_date": "2024-12-12"
   },
   "created_at": "2023-04-03 21:44:52.883699",
   "executed_at": null,
   "implant_id": "abf0b02e502cae7e6c7b8c368822fe9b",
   "opcode": 5,
   "operator_name": "admin",
   "output": null,
   "read_at": "2023-04-03 21:44:54",
   "status": "TASKED",
   "task_id": "64ce6a88b75a377a8320c87b9b6d4f3b"
  },
  {
   "args": [
    "ls",
    "-la"
   ],
   "created_at": "2023-04-04 01:26:48.660957",
   "executed_at": null,
   "implant_id": "abf0b02e502cae7e6c7b8c368822fe9b",
   "opcode": 0,
   "operator_name": "admin",
   "output": null,
   "read_at": null,
   "status": "CREATED",
   "task_id": "b0cb6baaad0b1ed69362cffb4384e6d6"
  }
 ]
}

/op/tasks/add

Example request body:

{
 "opcode": 0,
 "implant_id": "a5a90992dd62b62f78d5541c8a07c3b4",
 "args": [
  "ls",
  "-la"
 ]
}

Example responses:

  • 200:
{
 "status": true,
 "task": {
  "args": "{ls,-la}",
  "created_at": "2023-03-23 14:59:26.078969",
  "executed_at": null,
  "implant_id": "a5a90992dd62b62f78d5541c8a07c3b4",
  "opcode": 0,
  "operator_name": "admin",
  "output": null,
  "read_at": null,
  "status": "CREATED",
  "task_id": "9cf5780f66e1184600206d7c1327e1fb"
 }
}
  • 400: For example if the opcode field is missing:
{
 "msg": "Invalid task, missing fields: opcode",
 "status": false
}

/op/tasks/results/:task_id

This endpoint is used to get the output of a task. result is the base64 encoded output of the task. Depending on the type/opcode of the task, this could be anything from a string to a binary file.

Example response:


{
 "result": "dG90YWwgNDgKZHJ3eHIteHIteCAxOCBjYXJ0ZXIgc3RhZmYgIDU3NiBBcHIgIDEgMjI6NTMgLgpkcnd4ci14ci14IDEzIGNhcnRlciBzdGFmZiAgNDE2IE1hciAzMCAxMzoyNCAuLgotcnctci0tci0tICAxIGNhcnRlciBzdGFmZiAgMTY0IE1hciAyMyAxMDoyMiBEb2NrZXJmaWxlLmMyCi1ydy1yLS1yLS0gIDEgY2FydGVyIHN0YWZmICAxNTkgTWFyIDIzIDEwOjIyIERvY2tlcmZpbGUub3BlcmF0b3IKLXJ3LXItLXItLSAgMSBjYXJ0ZXIgc3RhZmYgMTk3MiBNYXIgMjQgMTI6NTMgTXlDZXJ0aWZpY2F0ZS5jcnQKLXJ3LS0tLS0tLSAgMSBjYXJ0ZXIgc3RhZmYgMzI3MiBNYXIgMjQgMTI6NTIgTXlLZXkua2V5Ci1ydy1yLS1yLS0gIDEgY2FydGVyIHN0YWZmICAgIDAgRmViIDIwIDE4OjA0IFJFQURNRS5tZApkcnd4ci14ci14ICA0IGNhcnRlciBzdGFmZiAgMTI4IEFwciAgMiAxNzo1MyBfX3B5Y2FjaGVfXwotcnctci0tci0tICAxIGNhcnRlciBzdGFmZiAzMzQ3IEFwciAgMiAxNzo1MCBhcHAucHkKZHJ3eHIteHIteCAgNCBjYXJ0ZXIgc3RhZmYgIDEyOCBBcHIgIDEgMjI6NTMgY29uZmlnCi1ydy1yLS1yLS0gIDEgY2FydGVyIHN0YWZmIDQyNTUgQXByICAzIDEwOjQ3IGNyZWF0ZV9vcGVyYXRvci5weQotcnctci0tci0tICAxIGNhcnRlciBzdGFmZiAxNDc2IEFwciAgMSAyMjo1MyBkb2NrZXItY29tcG9zZS55bWwKZHJ3eHIteHIteCAgMyBjYXJ0ZXIgc3RhZmYgICA5NiBGZWIgMjIgMTA6MjggaW5zdGFuY2UKLXJ3LXItLXItLSAgMSBjYXJ0ZXIgc3RhZmYgIDExNSBNYXIgMjMgMTA6MjIgbWFrZV9kYi5weQpkcnd4ci14ci14IDE0IGNhcnRlciBzdGFmZiAgNDQ4IEFwciAgMiAxNToyMiBtYWxpa2V0aAotcnctci0tci0tICAxIGNhcnRlciBzdGFmZiAgMzQ3IE1hciAgNSAxODoxNyByZXF1aXJlbWVudHMudHh0Ci1ydy1yLS1yLS0gIDEgY2FydGVyIHN0YWZmIDE4MjEgQXByICAyIDE3OjU2IHRlc3RfY2xpZW50LnB5Ci1ydy1yLS1yLS0gIDEgY2FydGVyIHN0YWZmIDIxOTcgQXByICAzIDE3OjQzIHRlc3RfY3J5cHRvLnB5Cg==",
 "status": true
}

/op/tasks/delete/:task_id

This endpoint deletes a task from the database. This is useful if you want to delete a task that has been executed or was accidentally created.

Example response:

{
 "status": true
}

If the task does not exist, the response will be:

{
 "msg": "Unknown task",
 "status": false
}

GET /op/implant/config/:implant_id

This endpoint is used to get the malleable configuration of an implant. See the profile document for more information on malleable configuration and the fields in it.

Example response:

{
    "config": {
        "cookie": "SESSID",
        "enc_key": "22WQpoz4PCyvifzO4GunTjP52fx4kkZElGtqFg8kuwM=",
        "jitter": 0.0,
        "kill_date": "",
        "max_retries": -1,
        "sleep_time": 0,
        "tailoring_hash_function": "sha256",
        "tailoring_hash_rounds": 1,
        "tailoring_hashes": []
    },
    "status": true
}

If the implant is not found:

{
    "msg": "Unknown implant",
    "status": false
}

POST /op/implant/config/:implant_id

This endpoint is used to set the malleable configuration of an implant. See the profile document for more information on malleable configuration and the fields in it. The request should be a valid JSON object with only the fields you want to change. The fields that are not specified will not be changed. Any invalid fields will be ignored.

Behind the scenes, this endpoint submits a job with the UPDATE_CONFIG opcode

Example request: If you want to change the kill_date and sleep_time fields.

{
    "kill_date": "2024-12-12",
    "sleep_time": 30
}

Example response:

The response is the same as the response for getting a task.

{
    "status": true,
    "task": {
        "args": {
            "asdf": 123,
            "kill_date": "2024-12-12",
            "sleep_time": 30
        },
        "created_at": "2023-04-04 02:32:10.22647",
        "executed_at": null,
        "implant_id": "abf0b02e502cae7e6c7b8c368822fe9b",
        "opcode": 5,
        "operator_name": "admin",
        "output": null,
        "read_at": null,
        "status": "CREATED",
        "task_id": "8869c6bdf9dd702026baa16b808f0272"
    }
}

If the request is invalid or empty:

{
  "msg": "No valid fields found in request",
  "status": false
}

/op/implant/list

This endpoint is used to get a list of all implants in the database.

Example response:

{
    "implants": [
        {
            "arch": null,
            "created_at": "2023-04-03 21:44:29",
            "hostname": "localhost:8080",
            "implant_id": "abf0b02e502cae7e6c7b8c368822fe9b",
            "implant_pk": "f+W9O+P34mPlijXC4GiPiJupedQAraIsZ1CTFl6MOgs=",
            "ip": "172.19.0.1",
            "last_seen": "2023-04-03 21:44:29",
            "os": null,
            "server_sk": "EuzRc4VlYg/Al0CaFqYjL0iRfh92758gbtVxDeb6yqg=",
            "user": ""
        },
        ...
    ],
    "status": true
}

/op/implant/kill/:implant_id

This endpoint is used to kill an implant. This is useful if you want to kill an implant that is no longer needed. NOTE: This will kill the implant immediately, and it will not be able to reconnect to the server.

Example response:

{
  "status": true
}

If the implant is not found:

  {
    "msg": "Unknown implant",
    "status": false
  }

/op/auth/token/request

Request headers:

Name Content
X-ID operator_name
X-Signature Base64(enc(sign(logon_secret, operator_signing_key), server_public_key))

Response fields:

Name Meaning
status Whether or not authentication succeeded
token The authentication token to use for all following requests
rmq_queue The RabbitMQ queue to subscribe to
rmq_host The hostname or IP address where the RabbitMQ server is located
rmq_port The TCP port of the RabbitMQ instance

Example request:

GET /op/auth/token/request
X-ID: admin
X-Signature: 85wV03Vh+BBb2LjTB9rkf1+0Cg==

Response (JSON):

The response will be encrypted with the operators public key. The following is an example decrypted response:

{
  "status": true,
  "token": "asdf1234qwertyuiop",
  "rmq_queue": "queue_name",
  "rmq_host": "rabbit.local",
  "rmq_port": 1338
}

If the operator request is invalid, the following will be returned (with status code 400):

{
  "status": false,
  "message": "Unknown operator key"
}

/op/auth/token/revoke

By default, authencation tokens have an expiration of 6 hours. This endpoint should be used to prematurely revoke a currently active authentication token.

Example request:

DELETE /op/auth/token/revoke
Authentication: Bearer <insert_token_here>

Example response: Success:

{
  "status": true
}

Failure:

{
  "status": false,
  "message": "Invalid token"
}

/op/auth/token/status

This endpoint can be used to check the status of the current authentication token.

Example request:

GET /op/auth/token/status
Authentication: Bearer <insert_token_here>

Example response: Success:

{
  "status": true,
  "msg": "Authenticated"
}

Failure:

{
  "status": false,
  "message": "Invalid token"
}

/op/implant/build

This endpoint is used to build a new implant. Note that depending on the power of the C2 server, this may take a while (a few minutes). The request should be a valid JSON object with any of the following fields:

Name Meaning Default
initial_sleep_seconds The number of seconds to wait before connecting to the server 180
schtask_persist Whether or not to use schtasks for persistence true
use_antidebug Whether or not to use antidebugging techniques true
kill_parent Whether or not to kill the parent process after spawning (unused) true
use_antivm Whether or not to use antivm techniques true
scheduled_task_name The name of the scheduled task MicrosoftEdgeUpdateTaskMachineUA
register_max_retries The maximum number of times to retry registering with the server 5

Example request:

{
  "initial_sleep_seconds": 180,
  "schtask_persist": true,
  "use_antidebug": true,
  "kill_parent": true,
  "use_antivm": true,
  "scheduled_task_name": "MicrosoftEdgeUpdateTaskMachineUA",
  "register_max_retries": 5
}

Example response:

{
  "implant": "base64_encoded_implant_pe"
}

/implant/<implant_id>/alias/create

This endpoint is used to create an alias for an implant with the given implant_id. This is useful if you want to give an implant a more memorable name.

Method: POST

Requirements:

  • The alias must be unique

The request should be a valid JSON object with the following fields:

Name Meaning
alias The alias to give the implant

Example request:

{
  "alias": "my_alias"
}

Example response (success):

{
  "status": true,
  "msg": "Alias created"
}

Example response (failure):

{
  "status": false,
  "msg": "Alias already exists"
}

/implant/<implant_id>/alias/delete/<alias>

This endpoint is used to change the alias of an implant with the given implant_id.

Requirements:

  • The alias must be unique
  • The alias must exist for the given implant

Method: DELETE

Example response (success):

{
  "status": true,
  "msg": "Alias deleted"
}

Example response (failure):

{
  "status": false,
  "msg": "Alias does not exist"
}

/implant/<implant_id>/alias/list

This endpoint is used to list all aliases for an implant with the given implant_id. If the implant does not exist, this will still return a 200 status code with an empty list.

Method: GET

Example response:

{
  "status": true,
  "aliases": [
    "alias1",
    "alias2"
  ]
}

/implant/alias/resolve/<alias>

This endpoint is used to get the implant_id of an implant with the given alias. If the alias does not exist, this will return a 404 status code.

Method: GET

Example response:

{
  "status": true,
  "implant_id": "06e65b07"
}

Example response (failure):

{
  "status": false,
  "msg": "Unknown alias"
}

Copyright © 2023 Carter Brainerd. All rights reserved.