We’re thrilled to announce a new release of PoshC2 packed full of new features, modules, major improvements, and bug fixes. This includes the introduction of a brand-new native Linux implant and the capability to execute Beacon Object Files (BOF) directly from PoshC2!
Download and Documentation
Please use the following links for download and documentation:
- PoshC2 v8.0 download: https://github.com/nettitude/PoshC2
- PoshC2 v8.0 documentation: https://labs.nettitude.com/tools/poshc2/poshc2-documentation/
In this release, we have introduced Joel Snape’s (@jdsnape) excellent method to run Cobalt Strike Beacon Object Files (BOF) in .NET, and its integration in PoshC2. This feature has a blog post unto itself available, but essentially it allows existing BOFs to be run in any C# implant, including PoshC2.
At a high-level, here is how it works:
- Receive or open a BOF file to run
- Load it into memory
- Resolve any relocations that are present
- Set memory permissions correctly
- Locate the entry point for the BOF
- Execute in a new thread
- Retrieve any data output by the BOF
- Clean-up memory artifacts before exiting
Read our recent blog post on this for more detail.
SharpSocks provides HTTP tunnelled SOCKS proxying capability to PoshC2 and has been rewritten and modernised to improve stability and usability, in addition to having its integration with PoshC2 improved, so that it can be more clearly and easily configured and used.
Last year, Rob Bone (@m0rv4i) and Ben Turner (@benpturner) released a whitepaper on “Process Hiving” along with a new tool “RunPE”, the source code of which can be found here. We have integrated this technique within this release of PoshC2 for ease of use, and it can be executed as follows:
By default, new executables can be added to /opt/PoshC2/resources/modules/PEs so that PoshC2 knows where to find them when using the
runpe-debug commands shown above.
We’ve added the
dllsearcher command which allows operators to search for specific module names loaded within the implant’s current process, for instance:
GetDllBaseAddress, FreeMemory & RemoveDllBaseAddress
Three evasion related commands were added which can be used to hide the presence of malicious shellcode in memory.
getdllbaseaddress is used to retrieve the implant shellcode’s current base address, for example:
Looking at our process in Process Hacker, we can correlate this base address memory location:
By using the
freememory command, we can then clear this address’ memory space:
removedllbaseaddress command is a combination of
freememory, which can be used to expedite the above process by automatically finding and freeing the relevant implant shellcode’s memory space:
Get-APICall & DisableEnvironmentExit
In this commit we implemented a means for operators to retrieve the memory location of specific function calls via
get-apicall, for instance:
In addition, we’ve included
disableenvironmentexit which patches and prevents calls to
Environment.Exit() within the current implant. This can be particularly useful when executing modules containing this call which may inadvertently kill our implant’s process.
C# Ping, IPConfig, and NSLookup Modules
Several new C# modules related to network operations were developed and added to this release, thanks to Leo Stavliotis (@lstavliotis). They can be run using the following new commands:
- ping <ip/hostname >
- nslookup <ip/hostname>
C# Telnet Client
A simple Telnet client module has been developed by Charley Celice (@kibercthulhu) and embedded in the C# implant handler to provide operators the ability to quickly validate Telnet access where needed. It will simply attempt to connect and run an optional command before exiting:
We have plans to add additional modules such as this one to cover a wider range of services.
C# Registry Module
Another module by Charley Celice (@kibercthulhu) was added. SharpReg allows for common registry operations in Windows. At this stage it currently consists of simple functionalities to search, query, create/edit, delete and audit registry hives, keys, values and data. It can be executed as shown below:
We’re adding more features to this module which will include expediating certain registry-based persistence, privilege escalation, UAC bypass techniques, and beyond.
PoshGrep can easily be used to parse task outputs. This can be particularly useful when searching for specific process information obtained from a large number of remote hosts. It can be used by piping your PoshC2 command into
poshgrep, for example:
The output task database retains the full output for tracking.
findfile was added, which can be used to search for specific file names and types. In the example below, we search for any occurrences of the file name “password” within
Bringing PoshC2 to Linux
One of the major new features we have incorporated in this release of PoshC2 is our new Native Linux implant, thanks to the great work of Joel Snape (@jdsnape). While it’s fair to say that we spend most of our time on Windows, we find that having the capability to persist on Linux machines (usually servers) can be key to a successful engagement. We also know that many of the adversaries we simulate have developed tooling specifically for Linux. PoshC2 has always had a Python implant which will run on Linux assuming that Python is installed, but we decided that it was time that we advanced our capabilities to a native binary that is harder to detect and has fewer dependencies.
To that end, Posh v8.0 includes a native Linux implant that can run on any* x86/x64 Linux OS with a kernel >= 2.6 (it should work on earlier versions, but we’ve not tested that far back!). It also works on a few systems that aren’t Linux but have implemented enough of the syscall interface (most importantly ESXi hypervisors).
When payloads are created in PoshC2 you will notice a new “native_linux” payload being written on startup:
This is the stage one payload, and when executed will contact the C2 server and retrieve the second stage. The first stage is a statically linked stripped executable, around 1MB in size. The second stage is a statically linked shared library, that the first stage will load in memory using a custom ELF loader and execute (see below for more detail). The dropper has been designed to be as compatible as possible, and so should just work out of the box regardless of what userspace is present.
The aim of the implant is not to be “super-stealthy”, but to emulate a common Linux userspace Trojan. Therefore, the implant just needs to be executed directly; how you do this will obviously depend on the level of access you have to your target.
Once the second stage has been downloaded and executed the implant operates in much the same way as the existing Python implant, supporting many of the same commands, and they can be listed with the
Most notably, the implant allows you to execute other commands as child processes using
/bin/sh, run Python modules (again, assuming a Python interpreter is present on your target), and run the
linuxprivchecker script that is present in the Python implant.
To meet our needs, we set the following high-level goals:
- Follow the existing pattern of a small stage one loader, with a second stage being downloaded from the C2 server.
- A native executable, with as few dependencies as possible and that would run on as many different distributions as possible.
- Compatibility with older distributions, particularly those with an older kernel.
- As little written to disk as possible beyond the initial loader.
- Run in user-space (i.e., not a kernel implant).
This gives us greater flexibility and stealth, and allows us to operate on machines that maybe don’t have Python installed or where a running Python process would be anomalous.
There are a few choices in language and architecture to build native executables. The “traditional” method is to use C or C++ which compiles to an ELF executable. More modern languages, like Golang, are also an option, and have notably been used by some threat groups to develop native tooling. For this project however we decided to stick with C as it lets us implement small and lean executables.
How it Works
The Linux implant comes in two parts, a dropper and a stage two which is downloaded from the C2.
Compilation of the native images can be a bit time consuming, so we have provided binary images in the PoshC2 distribution (you can see the source code here). This means that when a new implant is generated, PoshC2 needs a way to “inject” its configuration into the binary file. All configuration is contained in the dropper, except for a random key and URI which are patched over placeholder values in the stage two binary and is contained in an additional ELF section at the end of the binary. This is injected by PoshC2 using
objcopy when a new implant is generated. You should note that at the moment there is no obfuscation or encryption of the configuration so it will be trivially readable with
strings or similar.
When the dropper is launched it parses the configuration and connects to the C2 server to obtain the second stage using the configured hosts and URLs.
Loading the Second Stage
Our main aim with the execution of the second stage was to be able to run it without writing any artifacts to disk, and to have something that was easy to develop and compile. Given the above goals, it also needed to be as portable as possible.
The easiest way to do this would be to create a shared library and use the
dlsym() functions to load it and find the address of a function to call. Historically, the
dlopen() functions required a file to operate on, but as of kernel version 3.17 it is possible to use
memfd_create to get a file descriptor for memory without requiring a writable mount point. However, there are two issues with that approach:
muslstandard library we are using (see below) doesn’t support
dlopenas it doesn’t make sense in a context where everything is statically linked.
- Ideally, we’d like to support kernels older than 3.17, as although it was released in 2014, we still come across older ones from time to time.
Given these constraints, we implemented our own shared library loader in the dropper. More details can be found in the project readme, but at a high level it’s this:
- Parses the stage two ELF header, and allocates memory as appropriate.
- Copies segments into memory as required.
- Carries out any relocations required (as specified in the relocations section).
- Finds the address of our library’s entry function (we define this as
loopy()because it, well, loops…).
- Calls the library function with a pointer to a configuration object and a table of function pointers to common functions the second stage needs.
If you want to understand this process in more detail there is an excellent set of articles by Eli Bendersky that go through the process for load time relocation and position independent code.
In theory, the second stage could be any statically linked library, but we’ve not extensively tested the loader. In the future, we’d like to re-use this loader capability to allow additional modules to be delivered to the implant so you can bring your own tooling as needed (for example, network scanning or proxying).
At this point the second stage is now operating and can communicate with the C2, run commands, etc.
One of the key aims for the Linux implant was to make it operate on as many different distributions/versions as possible without needing to have any prior knowledge of what was running before deployment – something that can be difficult to achieve with a single binary.
Normally Linux binaries are “dynamically linked”, which means that when the program is run the OS runtime-linker (usually something like
/lib/ld-linux-x86-64.so.2) finds and loads the shared libraries that are needed.
For example, running
ldd /bin/ssh, which shows the linked library dependencies, demonstrates that it depends on a range of different system libraries to do things like cryptographic operations, DNS resolutions, manage threads, etc. This is convenient because your binaries end up being smaller as code is reused, however it also means that your program will not run unless that the specific version of the library you linked against at compile time is present on the target system.
Obviously, we can’t always guarantee what will be present on the systems we are deploying on, so to work around this the implant is “statically linked”. This means that the executable contains its code and all of the libraries that it needs to operate in one file and has no dependencies on anything other than the operating system kernel.
The key component that needs to be linked is the “standard library” which is the set of functions that are used to carry out common tasks like string/memory manipulation, and most importantly interface between your application and the OS kernel using the system call API. The most common standard library is the GNU C library (
glibc), and this is what you will usually find on most Linux distributions. However, it is fairly large and can be difficult to successfully statically link. For this reason, we decided to use the musl library, which is designed to be simple, efficient and used to produce statically linked executables (for example as on Alpine Linux).
Because the implant comes in two parts, if there are any common dependencies (e.g., we use
libcurl to make HTTPS requests) then they would normally have to be statically linked into each binary. This would obviously be inefficient as the process would end up having two copies of the library in memory, one from the dropper and one from the stage two, and the stage two would be unnecessarily large. Therefore, for the larger libraries like
libcurl a set of function pointers are provided from the dropper when it executes the stage two, so it can take advantage of the libraries that were already linked into the dropper.
The implant is built for x86 systems, as this means that it will run on both 32- and 64-bit operating systems. Other architectures (e.g., ARM) may follow.
Our implant would be pretty limited without the ability to execute other commands using the system shell. This is easily carried out using the
popen() function call in the standard library which executes the given command and opens a pipe so the command’s output can be read. However, some commands (e.g.
ping with default arguments) may not exit, and so our implant would “hang” reading the output forever. To get around this, we have written a custom
popen() implementation that allows us to launch our subcommand in a custom process group and set an alarm using
SIGALRM to kill it after a user-configurable timeout period. Any output written by the process is then read and returned to the C2. This does mean however that long running commands will be prematurely terminated.
We typically find that Linux environments have a lot less scrutiny applied than their Windows counterparts. Nevertheless, they are often hosting critical services and data and so monitoring for suspicious or unusual behaviour should be considered. Many security vendors are starting to release monitoring agents for Linux, and several open-source tools are available.
A full exploration of security monitoring for Linux is out of scope for this post, but some things that might be seen when using this implant are:
- Anomalous logins (for example SSH access at unusual times, or from an unusual location).
- Vulnerability exploitation (for example, alerts in NIDS).
curlbeing used to download files for execution.
- Program execution from an unusual location (e.g. from a temporary directory or user’s home directory).
- Changes to user or system cron entries.
The dropper itself has very limited operational security so we expect static detection of the binary by antivirus or NIDS to be relatively straightforward in this publicly released version.
It’s also worth reviewing the PoshC2 indicators of compromise listed at https://labs.nettitude.com/blog/detecting-poshc2-indicators-of-compromise.
Many other updates and fixes have been added in this version and merged to dev, some of which are briefly summarized below. For updates and tips check out @nettitude_labs, @benpturner, @m0rv4i and @b4ggio-su on Twitter.
- Miscellaneous fixes and refactoring
- Fixed MSTHA and RegSvr32 quickstart payloads
- Several runas and Daisy.dll related fixes
- Improved PoshC2 reports output and style
- Enforced the consistent use of UTC throughout
- FComm related fixes
- Added Native Linux implant and related functionalities from Joel Snape (@jdsnape)
- Added Get-APICall & DisableEnvironmentExit in Core
- Updated to psycopg2-binary so it’s not compiled from source
- Database related fixes
- RunPE integration
- Added GetDllBaseAddress, FreeMemory, and RemoveDllBaseAddress in Core
- Added C# Ping module from Leo Stavliotis (@lstavliotis)
- Fixed fpc script on PostgreSQL
- Added PrivescCheck.ps1 module
- Added C# IPConfig module from Leo Stavliotis (@lstavliotis)
- Updated several external modules, including Seatbelt, StandIn, Mimikatz
- Added EventLogSearcher & Ldap-Searcher
- Added C# NSLookup module from Leo Stavliotis (@lstavliotis)
- Added getprocess in Core
- Added findfile, getinstallerinfo, regread, lsreg, and curl in Core
- Added GetGPPPassword & GetGPPGroups modules
- Added Get-IdleTime to Core
- Added PoshGrep option for commands
- Added SharpChromium
- Added DllSearcher to Core
- Updated Dynamic-Code for PBind
- Added RunOF capability into Posh along with several compiled situational awareness OFs
- Updated Daisy Comms
- Added C# SQLQuery module from Leo Stavliotis (@lstavliotis)
- Added ATPMiniDump
- Added rmdir, mkdir, zip, unzip & ntdsutil to Core
- Fix failover retries for C# & Updated SharpDPAPI
- Updated domain check case sensitivity in dropper
- Fixed dropper rotation break
- Added WMIExec and SMBExec modules
- Added dcsync alias for Mimikatz
- Added AES256 hash for uploaded files
- Added RegSave module
- SharpShadowCopy integration
- Fixed and updated cookie decrypter script
- Updated OPSEC Upload
- Added FileGrep module
- Added NetShareEnum to Core
- Added StickyNotesExtract
- Added SharpShares module
- Added SharpPrintNightmare module
- Added in memory SharpHound option
- Updated Tasks.py to save Seatbelt output
- Added kill-remote-process to Core
- Fixed jxa_handler not being imported
- Updated posh-update script to accept -x to skip install
- Added process name in implant view from Lefteris Panos (@Lefterispan)
- Added SharpReg module from Charley Celice (@kibercthulhu)
- Added SharpTelnet module from Charley Celice (@kibercthulhu)
- kill-process with no arguments now terminates the implant’s current process following a warning prompt
- Added hide-dead-implants command
- Added ability to modify user agent when creating new payloads from Kirk Hayes (@l0gan54k)
- Added get-acl command in Core