Ir al contenido
  1. Posts/

Kraken Fundamentals - II

·2904 palabras·14 mins
secu
Persistence Defense Evasion kraken webshell redteam
Kraken Fundamentals - Este artículo es parte de una serie.
Parte : Este artículo

Portada

En la primera parte de esta serie: “Kraken Fundamentals”, hablé acerca de lo que es Kraken y su funcionamiento a alto nivel.

Tras esta introducción, en esta segunda parte, presentaré de forma detallada y en profundidad, dos componentes fundamentales en Kraken: los Módulos y los Agentes.

El objetivo consiste en explicar su diseño, estructura, funcionamiento y cómo se relacionan entre sí.

Aunque este post será algo más extenso y detallado que el anterior, confio en que sea posible comprender como opera Kraken y la posiblidad de escalarlo ante cualquier necesidad.

Dicho esto, y sin mayor demora… ¡Comencemos!

Módulos de Kraken #

La primera pregunta que se debe responder al abordar esta sección es la siguiente:

¿Qué es un módulo? #

Los módulos de Kraken son aquellas piezas de código que permiten realizar una o varias acciones sobre el sistema operativo en el cual se ejecuta.

Generalmente, un módulo es una clase, en el lenguaje de programación correspondiente, que contiene toda la lógica para cumplir con el propósito con el que se ha desarrollado. Para esto, utiliza todos los recursos disponibles en las librerias del lenguaje y/o en el sistema en el que se encuentra.

Tipos de módulos #

Aunque, a nivel práctico no es relevante, a nivel teórico existen dos tipos de módulos en Kraken: los módulos nativos y abstractos.

Los Módulos Nativos son reimplementaciones (nativas) de comandos del sistema operativo. Permiten obtener información similar a la que se obtendrá ejecutando el comando equivalente al que se pretende imitar.

Módulo “ls” de Kraken vs commando “ls” Unix

Estos módulos pueden ser de gran utilidad en la primera fase de reconocimiento. Pues, en ese momento, se desconoce la posible existencia de soluciones de seguridad u otros productos defensivos. De esta forma, se presenta una alternativa para evitar ser detectados nada más comenzar con la post-explotación.

No obstante, la exactitud con la que se puede recrear la funcionalidad de un comando del sistema, dependerá completamente del lenguaje de programación utilizado y del contexto. Pues, en algunas tecnologías puede verse una ausencia de funcionalidades por la incapacidad de realizar según que acciones.

Por otro lado, los Módulos Abstractos no se corresponden con comandos del sistema, sino que realizan una acción más compleja y que se abstrae del sistema operativo en cuestión. Un ejemplo de comando abstracto podría ser: la subida y descarga de archivos, o por ejemplo, un módulo para realizar un escaneo de puertos local o remoto.

Módulos Upload y Download de Kraken

Jerarquía de los módulos #

Un detalle importante a conocer acerca de los módulos es: cómo estan organizados. Pues su disposición y convención permite a Kraken seleccionar los módulos que necesita en el momento en el que los precisa.

Para explicar la jerarquía, tenemos que situarnos en la carpeta modules/ en la raíz del proyecto. Este directorio tiene la siguiente apariencia:

.
└── modules
    ├── modules.py            # config de los módulos
    ├── template.php          # plantillas de ejemplo
    ├── template.java
    ├── template.cs
    └── cd                    # directorio del módulo
        ├── cd.cs4.cs         # implementación en .NET 4
        ├── cd.java1.7.java   # implementación en Java 7
        ├── cd.java1.8.java
        ├── cd.php5.php       # implementación en PHP 5
        ├── cd.php7.php
        └── cd.php8.php

Por un lado, tenemos las plantillas: que son archivos modelo para la creación de módulos en sus respectivos lenguajes. También podemos encontrar el archivo modules.py el cual contiene la definición y configuración de todos los módulos de Kraken (esto lo explicaré más adelante).

Y por otro lado, tenemos diferentes directorios que se corresponden con los módulos que hay disponibles.

Como se puede observar, dentro de cada uno de estos directorios, se encuentran las diferentes versiones de cada módulo desarrolladas en los lenguajes de programación soportados. La convención de nombres que se sigue es la siguiente:

