---
description: >-
  Demonstrating a workflow to achieve embeded payloads on x64 executables using
  MSFVenom, BinaryNinja, and x64Dbg
---

# TTPs: Embedding Payloads with MSFVenom (x64)

### Part One: Introduction

In this article, we're going to follow the pattern that we followed with our last article covering embedded payloads with MSFVenom. If you haven't checked out the [x86 article](ttps-embedding-payloads-with-msfvenom-x86) on this topic I recommend that you do, this writeup will be an extension of the concepts we covered in that on.

Just a refresher, the goal of this writeup is to demonstrate embedding an MSFVenom payload into a pre-existing executable. MSFVenom provides the general mechanics for achieving this, but it does fall short in some respects with x64 executables.

Except in this case there's a known problem. Embedded payloads in x64 do not work properly. In this article we'll explore why that is and a technique we can apply to fix this.

### Part Two: Getting Started

We're going to use a "safe" template program.

```cpp
/* 
* By 0xTriboulet
* "good.exe" program
* 1/12/23
* compile with: x86_64-w64-mingw32-g++ good.cpp -o good_x64.exe -Wl,-subsystem,windows
*/

#include <stdio.h>
#include <Windows.h>

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "kernel32.lib")

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow) {
	MessageBox(NULL, "This is a safe program!", "Safe!", 0x0);
	return 0;
}
```

Now that we have our safe code, we can take our executable and embed a payload inside of it with the following command on our Kali machine.

```bash
msfvenom -p windows/x64/exec CMD="calc.exe" -x good_x64.exe -f exe -k -o even_better_x64.exe EXITFUNC=thread
```

_Note: The documentation states that the exe-only option should be used, but that option does not support the execution of our payload in a new thread (running the payload AND running the original program functionality does not work)._&#x20;

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FLfkSIVLqPmAsOYsKYUhg%2Fimage.png?alt=media&#x26;token=189e5c49-5a40-4b18-ab35-68d51a1e9d8e" alt=""><figcaption></figcaption></figure>

_Note cont.: This happens because the exe-only option clobbers the \_start() function and several of the other functions inside of our executable in order to achieve consistent payload execution._

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FvAyL0t7nLYh5kXzQPW0e%2Fimage.png?alt=media&#x26;token=9f618e8c-6587-4092-9958-ee96a3e5e063" alt=""><figcaption></figcaption></figure>

Now, transitioning back to our Windows machine and trying to run our payload we notice that our executable doesn't work!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FjXHLN0ZUNyCFpnaQKpGA%2Fimage.png?alt=media&#x26;token=8e4cb004-45d9-4abb-ae02-1b1bef1ee198" alt=""><figcaption></figcaption></figure>

### Part Three: What's the issue!?

Lets put the executable in BinaryNinja and see if we get a clue.

Looking at the \_start() function in good\_x64.exe we see normal startup behavior, nearly identical to what we saw in the x86 version of this program.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2F6G0Z5OvxYrthZW3sYrXa%2Fimage.png?alt=media&#x26;token=e5772f84-3c7d-44ea-828e-5900d9fd083b" alt=""><figcaption></figcaption></figure>

And inside of even\_better\_x64.exe, the \_start() function displays the exact behavior that the we would expect.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FaGP88E9hLWjlfxn9hemj%2Fimage.png?alt=media&#x26;token=e2492ab1-0671-4c40-abb6-f1f5adc278bc" alt=""><figcaption></figcaption></figure>

The MSFVenom source code validates that this is the assembly we should be seeing in the \_start() stub, and based on the behavior we reversed in the (x86) article we should have a working payload.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FlGOKowsqvA8bUVaZU4VP%2Fimage.png?alt=media&#x26;token=9d3af1a0-65d2-4ae1-ad75-f497c46ca954" alt=""><figcaption></figcaption></figure>

If we open up the even\_better\_x64.exe in x64dbg, we get a clue.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FSp5fXlqvBYju7zgJAQVB%2Fimage.png?alt=media&#x26;token=8b2249d4-ac72-4451-af88-eacf0bf8fe9c" alt=""><figcaption></figcaption></figure>

It looks like instead of loading the address of GetProcAddress, we load the address of a \_\_C\_specific\_handler. That's weird. If we execute the program until that part of the code, we see that GetProcAddress is not in rax like we would expect.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FknZZiqyg8xwn4FOUh2JF%2Fimage.png?alt=media&#x26;token=dd0ffde2-6453-451a-a077-1fd005ca67a3" alt=""><figcaption></figcaption></figure>

