The Universal Loader for Go

As promised in the previous post, Go Assembly on the arm64, I have been working on a very special project for the past couple months, and I’m very pleased to announce the Universal Loader!

This Golang library provides a consistent interface across all platforms for loading shared libraries from memory and without using CGO. When I say “all platforms”, I mean Linux, Windows, and OSX including the new M1 Apple chip.

Until someone tells me differently, I am claiming that this is the very first loader to work on the M1, Golang or not. I haven’t tried it myself yet, but it will likely also work on any POSIX system, like BSD variants, without changes. If you try this on a new platform, let me know!

Additionally, the Linux backend of the loader does not use memfd, and I believe this is the first Golang loader to do that as well. The Linux ramdisk implementation memfd is very easy to use, but it’s also relatively easy to detect.

Consistent Interface

On all platforms, this is a basic example of how to use Universal to load a shared library from memory and call an exported symbol inside it:

Just pass in your library as a byte array, call LoadLibrary on it and give it a name, then you can Call() any exported symbol in that library with whatever arguments you want.

All you have to do differently on a different platform is load the right type of library for your platform. On OSX, you would load myLibrary.dyld, on Linux myLibrary.so, and on Windows myLibrary.DLL.

Check out the examples/ folder in the repo and the tests for more details.

Algorithms and References

The Linux and Windows implementations are very straight-forward and based on the same basic algorithms that have been widely used for years, and I used malisal’s excellent repo as a reference, with some minor changes.

For the OSX loader, I referred heavily to MalwareUnicorn’s wonderful training, but I did have to make a few updates. For one thing, dyld is not guaranteed to be the next image in memory after the base image.

Also have to give a heartful thank you to C-Sto and lesnuages, who contributed code to the Windows and OSX loaders, respectively.

Last but not least, this library makes heavy use of our own fork of the debug library, so this would not have been possible without contributions over the years from the whole Binject crew, and it’s a perfect example of the power of the tools we’ve made.

This isn’t the first tool to come out of our work in Binject, and it definitely will not be the last.

Once you get locked into a serious m*****e collection, the tendency is to push it as far as you can.

Using Binject

Binject is a sweet multipart library, making up several tools for code-caving and backdooring binaries via golang. The project was originally inspired as a rewrite of the backdoor factory in go and now that it’s functional this post will show you how to use it. In this post we are going to explore how you can use the library operationally for a number of tasks. We will start with an example of using some of the command line tools included with the project for the arbitrary backdooring of files. Next we will look at using the library to backdoor a file programmatically. Finally we will use the bdf caplet with bettercap to backdoor some binaries being transmitted on the network, on-the-fly. I want to give a shout out to the homie Vyrus, as a lot of this was inspired by him but in non-public projects, so I can’t link to his stuff. I also want to give a shoutout to Awgh, as he’s been an awesome mentor and powerhouse in implementing a lot of the Binject features. Below you can see the binjection command line tool being used to backdoor an arbitrary windows PE, on Linux. In the next section we will explore some of the command line features of Binject.

Using the command line tools included with Binject is pretty straightforward; the main library Binject/binjection contains a command line interface tool that exposes all of the existing functionality for backdooring files on macOS, Windows, and Linux. Above we can see go-donut being used to turn a gscript program into position independent shellcode, then we use the binjection command line tool to backdoor a Windows PE (a .exe file), all on a Linux OS. The binjection cli tool takes 3 main command line flags, “-f” to specify the target file to backdoor, “-s” to specify a file containing your shellcode in a raw bytecode format, and “-o” specifying where to write your new backdoor file. Optionally you can give a “-l” to write the output to a logfile instead of standard out. You can also specify the injection method to use, although the tool only supports a very limited and mostly default set currently. The binjection cli tool will automatically detect the executable type and backdoor it accordingly. Another library and command line tool included with the framework is Binject/go-donut, which is essentially just a port of TheWover/donut. We can see this being used above to prepare another program to be embedded in our target executable. I really like both of these command line tools because it’s easy to cross compile them for linux or macOS, giving me a really convenient way to generate my target shellcode regardless of what OS I’m operating from. Having the entire tool chain in go allows me to easily move my tools to whatever operating system or use them all together in the same codebase. Even if you’re not familiar with go, you can just as easily compile the cli tools and script them together with something like bash or powershell. Below we can see the binjection cli tool being used to backdoor ELF executables on Linux.

