Skip to content

I. Creating Output Modules

s0lst1c3 edited this page Nov 16, 2020 · 5 revisions

Output modules are a bit more complicated than input modules, since they consist of a template file as well as Python class file. The template file is a templatized block of code that is rendered and mutated during the payload creation file. The Python file contains all of the information needed by DropEngine to successfully render and mutate the template file.

This section will begin by describing how to create an output module by hand from a piece of CSharp code. We'll then demonstrate how to automate this process using DropEngine's Module Maker.

Creating Output Modules by Hand

To understand how this process works, it’s best to go over how to create an output module from scratch. In this example, we’ll create a decryption key (or DKey) module from a piece of CSharp code.

Creating the Template File

We begin with the following block of CSharp code, which simply returns a static decryption key.

public class DKey
{
    public static string Generate()
    {
        byte[] dkey = { 0, 0, 0, 25 };

        return Encoding.UTF8.GetString(dkey);
    }
}

The first thing we need to do is convert this block of code into a Jinja template. If you’re not familiar with Jinja, check out this guide here:

For symbolic substitution to work, we need to provide DropEngine with a way of keeping track of symbol names in this file. We do this using the follow steps.

Step 1: Convert master class name to Jinja variable

The first thing we do is substitute the name of our class with the Jinja variable special[‘class_name’], as shown in the example below.

using System;

public class {{ special['class_name'] }}
{
    public static string Generate()
    {
        byte[] dkey = { 0, 0, 0, 25 };

        return Encoding.UTF8.GetString(dkey);
    }
}

Step 2: Convert entry-point function name to Jinja variable

Next, we substitute the name of our entry-point function with the Jinja variable func_name, as shown in the next example.

using System;

public class {{ special['class_name'] }}
{
    public static string {{ func_name }}()
    {
        byte[] dkey = { 0, 0, 0, 25 };

        return Encoding.UTF8.GetString(dkey);
    }
}

Step 3: Wrap remaining symbols in Jinja variables

Next, we substitute all remaining symbols with the Jinja variable v[‘$SYMBOL’], where $SYMBOL is the name of symbol we are wrapping.

using System;

public class {{ special['class_name'] }}
{
    public static string {{ func_name }}()
    {
        byte[] {{ v['dkey'] }} = {{ special['ekey_data']['val_array'] }};

        return Encoding.UTF8.GetString({{ v['dkey'] }});
    }
}

Step 4: Remove all import statements

Finally, we remove the modules imports from the template. Save the list of imports – you’ll need them when you create the Module Class Definition in the next section.

using System;

public class {{ special['class_name'] }}
{
    public static string {{ func_name }}()
    {
        byte[] {{ v['dkey'] }} = {{ special['ekey_data']['val_array'] }};

        return Encoding.UTF8.GetString({{ v['dkey'] }});
    }
}

Step 5: Place template in its appropriate folder

Finally, place the resulting template file in the appropriate subdirectory of DropEngine's templates directory.

Creating the Module Class Definition

We now have a completed Jinja template file. Next, we need to create the module class definition. We begin with the following template:

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()

        self.name = ''
        self.mtype = ''
        self.author = ''
        self.description = ''

        self.compatible_interfaces = [

            'csharp_runner_interface',
        ]
        self.compatible_imodules = [

            'ekey_static',
        ]

        self.functions = []
        self._vars = [

            'dkey',
        ]
        self.comments = []
        self.whitespaces = []

        self.imports = []

        self.template = ''

        self.func_name = ''
        self.class_name = ''

        self.mutate_func_name = True
        self.mutate_class_name = True

To create your Module Class Definition, you’ll need to perform the following steps.

Step 1: Add list of symbol names

Remember the list of symbol names that you wrapped with Jinja variables when you built the module template? DropEngine needs them to render your module into a payload component. Edit the template by adding these symbol names to as strings within self._vars list as shown below.

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()

#...snip...

        self._vars = [

            'dkey',
        ]
#...snip...

Step 2: Add class name and entry-point function name

You’ll want to add the original class and entry-point function names to the self.class_name and self.func_name attributes, respectively (see the example below).

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()
#...snip...

        self.func_name = 'generate'
        self.class_name = 'DKey'
#...snip...

Step 3: Add path to template file

You’ll want to specify the path to your template file (you should place the template file in one of the subdirectories of DropEngine’s template folder). You can do this by editing the value of the self.template attribute.

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()
#...snip...

        self.template = 'dkeys/dkey_csharp_static.cs'

#...snip...

Step 4: Specify whether to mutate class and function name

