Assignment #1 for the SLAE certification asks the student to do the following:
- Create a Shell_Bind_TCP shellcode
- Binds to a port
- Execs Shell on incoming connection
- Port number should be easily configurable
Analysis of Metasploit Shellcode
To begin, I first wanted to analyze how a TCP bind shell works. I used msfvenom to generate the shellcode, and then piped the shellcode into libemu for analysis. This would allow for me to quickly see what linux syscalls were being made by the shellcode, and what I would need to use in my own shellcode. The output from libemu is shown below:
The generated shellcode does four things:
- Create a TCP socket
- bind a port to the socket (port 8080 in the example above)
- Set the TCP socket to listen to incoming connections
- set the TCP socket to accept incoming connections
This is all done using the “sys_socketcall” linux syscall, which is number 102 or 0x66 in hexadecimal. These syscalls are setting up the network socket so that the application can listen for and accept a TCP connection on the specified port.
After the network socket is setup, the shellcode then sets up the shell that will be executed when the connection is received. The output from libemu is shown below:
This is doing the following:
- Using dup2 to duplicate the socket file descriptor into the stdin, stdout, and stderr file descriptors
- Using execve to launch /bin//sh
So, when a network connection is received by the socket, the execve syscall will launch /bin/sh to provide the connection a shell, and the socket file descriptor will redirect stdin, stdout, and stderr to the connection.
To begin coding the TCP bind shell in Assembly, I first needed to know what the requirements for the “sys_socketcall” syscall were. This syscall is used repeatedly to create the socket, bind the port, create the listener, and accept new connections.
The requirements for sys_socketcall can be viewed with “man 2 socketcall”
sys_socketcall has two arguments that need to be passed to it:
- int call
- unsigned long *args
“int call” is an integer that relates to what specific function of sys_socketcall is being used. These functions can be view in the file /usr/include/linux/net.h
From this, the calls that will be used by the TCP bind shell will be:
- 1 – socket
- 2 – bind
- 4 – listen
- 5 – accept
The “unsigned long *args” requires that sys_socketcall be provided a pointer to a list of arguments that will then be used by the call made by sys_socketcall. Socket, bind, listen, and accept all have their own required arguments that must be provided when making the sys_socketcall.
Creating the “socket” sys_socketcall
The first sys_socketcall to be made by the shellcode will be “socket”. Using “man 2 socket” will provide the requirements to create the socket:
The requirements are:
- int domain
- int type
- int protocol
“int domain” selects the “protocol family” to be used by the socket. The man page shows the different families that socket can use:
The protocol family to be used in this bind shell will be “AF_INET” to use IPv2. This means using “2” for the value of int domain.
“int type” is the type of network socket that will be used, or the “network semantics.”
To create a TCP socket, the first option “SOCK_STREAM” will be used. This is a value of “1” for int type.
Finally, “int protocol” specifies the protocol to be used with the socket. As the man page for socket states:
“Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0.
The protocol family being used is AF_INET or IPv4. By checking the man page with “man 7 ip”, it appears that for a TCP socket to be used, “0” should be provided as the value for int protocol.
So, before the syscall for “sys_socketcall” is made, the registers will need to be configured to:
- EAX = 0x66 for the sys_socketcall
- EBX = 0x1 for int call = 1
- ECX = memory pointer to the arguments for “socket”
The assembly code below makes the sys_socketcall to create a new TCP socket:
push eax ; this is pushing 0x0 to the stack which will be the “int protocol” param for sys_socket for a tcp socket
push ebx ; this is pushing 0x1 to the stack which will be the “int type” param for sys_socket for a SOCK_STREAM
push byte 0x2 ; push 0x2 to stack. Will be “int domain” param for sys_socket, which is AF_INET/IPv4
mov ecx, esp ; put the address of the stack pointer into ECX. This is a pointer for the arguments passed to sys_socket
mov al, 0x66
After the syscall for sys_socketcall is made, the TCP socket will be created, and the file descriptor for the TCP socket will be saved in EAX. The value of the file descriptor will be used in future syscalls.
Creating the “bind” sys_socketcall
The next step is to make the “bind” sys_socketcall. bind will specify the port used by the TCP socket, as well as the IP address to bind to. For the creating the assembly code, the port “8081” will be used, and the IP address “0.0.0.0” will be used so that it binds to all interfaces on the system.
“man 2 bind” shows the requirements to make the bind sys_socketcall.
“int sockfd” is the file descriptor for the socket that was previously made. This value is stored in EAX.
“const struct sockaddr *addr” is a pointer to the address to bind to. This will be the IP address, “0.0.0.0”, and the port, “8081.”
“socklen_t addrlen” is the length of the address. For this, the length witll be 16 bytes, or 0x10.
To begin, the values for the address will be pushed onto the stack. This will be a dword of 0’s for 0.0.0.0, 8081 in hex which is 911f, and the value “2” to specify that it is an address for AF_INET/IPv4. The assembly looks like this:
xor ebx,ebx ; 0 out ebx
; pushing the the value for sockaddr. First push the address which is 0x0, or all interfaces. Then push the port number
; which is 8081 in decimal or 1f91 in hex. Then set the type/domain/whatever to 0x2 or AF_INET/IPv4
push ebx ; 0x00000000 for the network address. 8 bytes
push word 0x911f ; port number. 8081 in hex is 1f91. 0x911f is 8081 in reverse order. 4 bytes, 12 total
push word 0x2 ; push 0x2 to use AF_INET/IPv4. 4 bytes, 16 total
After this has all been pushed to the stack, the memory address of the stack pointer will need to be saved. This will be saved in the register ECX for now.
mov ecx, esp ; put stack pointer address in ECX. This is a pointer to the parameters defined above for the sockaddr
Now, the stack will need to be filled with the values for the bind call. First, the value for “socklen_t addrlen” will be pushed, which is 0x10 or 16. Next, the pointer stored in ECX, which points to the values for the address to be used, is pushed onto the stack. Finally, the socket file descriptor is pushed, which is stored in EAX. With the stack set up with all the arguments for the bind call, the stack pointer is moved into ECX.
; now set up the stack to contain the parameters for the bin. This is the socket file descriptor, the pointer to the values for
; sockadd, and the length of the address
push 0x10 ; size of the server address or 16 in decimal
push ecx ; pointer to the values for sockaddr
push eax ; eax contains the FD of the socket from the last syscall
mov ecx, esp ; after above is done, set ECX to the stack pointer address so it can all be passed to sys_socketcall
; make the syscall for the bind
mov bl, 0x2 ; moving 0x2 into EBX so that the bind call from sys_socketcall will be used
mov al, 0x66
When the bind call completes successfully, the value “0” is returned in the EAX register.
Creating the “listen” sys_socketcall
The next call to make for the socket is the “listen” call. Its requirements can be seen with “man 2 listen”.
The listen call has two requirements, the value of the socket file descriptor, and the backlog.
The backlog is the maximum number of pending requests for the socket file descriptor. There shouldn’t be any pending requests for this TCP bind shell, so that can be 0. XOR EBX with itself and push it onto the stack.
The value of the socket file descriptor now needs to be pushed onto the stack. That was previously stored in EAX, but has since been overwritten. However, ECX still contains the memory pointer for the arguments to bind, and the first argument at that address is the value for the socket file descriptor. So, the value for the socket file descriptor can be pushed onto the stack by pushing the value of the memory address stored in ECX onto the stack.
; setup the “listen” sys_socketcall. Need to provide listen with the socket file descriptor and a “backlog”, which
push ebx ; this is “int backlog” for listen. It will be 0
push dword [ecx] ; ecx is currently pointing at the file descriptor from the previous syscall. Push to the stack
mov ecx, esp ; put the pointer to the parameters needed for listen into ECX to be used in the sys_socketcall
mov al, 0x66
mov bl, 0x4 ; listen
When the listen call completes successfully, the value “0” will be returned into EAX.
Creating the “accept” sys_socketcall
The last sys_socketcall call will be for “accept.” The requirements for the accept call can be seen with “man 2 accept”
The value for the socket file descriptor is again stored at the memory address stored in ECX. The vallue for the sockaddr isn’t necessary for this bind shell to work, can be set to null. The socklen_t value will be set to 0x10, same as before. The assembly code to get this all onto the stack and make the sys_socketcall is shown below:
; set up the socket “accept”. All that’s really needed is the socket FD which ECX points to, and then two nulls
push dword [ecx] ; ECX stores the address that points to the value 0x3, or the socket FD
mov ecx,esp ; move stack pointer into ECX since that will be needed for the args in the sys_socketcall
mov bl, 0x5 ; accept
mov al, 0x66
When this syscall completes successfully, it will return a value for the file descriptor for the accepted network socket. This will occur when a connection has been established to the TCP bind shell.
Creating the shell
To have a shell prompt for the user when they make a connection to TCP bind shell, two things need to happen:
- stdin, stdout, and stderr need to be redirected to the socket
- a shell such as /bin/bash needs to be executed upon connection
Step 1 can be accomplished using the “dup2” syscall. Dup2 takes a file descriptor and duplicates it into a new file descriptor. The file descriptor for the accepted socket is currently stored in EAX. The file descriptors that the socket file descriptor need to be duplicated into in order to have a functioning shell are 0, 1, and 2. These provide the shell with stdin, stdout, and stderr.
The assembly code looks like this:
; set up the input/output for the accept connection. Will need to use dup2 to copy the accepted socket FD into new fd’s
mov ebx,eax ; eax contains the FD for the socket. Move value EAX into EBX as EBX needs the FD value for the dup2 call
mov cl, 0x2 ; ecx is being used for the “new” FD to duplicate the socket FD in. Need it to be for 0,1,2
;this will perform the dup2 syscall three times to copy the socket FD into the “new” FDs of 2, then 1, then 0. This is to get stdin, stdout, and stderr
mov al, 0x3f
ECX is set to the value 0x2. This is used to stored the value of the “new” file descriptor for the dup2 sys call. After the dup2 syscall is made, the value of ECX is decreased by one, and then as long as an operation didn’t result in a negative, the code jumps back to run the dup2 call again. After ECX is 0, and the final file descriptor for stdin, stdout, and stderr is made, the dec ECXoperation results in a negative, and the jumps stop.
After the file descriptors are set up, the syscall for execve will be used to launch /bin/bash to provide the connection a bash shell. This is done by pushing the value “////bin/bash” onto the stack, storing the stack pointer in EBX, and then running the execve syscall.
; after the new FD’s have been created, need to set up the execve call for /bin/bash so that the new connection gets a shell
;push the dword of nulls onto the stack
;push the string of “////bin/bash” onto the stack. Extra slashes to pad out the string to a multiple of four
; ////bin/bash is on the stack, at the ESP register. So, to get the address of the location of the string into ebx, mov esp into ebx
mov ebx, esp
; push another null to get EDX to point to a memory address that is a dword of nulls
mov edx, esp
; now for ecx, we need the address of EBX on the stack
mov ecx, esp
; make the syscall
mov al, 0xb
After this is made, the TCP bind shell will execute /bin/bash after a successful TCP connection.
Testing the TCP bind shell
The assembly code was compiled and executed
Netcat was used to connect to the local system over port 8081
A shell was successfully received!
The assembly code for the bind shell can be found here: https://github.com/FatRodzianko/slae-exam/blob/master/1-bind_shell/bind-shell.nasm
To make this TCP bind shell configurable, a python script was created that would accept a port value provided by the user, insert it into the shellcode, and then print out the shellcode to the user. The shellcode could be copied into a shellcode runner, and then launched.
Netcat was again used to connect to the TCP bind shell on the local system.
The connection was accepted and a /bin/bash shell was provided to the connection.
Additionally, the python script does some basic checks to make sure that the provided port number is a valid TCP port, and makes sure that when the port value is converted to hexadecimal, it does not result in any null values or “\x00″s in the shellcode.
The code for the python script can be found here: https://github.com/FatRodzianko/slae-exam/blob/master/1-bind_shell/generate-bind-shellcode.py
All of the shellcode, scripts, and notes used for this can be found at: https://github.com/FatRodzianko/slae-exam/tree/master/1-bind_shell
SLAE Student Disclaimer
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/onlinecourses/securitytube-linux-assembly-expert/
Student ID: SLAE- 1373