Extending PoshC2

Any red-teamer worth their salt will get familiar with their tooling and customise or extend it to suit their needs and the environment.

In addition to being customisable through the configuration file, such as the UserAgent and HTTP Responses in use, PoshC2 can be easily altered to suit the given situation.

It is recommended to git commit changes locally to PoshC2 if you alter files in the installation directory, as this means that you can still update PoshC2 and have your changes merged on top of incoming changes.

For any changes that would be useful for others, please consider adding a pull request.

Adding Modules

To add a new module, simply place it in the Modules directory in the PoshC2 installation directory.

Any PowerShell script can be loaded by the PowerShell implant and executed in memory:

loadmodule MyPowerShellScript.ps1
Invoke-MyCmdlet

Or similarly for Python Implant

loadmodule MyPythonScript.py
my_python_function("arg")

The C# Implant also has the ability to load and execute modules in memory, but these modules are C#.NET executables. These modules are invoked through .NET reflection and do not touch disk, and PoshC2 requires a few extra bits of information.

  • The Namespace of the class containing the Main method
  • The Class name of the class containing the Main method
  • The Assembly name
loadmodule MySharpStuff.exe
run-exe Namespace.Class Assembly <args>

For example, if the below code was compiled into an executable:

using System;

namespace ConsoleApp1
{
    class MySuperProgram
    {
        static void Main(string[] args)
        {
            if (args.Length >= 1)
            {
                Console.WriteLine(args[0]);
            }
        }
    }
}

Then we can see the namespace is ConsoleApp1, and the class is MySuperProgram

The Assembly Name can be found in Visual Studio by right-clicking the project (not the solution) and choosing Properties

../_images/assemblyname.png

Alternatively it can be found in the Projects .csproj file:

../_images/assemblyname2.png

In this case the Assembly Name is PwnStuff.

The command then to run the executable and print “PoshC2Rocks” would be:

run-exe ConsoleApp1.MySuperProgram PwnStuff PoshC2Rocks
../_images/run-exe.png

There is also a run-dll command which works exactly the same way as run-exe for C#.NET DLLs, however there is one additional argument which specifies the entry point (and the class and namespace must match that of this entrypoint):

run-dll ConsoleApp1.MySuperProgram PwnStuff MyEntryMethod PoshC2Rocks

Adding Aliases

We fully appreciate that nobody wants to type run-exe ConsoleApp1.MySuperProgram PwnStuff PoshC2Rocks over and over, and that is why we added aliases in Alias.py.

Aliases can either replace full or part commands, depending on the preference:

# C# Implant
cs_alias = [
    ["s", "get-screenshot"],
]

# Parts of commands to replace if command starts with the key
cs_replace = [
    ["safetydump", "run-exe SafetyDump.Program SafetyDump"],
    ["sharpup", "run-exe SharpUp.Program SharpUp"],
    ["seatbelt", "run-exe Seatbelt.Program Seatbelt"],
    ["rubeus", "run-exe Rubeus.Program Rubeus"],
    ["sharpview", "run-exe SharpView.Program SharpView"],
    ["sharphound", "run-exe Sharphound2.Sharphound Sharphound"],
    ["sharpweb", "run-exe SharpWeb.Program SharbWeb"],
    ["watson", "run-exe Watson.Program Watson"],
    ["pwnstuff", "run-exe ConsoleApp1.MySuperProgram PwnStuff"]
]

There are separate lists for each Implant type, and the _alias lists replace the full typed command with the value if the full typed command matches the alias.

For example, if the user types s, that is replaced with get-screenshot. If the user types sort, that is not.

The *_replace lists replace the alias with the value if the typed command starts with the alias, allowing the command to take arguments.

For example, if the user types rubeus kerberoast, it will be replaced with run-exe Rubeus.Program Rubeus kerberoast. If the user types echo "rubeus", no substitution will take place.

Adding the pwnstuff alias then allows us to run pwnstuff PoshC2Rocks instead of run-exe ConsoleApp1.MySuperProgram PwnStuff PoshC2Rocks.