Formato de nombres de los módulos de Kraken

Este formato es importante pues:

  1. Permite la separación entre los lenguajes de programación utilizados (ya que hay módulos que sólo estan disponibles en determinados lenguajes).
  2. Facilita el versionado de los módulos que permite contemplar diferentes implementaciones en función de la versión del lenguaje de programación o tecnología (esto es útil de cara a la retrocompatibilidad, pues si utilizamos funciones del lenguaje relativamente nuevas, nuestro módulos no funcionarán en versiones anteriores).
  3. Para aquellos módulos cuya implementación no difiera en distintas versiones, se mantiene el mismo contenido pero utilizando un enlace simbólico para evitar el duplicado de archivos.

Con esta información, Kraken, en tiempo de ejecución sabe, con qué tipo de agente se esta comunicando, y es capaz de ofrecer al operador aquellos módulos que puedan ser ejecutados en el contexto en el que se encuentra.

Autocompletado de comandos en Kraken

Un ejemplo de esto es: si no dispones del módulo “ls” para la versión 5.4 de PHP, Kraken no te lo mostrará en la tty. En cambio, si tienes un módulo para todas las subversiones de PHP 5, entonces Kraken si te permitirá utilizarlo.

Estructura de un módulo #

La estructura de los módulos de Kraken puede variar ligeramente entre lenguajes de programación (pues cada uno tiene sus métodos de carga y eso afecta a su implementación), no obstante, los módulos se han diseñado para que sigan una idea común.

Para mostrar este patrón entre módulos, se utilizará como ejemplo la plantilla Java actual:

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;
    }
}

Tras examinar el código de la plantilla, se destacan algunos aspectos de interés:

  1. El módulo consta de una Clase, la cual es instanciada e invocada (el modo dependerá del ejecutor utilizado en cada tecnología) pero en todas se sigue esta línea.
  2. En esta clase podemos ver un método “main” el cuál es importante pues, el módulo debe poderse compilar en local y ejecutarse. De esta forma, puedes mantener una estructura cómun que te permita que los mismo módulos que Kraken invoca, sean facilmente compilables y ejecutables en local (obviamente con sus modificaciones pertinentes).

Compilación local de módulo Java de Kraken

  1. Los módulos, deben tener un método público: go() que permita pasar los argumentos necesarios desde el agente al módulo. El flujo de esta función es el siguiente:
    1. Con hex2str(), obtenemos los valores de los argumentos que se pasan en formato hexadecimal al módulo.
    2. Con changeCWD() se establece el “Current Working Directory” (cwd) del módulo. Pues el cambio entre directorios se tiene que mantener en el cliente y enviar la referencia al agente.
    3. Después se procesan los argumentos con parseArgs(), que los devuelve en un array listos para ser utilizados por el módulo.
    4. Finalmente, se llama al método execute() que se encargará de procesar los argumentos e invocar a la función que realiza la acción objetivo.

El concepto de esta función go() es similar a cómo se utiliza en los BOF de Cobaltstrike1.

Es importante destacar que: en el caso de PHP, la función go() es suprimida debido a que, el ejecutor que hay por defecto en PHP, evalua el código del módulo tal cual. Esto hace que no sea necesario instanciar la clase y llamar al método (como haríamos en Java o .NET) sino ejecutar el código directamente.
  1. Tras esto, se acaba llamando a la función do...() que se encargará de realizar las acciones para las cuales se ha desarrollado el módulo. Este método devolverá un array con dos elementos:
    1. El ćodigo de retorno o estado (status_code): el que indica si la ejecución ha sido exitosa o no.
    2. El resultado como contenido del mensaje a mostrar (message).
  2. Por último, tanto las funciones previas al doAction() de este ejemplo, como los atributos de la clase, serán elementos imprescindibles para que los módulos funcionen. Por ello es importante utilizar las plantillas disponibles para que los módulos sigan el mismo formato.

En futuros posts hablaré acerca de la creación de los módulos (y otros elementos de Kraken) y cómo utilizarlos.

Configuración de los módulos #

