Skip to main content
Following the Evidence Through OffSec's Dune Phantom
  1. Blog/
Incident Response

Following the Evidence Through OffSec's Dune Phantom

Four investigations across AWS, phishing, Active Directory and AI infrastructure—and the lessons that connected them.

Mete Demirci
Author
Mete Demirci
Documenting a cybersecurity journey through practical work, technical depth, and honest reflection.
Editorial basis First-hand lab investigation Last reviewed How MeteSec verifies content

Four investigations. Four completely different environments. One lesson that kept returning: follow the evidence, not the loudest alert.

Over the last few weeks, I worked through OffSec’s Dune Phantom challenge series.

I considered writing four separate walkthroughs. Instead, I wanted to capture the investigation as a single story—because what made the series memorable was not any individual answer. It was how radically the environment changed from one week to the next while the investigative method stayed the same.

One week began with a public S3 bucket and ended in destructive AWS access. The next involved reverse engineering a phishing toolkit. Then came encrypted SMB3 traffic, Remote Registry and an Active Directory compromise. The final week turned an incident-response lab into an AI-security investigation spanning RAG, MLflow, model artifacts and an external LLM backend.

This is not a question-by-question solution dump. It is the condensed version of the attack chains, the pivots that mattered and the detection ideas I took away from them.

Four Investigations at a Glance
#

ChallengeStarting pointDecisive pivotFinal impact
01 — CloudPublic S3 website bucketGitLab CI/CD into AWS SSM and role chainingS3 objects overwritten with attacker-controlled SSE-C encryption
02 — PhishingMailbox and compiled Go toolkitCorrelating a generated SVG with the toolkitDelayed redirect to a typosquatted phishing domain
03 — EnterpriseWeb exploitation and domain activityDecrypting SMB3 and following Remote RegistryCertificate-based path to Domain Administrator and evidence destruction
04 — AI platformMalicious support documentRAG retrieval into tool use, egress and MLflowMalicious model promotion and external LLM routing

The technologies barely overlapped. The investigative rhythm did:

Establish the active identity
→ identify the next trust boundary
→ correlate the records on both sides
→ reconstruct what actually executed
→ measure the resulting impact

Challenge 1 — From a Public S3 Bucket to AWS Compromise
#

Initial clue
#

The first challenge started almost innocently: a publicly accessible S3 website bucket.

The attacker had been probing public buckets for exposed Git metadata such as .git/config, .git/index and .git/logs/HEAD. One bucket, edunexus-blackfriday2023, returned .git material through anonymous GetObject requests. The exposed repository data led to a leaked GitLab Personal Access Token.

The attacker authenticated as emily.johnson and immediately validated the token through GitLab API requests:

GET /api/v4/user
GET /api/v4/personal_access_tokens/self

Both returned HTTP 200. But GitLab itself was only the first trust boundary.

Investigation pivot: CI/CD became a cloud identity
#

The attacker created a branch, changed project content and triggered a CI/CD pipeline running on a GitLab Runner hosted on EC2.

Because the instance carried an AWS instance profile, pipeline execution effectively became access to the identity:

GitLabRunner/i-0767c6d302293aedf

The attacker validated it with GetCallerIdentity, attempted IAM enumeration and discovered ssm:SendCommand. The runner role could ask AWS Systems Manager to execute commands on another EC2 instance.

CloudTrail confirmed the SendCommand operations, but recorded the bodies only as HIDDEN_DUE_TO_SECURITY_REASONS. That was not the end of the evidence. It was a pivot point.

Using the SSM command IDs, I correlated the control-plane events with the SSM Agent logs on the target host. Those logs revealed what actually ran. The first command was an execution check:

uname -a; id

The next added an attacker-controlled SSH key to /home/ec2-user/.ssh/authorized_keys and queried the system’s public IP address. An API permission had become persistent host access.

Identity chaining and final impact
#

The target EC2 instance carried its own bastion role. Compromising the host introduced a new AWS identity and a longer role chain:

GitLabRunner
bastion
Ops_t1
Ops_t2
DevOps_full

Ops_t1 mainly enabled IAM reconnaissance. Ops_t2 could assume DevOps_full, which held broad permissions across EC2, IAM, S3, Secrets Manager, Lambda, DynamoDB and RDS.

The final action was the most unusual part. The attacker used s3:PutObject with SSE-C and AES256. AWS performs server-side encryption but does not retain the customer-provided key. If an attacker overwrites objects with a key only they possess, the victim can still own valid S3 objects while being unable to decrypt them.

The evidence contained 229 PutObject operations using SSE-C from the compromised DevOps_full/ghost identity.