If you don’t want the class or function name to be mutated, you’ll need to change the values of self.mutate_func_name and self.mutate_class_name as shown below.

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()
#...snip...

        self.mutate_func_name = True
        self.mutate_class_name = True

#...snip...

Step 5: Add list of imports

Remember the list of module imports that you removed from the template file? You need to add these as members of self.imports as shown below.

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()
#...snip...

        self.imports = [
            'System',
        ]

#...snip...

Step 6: Fill out module metadata

Ass with all modules, you will need to fill out the following metadata within the template:

  • self.name - the name of the module

  • self.mtype - the module type (for output modules, this should be set to one of the following: decrypters, dkeys, executors, runners, premodules, postmodules)

  • self.author- the name of the module author

  • self.description - a brief description of what the module does

  • compatible_interfaces - a list of interface modules that are compatible with the module

  • compatible_omodules - a list of compatible output modules (for example, an ekey module will have a list of compatible dkey modules)

Our example with added metadata is shown below.

import json
import config

from base.output.example.csharp_example import CSharpExample

class MExample(CSharpExample):

    def __init__(self):     

        super().__init__()
#...snip...

        self.name = 'dkey_csharp_static'
        self.mtype = 'dkey'
        self.author = '@s0lst1c3'
        self.description = 'Static dkey for testing purposes'

        self.compatible_interfaces = [

            'csharp_runner_interface',
        ]
        self.compatible_imodules = [

            'ekey_static',
        ]

#...snip...

Step 7: Modify class definition

Next, we need to modify our class definition according to the following rules:

  • DKey modules should always have class type MDKey and inherit from CSharpDKey
  • Decrypter modules should always have class type MDecrypter and inherit from CSharpDecrypter
  • Executor modules should always have class type MExecutor and inherit from CSharpExecutor
  • PreModules modules should always have class type MPreModule and inherit from CSharpPreModule
  • PostModules modules should always have class type MPostModule and inherit from CSharpPostModule
  • PostModules modules should always have class type MPostModule and inherit from CSharpPostModule
  • Runner modules should always have class type MRunner and inherit from ShellcodeRunner

In this example, we're creating a DKey module, so our class definition should look like this:

import json
import config

from base.output.example.csharp_example import CSharpExample

class MDKey(CSharpDKey):

    def __init__(self):     

        super().__init__()
#...snip...

Step 7: Modify parent class import

Next, we need to modify the import statement for our module's parent class according to the following rules:

  • DKey modules must import CSharpDKey from base.output.dkey.csharp_dkey
  • Decrypter modules must import CSharpDecrypter from base.output.decrypter.csharp_decrypter
  • Executor modules must import CSharpExecutor from base.output.executor.csharp_executor
  • PreModule modules must import CSharpPreModule from base.output.premodule.csharp_premodule
  • PostModule modules must import CSharpPostModule from base.output.postmodule.csharp_postmodule
  • Runner modules must import ShellcodeRunner from base.output.runner.runner

In this example, we're creating a DKey module, so our import statement should look like this:

Step 8: Place Module Class Definition in its appropriate folder

import json
import config

from base.output.dkey.csharp_dkey import CSharpDKey

class MDKey(CSharpDKey):

    def __init__(self):     

        super().__init__()
#...snip...

Putting it all together

We should now have a finished DKey module that looks like this:

Module Class Definition

class MDKey(CSharpDKey):

    def __init__(self):

        if config.debug:
            print('calling MDKey.__init__()')

        super().__init__()

        self.name = 'dkey_csharp_static'
        self.mtype = 'dkey'
        self.author = '@s0lst1c3'
        self.description = 'Static dkey for testing purposes'

        self.compatible_interfaces = [

            'csharp_runner_interface',
        ]
        self.compatible_imodules = [

            'ekey_static',
        ]

        self.functions = []
        self._vars = [

            'dkey',
        ]
        self.comments = []
        self.whitespaces = []

        self.imports = [
            'System',
        ]

        self.template = 'dkeys/dkey_csharp_static.cs'

        self.func_name = 'generate'
        self.class_name = 'DKey'

        self.mutate_func_name = True
        self.mutate_class_name = True

Template

{
    public static string {{ func_name }}()
    {
        byte[] {{ v['dkey'] }} = {{ special['ekey_data']['val_array'] }};

        return Encoding.UTF8.GetString({{ v['dkey'] }});
    }
}

Generating Output Modules Using Module Maker

The manual approach documented in the previous section can be time consuming and labor intensive, especially for larger templates. To solve this problem, DropEngine includes a standalone tool called Module Maker that uses ANTLR4 Abstract Syntax Trees (ASTs) to automate the process of turning raw payload components into DropEngine modules.