Adding Autoloads

PoshC2 allows certain modules to be loaded automatically when a command is run. This is useful for loading a frequently used module without requiring the user to load it manually first, but has the risk of loading modules into memory by mistake if the user runs the wrong command at the wrong time.

To add an autoload, edit Autoloads.py and add a line for your command for the Implant type. For example, we can load PwnStuff.exe automatically when the user runs a pwnstuff ... command.

def run_autoloads_sharp(command, randomuri, user):
    command = command.lower().strip()
    if command.startswith("run-exe seatbelt"): check_module_loaded("Seatbelt.exe", randomuri, user)
    elif command.startswith("run-exe sharpup"): check_module_loaded("SharpUp.exe", randomuri, user)
    elif command.startswith("run-exe safetydump"): check_module_loaded("SafetyDump.exe", randomuri, user)
    elif command.startswith("run-exe rubeus"): check_module_loaded("Rubeus.exe", randomuri, user)
    elif command.startswith("run-exe sharpview"): check_module_loaded("SharpView.exe", randomuri, user)
    elif command.startswith("run-exe watson"): check_module_loaded("Watson.exe", randomuri, user)
    elif command.startswith("run-exe sharphound"): check_module_loaded("SharpHound.exe", randomuri, user)
    elif command.startswith("run-exe internalmonologue"): check_module_loaded("InternalMonologue.exe", randomuri, user)
    elif command.startswith("run-exe sharpsocks"): check_module_loaded("SharpSocks.exe", randomuri, user)
    elif command.startswith("run-exe sharpweb"): check_module_loaded("SharpWeb.exe", randomuri, user)
    elif command.startswith("run-exe wmiexec.program"): check_module_loaded("WExec.exe", randomuri, user)
    elif command.startswith("run-exe smbexec.program"): check_module_loaded("SExec.exe", randomuri, user)
    elif command.startswith("run-exe invoke_dcom.program"): check_module_loaded("DCOM.exe", randomuri, user)
    elif command.startswith("sharpsocks"): check_module_loaded("SharpSocks.exe", randomuri, user)
    elif command.startswith("safetykatz"): check_module_loaded("SafetyKatz.exe", randomuri, user)
    elif command.startswith("pwnstuff"): check_module_loaded("PwnStuff.exe", randomuri, user)

Adding Auto-completion

If you want to add an entry in the autocompletion prompt for your module, or add it to the help command, then you can add it to Help.py.

Simply add it to the relevant lists for the different prompt contexts (C# Implant, PS Implant etc) for the autocompletion or to the relevant help text block and restart the Implant-Handler.

These commands can include aliases.

SHARPCOMMANDS = ["get-userinfo", "stop-keystrokes", "get-keystrokes", "delete", "move", "label-implant", "upload-file", "quit",
             "download-file", "get-content", "ls-recurse", "turtle", "cred-popper", "resolveip", "resolvednsname", "testadcredential",
             "testlocalcredential", "get-screenshot", "modulesloaded", "get-serviceperms", "unhide-implant", "arpscan", "ls", "pwd", "dir",
             "inject-shellcode", "start-process", "run-exe", "run-dll", "hide-implant", "help", "searchhelp", "listmodules", "loadmodule",
             "loadmoduleforce", "back", "ps", "beacon", "setbeacon", "kill-implant", "get-screenshotmulti", "safetydump", "seatbelt", "sharpup",
             "sharphound", "rubeus", "sharpview", "watson", "get-hash", "migrate", "sharpsocks", "safetykatz", "get-computerinfo", "get-dodgyprocesses", "sharpweb", "pwnstuff"

Adding Payloads

Payloads are generated when the C2 Server first starts for a new project, or when a new payload is created from the Implant-Handler through create*payload commands, such as createnewpayload and createdaisypayload.

The generation of these payloads can be altered by editing the templates in the Files directory of the installation directory and/or editing Payloads.py, which is responsible for the generation of the payloads.