Public S3 bucket
→ exposed .git metadata
→ leaked GitLab PAT
→ attacker-controlled CI/CD pipeline
→ EC2 runner instance profile
→ SSM remote execution
→ bastion persistence
→ IAM role chaining
→ DevOps_full
→ S3 SSE-C data destruction

Detection angle
#

The strongest signal is not merely a new branch or one AssumeRole event. It is the sequence:

New branch and pipeline
→ unusual runner execution
→ runner identity calls AWS APIs
→ ssm:SendCommand targets an unrelated instance
→ new role chain reaches a privileged role
→ bulk PutObject operations use SSE-C

CI/CD systems should be monitored as privileged infrastructure because they frequently sit one configuration mistake away from cloud control-plane access.

Challenge 2 — Reverse Engineering a Phishing Toolkit
#

Initial clue—and the wrong answer
#

The second challenge provided two central artifacts: Inbox-1, an mbox mailbox, and lofc, a compiled Linux ELF binary written in Go. The task was to identify which attachment had been generated by the captured phishing toolkit.

My first instinct was wrong.

One message looked like a perfect classic phishing candidate: urgent wire-transfer language, a suspicious sender, a ZIP attachment and obfuscated JavaScript. It was loud, obviously malicious and still not the requested payload.

The challenge was not asking which attachment looked most dangerous. It was asking which attachment could be tied to this specific toolkit.

Investigation pivot: an SVG that was not merely an image
#

The correct artifact was megacorpone_sales.svg.

SVG is XML-based and can contain JavaScript. This file presented itself as a normal business dashboard while hiding a data-analytics attribute and logic that reconstructed a URL from harmless-looking business terms:

revenue    → h
operations → t
risk       → p
shares     → s
quarterly  → :

The decoded destination was https://login.mlcrosoft.com. The domain used a lowercase l in place of the i in Microsoft. The SVG then waited roughly 15 seconds before redirecting the victim—long enough for the graphic to appear legitimate before the phishing behavior became visible.

Reverse engineering the generator
#

Running strings against a Go binary produced an enormous amount of runtime noise, so I moved into Ghidra. Instead of chasing generic strings such as password, I searched for a specific application response: Invalid credentials.

The cross-reference led to main.(*App).handleLogin, where the decompiled function contained hardcoded authentication checks. After recovering the challenge credentials, I ran the toolkit in Kali and accessed its local web interface.

I generated a fresh SVG pointing to a controlled challenge destination and compared its data-analytics structure with the mail attachment. That correlation—not how scary the email looked—proved which payload belonged to the toolkit.

Captured phishing toolkit
→ generated business-themed SVG
→ matching hidden payload structure
→ JavaScript reconstruction
→ delayed redirect
→ typosquatted destination

Detection angle
#

Mail controls should not automatically treat SVG as harmless image content. Useful signals include active scripting, encoded custom attributes, delayed navigation and destinations visually similar to trusted domains.

More importantly, detection logic should preserve the distinction between a malicious artifact and an artifact produced by the investigated tool. The second claim requires correlation and carries a much higher evidential burden.

Challenge 3 — From Web Exploit to Domain Administrator
#

Initial access and changing identities
#

Week three was the most traditional enterprise compromise of the series. The initial access vector was CVE-2025-24813, after which the attacker moved through a sequence of Active Directory identities:

leo.sanders
→ mono.taylor
→ kate.wilson
→ charlie.brown
→ henry.thomas
→ Administrator

Every hop used a different technique.

The first normal domain user was leo.sanders. Leo’s access was used to take over or reset mono.taylor. Mono held rights over kate.wilson, which were abused through a Shadow Credentials attack by changing Kate’s msDS-KeyCredentialLink and introducing attacker-controlled certificate and key material for PKINIT authentication.

Kate’s activity revealed the next failure: an earlier net use command had contained Charlie Brown’s credentials directly on the command line. No LSASS dump or password cracking was necessary. The password was already present in the evidence.

Investigation pivot: decrypting SMB3
#

The next credential source was not visible in the WinRM evidence. The activity was hidden inside encrypted SMB3 traffic.

At first, Wireshark showed little beyond encrypted sessions. After supplying the relevant NTLMSSP and SMB session material, the traffic became readable:

IPC$
svcctl
winreg

That changed the interpretation completely. The attacker was not simply browsing files through an administrative share. They were using Remote Registry over SMB.

The registry path led into Henry Thomas’s user hive:

HKU\<Henry-SID>\
Software\Martin Prikryl\
WinSCP 2\Sessions\
henry.thomas@10.40.50.151