Say we want to create a decrypter module from the following block of CSharp code:

using System;

public class TestClassName
{
    public static byte[] decrypt(byte[] ct, byte[] ekey)
    {
        byte [] iv = { };
        SHA256Managed hashstring = new SHA256Managed();
        byte[] hashed_key = hashstring.ComputeHash(ekey);

            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = hashed_key;
                rijAlg.IV = iv;
                rijAlg.Padding = PaddingMode.PKCS7;
                rijAlg.BlockSize = 128;
                rijAlg.Mode = CipherMode.CBC;
                ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

                return decryptor.TransformFinalBlock(ct, 0, ct.Length);
            }
    }
}

We first need to obtain a complete list of symbol names from this block of code using the csharp_ini_maker.py script included with DropEngine. Point the script towards the code's source file using the --input-file flag, and use the --output-file flag to specify an output file where the symbols should be sent (see next example).

python csharp_ini_maker.py --input-file main.cs  --output-file main.ini

The csharp_ini_maker.py script will use an Abstract Syntax Tree (AST) created with ANTLR4 to enumerate all of the symbols in the source file and store them in the output file. Open up the output file, which should look something like this:

[vars]       
hashed_key   
rijAlg       
decryptor    
hashstring   
iv

[methods]    
decrypt      

[class_decls]
TestClassName

[params]
ct
ekey

[delegates]

[imports]
System

Finally, point the csharp_module_maker.py script included with DropEngine at both the CSharp source file and the INI file generated in the previous step as shown in the following example. Note that the --source-file flag points to our source file, --symbol-file points to our symbol INI file, --class-name is used to specify the CSharp class name, and --func-name is used to specify the entrypoint function of our original block of code. The rest of the flags are used to pass metadata. All of the flags shown in this example are mandatory.

python csharp_module_maker.py \
 --source-file main.cs \
 --symbol-file main.ini \
 --type decrypter \
 --name test_decrypter \
 --author s0lst1c3 \
 --description itsamodule \
 --class-name TestClassName \
 --func-name decrypt \
 --compatible-imodules asdf

Once you've run csharp_module_maker.py, you should have two files: a template file and a module class definition file. These files will automatically be saved to their correct locations within DropEngine's directory tree. Both files are shown below: Template:

public class {{ special['class_name'] }}
{
    public static byte[] {{ func_name }}(byte[] {{ v['ct'] }}, byte[] {{ v['ekey'] }})
    {
        byte [] {{ v['iv'] }} = { };
        SHA256Managed {{ v['hashstring'] }} = new SHA256Managed();
        byte[] {{ v['hashed_key'] }} = {{ v['hashstring'] }}.ComputeHash({{ v['ekey'] }});

            using (RijndaelManaged {{ v['rijAlg'] }} = new RijndaelManaged())
            {
                {{ v['rijAlg'] }}.Key = {{ v['hashed_key'] }};
                {{ v['rijAlg'] }}.IV = {{ v['iv'] }};
                {{ v['rijAlg'] }}.Padding = PaddingMode.PKCS7;
                {{ v['rijAlg'] }}.BlockSize = 128;
                {{ v['rijAlg'] }}.Mode = CipherMode.CBC;
                ICryptoTransform {{ v['decryptor'] }} = {{ v['rijAlg'] }}.CreateDecryptor({{ v['rijAlg'] }}.Key, {{ v['rijAlg'] }}.IV);

                return {{ v['decryptor'] }}.TransformFinalBlock({{ v['ct'] }}, 0, {{ v['ct'] }}.Length);
            }
    }
}

Module Class Definition:

import json
import config

from base.output.decrypter.csharp_decrypter import CSharpDecrypter

class MDecrypter(CSharpDecrypter):

    def __init__(self):

        if config.debug:
            print('calling MDecrypter.__init__()')
        super().__init__()

        self.name = 'test_decrypter'
        self.mtype = 'decrypter'
        self.author = 's0lst1c3'
        self.description = 'itsamodule'

        self.compatible_interfaces = [


        ]

        self.compatible_imodules = [


            'asdf',

        ]

        self.functions = []
        self._vars = [


            'hashed_key',

            'hashstring',

            'decryptor',

            'rijAlg',

            'iv',

            'decrypt',

            'ekey',

            'ct',

        ]
        self.comments = []
        self.whitespaces = []

        self.imports = [


            'System',

        ]

        self.template = 'decrypters/test_decrypter.cs'

        self.func_name = 'decrypt'
        self.class_name = 'TestClassName'

        self.mutate_func_name = True
        self.mutate_class_name = True
Clone this wiki locally