
Un simple PoC para invocar un shellcode cifrado mediante una llamada oculta
Un PoC simple para invocar un shellcode cifrado mediante una llamada oculta.
Este código utiliza un truco simple para ocultar la instrucción que efectivamente saltará a nuestro shellcode. Esto debería hacer que el análisis estático o la emulación sean más difíciles.
Como siempre, si este concepto ya fue explicado en otros documentos, envíame un mensaje y lo añadiré con gusto a las referencias.
La disposición de memoria para usar esta técnica es la estándar y se describe en la Figura 1.
┌───────────────────────────────┐
│ │
│ │
│ │
│ │
│ shellcode cifrado │
│ │
│ │
│ │
│ │
│ │
│ │
├───────────────────────────────┤
│ │
│ │
│ bucle de descifrado │
│ │
│ │
└───────────────────────────────┘
Figura1. Disposición de memoria
El bucle de descifrado descifrará el shellcode y saltará a él. Toda la "magia" está dentro del bucle de descifrado, ya que después de terminar de descifrar el shellcode, el bucle de descifrado comenzará a descifrar su propio código. El descifrado de la primera instrucción resultará en un salto a nuestro shellcode que se ejecuta en la siguiente iteración del bucle :)
A continuación la parte relevante:
@decryption:
xor word ptr [eax], 06799h ; <jmp esi bytes> XOR <primeros dos bytes de esta instrucción>
add eax, sizeof word
dec ecx
loop @decryption
Como puedes ver, el bucle de descifrado no contiene ninguna instrucción que salte al shellcode descifrado. El código ensamblador se ensambla en el siguiente formato binario:
66:8130 9967 | xor word ptr ds:[eax],6799
83C0 02 | add eax,2
49 | dec ecx
E2 F5 | loop 450006
en este caso, la clave de descifrado debe ser 06799h, ya que la operación XOR entre 8166h (los primeros dos bytes de la primera instrucción del bucle de descifrado) y 6799h (la clave XOR) es e6ffh que se ensambla como jmp esi. En otras palabras:
0x8166 (xor word ptr ds:[eax],...) XOR 0x6799 (clave de descifrado) == 0x6eff (jmp esi)
Al establecer el registro ESI al inicio de nuestro shellcode, podemos lograr la ejecución :)
A continuación un ejemplo de depuración. Inicialmente se copia el shellcode cifrado, seguido del código utilizado para descifrar y llamar al shellcode. Es posible notar que después de la tercera ejecución del bucle de descifrado, la instrucción xor word ptr ds:[eax],6799 cambia a jmp esi.

Los pasos para usar esta técnica son:
Para hacer que el código de descifrado sea menos identificable, es posible usar métodos alternativos para llamar al shellcode. Para tener más libertad, podemos considerar aumentar el tamaño de fragmento que se cifra durante cada iteración. En mi PoC usé 2 bytes porque jmp esi necesita dos bytes, pero podemos usar un tamaño de fragmento de 4 u 8 bytes, lo que permite al operador tener más alternativas que encajen en fragmentos de 4 u 8 bytes. Según la forma elegida de llamar al shellcode, la constante de cifrado también cambiará.