Files
2026-05-27 17:43:06 -07:00

8.3 KiB
Raw Permalink Blame History

Module Development

Modules are driven by a yaml configuration per module. In most cases, only a yaml is needed to create a module.

{% embed url="https://youtu.be/ZS3Rdld_Ebo" %}

Basic Structure

Each module is defined by a set of metadata (like authors, description, and tactics) and options. These options define what values can be set when the module is executed.

name: ExampleModule
authors:
  - name: John Doe
    handle: '@johndoe'
description: A sample module demonstrating Empire module structure.
software: ''
tactics:
  - TA0002
techniques:
  - T1059
background: true
output_extension: ps1
needs_admin: false
opsec_safe: true
language: powershell
min_language_version: '2'
comments: []
options:
  - name: SampleOption
    description: An example option.
    required: true
    value: 'default_value'
    strict: true

MITRE ATT&CK Fields

Every module should include proper MITRE ATT&CK metadata. The fields are:

  • tactics: A list of ATT&CK tactic IDs (e.g., TA0001 through TA0043). Every module should have at least one tactic — do not leave this as an empty list.
  • techniques: A list of ATT&CK technique or sub-technique IDs. Use the format T#### for techniques (e.g., T1059) or T####.### for sub-techniques (e.g., T1059.001).
  • software: If the module wraps a known ATT&CK software entry, set this to its ID (e.g., S0002 for Mimikatz, S1071 for Rubeus). Leave as '' if the tool is not cataloged in ATT&CK.

Refer to the MITRE ATT&CK Enterprise Matrix for valid tactic, technique, and software IDs.

Special Options

Empire reserves certain option names that receive special handling during module execution. These are filtered out of the parameters passed to the module's script and instead control how the task is dispatched or processed.

Agent

Required on all modules. Identifies which agent should execute the module. This is automatically populated by Empire and should not be included in the module's script logic.

Background

Allows the operator to override the module-level background field at runtime. When a module defines background: true in its YAML metadata, it will run as a background job by default. Adding a Background option lets operators choose per-execution whether to run in the foreground or background.

options:
  - name: Background
    description: Run as a background job (non-blocking). Can be killed via the jobs/kill_job endpoint.
    required: false
    value: 'true'
    type: bool
    suggested_values:
      - 'true'
      - 'false'
    strict: true

If the Background option is not defined on the module, the module-level background field is used as-is.

OutputFunction

PowerShell-specific. Controls how module output is formatted. Substituted into the script via the {{ OUTPUT_FUNCTION }} placeholder. Defaults to Out-String. See PowerShell Modules for details.

Advanced Options

Empire modules support advanced configuration for dynamic dependencies between options. For example, one option may depend on the value of another option. This is handled using the depends_on field.

Dynamic Option Dependencies

The depends_on field allows an option to be displayed or required based on the value of another option. When a dependency is not met, the option is hidden from the UI and skipped during validation — but its default value is still passed to the module's generate() function so that code accessing the parameter does not fail.

If an option has both required: true and depends_on, it is conditionally required: the option must be provided only when its dependency condition is met. This is useful for options like Listener (required when Payload=Empire) or Command (required when Payload=Manual).

  - name: Listener
    description: Listener to use.
    required: true
    value: ''
    depends_on:
      - name: Payload
        values: ['Empire']
  - name: Command
    description: Custom command to run.
    required: true
    value: ''
    depends_on:
      - name: Payload
        values: ['Manual']

In this example, when Payload is set to Empire, the Listener option is required and Command is hidden. When Payload is set to Manual, the reverse applies.

Example: Switching Between URL and File Inputs

The PowerShell Invoke-Script module uses an internal selector to choose between a URL-based script or a file upload. When ScriptType is set to URL, the UI presents ScriptUrl. When set to File, it presents the File option instead.

  - name: ScriptType
    description: Type of script you want to execute.
    required: true
    value: 'URL'
    internal: true
    strict: true
    suggested_values:
      - URL
      - File
  - name: File
    description: PowerShell script to load and run from memory.
    required: false
    value: ''
    type: file
    depends_on:
      - name: ScriptType
        values: ['File']
  - name: ScriptUrl
    description: URL to download a PowerShell script from.
    required: false
    value: 'https://raw.githubusercontent.com/samratashok/nishang/master/Gather/Get-Information.ps1'
    depends_on:
      - name: ScriptType
        values: ['URL']

In this pattern, depends_on hides or reveals fields depending on the ScriptType selection. The internal: true flag keeps the selector out of the final module execution input while still influencing the UI logic.

options:
  - name: Credentials
    description: Manually enter credentials or credential ID.
    required: true
    value: 'Manual'
    strict: true
    internal: true
    suggested_values:
      - Manual
      - CredID
  - name: CredID
    description: Use CredID from the store.
    required: false
    value: ''
    depends_on:
      - name: Credentials
        values: ['CredID']

Internal Options

The internal field is used to manage dynamic options in Empire modules, such as top-tier switches that control which options are displayed to the user. These options are internal to Empires logic and are not used during the execution of the module itself. Instead, they help control the visibility and behavior of other options.

For example, an internal option can act as a switch to determine whether certain options appear based on the users selection.

- name: Credentials
  description: Manually enter credentials or credential ID.
  required: true
  value: 'Manual'
  strict: true
  internal: true
  suggested_values:
    - Manual
    - CredID

In this example, Credentials is an internal option that controls whether CredID or Password is shown to the user, depending on its value. This logic helps ensure the correct options are visible and modifiable based on the selected configurations.

options:
  - name: Credentials
    description: Manually enter credentials or credential ID.
    required: true
    value: 'Manual'
    strict: true
    internal: true
    suggested_values:
      - Manual
      - CredID
  - name: CredID
    description: CredID from the store to use.
    required: false
    value: ''
    depends_on:
      - name: Credentials
        values: ['CredID']
  - name: Password
    description: Password for manual credentials entry.
    required: false
    value: ''
    depends_on:
      - name: Credentials
        values: ['Manual']

Modules like Invoke-RunAs use a top-level selector to switch between manual credentials and a stored credential ID. This keeps the UI focused on only the fields you need.

  - name: Credentials
    description: Manually enter credentials or credential ID.
    required: true
    value: 'Manual'
    strict: true
    internal: true
    suggested_values:
      - Manual
      - CredID
  - name: CredID
    description: CredID from the store to use.
    required: false
    value: ''
    depends_on:
      - name: Credentials
        values: ['CredID']
  - name: UserName
    description: Username to run the command as.
    required: false
    value: ''
    depends_on:
      - name: Credentials
        values: ['Manual']
  - name: Password
    description: Password for the specified username.
    required: false
    value: ''
    depends_on:
      - name: Credentials
        values: ['Manual']

Here, suggested_values drives the UI dropdown, and depends_on ensures that either the CredID field or the manual UserName/Password fields are presented, but not both.