Skip to main content
  1. Posts/

Kraken Fundamentals - II

·2834 words·14 mins
secu
Persistence Defense Evasion kraken webshell redteam
Kraken Fundamentals - This article is part of a series.
Part : This Article

Home Page

In the first part of this series: “Kraken Fundamentals”, I talked about what Kraken is and how it works at a high level.

After this introduction, in this second part, I will present in detail and in depth, two fundamental components in Kraken: Modules and Agents.

The objective is to explain their design, structure, operation and how they relate to each other.

Although this post will be a little longer and more detailed than the previous one, I hope that it will be possible to understand how Kraken operates and the possibility of scaling it to any need.

And after this… Let’s get started!

Kraken modules #

The first question to answer when approaching this section is the following:

What is a module? #

Kraken modules are those pieces of code that allow you to perform one or more actions on the operating system on which it runs.

Generally, a module is a class, in the corresponding programming language, that contains all the logic to fulfill the purpose for which it has been developed. For this, it uses all the resources available in the language libraries and/or in the system in which it is located.

Types of modules #

Although, it is practically irrelevant, but theoretically there are two types of modules in Kraken: native and abstract modules.

Native Modules are (native) re-implementations of operating system commands. They allow to obtain information similar to that displayed by a system command, that is being imitated, when executed.

Kraken “ls” module vs Unix “ls” command

These modules can be very useful in the first reconnaissance phase. At that time, the possible existence of security solutions or other defensive products is unknown. In this way, an alternative is presented to avoid detection as soon as post-exploitation begins.

However, the accuracy with which the functionality of a system command can be recreated depends entirely on the programming language used and the context. In some technologies, there may be a lack of functionality due to the inability to perform certain actions.

On the other hand, Abstract Modules do not correspond to system commands, but perform a more complex action that is abstracted from the operating system. An example of an abstract command could be: the action to upload and download files, or for example, a module to perform a local or remote port scan.

Upload and Download modules of Kraken

Hierarchy of the modules #

An important detail to know about the modules is: “how they are organized”. Because their arrangement allows Kraken to select the modules it needs when it needs them.

To explain the hierarchy, we have to go to the modules/ folder in the root of the project. This directory looks like this:

.
└── modules
    β”œβ”€β”€ modules.py            # module configuration
    β”œβ”€β”€ template.php          # example templates
    β”œβ”€β”€ template.java
    β”œβ”€β”€ template.cs
    └── cd                    # module directory
        β”œβ”€β”€ cd.cs4.cs         # implementation in .NET 4
        β”œβ”€β”€ cd.java1.7.java   # implementation in Java 7
        β”œβ”€β”€ cd.java1.8.java
        β”œβ”€β”€ cd.php5.php       # implementation in PHP 5
        β”œβ”€β”€ cd.php7.php
        └── cd.php8.php

On the one hand, we have the templates: which are model files for the creation of modules in their respective languages. We can also find the modules.py file which contains the definition and configuration of all Kraken modules (I will explain this later).

And on the other hand, we have different directories that correspond to the modules available in Kraken.

As you can see, inside each of these directories, you can find the different versions of each module developed in the supported programming languages. The naming convention is as follows:

Name convection in Kraken modules

This format is important because:

  1. It allows the separation between the programming languages used (because there are modules that are only available in certain languages).
  2. It facilitates the versioning of the modules that allows to keep different implementations depending on the version of the programming language or technology (this is useful for backward compatibility, because if we use relatively new language functions, our modules will not work in previous versions).
  3. For those modules whose implementation does not differ in different versions, the same content is maintained but using a symbolic link to avoid duplicate files.

With this information, Kraken, at runtime, knows what type of agent it is communicating with, and is able to offer the operator those modules that can be executed in the context in which it is.

Kraken autocompler

An example of this is: if you do not have the module “ls” for PHP version 5.4, Kraken will not show it in the tty. On the other hand, if you have a module for all PHP 5 subversions, then Kraken will allow you to use it.

Module structure #

The structure of Kraken modules may vary slightly between programming languages (as each has its own loading methods and this affects their implementation), however, the modules have been designed to follow a common idea.

To show this pattern between modules, the current Java template will be used as an example:

import java.io.*;
import java.util.*;
import java.text.*;
import javax.xml.bind.*;
import java.util.regex.*;


public class Module_template
{
    private final String SUCC_CODE = "0";
    private final String ERR_CODE  = "1";
    private final String JAVA_EOL  = System.lineSeparator();

    private String hex2str(String data)
    {
        byte[] data_bytes = DatatypeConverter.parseHexBinary(data);
        String data_string = new String(data_bytes);
        return data_string;
    }

    private String normalizePath(String currPath)
    {
        currPath = currPath.replace("\"", "");
        currPath = currPath.replace("'", "");
        currPath = currPath.replace("\\", "/");
        return currPath;
    }

    private String[] parseArgs(String args)
    {
        String regex = "\"[^\"]+\"|'[^']+'|\\S+";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(args);
        List<String> arguments = new ArrayList<String>();

        while (matcher.find())
            arguments.add(matcher.group(0));

        String[] arguments_arr = new String[arguments.size()];
        arguments_arr = arguments.toArray(arguments_arr);
        return arguments_arr;
    }

