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.

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.

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.

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).

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.

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.

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

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.

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.

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.