Example of a Payload Delivered Through Steganography

    Published: 2025-04-25. Last Updated: 2025-04-25 07:20:44 UTC
    by Xavier Mertens (Version: 1)
    0 comment(s)

    In this diary, I’ll show you a practical example of how steganography is used to hide payloads (or other suspicious data) from security tools and Security Analysts’ eyes. Steganography can be defined like this: It is the art and science of concealing a secret message, file, or image within an ordinary-looking carrier—such as a digital photograph, audio clip, or text—so that the very existence of the hidden data is undetectable to casual observers (read: security people). Many online implementations of basic steganography allow you to embed a message (a string) into a picture[1].

    Let’s have a look at the sample I found. Because it’s a .Net binary, it can be easily decompiled. But, because the source code is easily accessible, many malware are often obfuscated. A classic technique is to use UFT-16 in functions, classes, variable names, etc. In the following example, the registry key name is obfuscated:

    Another common behaviour of malware in .Net is to use reflective code loading techniques. Reflective code loading is the ability of a running program to inspect, load, and use code—classes, functions, libraries, even entire assemblies—that was not statically linked or explicitly referenced at compile time. Instead, the program decides at runtime what to bring into memory and how to invoke it. Therefore, when you reverse a .Net program, it’s a good idea to search for methods like .Load(), .LoadFrom() or .LoadFile(). This may indicate that more code will be loaded (passed as a parameter).

    That’s what I found in this sample, but there were other interesting strings: “Bitmap”, “WebClient”, or “OpenRead”:

    A Bitmap payload (read: a picture) is downloaded from a URL. Once deobfuscated, we get:

    hxxps://i[.]ibb[.]co/LgqktNn/freemaosnry[.]png

    The deobfuscating function is here:

    Once the picture has been downloaded, a loop will process all the pixels from the top row and extract the “red” component value. All the values are added in a byte array to rebuild the next payload. We can reproduce this with a few lines of Python:

    #!/usr/bin/env python3
    import sys
    from PIL import Image
    
    def main():
        img = Image.open(“freemaosnry[.]png”).convert("RGBA")
        w, h = img.size
        pix = img.load()
    
        with open("payload.tmp", "wb") as f:
            for y in range(h):
                for x in range(w):
                    r, g, b, a = pix[x, y]
                    f.write(bytes([r]))
    
    if __name__ == "__main__”:
        main()

    Let’s have a look at the picture:

    remnux@remnux:/MalwareZoo/20250418$ file freemaosnry.png
    freemaosnry.png: PNG image data, 31744 x 1, 8-bit/color RGBA, non-interlaced

    Based on the file details, we should get a payload of 31744 bytes:

    remnux@remnux:/MalwareZoo/20250418$./decode_pic.py
    remnux@remnux:/MalwareZoo/20250418$ ls -al payload.tmp
    -rwx------ 1 501 dialout 31744 Apr 18 08:27 payload.tmp*
    remnux@remnux:/MalwareZoo/20250418$ file payload.tmp
    payload.tmp: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows

    Bingo! We have the next payload that, during execution, has been generated in memory. It is now ready to be invoked with:

    AppDomain.CurrentDomain.Load(array).EntryPoint.Invoke(null, null);

    What about this malware? The initial PE file was named “voice_recording.bat” (SHA256:ce77b1bc3431139358e2a70fa5f731d1be127e77efe8b534df5ccde59083849d[2]). It belongs to the XWorm family and has the following config:

    {
        “c2": [
            "cryptoghost[.]zapto[.]org"
        ],
        "family": "latentbot"
    }
    {
        "attr": {
            "install_file": "MasonUSB.exe"
        },
        "rule": "Xworm",
        "family": "xworm"
    }

    Happy hunting!

    [1] https://manytools.org/hacker-tools/steganography-encode-text-into-image/
    [2] https://www.virustotal.com/gui/file/ce77b1bc3431139358e2a70fa5f731d1be127e77efe8b534df5ccde59083849d/detection

    Xavier Mertens (@xme)
    Xameco
    Senior ISC Handler - Freelance Cyber Security Consultant
    PGP Key

    0 comment(s)
    ISC Stormcast For Friday, April 25th, 2025 https://isc.sans.edu/podcastdetail/9424

      Comments


      Diary Archives