Post

Concealing Payloads: Hiding Shellcode in Image Files with Python and C/C++

Learn how to hide shellcode payloads in plain sight by embedding them into image files, such as PNGs, using Python. Discover how to store embedded images in the resources section of a binary file and extract the hidden payload using C/C++ for stealthy payload delivery and EDR evasion.

Concealing Payloads: Hiding Shellcode in Image Files with Python and C/C++

Introduction

In this post, I will demonstrate how to hide a shellcode payload in an image file using Python and retrieve it using C/C++. I will use PNG files as an example.

PNG files use an IEND chunk to mark the end of image data. Any bytes appended after this chunk do not affect the file’s integrity or visual appearance, making it possible to hide payloads.

This technique leverages end-of-file markers, allowing data to be hidden in any image format with a similar structure, not just PNG files.

With this method, you can hide encrypted shellcode for discreet payload execution or secretly store sensitive information, such as a list of passwords, without altering the image’s appearance.

This approach makes it easier to bypass security tools and harder for phishing campaigns to be detected.

Check out the video below for a quick demonstration of this project. To dive deeper into the details, scroll down to the table of contents.

If you want to checkout the GitHub Repository you can do so here:

https://github.com/WafflesExploits/hide-payload-in-images

Looking for a Pentester? I’m open for contracts and full-time opportunities – feel free to DM me!

Table Of Contents

Support

Enjoying my content? Show your support by sharing, liking, or leaving a comment!

You can also support me on buy me a ko-fi to fuel more awesome content:

Buy me a KO-FI

Hide a payload in a Image file by appending data at the end

As mentioned earlier, PNG files use an IEND chunk to mark the end of image data. Any bytes appended after this chunk do not affect the image’s integrity or visual appearance, making it a perfect spot to hide payloads.

A simple way to do this is by using the cat command:

1
cat payload.bin >> image.png

This appends the payload’s bytes to the end of the image file. However, I wanted a cross-platform solution using Python, so I wrote a script called payload-embedder.py.

You can check it out here:

With this script, you can easily embed shellcode into an image file like this:

  • 6cdd406d49bf9692d2e10ee5df85f9b4.png

As expected, the resulting embedded.png looks exactly the same as the original image:

  • 541d2914cb505c67d1b3a60232eb997d.png

If you want to use this in practice, you can hide encrypted shellcode in image files and later extract it when needed.

There are two methods to retrieve the payload:

  1. From Disk: Store the image file on disk and extract the payload bytes directly from the file.
  2. From Resources Section (.rsrc): Store the image file in the resources section of a binary file and Extract the payload from there.

Extract the payload from a Image file on disk using C/C++

Once we embed a payload into an image file, the next step is to extract it. The code below, written in C/C++, demonstrates how to retrieve the payload bytes from the end of an image file stored on disk.

This program assumes that you know the size of the original image file. By comparing the size of the modified image file (with the payload) and the original size, we can determine the exact location and size of the payload.

You can view the code in my GitHub:

This code is split into two main parts:

  1. Constants in Code
  2. Payload Extraction
  3. Executing the Payload

Constants in Code

Before running the code, there are two constants you need to configure:

  1. Original File Size (ORIGINAL_FILE_SIZE):
    • You can obtain the original file size from the output of the Python script payload-embedder.py when embedding the payload into the image file.
    • This value represents the size of the image file before the payload was appended.
  2. Target File Path (TARGET_FILE_PATH):
    • This constant specifies the path to the modified image file containing the hidden payload.

Payload Extraction

The extraction process is handled by the extract_payload() function. Here’s how it works step by step:

  1. Retrieve the File Size:
    • The function calls get_file_size() to obtain the size of the target image file. This helps validate that the payload exists and gives us the total file size.
  2. Calculate the Payload Size:
    • The size of the payload is determined by subtracting the original file size from the modified file size.
  3. Seek to the Payload Location:
    • Using fseek, the program moves the file pointer to the position where the payload starts, which is right after the original image data.
  4. Read the Payload:
    • The payload bytes are read into a dynamically allocated buffer using fread(). This ensures we have the extracted payload stored in memory for further use.

Executing the Payload

The execution process is handled by the ExecutePayloadViaCallback() function, which performs the following steps:

  1. Allocate Executable Memory:
    • The program uses VirtualAlloc to allocate a block of memory that is both readable and executable. This memory is where the payload (shellcode) will be stored.
  2. Copy the Payload:
    • The extracted payload is copied into the allocated memory using memcpy.
  3. Execute the Payload:
    • The SetTimer API is used to execute the payload via a callback function. This approach is clean and effective for executing shellcode
    • You can view a list of callback functions for payload execution by aahmad097 here:
  4. Free Allocated Memory:
    • After execution, the memory allocated for the payload is released using VirtualFree to prevent memory leaks.

