As explained previously, stagers contain hardcoded shellcode,
which is easy to detect.
Metasploit ships with various encoders and encryptors that can
be used by passing the ‑e flag to msfvenom.
Alternatively,
encrypters can be used by passing the ‑‑encrypt and
‑‑encrypt‑key flags to msfvenom.
Some encoders are polymorphic, which makes them particularly hard to detect.
A good example is the 64‑bit Zutto Dekiru encoder.
Let's take a closer look at how this encoder works.
This diagram demonstrates how the Zutto Dekiru encoder works at a high level.
The first step consists of compiling the stager.
The shellcode is then sent to the encoder if a ‑e
flag was specified in msfvenom.
The encoder generates a random 8‑byte key,
which is used to perform an XOR operation on 8‑byte chunks.
It repeats the process until every byte in the stager has been encoded.
At this point,
a small stub of shellcode is built using randomly chosen CPU registers.
This makes it hard to create a signature since the
code will always be different.
When executed,
this stub will loop over all the encoded bytes and apply the same XOR key,
which was used whilst encoding the stager.
This will effectively restore the original stager code at runtime in memory.
Because this is happening in memory,
it makes this technique ideal to hide our stager on disk.
However,
it does require us to specify read/write execute permissions on the
section of the executable, which will hold this data since the code needs
to modify itself and execute itself at runtime.
This should be avoided as we will discover it later on in the module.
Let's take a look at the Zutto Dekiru encoder in practice.
The code for this encoder can be found in the zutto_dekiru.rb file.
As explained, the Zutto Dekiru encoder is an XOR encoder.
The function which performs the encoding is the xor_string function.
As you can see, it takes two parameters.
The first is text, which contains the compiled stager shellcode.
The second is the random XOR key,
which will be used to encode the stager shellcode.
If we now take a look at the encode_block function,
we can see all the CPU registers,
which can be used to build the polymorphic decoder stub,
which will be responsible for decoding the stager in memory when executed.
Because the registers are shuffled and some instructions are chosen randomly,
it makes it very difficult for an antivirus to develop a reliable signature.
If we go down to the end of the function,
we will see the polymorphic decoder stub being added in
front of the encoded stager shellcode.
Let's take a closer look at this by generating a stager and
debugging the Ruby code. I have already placed breakpoints
before and after the encoding,
so we can take a look at our stager before and after the encoding occurs.
We can now go ahead and run the following command to generate an encoded stager,
which we will place in our shellcode runner.
Once our first breakpoint is hit,
we can take a look at the stager before the encoding occurs.
We can see the IP address of the handler and the wininet string
which gets passed to the LoadLibraryA API,
as well as the URI.
If we let the code run until the next breakpoint, and we
take a look at the stager once again,
you will notice that the artifacts we saw previously are no longer visible.
We can take a look at the decoder stub and get its size in bytes.
We can do the same for the encoded stager.
We will use this when analyzing the stager executing in our shellcode runner.
We can then let the code run, which should generate a stager,
which can be embedded inside our shellcode runner.
We can then place a breakpoint before executing our stager so we can
take a look at the decoding process in memory.
Once the breakpoint is hit,
we can switch to the assembly view and step into our stager.
We can get the address of our encoded stager by adding the size in bytes of
our decoder stub and copy the address into the Memory view.
We can place a breakpoint just after our decoder stub and let the code run.
If we then check the Memory view,
we can see that the stager's shellcode has been
deobfuscated in memory by the decoder stub.
And we can now see the IP address and the URI of our stager,
as well as the wininet string we saw previously.
If we now attempt to upload our encoded stager to VirusTotal,
you will notice that only 10 antivirus scanners have flagged our file.
You may recall that our shellcode runner with the
unencoded stager was flagged by 24 scanners.
If we encode the stager and leverage the exe‑only format with the 7 zip
executable file template and upload it to VirusTotal,
our stager is only flagged by 5 antivirus scanners.
This is a significant improvement.
If we take a closer look at the detections in the VirusTotal report,
you will notice that some of these detections were due to heuristic
runtime analysis and not due to on‑disk scans.