The saved WinSCP session contained HostName, UserName and an encoded Password value. Decoding the value and removing the username-plus-hostname prefix recovered the next challenge credential.

Encrypted SMB3
→ session decryption
→ IPC$
→ winreg
→ Remote Registry
→ saved WinSCP session
→ decoded credential

This was my favorite pivot in the series: a view full of opaque SMB3 traffic became a precise record of the attacker’s remote-registry actions.

AD CS abuse: one failure and one success
#

The attacker attempted two certificate-based paths.

In the first, Kate Wilson’s UPN was temporarily changed to Administrator. The resulting authentication still mapped back to kate.wilson.

Certificate used
Administrator compromise

That distinction matters. A suspicious certificate operation is not proof of successful privilege escalation.

The later attempt produced a different result. A certificate with CN=Henry.thomas was mapped by the KDC to Administrator, and the Kerberos TGT request succeeded with certificate-based PKINIT. The attacker had reached Domain Administrator.

Disguised tooling and evidence destruction
#

On S-APP04, a file named splunk_license.exe was executed with ./splunk_license.exe --write license.dmp. The target PID belonged to lsass.exe. Despite its name, the executable was being used as a disguised credential dumper. The attempt failed because it could not obtain a handle to the process.

The attacker then moved from credential access to evidence destruction. From Splunk’s data directory, they recursively removed the indexed-data folder.

Web exploitation
→ domain credentials
→ delegated account control
→ Shadow Credentials
→ command-line password exposure
→ SMB3 decryption
→ Remote Registry
→ WinSCP credential recovery
→ AD CS abuse
→ Domain Administrator
→ failed LSASS dump
→ Splunk data destruction

Detection angle
#

Several high-confidence opportunities appeared along this path:

  • changes to msDS-KeyCredentialLink by unusual delegated identities;
  • credentials supplied directly in net use or similar command lines;
  • Remote Registry activity over IPC$ from unexpected systems;
  • certificate subjects that do not align with the Kerberos identity selected by the KDC;
  • renamed binaries attempting to access LSASS; and
  • destructive changes beneath security-platform data directories.

Challenge 4 — When Incident Response Becomes AI Security
#

Initial architecture
#

The final challenge moved into an internal AI support platform:

Upload and intake
→ object storage
→ RAG
→ tool gateway
→ egress
→ MLflow
→ model serving
→ LiteLLM
→ external model backend

Fifteen investigation questions eventually connected into one attack chain.

It began with a malicious support document uploaded into the platform and ingested into its RAG index. A later support query retrieved one of the document’s chunks, which triggered a tool action: connector.registry_status.

That invocation appeared only once in the available telemetry, making it an excellent pivot. Its outbound traffic exposed credentials for an MLflow model registry.

From registry access to malicious model promotion
#

An external source probed the MLflow API with failed authentication attempts, wrong identities, invalid object references and permission errors. Eventually, one request succeeded:

POST /api/2.0/mlflow/model-versions/create
→ HTTP 200

The legitimate support-router model had started at version 1. The attacker-created model became version 38, and both the Candidate and Production aliases eventually pointed to it.

The artifact changed as well: python_model.pkl. The original and malicious objects differed in both size and ETag. That was significant because a serialized Python model can influence what executes when the serving process loads it.

The serving environment then behaved exactly as designed. It resolved the Production alias, downloaded version 38 and loaded the artifact. During that reload, the process accessed /etc/support-router/runtime.ini.

Runtime configuration—including the LiteLLM base URL and an administrative key—was then exposed through an outbound callback.

The attacker had crossed three distinct planes:

RAG content compromise
→ MLOps supply-chain compromise
→ AI gateway control-plane access

Reconfiguring the model route
#

An internal source used LiteLLM administrative endpoints such as /key/generate and /model/new. After several denied requests, successful calls created a scoped key and a new route named support-priority-review.

The key was not merely provisioned. It was used for a completion. More importantly, the route no longer terminated at the intended local backend:

Normal:
support-standard
→ ollama
→ 127.0.0.1:11434

Compromised:
support-priority-review
→ ollama
→ external host:11434

An internal AI gateway had been reconfigured to send requests to an external Ollama service.

Malicious support document
→ RAG ingestion
→ retrieved chunk
→ tool invocation
→ registry credentials exposed
→ external MLflow access
→ malicious model version
→ Production alias changed
→ serving host loads model
→ runtime configuration read and leaked
→ LiteLLM administrative access
→ scoped key and model route created
→ external Ollama backend

Detection angle
#

AI security monitoring becomes much stronger when it follows effects rather than isolated words:

Suspicious retrieved chunk
→ unusual tool invocation
→ outbound network activity

