Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Fun little loader shellcode that executes an ELF in-memory using an anonymous file descriptor (inspired by
;;; Copyright (C), zznop,
;;; This software may be modified and distributed under the terms
;;; of the MIT license. See the LICENSE file for details.
;;; This PoC shellcode is meant to be compiled as a blob and prepended to a ELF
;;; (or bash script). It opens an anonymous file descriptor (backed by vmem)
;;; using the memfd_create syscall and proceeds to write the executable to the
;;; anonymous file descriptor and exec it.
;;; $ nasm -f bin -o mem-loader.bin mem-loader.asm
;;; $ cat mem-loader.bin > mem-loader-final.bin
;;; Then use a hex editor and fixup executable_size placeholder with the size of
;;; the appended executable (little endian). The placeholder value is 8 bytes
;;; (all A's) and starts at byte 3.
;;; Resources:
[bits 64]
global start
jmp past
executable_size: dq 0x4141414141414141 ; fixed up with size of executable
fd_name: db 0 ; emtpy file descriptor name
fd_path: db "/proc/self/fd/", 0, 0, 0, 0, 0 ; path to file descriptor for exec call
mov rax, 319 ; __NR_memfd_create syscall num
lea rdi, [rel fd_name] ; ptr to empty file descriptor name
mov rsi, 1 ; MFD_CLOEXEC (close file descriptor on exec)
syscall ; create anonymous fd
test rax, rax ; good file descriptor?
js done ; return if bad file descriptor
mov rdi, rax ; file descriptor (arg_0)
mov rax, 1 ; __NR_write
lea rsi, [rel executable] ; pointer to executable base (arg_1)
mov rdx, qword [rel executable_size] ; load size of executable into rdx (arg_2)
syscall ; write the executable to the fd
cmp rax, rdx ; did everything get written successfully?
jnz done ; fail out if all bytes were not written
call fixup_fd_path ; fixup the fd path string by converting the fd to a str
mov rax, 59 ; execve syscall num
lea rdi, [rel fd_path] ; filename
xor rcx, rcx ; zeroize rcx (terminator for argv)
push rcx ; push 0 to stack
push rdi ; push address of fd path to the stack
mov rsi, rsp ; argv (address of fd path, null)
xor rdx, rdx ; envp = NULL
syscall ; call execve (won't return if successful)
add rsp, 16 ; restore the stack
ret ; return
;;; fixup the fd path string with the file descrpitor -
;;; basically sprintf(foo, "/proc/self/fd/%i", fd);
mov rax, rdi ; number to be converted
mov rcx, 10 ; divisor
xor bx, bx ; count digits
xor rdx, rdx ; high part = 0
div rcx ; rcx = rcx:rax/rcx, rdx = remainder
push dx ; dx is a digit in range [0..9]
inc bx ; count digits
test rax, rax ; rax is 0?
jnz .divide ; no, continue
; pop digits from stack in reverse order
mov cx, bx ; number of digits
lea rsi, [rel fd_path] ; rsi points to fd path string buffer
add rsi, 14 ; start of location to write the fd (as a string)
pop ax
add al, '0' ; convert to ASCII
mov [rsi], al ; write it to the buffer
inc si
loop .next_digit
; appended script or ELF executable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.