Skip to content

Modules Overview

Maxime Landon edited this page Mar 4, 2020 · 10 revisions

As said previously when presenting Payload modules, Wiregost gives a consequent part of its functionality through its Modules.

At the moment, two types of Modules are usable in Wiregost (Payload and Post), and one type allows you to easily write new ones (Post).

This page is an overview of the code through which Modules are implemented, and used.


Modules are an interface

In order to provide extensible module functionality through functions and properties, we leverage Go interfaces: this way, we can make many types of modules, or extend them easily, while retaining the capacity of the client to run them as well. If you know about Go, you will guess why the implicit interface implementation of Go is really useful in the present case.

The Module interface is the following:

type Module interface {
	Init(int32) error
	Run(string) (string, error)
	SetOption(string, string)
	ToProtobuf() *pb.Module
}
Init(int32) error

When a module is loaded onto a user's module stack, it inits itself by parsing its metadata file (metadata.json) and by registering the ModuleUserID (int32) to the module. This ModuleUserID is a unique number produced by each connected console. When a module produces events (we'll see events below), it specifies the ModuleUserID. When all consoles receive the event, only the console that ran the module will print the event.

Run(string) (string, error)

When the client console 'runs' the module, its sends its command to the module. Because payload modules have several commands for either spawning listeners or compiling implants, they make use of the string parameter for dispatching to the good function. While your module must thus have this Run(command string) (result string, err error) function, it is NOT obliged to make use of this parameter. We'll see below how to use the return values string and error, in conjunction with Module Events.

SetOption(string, string)

When you set a module's option in the console, the server-side Module object uses this function to set the option to the given value.

ToProtobuf() *pb.Module

When you load a module onto the stack and after it has inited itself, the Server will use this function to send the Module to the console.

NOTE As you will see below and in the following pages, the only function that you will actually see when writing a module is the Run() function.


Modules are a Struct

If you already code in Go, you must know that Modules are also a struct with its fields and methods. Below is the Module struct type, and its Options struct type:

type Module struct {
	Name        string   `json:"name"`        // Name of the module
	Type        string   `json:"type"`        // Type of module (auxiliary, exploit, post, payload)
	Path        []string `json:"path"`        // Path to the module (ie. post/windows/x64/powershell/gather/powerview)
	Description string   `json:"description"` // Description of the module
	Notes       string   `json:"notes"`       // Notes about the module
	References  []string `json:"references"`  // A list of references to vulnerabilities/others (ie. CVEs)
	Author      []string `json:"author"`      // A list of module authors
	Credits     []string `json:"credits"`     // A list of people to credit for underlying tools/techniques
	Platform    string   `json:"platform"`    // Operating system the module can run on.
	Targets     []string `json:"targets"`     // A list of operating system versions the modules works on
	Arch        string   `json:"arch"`        // CPU architecture for which the module works
	Lang        string   `json:"lang"`        // Programming language in which the module is written
	Priviledged bool     `json:"priviledged"` // Does the module requires administrator privileges

	Options map[string]*Option
	UserID  int32
}

// Option - Module option
type Option struct {
	Name        string `json:"name"`        // Name of the option
	Value       string `json:"value"`       // Value of the option (default is filled here)
	Required    bool   `json:"required"`    // Is this a required option ?
	Flag        string `json:"flag"`        // Flag value of the option, used for execution
	Description string `json:"description"` // A description of the option
}

When the Module is loaded onto the stack, it calls his method Init() and parses the following JSON metadata file:

{
        "type": "post",
        "name": "MimiPenguin",
        "path": ["post", "linux", "x64", "bash", "credentials", "MimiPenguin"],

        "author": ["Dan Borges (@ahhh"],
        "credits": ["Hunter Gregal (@HunterGregal)"],

        "platform": "linux",
        "arch": "x64",
        "lang": "bash",
        "privileged": true,

    "description": "mimipenguin is a bash script used to find plaintext credentials in memory during post-exploitation. Must be run as root. It automates the dumping of 
    memory and searches for Linux user credentials. The MimiPenguin script is downloaded from the C2 Server, executed and then deleted.",
        "notes": "https://attack.mitre.org/wiki/Software/S0179",

        "options": {
                "Session": {"name": "Session", "value": "", "required": true, "flag": "", "description":"Implant session to run this module on"},
                "TempDirectory": {"name": "TempDirectory", "value": "/tmp", "required": true, "flag": "", "description":"Directory in which to download the MimiPenguin script."}
        }
}

Therefore, you can add any number of options to your module, all of them will be usable.


The Module type is inherited by all Modules

The Module type presented above is inherited (in Go, embedded) by all Modules that you can use in Wiregost. Therefore, the Modules you can write will automatically implement the Module interface, and you won't have to deal with Init(), SetOption() or ToProtobuf().

Below is an example of a Module template, that you can find (if you clone the repo) in the modules/templates/ directory:

package main

import (
	"github.com/maxlandon/wiregost/server/log"
	"github.com/maxlandon/wiregost/server/module"
)

// [ Base Methods ] ------------------------------------------------------------------------//

// Post - A Post Module (Change "Post")
type Post struct {
	*module.Module
}

// New - Instantiates a Post Module, loading its metadata
func New() *Post {
	mod := &Post{&module.Module{}}
	mod.Path = []string{"post", "path/to/module/directory"}
	return mod
}

var modLog = log.ServerLogger("path/to/module/directory", "module")

// [ Module Methods ] ------------------------------------------------------------------------//

// Run - Module entrypoint.
func (s *Post) Run(command string) (result string, err error) {

	return "Module executed", nil
}

We will explain in detail how to modify and use this template in order to write a module in the following pages, but for now, you should notice:

type Post struct {
	*module.Module
}

When you write a module, you declare a new type instead of a variable, because it must declare the Run() function, which will be specific to this module. (In Go, you cannot declare type methods outside of the package where the type is declared). The *module.Module is the way, in Go, to make your Module to inherit the base Module's type properties and functions

func New() *Post {
	mod := &Post{&module.Module{}}
	mod.Path = []string{"post", "path/to/module/directory"}
	return mod
}

This function instantiates the module, and embeds a module.Module{} base module type in it, so that it inherits its methods. It also specifies the path in which to find the metadata file, (this path will be overwritten when the module is inited)

Clone this wiki locally