It is now one year since I wrote the first blog post about RustRAT. Even though I did not start writing any actual code until this year, I still consider this some kind of anniversary. On this anniversary, few of the goals I set for version 0.1 remain. However, as I have worked on RustRAT, I have come to realise that quite a lot remains until RustRAT is something I would consider using in an engagement.
The rat is almost fully functional (for certain values of fully functional), and communication between rats and the server is operational. It is not quite ready, but a new update with a new git commit should hopefully be coming this or next week. While I think everything has proceeded surprisingly smoothly, I have spent many hours debugging stupid mistakes, and this post is about yet another mistake.
May is almost upon us, and it is time for another quick post on Rustrat progress. Implementing the random seed “generators” from last post went smoothly as soon as I realized that inline assembly is only available in nightly, and figured I needed to do something else. I decided to go for separate files containing the assembly for the required functionality, adding a custom build script to build the assembly file using CC. This post is not about my adventures with MASM, however, although I might revisit that later on. As you may have guessed from the title, this post is another cautionary tale about what can go wrong when you play with unsafe rust and pointers.
A couple of weeks ago I accidentally installed Civ 6 again, which reduced the time I have had available for other non-essential activity. Fortunately, I have just about had enough Civ for this round, which means that I should shortly have more time for things like RustRAT again. Progress has not halted to a complete stop, however, and rustrat-server is now more or less ready to receive incoming connections over HTTP. The server is not very usable at the moment, and before I have something even remotely ready for testing, basic logging and some interface to actually interact with the rats is needed.
Progress has not been as fast as I hoped lately. There are several reasons for this, but most of it is probably due to a lack of planning. When I finally started programming RustRAT I had a very specific idea of what I wanted to start with. After I finished that specific, little part of RustRAT, progressed seemed to halt to a grind.
While I have slowed down some, progress seems slower than it is because my plan is less detailed for this part of the development. My initial plan looked something like this:
Glue together wasm3 and libffi so that wasm blobs can call arbitrary functions.
When deciding on the architecture for RustRAT’s C2 server, I went for something asynchronous. There are no very good reasons for this, but I have never really written anything major in an asynchronous manner before. Besides, a lot of nice looking Rust crates are also written using asynchronous code, so this lets me play with them.
As I am doing something I have never done before, progress is slow as I play with tokio, figure out what to put in my .env file to get sqlx to compile, and attempt to handle HTTP requests with warp. All in all it is an enjoyable experience, mostly.
The libffi bindings are now working, which means that RustRAT is now able to execute WebAssembly binaries, and those binaries can call arbitrary functions from dlls. I have also uploaded the code to github, so it is possible to follow the progress at https://github.com/rustrat/rustrat
. In this post I will briefly mention what has been done so far, the next step on the way to a full-fledged RAT, and finally a little guide on how to call MessageBoxA from a WebAssembly binary.
The present Currently RustRAT is not very exciting, but it is possible to demonstrate the libffi capabilities. The “rustrat-client” executables can be used to execute WebAssembly binaries, for example from the “demo-messagebox” crate, which will call MessageBoxA.
RustRAT uses libffi (more specifically the libffi-rs bindings) to call arbitrary functions at run-time. This works by putting the function’s argument and return types into a struct and providing libffi with a function pointer. Libffi then makes sure that all the arguments are placed in the correct registers or on the stack and calls the function pointer. I am not that proficient with Rust yet, but if anything is unsafe, I am pretty sure this is it.
While developing RustRAT, my tests would initially contain calls to libffi-rs’s own call function and everything was proceeding smoothly. The call function is essentially something like this:
It has been three weeks since my last post, and progress has been slow. I will be trying to write small status updates semi-regularly both to keep myself motivated and try to document the development of RustRAT.
Since last time, I have written code for running WebAssembly programs and for calling arbitrary functions from DLLs at run-time using libffi, more specifically with the libffi-rs bindings. Currently, I am working on trying to put these two parts together using a lot of ugly and unsafe Rust. The code gluing these two parts together will not be easy to read, even harder to alter, and quite possibly impossible to debug, but I am hoping to have something that works (for certain values of work) in a few weeks.
It has been more than six months since I wrote the first blog post about RustRAT. During these months, the closest I have come to actually doing something has been to build myself a new computer with many cores in order to compile code faster or something like that. However, rather than develop RustRAT I have been busy playing all the games I have been unable to play the last ten years due to lack of a computer able to play recent games.
It is about time for me to actually attempt to create RustRAT. The plans are mostly unchanged since the last blog post, but I have made one significant change.