Store the Image file in the resources section (.rsrc) of a binary file

In this section, I’ll show you how to embed an image file (or any payload) into the resources section (.rsrc) of a binary file using Visual Studio. I’ll also explain how to find the image file’s Resource ID and Resource Type, which are essential for accessing and extracting the payload programmatically later.

  1. How to store a image file in the .rsrc section.
  2. Finding the Resource file ID and Type

How to store a image file in the .rsrc section.

Follow these steps to embed your image file into the resources section:

  1. Inside Visual Studio, right-click on 'Resource files'.
  2. Click Add > New > Show all Templates Item.
    • 4d2e8fca2ef152ba1cb3d6bcaa3d8393.png
  3. Go to 'Resource', and select 'Resource File .rc'.
  4. This will generate a new sidebar, the Resource View.
  5. Right-click on the .rc file (Resource.rc is the default name).
  6. Select the 'Add Resource' option.
    • 42cbe1aaba73ce0b1d64ab8f88738a90.png
  7. Click 'Import'.
  8. Select your Embedded Image file:
    • 11b4ed96bfe4487212a38c0ca7aa0d6c.png
  9. You should see a new file that represents your Image file.
    • 76ad25e3c7177af91036e66e2db5a0e9.png
    • In this case, IDB_PNG1 is the Resource name of my Image file.

Finding the Resource file ID and Type

To access your image file via code, you need to find its Resource ID and Type.

To access the image file programmatically, you need two key pieces of information:

  1. Resource ID
  2. Resource Type

To find the Resource ID you need to:

  1. The Resource ID is defined as a macro named after your image file’s resource name.
  2. This macro is located in the resource.h file. Open resource.h to see the Resource ID:
    • In my case, the Image file’s Resource name was IDB_PNG1.
    • 23396645d7314ec3bd8a876f3c355940.png

To find the Resource Type you need to:

  1. Go to your Project and Open the Solution Explorer window.
  2. Right-click on Resource.rc, Select Open with... and Select C++ Source Code Editor (with Encoding).
    • 5afb55350199657bd350f67a2faa62b1.png
  3. With the Resource.rc tab open, Search by your Image file’s Resource name by pressing CTRL+F to open.
  4. You should see the Image file’s Resource Type after the Resource name, as shown below:
    • a6d111804abd7b098b6a2f424d2b6d90.png

Extract the Payload from the Image File in the Resources Section (.rsrc)

After storing the modified image file (containing the payload) into the resources section of a binary file, the next step is to extract and execute it. The code below, written in C/C++, demonstrates how to achieve this.

This code works similarly to the previous example of extracting a payload from a Image file on disk. The key difference lies in the Payload Extraction, which retrieves the payload directly from the resources section instead of a file.

You can view the code in my GitHub:

This code is split into two main parts:

  1. Constants in Code
  2. Payload Extraction
  3. Executing the Payload
    1. This part is the same as extracting a payload from an image file on disk. If you want to learn more about it, visit the Extract the payload from a Image file on disk using C/C++ section.

Constants in Code

Before running the code, there are three constants that you need to set:

  1. Original File Size (ORIGINAL_FILE_SIZE):
    • You can get the original file size from the output of the Python script payload-embedder.py when you embed the payload into the image file.
    • This value represents the exact size of the original image before the payload was appended.
  2. Resource ID (PAYLOAD_RESOURCE_ID):
  3. Resource Type (PAYLOAD_RESOURCE_TYPE):

Payload Extraction

The extract_payload_from_resource() function is responsible for retrieving the hidden payload directly from the binary’s resources.

Here’s how it works step by step:

  1. Find the Resource:
    • The function uses FindResourceW() to locate the resource by its ID (e.g., IDB_PNG1) and type (e.g., PNG).
  2. Load and Lock the Resource:
    • LoadResource() loads the resource into memory, and LockResource() locks it to ensure safe access to its data.
  3. Calculate the Payload Size:
    • The total resource size is determined using SizeofResource().
    • The payload size is then calculated by subtracting the original file size from the total resource size.
  4. Extract the Payload Bytes:
    • Using memcpy, the function extracts the payload bytes starting from the offset where the original file ends.
This post is licensed under CC BY 4.0 by the author.