    private String changeCWD(String cwd) throws Exception
    {
        File target_dir = new File(cwd).getAbsoluteFile();

        if (target_dir.exists() == false)
            throw new Exception("Directory: '" + cwd + "': does not exist");

        if (target_dir.canRead() == false)
            throw new Exception("Can't move to path: '" + cwd + "': permission denied");

        if (target_dir.isDirectory() == false)
            throw new Exception("Path: '" + cwd + "': is not a directory");

        System.setProperty("user.dir", target_dir.getCanonicalPath());

        return normalizePath(target_dir.getCanonicalPath());
    }

    private String[] doAction(String param_one, String param_two, String param_three)
    {
        String output = "";
        try
        {
            output = "do something" + JAVA_EOL;
        }
        catch(Exception ex)
        {
            return new String[]{ERR_CODE, ex.ToString() + JAVA_EOL};
        }
        return new String[]{SUCC_CODE, output};
    }

    public String[] execute(String[] args)
    {        
        if (args.length != 1)
            return new String[]{ERR_CODE, "Invalid arguments provided. Only one directory is allowed to be moved" + JAVA_EOL};
        
        return doAction(args[0]);
    }

    public String[] go(String module_cwd, String module_args)
    {
        try
        {
            String new_cwd = changeCWD(hex2str(module_cwd));
            String[] args = parseArgs(hex2str(module_args));
            return execute(args);
        }
        catch(Exception ex)
        {
            return new String[]{ERR_CODE, ex.getMessage() + JAVA_EOL};
        }
    }

    public static void main(String[] args)
    {
        Module_template m = new Module_template();
        String[] results = m.execute(args);
        System.out.println(results[1]);
        return;
    }
}

After examining the template code, some aspects of interest will be highlighted:

  1. The module consists of a Class, which is instantiated and invoked (the way will depend on the executor used in each technology) but all of them follow this line.
  2. In this class we can see a “main” method which is important because, the module must be able to be compiled locally and executed. In this way, you can maintain a common structure that allows the same modules that Kraken invokes, to be easily compiled and executed locally (obviously with its pertinent modifications).

Compilation of Kraken module developed in Java

  1. The modules must have a public method: go() that allows passing the necessary arguments from the agent to the module. The flow of this function is the following:
    1. With hex2str(), we obtain the values of the arguments that are passed in hexadecimal format to the module.
    2. With changeCWD() the “Current Working Directory” (cwd) of the module is set. The change between directories has to be maintained in the client and send the reference to the agent.
    3. Then, the arguments are processed with parseArgs(), which returns them in an array ready to be used by the module.
    4. Finally, the execute() method is called, which will process the arguments and invoke the function that performs the target action.

The concept of this go() function is similar to how it is used in Cobaltstrike BOF’s1.

It is important to note that, in the case of PHP, the go() function is suppressed because the default PHP executor evaluates the module code as is. This makes it unnecessary to instantiate the class and call the method (as we would do in Java or .NET) but to execute the code directly.
  1. After this, the do...() function is called, which will be used to perform the actions for which the module has been developed. This method will return an array with two elements:
    1. The return code (status_code): the one that indicates whether the execution has been successful or not.
    2. The result as the content of the message to be displayed (message).
  2. Finally, both the functions previous to the doAction() of this example, as well as the attributes of the class, will be essential elements for the modules to work. Therefore, it is important to use the available templates so that the modules follow the same format.

In future posts I will talk about the creation of modules (and other Kraken elements) and how to use them.

Module configuration #

Now, Kraken modules are not just code files, they must also provide some characteristics to be used, for example:

  • What arguments do you need, and what type?
  • For which technologies is it available?
  • Can it be used on any operating system?
  • How should the module work? Should it interact with any element of the client?
  • How should the result of the execution be displayed?

These and more questions are answered in the file: modules/modules.py mentioned above.

Configuration file of Kraken modules

Using this Python language structure, Kraken is able to understand how it should handle each existing module. To show each of the fields, the configuration related to the ls module will be used as an example:

{
    "name" : "ls",
    "template" : "ls",
    "description" : "List files or directories",
    "author" : "@secu_x11",
    "examples" : [
        "ls",
        "ls ..",
        "ls /etc",
        "ls -R /etc",
        "ls C:/Users"
    ],
    "so" : [
        {
            "name" : "Linux",
            "agents" : ["php","java"]
        },
        {
            "name" : "Windows",
            "agents" : ["php","java","cs"]
        }
    ],
    "args" : [
        {
            "-R": {
                "help": "Recursive mode",
                "action" : "store_true",
                "required": 0
            },
            "files": {
                "help": "Files to list",
                "nargs" : "*",
                "type":  str
            }
        }
    ],
    "dispatcher" : "default",
    "formater" : "columns"
}

