PowerShell Enhanced Logging Capabilities Bypass
A blog post about a new enhanced logging capabilities bypass for PowerShell, which allows to bypass transcription logging.
Investigations and further thoughts on in-memory execution and detection.
Sometimes, as an attacker in cyberspace, the goal is not to completely defeat a defense mechanism, but only to minimize the chance of detection. Since the defense side does not sleep and continuously develops new detections and mechanisms, research must also be done by the attacker side. At avantguard cyber security, we are always looking to evolve our tools and processes to keep up with the threats of today, as well as tomorrow. One of our most important and favorite tools is Covenant, the open source part of our Command & Control (C2) infrastructure, which is also used e.g. by the APT group HAFNIUM. Covenant already offers an operator many ways to execute tools only in memory. This method has become popular with attackers in recent years because it means that nothing has to be written to disk and antivirus programs cannot scan the malicious files at all. As a result, defenders now also need to monitor memory for potentially malicious processes. More and more EDRs are therefore using so-called memory scanners.
This blog post describes our current research on Covenant and memory scanners, and is aimed specifically at Blue Teams and other Red Teams. Many concepts are not explained beforehand, otherwise the post would take on too large a scope. A possible approach is explained that could make certain functionalities in Covenant less discoverable by memory scanners, without having already created a working PoC for it. Questions and feedback are encouraged and can be sent to research@avantguard.io.
Covenant uses SharpSploit, which in turn uses DInvoke. DInvoke allows developers to implement certain functionality in C# in a way that makes it harder for EDR agents to detect. One such functionality is loading and executing functions in a DLL. This is made possible by two different ways:
For memory scanners, this already makes it more difficult to determine whether a DLL in a process is legitimate or being used for malicious purposes. Fortunately, there are tools that can determine this. Examples of these, which we have already used, are pe-sieve and Moneta. Both are open source and available on GitHub. As an example, let's examine a running process of an implant (Covenant calls these Grunts) with pe-sieve before and after a DLL is added to process memory via overload mapping.
The above snippet shows the results of pe-sieve for a process that was started via double-clicking a Grunt-implant. The antivirus on the test system does not block the file or the resulting process. Pe-sieve reports three suspicious findings, which are not yet relevant for the current investigation. Now the C2 server sends the command to the implant to run Mimikatz. Specifically, this means that methods in SharpSploit's Mimikatz.cs and Overload.cs are used to load a DLL from the file system and the referenced "powerkatz_x64.dll" DLL is then overwritten into memory. Then the desired command is executed using the DLL. After this procedure, a scan with pe-sieve returns the following values:
It is noticeable that the value of "Replaced" has changed from zero to one. This makes sense, since a DLL was loaded and overwritten with another DLL. How exactly the change is detected is not yet clear to us from initial research in the source code. It looks as if the tool compares the exported functions of the DLL on the file system with those of the DLL in the memory (could there be another way of concealment lurking here?), this would have to be tested however with a first PoC more exactly. In summary, these were our first findings.
To get to the actual point of this article, we have to go back one step. As you can see from the two screenshots above, pe-sieve reports not only "Replaced" but also other suspicious findings. Although the implant can be used relatively undetected as it is at the moment, there is potential for improvement that we want to exploit. One approach to fool memory scanners has been published by mgeeky as a PoC on his GitHub as ShellcodeFluctuation. While the PoC relates to the C2 Cobalt Strike, it also got us thinking for Covenant. Cobalt Strike is closed source, but very modular and forms another part of our C2 infrastructure. ShellcodeFluctuation roughly does the following: While the implant is not in use, i.e. no commands are executed, but the implant simply "sleeps" in memory, the implant's code is encrypted. When the implant "wakes up", i.e. when it is to be used again, the code is decrypted and can be executed again. Once executed, it is encrypted again and put to sleep. The memory region of the code is also marked as non-executable. A memory scanner, which checks the memory during the sleep phase, thus only sees unreadable code in a non-executable memory region. This may seem strange, but is not considered suspicious by pe-sieve, for example. Since the sleep phase also often lasts much longer than the actual execution of commands, the risk of detection is therefore significantly reduced, although not completely eliminated.
ShellcodeFluctuation's solution is so effective and elegant that we asked ourselves: Can't something like this be done for overload mapping? And with our current state of knowledge we claim: Yes, for overload mapping it should be possible to significantly reduce the detection risk by changing the memory during runtime too. A possible solution could look like the following:
The key is the same size as powerkatz.dll. At the moment there is still win.dll in the memory region which should be overwritten. Thanks to the properties of XOR, win.dll XOR key can now be executed to write powerkatz.dll to the desired memory region.
This is only done if we need an exported function of powerkatz.dll. Once the function has been performed, powerkatz.dll XOR key is now executed again. What is finally back in the memory region is win.dll.
As already mentioned, a PoC has not yet been implemented. The corresponding places in the source code should be known, but the development effort has not yet been done at the time of this blog post. Further testing would then be required to evaluate the actual effectiveness of this approach. Open questions include:
Avantguard cyber security is happy and proud to give high priority to research and further development. This is the only way to understand and simulate today's threats. We would like to thank everyone who has read this far despite the rather dry topic and will soon publish another article that addresses a larger target group. Here we would like to ask again for questions and feedback to research@avantguard.io, because research benefits from and is most fun with knowledge and experience sharing!
A blog post about a new enhanced logging capabilities bypass for PowerShell, which allows to bypass transcription logging.
A blog post about attacking the default configuration of KeePass and possible hardening measures.