Additional high-value signals include:

  • a Production alias moving immediately to a newly created model version;
  • a changed python_model.pkl size, digest or ETag;
  • model-serving reloads followed by unexpected configuration-file access;
  • administrative LiteLLM requests from a serving workload; and
  • an internal model route changing from a loopback backend to an external address.

What Connected All Four Challenges
#

The environments were different enough to feel like four separate disciplines:

Cloud and CI/CD
Phishing and reverse engineering
Active Directory and network forensics
RAG, MLOps and AI gateways

But the same three lessons kept returning.

1. Correlation beats isolated logs
#

CloudTrail showed that SendCommand happened. SSM Agent logs showed what ran.

WinRM evidence did not reveal the next credential source. SMB3 decryption and Remote Registry did.

The AI compromise required joins across RAG retrieval, tool telemetry, egress, MLflow access, registry state, object metadata, serving logs and LiteLLM audit events.

Many individual records looked harmless. The attack became visible only when the records were connected across trust boundaries.

2. Suspicious does not automatically mean relevant
#

The urgent wire-transfer attachment looked more malicious than the SVG. It was still the wrong answer to the attribution question.

The AI environment contained large amounts of suspicious instruction-like text. Much of it became noise once downstream behavior was correlated.

Suspicious
Relevant

Good investigations continually test whether an artifact explains the rest of the evidence—not merely whether it looks dangerous in isolation.

3. Identity context determines what actually happened
#

Every challenge depended on knowing which identity was active at a specific moment:

Linux user
≠ EC2 instance role
≠ assumed AWS role

Certificate subject
≠ account selected by the KDC

Serving host
≠ management source
≠ registry client
≠ external model backend

Without that context, privilege transitions can be missed and failed attacks can easily be mistaken for successful compromises.

Detection Engineering Takeaways
#

The most useful detections from the series are behavioral chains rather than single-event signatures:

  1. CI/CD to cloud: new branch or pipeline → unusual runner execution → cloud API use → remote command execution.
  2. Unexpected AWS role chains: workload identity → operations role → highly privileged role, especially across systems with no normal relationship.
  3. Shadow Credentials: unusual modification of msDS-KeyCredentialLink, followed by certificate-based authentication.
  4. Certificate identity mismatch: certificate subject and KDC-mapped account do not represent the same principal.
  5. Security-data destruction: recursive deletion or mass modification under logging, SIEM or endpoint-telemetry storage paths.
  6. RAG-to-egress correlation: retrieved untrusted content → sensitive tool invocation → outbound connection.
  7. MLOps integrity: new model version → Production promotion → changed model artifact → immediate serving reload.
  8. AI route drift: an internal model gateway begins sending requests to a newly introduced external backend.

Each chain expresses intent more clearly than any one event inside it.

Final Thoughts
#

Dune Phantom was one of those challenge series where the most valuable lessons had little to do with entering the final answers.

I followed EC2 instance profiles across AWS identities. I reverse engineered a Go binary in Ghidra. I analyzed JavaScript hidden inside an SVG. I supplied session material to Wireshark and watched encrypted SMB3 traffic become readable. I followed Remote Registry into a saved WinSCP session, traced Shadow Credentials and certificate-based Kerberos authentication, and eventually ended up investigating poisoned RAG content, MLflow model versions and an external Ollama backend.

Four challenges. Four different environments.

Almost every breakthrough came from the same discipline:

Follow the evidence—not the loudest alert, the most suspicious filename or the first theory. Keep correlating until every piece tells one consistent story.

Related

Living Off the Vault: From ‘I’m Staff’ to Breaking Custom Cryptography

It started with a chatbot. It ended with an enterprise password vault decrypted without its master password. Everything in between involved an FTP jail escape, an Electron AppImage, a native Node.js add-on and a custom cipher that collapsed under its own mathematics. I recently worked through OffSec’s Living Off the Vault challenge, and it had one of the strangest technical progressions I have seen in a lab so far. The first page looked like a normal support portal. A few stages later, I was analyzing an affine substitution box and using the application’s own native encryption primitive against it.

MDR und XDR: Moderne Security Operations verstehen

This keynote opened the IT-SICHERHEIT web conference on detection and response with MDR and XDR. About the Keynote # Organizations need to identify and handle security-relevant activity across endpoints, cloud environments, and identities. Phishing, compromised accounts, and suspicious behavior rarely stay inside a single technical boundary. The keynote explains how modern MDR and XDR approaches connect detection and response across those environments and where they fit in relation to traditional SIEM-centered security operations.
Continue exploring

Go beyond this article