Ahora bien, los módulos de Kraken, no son sólo archivos de código, pues también deben aportar unas características para poder ser utilizados, por ejemplo:

  • ¿Qué argumentos necesita? ¿Y de qué tipo?
  • ¿Para qué tecnologías esta disponible?
  • ¿Se puede utilizar en cualquier Sistema Operativo?
  • ¿Cómo debe funcionar el módulo? ¿Debe interactuar con algún elemento del cliente?
  • ¿Cómo se debe mostrar el resultado de la ejecución?

Estas y más preguntas son respondidas en el archivo: modules/modules.py que hemos mencionado anteriormente.

Archivo de configuración de los módulos de Kraken

Por medio de esta estructura en lenguaje Python, Kraken es capaz de entender cómo debe tratar a cada módulo existente. Para mostrar cada uno de los campos, se utilizará la configuración relativa al módulo ls como ejemplo:

{
    "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"
}

A continuación se detallan algunos de los campos que pueden despertar preguntas:

  • Template: corresponde al nombre de la plantilla que se utilizará en la jerarquía de archivos del directorio modules/ (este suele/debe coincidir con el nombre del módulo en cuestión).
  • Sistema Operativo: aquí se plasma la información acerca de los sistemas operativos y tecnologías para los cuales esta soportado el módulo.
  • Argumentos: lo único a destacar acerca de este campo, es que los argumentos son definidos con la estructura que utiliza argparser. De esta forma, se puede delegar la validación de los argumentos a esta librería.
  • Dispatcher: aquí figura el nombre del componente que se encargará de definir cómo es procesado el módulo por el Cliente de Kraken (de esto se hablará más adelante).
  • Formater: de igual forma que con el dispatcher, el formater es un componente que define como deben mostrarse los resultados que devuelve el agente.

Otras consideraciones #

Otro detalle de importancia es que, gracias a esta jerarquía, el operador puede hacer “edición en caliente” de los módulos.

Por ejemplo, puede suceder que, se desarrolle un módulo de post-explotación para Kraken, y… al ejecutarse, este no devuelva la información que se espera o directamente falle. En ese caso, es posible editar el contenido del módulo en disco y volver a ejecutarlo (en PHP y .NET) o recompilarlo (en Java).

Edicción en caliente de módulo de Kraken

De esta forma, se aporta una cierta flexibilidad al operador que puede ajustar su intrusión al entorno en el que se encuentra.

Agentes de Kraken #

Para que los módulos sean ejecutados y se obtenga la respuesta, estos deben ser recibidos y procesados por el otro componente fundamental en Kraken: los Agentes.

¿Qué es un Agente? #

Un Agente de Kraken es un archivo que se situa en un directorio accesible del servicio web y permite la carga de módulos de Kraken. Para ello, recibe, procesa e interpreta el contenido de los módulos y argumentos necesarios para su correcta ejecución.

Tipos de Agentes #

Actualmente, los agentes se pueden encontrar bajo el directorio agents/. Existen 2 tipos de agentes que son:

  • Agentes Standard: para utilizar con el modo st.
  • Agentes Command-and-Control: para utilizar con el modo c2.

Bajo el subdirectorio c2/ simplemente se encuentra un sólo agente, ya que en este momento sólo me ha dado tiempo de realizar la implementación en PHP. Este modo es “experimental” y aún no ha sido testeado por completo. No obstante es usable y funciona correctamente.

Por otro lado, en el subdirectorio st/ podemos encontrar 3 versiones de agentes que se corresponden a las tecnologías disponibles: PHP, Java y .NET

En futuras versiones de Kraken, se añadirá la posibilidad de utilizar un generador de Webshells que permita seleccionar entre distintos tipos de Webshells en función del entorno y características que se desea. Por ahora, las webshells/agentes disponibles son fijas y deben modificarse algunos campos en su interior para customizar su uso.

Estructura general #

La estructura del Agente varía en función de su tipo. Aunque, al igual que los módulos, estos siguen un mismo patrón. El siguiente diagrama muestra, de forma simplificada, el flujo de un Agente de Kraken:

graph TD; A[Agente] -->|Recibe petición HTTP| B{Autenticación} B --> |Incorrecta| C(Devolver Respuesta) B --> |Correcta| D[Handle] D --> E{Extracción de \nParámetros} E --> |Inválidos| C E --> |Válidos| F{Comprobar Código \nde Acción} F --> |Inexistente| C F --> |Existente| G[Invocar Acción] G --> H[Extracción de \nla Respuesta] H --> C

Los agentes realizan una rigurosa comprobación de los parámetros encapsulados en la petición HTTP. Pues, a partir de estos, se realizan las acciones en función de los valores contenidos en los mismos.

Algunos elementos destacables en el agente son:

  • Autenticación: el primer paso para que la comunicación entre Cliente y Agente de Kraken sea exitosa, consiste en un proceso de autenticación mediante una clave simétrica:
    • La clave debe ser conocida por el Agente y por el Cliente (de esto se hablará en profundidad en el siguiente post).
    • La misma clave de autenticación se utiliza para cifrar el contenido de los módulos y argumentos.
    • El proceso de cifrado y descifrado es una simple operación XOR sobre el valor la clave elegida en ambos extremos.

Campos editables en Agente .NET

  • Extracción de Parámetros: tanto el módulo como los argumentos necesarios, son encapsulados en la petición HTTP por parte del Cliente. Al igual que este encapsula, el Agente deberá conocer dónde son incrustados pues tiene que realizar su proceso inverso para obtenerlos. Esta información es relativa a los Perfiles de Conexión que se explicarán más adelante:

Extracción de Parámetros en el Agente PHP

  • Códigos de Acciones: los agentes tienen varios tipos de acciones, estos son indicados en función de un valor entero que se envia por el Cliente. Aunque se entrará en detalle en los siguientes posts, se citan a continuación los posibles valores:

    • Consulta de Estado (STATUS)
    • Carga de módulos (LOAD)
    • Invocación de módulo (INVOKE)
    • Descarga de módulo (UNLOAD)
    • Limpieza de módulos (CLEAN)
  • Invocación: una vez se ha decidido que acción realizar, se procede a su invocación. En este caso, lo más relevante es hablar acerca de la acción “INVOKE”, que es la que se utiliza para ejecutar el módulo enviado por el cliente:

Ejecutores disponibles #

Los encargados de realizar esta evaluación o ejecución del módulo recibido toman el nombre de Ejecutores.

Cada ejecutor tiene su forma particular de tratar al módulo y, dependiendo de este, el cliente tendrá que enviarlo en el formato adecuado. Por ello es importante elegir bien el agente utilizado a la hora de su despliegue.

Los ejecutores utilizan las funciones de la tecnología en uso para conseguir ejecutar el código del módulo. Actualmente el listado de los que son soportados es el siguiente:

Ejecutores disponibles en los Agentes de Kraken

Nota: obviamente hay más funcionalidades que te permiten ejecutar código2 en estos tres lenguajes de programación pero, para este momento, se ha decidido centrar los esfuerzos en uno de ellos suficientemente versátil para poner el foco en otros puntos de mayor interés.

Al momento de realizar su invocación, se pasan los argumentos requeridos directamente al módulo de la forma indicada. Para este caso se ejemplificará con el ejecutor por defecto del agente Standard en Java:

Ejecutor por defecto del Agente Java de Kraken

El resultante de la ejecución es un array de dos elementos del tipo String que contiene el código de estado (return code) y el resultado de la ejecución (message). Con estos datos, se puede retornar la respuesta al Cliente encapsulando la información en su lugar correspondiente.

Al recibirla, el Cliente determinará como mostrar esta respuesta, pero esto ya es otra historia por contar.

Conclusiones #

Llegados a este punto, se concluye esta segunda parte de la serie. Obviamente, aun quedan muchas dudas por resolver y muchos conceptos por explicar.

En el siguiente post hablaré acerca de los 2 modos que existen actualmente en Kraken (ST y C2). También explicaré en profundidad cómo es la comunicación entre Cliente y Agente. Desglosando la estructura del paso de mensajes que se utiliza y mostrando todo el potencial que aporta.

¡Espero que os haya gustado el post y nos vemos muy pronto!

Kraken Fundamentals - Este artículo es parte de una serie.
Parte : Este artículo