Looking at the function address, it looks like the address that the program is pointing at is correct, but the correct data is not located at that address!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FuBwsuwqSV41WAVnQXi9V%2Fimage.png?alt=media&#x26;token=fc8aa4a0-acc3-485f-8640-c12c0c465b20" alt=""><figcaption></figcaption></figure>

It's a little bit difficult to demonstrate with screenshots, but if we open up even\_better\_64.exe in PE-Bear, we can see that even\_better\_x64.exe has an import address collision at offset 81f8. At that offset GetProcAddress is loaded AND \_\_C\_specific\_handler get loaded at runtime.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FhZfR2us92xwsYGEB9Z0Q%2Fimage.png?alt=media&#x26;token=330f8684-8d08-4843-8558-6f2d9ffc827a" alt=""><figcaption></figcaption></figure>

So when we run our program, \_\_C\_specific\_handler overrides GetProcAddress and causes the issues we've seen above.

We can manually correct these functions in PE-Bear by simply changing the FirstThunk of msvcrt.dll

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FE0Qpgo0QqmTHMBIf0hA4%2Fimage.png?alt=media&#x26;token=e2333809-c377-42e1-9457-07d8354fa612" alt=""><figcaption></figcaption></figure>

Once we've made this change, we can save a new copy of our executable as even\_better\_x64\_PATCHED.exe. We see that now we successfully load the address of GetProcAddress during runtime!

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FjzzHN0DA8fMSpHqL19bf%2Fimage.png?alt=media&#x26;token=715cada4-71b0-4757-91b7-30df09c9ed99" alt=""><figcaption></figcaption></figure>

### Part Four: But WAIT! There's more!

If we now try to run the program inside of our debugger, we receive an access violation.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FX65q3ms5K8ackoeRouba%2Fimage.png?alt=media&#x26;token=50d58b07-4f8d-4362-a340-b19b3f9a5927" alt=""><figcaption></figcaption></figure>

There's a pretty easy way to remediate this: we patch the program to jump to our main function at the end of the \_start() function.

So we take this address:

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2Fq6dFNO0TR8GduizXSTo4%2Fimage.png?alt=media&#x26;token=bd831c56-3685-4e61-9875-726074f5624c" alt=""><figcaption></figcaption></figure>

And we patch this jmp instruction at the end of the entry stub such that we jmp straight into main(). You can do this with the debugger or disassembler of your choice.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2FUS9ZPmDROwMRUaSpMwwf%2Fimage.png?alt=media&#x26;token=6448eec7-4fc7-4e79-a1cc-bdc532f16bcb" alt=""><figcaption></figcaption></figure>

### Part Five: ???

\[Intentionally left blank]

### Part Six: Profit

And if you did this correctly, the program should work! Our embedded x64 payload now runs as a seperate thread inside a pre-existing binary.

<figure><img src="https://4223093509-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsOcwlgEs4z7TZgzT3Eq0%2Fuploads%2F2UVETxmgh1Co5DH63DFV%2Fimage.png?alt=media&#x26;token=7b61dd09-0bae-4121-887c-b01989861756" alt=""><figcaption></figcaption></figure>

This works because our template program (good\_x64.exe) is pretty simple and does not rely on robust setup protocols, error handling, or really any functionality that gets setup between the \_start() and main() functions.&#x20;

### Part Seven: Conclusion

In this writeup we discussed a method of making embeded x64 MSFVenom payloads viable by making a couple of corrections to our executable. This discussion was limited to a very simple executable template and a very simple payload. More complicated programs will necessairily require more effort to reverse engineer and patch.&#x20;

We discussed a couple of the issues and developed practical solutions but we glossed over the granular details about why these issues were happening. The details about these errors are buried in the injection mechanics of MSFVenom, but the short story is that MSFVenom does not appropriately inject additional functions into the imports section of our executable. We were able to correct for this by using PE-Bear to manually overwrite the PE header.

Additionally the initiallization stub injected into our payload does not appear to provide robust on/off ramping. We bypassed this instability by jumping straight to main() from the end of the MSFVenom stub.

Overall, we validated a powerful capability provided by MSFVenom and found a work around for its current limited support for x64 embedded payloads.

### References:

{% embed url="https://github.com/0xTriboulet/Red_Team_Code_Snippets/tree/main/Cpp/embedding_payloads" %}

{% embed url="https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/exe/segment_injector.rb" %}

{% embed url="https://github.com/hasherezade/pe-bear" %}

{% embed url="https://docs.metasploit.com/docs/using-metasploit/basics/how-to-use-msfvenom.html" %}

{% embed url="https://www.offensive-security.com/metasploit-unleashed/backdooring-exe-files/" %}

{% embed url="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-reloc-section-image-only" %}
