At this point,
we have covered all the main components that make up the Meterpreter shell.
Let's now apply this knowledge in a demo in which we take a
look at Meterpreter's initialization.
We will once again generate an HTTP stager and
embed it inside a shellcode runner.
We will then start the handler, execute our shellcode runner,
and step through the code as it executes.
Before we start writing code,
we need to disable the antivirus on the machine which will run our stager.
You will of course be bypassing the antivirus in another module.
We can do this by opening the Start menu and clicking on Settings.
We can then open the Update and Security menu and
go to the Windows Security tab.
We can then open the Virus and threat protection page,
and click Manage settings under the Virus and threat protection settings.
We can then turn off real‑time protection.
We can generate a stager using MSFvenom once again.
This time, we will specify c for the format parameter,
since we are going to be writing our shellcode runner in C. We can
use the ‑v parameter to specify a variable name,
which will hold our stager.
We can then copy the output for later use inside our shellcode runner.
At this point, we can go over to Visual Studio and create an empty project.
We can then add a file called main.c, which will hold our code.
The code we're going to write will be using the VirtualAlloc
API to allocate a memory buffer.
We will then use the memcpy function from the C runtime
library, to copy the shellcode we generated using MSFvenom
inside the allocated memory buffer.
Finally, we will cast our memory buffer to a function pointer and transfer
execution to it, effectively bootstrapping the stager's shellcode. We first
include the windows.h header file, so we can make use of VirtualAlloc. We then
copy the stager provided by MSFvenom, and finally, we define our main
function, which will be the executable file's entry point. Within the main
function, we can call VirtualAlloc to allocate enough memory for our stager,
and store the pointer to the allocated memory returned to us by the API inside
the variable.
Now that we have a memory buffer, which is both writeable and
executable, we can use the memcpy function to copy the stager's
shellcode inside the memory buffer.
Once copied, we cast the pointer to our memory buffer into a function
pointer and execute it. Before we execute this code, let's place a
breakpoint on the line of code where we execute the function pointer, so
that we can take a look at our stager while it's executing. Next, let's
start a Metasploit handler on the attacker's machine, which will catch
the reverse shell.
We can now ensure that we have selected the correct target CPU
architecture which matches the stager shellcode's architecture we
specified when using MSFvenom. At this point, we can go ahead and
execute the code using the debugger, and it should hit our breakpoint
before executing the stager.
We can now open the assembly view so that we can single step one instruction
at a time instead of one line of code at a time. To do so, we can go to the
Debug tab and select Disassembly from the Windows dropdown menu. We can now
see our C code followed by the corresponding assembly instructions. Stepping
into the call, we'll indeed jump to the stager's shellcode. If we check the
first couple of instructions and compare them with the inline assembly we
reviewed in the generate_reverse_http method, we will notice that they match.
If we scroll down, we will find the ror instruction, which is used
when calculating the ROT13 hash. If a value is 0D in hexadecimal,
it's the decimal equivalent of 13.
If we scroll down even further, we can find the ret instruction, which
we can also see inside the asm_reverse_http method.
We can place a breakpoint in the ret instruction by
clicking just next to the memory address, and let the code
run. Once the breakpoint is hit,
just as we're about to execute the ret instruction,
we can take a look at the stack.
The reason being that when a ret instruction is executed, an address is
popped off the top of the stack into the RIP register,
also known as the instruction pointer,
which effectively transfers code execution to that address.
We can do this by copying the RSP register value, which always
points to the top of the stack into the memory view.
We can then copy the address located on the stack into the memory view to
figure out where we are about to return. You may have noticed that the address
that I copied in the memory view was in reverse order.
This is to account for the fact that my CPU works in little‑endian. At
this point, we can see the distinctive MZ PE header signature, which
confirms that we are looking at the payload.
If we take a look at our handler,
we can also see that the payload was indeed sent to the stager.
However, we do not have the Meterpreter prompt.
The reason being that we paused the execution just
before jumping into the payload.
If we step over the ret instruction,
you will notice that this code looks familiar.
It is the code we saw in the asm_invoke_metserve method.
We can see the call to the reflective loader,
followed by a call to the DLL main function,
which completes the initialization. At this point, if we let execution continue,
it should result in a fully functional Meterpreter shell.