Using binjection programmatically as a go library is also super simple and arguably far more useful because you can now integrate it into so many more projects. The library calls are just as straight forward, basically a single function call depending on the binary type your backdooring. Here we can see it as a standalone example for others to use. We can also see it being implemented here for Windows in Sliver, a golang based c2 framework with tons of features. We can also use binjection in gscript, although it requires this embarrassingly small shim interface. This is insanely powerful functionality to be able to ship in an implant binary, as the implant can now backdoor, already persisted, legitimate binaries on the target system. You can even break down the supporting libraries and use other parts of Binject, like Binject/debug, as a triage tool, which we demonstrate with bintriage. Finally, to bring the project full circle, Binject has been integrated with bettercap for the on-the-fly backdooring of files on the network. It currently accomplishes this using bettercap’s ARP spoofing module, the network proxy module, and a helper tool to manage the file queue, making the whole process really clean. Using the integration is easy with the Binject/backdoorfactory helper tool. Simply follow these usage instructions, which just involves installing all of the necessary prerequisite tools, and then Binject/backdoorfactory will spit out the caplet and command you need you need for bettercap. You can see a demo of all of this together in the video at the end. So now you have a pretty good idea of some different ways you can use Binject. We also encourage people to submit pull requests to the library with new injection methods or even further enumerating the executable types. There is still a lot of work to be done here but you can use the library currently to great effect.

Back to the Backdoor Factory

backdoorfactory setting up the man-in-the-middle with bettercap and injecting a binary inside of a tar.gz as it’s being downloaded by wget (courtesy of sblip)

Backdoor Factory documentation

Backdoor Factory source code

About six years ago, during a conversation with a red teamer friend of mine, I received “The Look”. You know the look I’m talking about. It’s the one that keeps you reading every PoC and threat feed and hacker blog trying to avoid. That look that says “What rock have you been under, buddy? Literally everyone already knows about this.

In this case, my transgression was my ignorance of The Backdoor Factory.

The Backdoor Factory was released by Josh Pitts in 2013 and took the red teaming world by storm. It let you set up a network man-in-the-middle attack using ettercap and then intercept any files downloaded over the web and inject platform-appropriate shellcode into them automatically.

Man-in-the-Middle Attack Using ARP Spoofing

In the days before binary signing was widely enforced and wifi security was even worse than it is now, this was BALLER. People were using this right and left to intercept installer downloads, pop boxes, and get on corpnet (via wifi) or escalate (via ARP). It was like a rap video, or that scene in Goodfellas before the shit hits the fan.

But nothing lasts forever. Operating systems made some subtle changes and entropy took over, and so the age of The Backdoor Factory came to an end. Some time later, the thing actually stopped working and red teamers sadly packed up their shit and lumbered off to the fields of Jenkins.

Fear not, gentle reader, for our tale does not end here.

For some reason, a year and change back, I found myself once again needing something very much like The Backdoor Factory and stumbled on this “end of support” announcement. Perhaps still motivated by my shameful ignorance years ago, I thought “maybe I owe this thing something for all the good times” and took a look into the code to see if something could be fixed easily.

No, no it couldn’t. Not at all. But the general design and the vast majority of the logic was all in there. It worked alongside ettercap to do ARP spoofing, then intercepted file downloads, determined what format they were, selected an appropriate shellcode if one was available, and then had a bunch of different configurable methods to inject shellcode into all binary formats.

…It’s just that it was heaps and heaps of prototype-grade Python and byte-banged files. I have heard a rumor, similar to On The Road, that the original version had been written in a single night. It clearly was going to take longer than that to port this to something maintainable, but… I mean… automatic backdooring of downloaded files! This needed to happen. This needed to be a capability that red teamers just had available in the future. Fuck entropy.

Around this time, I pitched the idea of an end-to-end rewrite to some others and we started a little group of enthusiasts.

For each of the abstract areas of functionality from the original, we made a separate Go library. The shellcode repository functions went into shellcode. The logic that handles how to inject shellcode into different binary formats went into binjection. To replace the binary parsing and writing logic, we forked the standard Golang debug library, which already parsed all binary formats, and we simply added the ability to write modified files back out.

This gives us a powerful tool to write binary analysis and modification programs in Go. All of these components work together to re-implement the original functionality of BDF, but since they’ve been broken into separate libraries, they can be re-used in other programs easily.

