-
Launch
sleep
-
Using
/proc/self/maps
, find all required gadgets* in loaded libraries, if we can't find a gadget in automatically loaded libraries, find one anywhere in/usr/lib
, and remember in which file it was found -
Execute a longer running sleep, if necessary adding the new libraries from 2 via
LD_PRELOAD
-
Re-find the gadgets, with the correct ASLR offset, and add them to our payload, along with any data
-
dd
the payload directly over the stack, found in/proc/${PID}/maps
, over/proc/${PID}/mem
, pre-padded with NOPs -
Wait for sleep to return from the
nanosleep
syscall, and our code is executed. -
The payload I've written
open
s the file specified on the CLI, creates amemfd
, usessendfile
to copy the binary to memory, and then usesfexecve
to execute the in memory binary (which usesexecve(/proc/self/fd/X)
under the hood). Any payload ROP payload is possible, previously I had a payload which wouldmmap
aPROT_EXEC
section, copy a standard shellcode file into memory and execute that, which itself mounted a FUSE filesystem. This method was more flexible but also seemingly more brittle, and had large amounts of handwritten ASM.
- We only actually need a
NOP
,POP {RDI, RSI, RDX, RCX, R8, R9}
,SYSCALL
and aJMP [SOMETHING]
(I've usedRAX
for parity with syscalls) for syscalls and PLT calls
memfdcreate.sh
- The actual payloadoverwrite.sh
- The entrypoint (bash overwrite.sh ./busybox-x86_64
)payload.sh
- Wrapper aroundmemfdcreate.sh
to create the payloadreadsyms.sh
- ELF Parserutils.sh
- Utility functions, including ROP generator
-
We can call any PLT (e.g glibc) function or syscall, with arbitrary arguments, including string arguments
-
Pure Bash ROPChain generator, including ELF parser to ensure grepped gadgets are within the
r-x
.text
section
-
Cache gadget offsets from the ASLR base on the first run, so the second run is faster
-
Interactivity with processes executed via the
fexecve
method. This can be achieved using FUSE'spassthrough
example, but this requireslibfuse
to be available.
Parent processes can write to their children's /proc/${PID}/mem
in most distros, due to the default value of /proc/sys/kernel/yama/ptrace_scope (1
). The
less secure setting (0
) allows for any process sharing a UID to write to another processes /proc/${PID}/mem
.
To be the correct parent, we have to exec dd
after we've generated the payload. This means that dd
becomes the parent of sleep
, but we then are unable to
execute something like wait
to make the process interactive.
- Bash
- dd
- GNU grep (this can be worked around but it's slow)
Bash does not handle binary data. ELF objects are binary data. This was 'fun'.