Here are some of the fields that may cause questions:

  • Template: corresponds to the name of the template to be used in the file hierarchy of the modules/ directory (this usually/must match the name of the module in question).
  • Operating System: here is the information about the operating systems and technologies for which the module is supported.
  • Arguments: the only thing to note about this field is that the arguments are defined with the structure used by argparser. In this way, the validation of the arguments can be delegated to this library.
  • Dispatcher: here is the name of the component that will be responsible for defining how the module is processed by the Kraken Client (this will be discussed later).
  • Formater: as with the dispatcher, the formater is a component that defines how the results returned by the agent should be displayed.

Other considerations #

Another important detail is that, thanks to this hierarchy, the operator can “hot edit” the modules.

For example, it can happen that a post-exploitation module is developed for Kraken, and… when executed, it does not return the expected information or fails directly. In that case, it is possible to edit the content of the module on disk and re-execute it (in PHP and .NET) or recompile it (in Java).

Kraken Hot Fixes

In this way, a certain flexibility is provided to the operator who can adjust his intrusion to the environment in which he finds himself.

Kraken agents #

In order for the modules to be executed and the response obtained, they must be received and processed by the other fundamental component in Kraken: the Agents.

What is an Agent? #

A Kraken Agent is a file that is located in an accessible directory of the web service and allows the loading of Kraken modules. To do this, it receives, processes and interprets the content of the modules and arguments necessary for its proper execution.

Types of Agents #

Currently, agents can be found under the agents/ directory. There are 2 types of agents:

  • Standard Agents: to use with st mode.
  • Command-and-Control agents: for use with c2 mode.

Under the subdirectory c2/ there is only one agent, since at this moment I have only had time to make the implementation in PHP. This mode is “experimental” and has not yet been fully tested. Nevertheless it is usable and works correctly.

On the other hand, in the subdirectory st/ we can find 3 versions of agents that correspond to the available technologies: PHP, Java and .NET.

In future versions of Kraken, we will add the possibility of using a webshell generator that allows you to select between different types of webshells depending on the environment and features you want. For now, the available webshells/agents are static and some fields inside them must be modified to customize their use.

General structure #

The structure of the Agent varies according to its type. Although, like the modules, they follow the same pattern. The following diagram shows, in a simplified way, the flow of a Kraken Agent:

graph TD; A[Agent] -->|Receives HTTP request| B{Authentication} B --> |Wrong| C(Return Response) B --> |Right| D[Handle] D --> E{Parameter \nextraction} E --> |Invalid| C E --> |Valid| F{Check \nCode Action} F --> |Nonexistent| C F --> |Existent| G[Invoke Action] G --> H[Extraction of \nthe Response] H --> C

The agents perform a rigorous check of the parameters encapsulated in the HTTP request. Based on these parameters, actions are performed according to the values contained in them.

Some of the most important elements in the agent are:

  • Authentication: the first step for the communication between Client and Kraken Agent to be successful, consists of an authentication process with symmetric key:
    • The key must be known by the Agent and by the Client (this will be discussed in depth in the next post).
    • The same authentication key is used to encrypt the content of the modules and arguments.
    • The encryption and decryption process is a simple XOR operation on the value of the chosen key at both ends.

Editable fields in .NET Agent

  • Parameter Extraction: module and the required arguments are encapsulated in the HTTP request by the Client. As it encapsulates them, the Agent must know where they are embedded because it has to perform the reverse process to obtain them. This information is related to the Connection Profiles that will be explained later:

Parameter Extraction in PHP Agent

  • Action Codes: the agents have several types of actions, these are indicated according to an integer value that is sent by the Client. Although we will go into detail in the following posts, the possible values are listed below:

    • Status Query (STATUS)
    • Module loading (LOAD)
    • Module invocation (INVOKE)
    • Module unload (UNLOAD)
    • Module cleaning (CLEAN)
  • Invocation: once it has been decided which action to perform, it is invoked. In this case, the most relevant thing to talk about is the “INVOKE” action, which is the one used to execute the module sent by the client:

Executors available #

Responsible for the evaluation or execution of the received module are called executors.

Each executor has its particular way of dealing with the module and, depending on this, the client will have to send it in the appropriate format. Therefore, it is important to choose the right agent to use when deploying it.

The executors use the functions of the technology to execute the module code. Currently the list of those that are supported is as follows:

Executors available in Kraken Agents

Note: obviously there are more functionalities that allow you to execute code2 in these three programming languages. But, for this moment, it has been decided to focus the efforts on one of them versatile enough to put the focus on other points of major interest.

At the time of invocation, the required arguments are passed directly to the module as indicated. In this case, the default executor of the Standard Agent in Java will be used as an example:

Kraken Java Agent Default Executor

The result of the execution is an array of two String elements containing the status code (return code) and the result of the execution (message). With this data, the response can be returned to the Client encapsulating the information in its corresponding place.

Upon receipt, the Client will determine how to display this response, but this is another story to tell.

Conclusions #

At this point, this second part of the series is concluded. Obviously, there are still many questions to be answered and many concepts to be explained.

In the next post I will talk about the 2 modes that currently exist in Kraken (ST and C2). I will also explain in depth how is the communication between Client and Agent. Breaking down the message passing structure that is used and showing all the potential it brings.

I hope you liked the post and see you soon!

Kraken Fundamentals - This article is part of a series.
Part : This Article