Finally, to replace the ailing ettercap, we used bettercap, the new Golang replacement, which supports both ARP spoofing and DNS poisoning attacks. bettercap allows for extension “caplet” scripts using an embedded Javascript interpreter, so we wrote the Binject caplet that intercepts file downloads and sends them to our local backdoorfactory service for injection via a named pipe and then passes the injected files along to the original downloader.

The flow of a file through the components of the Backdoor Factory, on its journey to infection

Injection methods have been updated to work on current OS versions for Mach-O, PE, and ELF formats, and will be much easier to maintain in the future, since they’re now written to an abstract “binary manipulation” API.

To put a little extra flair on it, we’ve added the ability to intercept archives being downloaded, decompress them on the fly, inject shellcode into any binaries inside, recompress them, and send them on. Just cuz. In the future, we’re planning on adding some extra logic to bypass signature checks on certain types of files and some other special handlers for things like RPMs.

Now you will have to provide your own shellcode, backdoorfactory only ships with some test code, but if you’re targeting Windows, I’ve also ported the Donut loader to Golang, so you can use go-donut to convert any existing Windows binary (EXE/DLL/.NET/native) to an injectable, encrypted shellcode. It even has remote stager capabilities.

We fully intend to get into a lot more detail about how to use Donut and BDF in future posts, but don’t wait for us to get it together for some vaporware future blog post that may never come… You can try it yourself right now!

PT_NOTE to PT_LOAD Injection in ELF

We’ve recently checked in a new injection method for ELF executables into binjection, based on the PT_NOTE segment. This method of infection is both really handy (read: easy to implement) and has a high success rate on ET_EXEC binaries without breaking the binary.

Understanding the techniques of binary injection is useful to both the penetration tester and the reverse engineer who is analyzing files for malware. For the pentester, having a common file such as /bin/ls execute arbitrary code each time it is run is useful in establishing and maintaining persistence. A reverse engineer who notices that an ELF binary has 3 PT_LOAD segments and no PT_NOTE segment can safely assume that the binary at the very least is suspect, and should be evaluated further.

The breakdown of the algorithm

Most ET_EXEC binaries (but not golang! [1]) have a PT_NOTE segment that can be entirely overwritten without affecting program execution. The same segment can also be transformed into a loadable segment, where executable instructions can be placed.

We will go through the algorithm next, step by step. If you need a primer on the basics of ELF files and their headers, read this article before proceeding. When you’re ready, open the binjection source code and follow along.


1. Open the ELF file to be injected:


2. Save the original entry point, e_entry:


3. Parse the program header table, looking for the PT_NOTE segment:


4. Convert the PT_NOTE segment to a PT_LOAD segment:


5. Change the memory protections for this segment to allow executable instructions:


6. Change the entry point address to an area that will not conflict with the original program execution.

We will use 0xc000000. Pick an address that will be sufficiently far enough beyond the end of the original program’s end so that when loaded it does not overlap. 0xc000000 will work on most executables, but to be safe you should check before choosing.


7. Adjust the size on disk and virtual memory size to account for the size of the injected code:


8. Point the offset of our converted segment to the end of the original binary, where we will store the new code:


9. Patch the end of the code with instructions to jump to the original entry point:


10. Add our injected code to the end of the file:


11. Write the file back to disk, over the original file:


-= We’re done! =-

Notes on system and interpreter differences of PT_NOTE

The ELF ABI specification doesn’t describe a way in which this segment must be used, therefore it is inevitable that the researcher will find binaries that use the NOTE segment in unique ways. These are sometimes system specific
(BSD varieties have a lot of possible notes), and sometimes original language specific, as in Go binaries.

Final Thoughts

PT_NOTE injection of ET_EXEC ELF binaries is a solid and tested method for the pentester. It is fast and relatively easy to implement, and will work on a high percentage of Linux binaries without modification to the algorithm.

It is probably the easiest of the injection algorithms to start out with, for those new to this area.

Understanding this method of injection is valuable to the budding reverse engineer, an easy red flag to look for in the first of many injection methods that you will encounter over time.

ELF injection is an art-form, and to people on either side of the aisle who enjoy this type of binary analysis and manipulation, the myriad of tricks to implement and detect these methods provide a vast playground with nearly
endless possibilities.

I hope that this article will be useful to those attempting to begin and advance their work in this area.

All that we are is the result of what we have thought.
— Buddhist quote

[1] golang binaries do a unique thing with the PT_NOTE segment. It contains data that must be present during execution, therefore this injection method will not work on them… more on this to follow…