NINJAPAW / DEFENSE DOJOPUBLIC DEMO

PHP TO PROOF / GUIDED STORY

The request that wanted a shell.

Follow Ghostpaw one card at a time. Expand the evidence when you need it.

STEP 1 OF 10

1 / 10

Brief the team

Ghostpaw is fictional. The goal: reach execution and valuable data. Find where evidence ends and assumptions begin.

Linux PHP, three fabricated records, zero runtime role grants. Front Door/WAF is public; its service tag and profile header restrict the origin.

Application state: training. Reading this guide does not run the synthetic endpoints.

REAL / edge behavior · SYNTHETIC / app excerpts · REFERENCE / conditional detections

Prepare your own lab

Validate before replaying

Use an approved owned dev lab. Read health JSON; run plan, doctor, validate. Origin must stay 403 even with copied FDID. No extra app roles. Stop on ownership/HTTPS/origin failures.

Generate a bounded run

bash scripts/dojo.sh simulate --environment dev --yes

Five benign requests share a new UUIDv4; only the first carries dojoProbe=waf-marker. Save the run/UTC window. Detection: five HTTP 200s. No command, payload, arbitrary destination, or browser run control.

Expiry is not deletion; the log cap is not a budget.

STEP 2 OF 10

2 / 10

A stranger at the edge

Story order is not a detection or ingestion timetable.

Ghostpaw's move: Ghostpaw probes the public PHP entry point.

WITHOUT WORKLOAD DEFENDER / RAW CLUES

What the analyst can see

Triage request/WAF logs manually; these are not workload alerts.

Actual ingested log from the recorded run

HTTP 200 / historical capture.

{
    "Utc": "2026-10-24T16:33:41.5811447Z",
    "Signal": "request",
    "Synthetic": true,
    "EvidenceKind": "synthetic-application-log"
}

Captured WAF marker (Block/Detection, not enforced blocking):

{
    "Utc": "2026-10-24T16:33:41.0000000Z",
    "Rule": "DojoBenignMarker",
    "Action": "Block",
    "WafMode": "detection"
}

WITH SUPPORTED DEFENDER / CONDITIONAL RESULT

DETECTION REFERENCE / NOT OBSERVED

Injection attempts

Supported injection detectors need real patterns, not this marker.

No provider alerts in this run. Category reference, not a fired alert.

Break the chain: Keep the origin Front Door-only; verify the WAF rule/action.

Check the detector reference

App Service alert reference: verify Linux/detector support. No fixed names, CVE coverage, or canary detector are asserted.

STEP 3 OF 10

3 / 10

An error becomes a clue

Ghostpaw's move: Ghostpaw tries to turn PHP input into execution.

WITHOUT WORKLOAD DEFENDER / RAW CLUES

What the analyst can see

An error fixture is not a crash or affected-runtime finding.

Actual ingested log from the recorded run

HTTP 200 / historical capture.

{
    "Utc": "2026-10-24T16:33:41.9488945Z",
    "Signal": "php-error",
    "Synthetic": true,
    "EvidenceKind": "synthetic-application-log"
}

WITH SUPPORTED DEFENDER / CONDITIONAL RESULT

DETECTION REFERENCE / NOT OBSERVED

Remote code execution

A fixture proves no CVE coverage; inspect actual request detections.

No provider alerts in this run. Category reference, not a fired alert.

Break the chain: Check the actual PHP version/advisory; patch affected components.

Check the detector reference

App Service alert reference: verify Linux/detector support. No fixed names, CVE coverage, or canary detector are asserted.

STEP 4 OF 10

4 / 10

The attempted jump to a shell

Ghostpaw's move: Ghostpaw attempts a child process outside the request handler.

WITHOUT WORKLOAD DEFENDER / RAW CLUES

What the analyst can see

Process fixture only; no process started or endpoint telemetry.

Actual ingested log from the recorded run

HTTP 200 / historical capture.

{
    "Utc": "2026-10-24T16:33:42.2912096Z",
    "Signal": "process-synthetic",
    "Synthetic": true,
    "EvidenceKind": "synthetic-application-log"
}

WITH SUPPORTED DEFENDER / CONDITIONAL RESULT

DETECTION REFERENCE / NOT OBSERVED

Web shell activity

Inspect real supported detections; this fixture creates no process or MDE event.

No provider alerts in this run. Category reference, not a fired alert.

Break the chain: Remove execution paths; keep zero runtime role grants.

Check the detector reference

App Service alert reference: verify Linux/detector support. No fixed names, CVE coverage, or canary detector are asserted.

STEP 5 OF 10

5 / 10

A call that never leaves

Ghostpaw's move: Ghostpaw tries to contact an external destination.

WITHOUT WORKLOAD DEFENDER / RAW CLUES

What the analyst can see

Connection fixture only; no connection or real destination.

Actual ingested log from the recorded run

HTTP 200 / historical capture.

{
    "Utc": "2026-10-24T16:33:42.9044386Z",
    "Signal": "outbound-synthetic",
    "Synthetic": true,
    "EvidenceKind": "synthetic-application-log"
}

WITH SUPPORTED DEFENDER / CONDITIONAL RESULT

DETECTION REFERENCE / NOT OBSERVED

Compromise indicators

No egress; no network detection is demonstrated.

No provider alerts in this run. Category reference, not a fired alert.

Break the chain: Corroborate network evidence; a fixture is not command and control.

Check the detector reference

App Service alert reference: verify Linux/detector support. No fixed names, CVE coverage, or canary detector are asserted.

STEP 6 OF 10

6 / 10

The ledger stays fictional

Ghostpaw's move: Ghostpaw searches for records and credentials.

WITHOUT WORKLOAD DEFENDER / RAW CLUES

What the analyst can see

Three fabricated records; no secret/data-plane read or exfiltration.

Actual ingested log from the recorded run

HTTP 200 / historical capture.

{
    "Utc": "2026-10-24T16:33:43.4246796Z",
    "Signal": "canary",
    "Synthetic": true,
    "EvidenceKind": "synthetic-application-log"
}

WITH SUPPORTED DEFENDER / CONDITIONAL RESULT

DETECTION REFERENCE / NOT OBSERVED

No guaranteed data-access alert

No sensitive data or canary detector; inspect actual findings.

No provider alerts in this run. Category reference, not a fired alert.

Break the chain: Keep records fabricated; verify removal of training endpoints.

Check the detector reference

App Service alert reference: verify Linux/detector support. No fixed names, CVE coverage, or canary detector are asserted.

STEP 7 OF 10

7 / 10

Investigate the evidence

HISTORICAL CAPTURE / NOT LIVE

Historical reference capture, not your new deployment's logs.

92ad189c-9a88-4403-b773-52453963e4b8: 2026-10-24T16:33:41.463Z to 2026-10-24T16:33:43.454Z; collected 2026-10-24T16:36:07.232Z (UTC).

Baseline: five HTTP 200s / five synthetic records. Block/Detection is not enforced blocking. Defender: AppServices/CloudPosture/Containers Standard. The direct provider alert query succeeded and returned [] for the exact app/window. Four Healthy assessments are not four detections, not necessarily exclusive to paid CSPM.

Not a Defender-disabled control run. References are not fired alerts; missing alerts are not proof of safety.

Alerts and expected results

The alert queue is not the log stream

Expected outcomes, not live alert counts. Missing evidence is not proof of safety.
ProfileThis safe replayWhat not to expect
Without optional Defender/SentinelDelivered WAF/app logs; manual KQL.No Defender or scheduled Sentinel alert.
With paid Defender CSPMActual supported posture/graph results.No guaranteed CVE finding, attack path or shell alert.
With Defender for App ServiceFixtures; inspect independently observed detections.No guaranteed alert from synthetic activity.
With Sentinel onlyRule installed disabled.Scheduled evaluation or an incident.
With Sentinel + analyticsPossible Informational synthetic correlation when logs match.Not a Defender detection or automatic incident.

Defender for Cloud > Security alerts: filter asset/time; save IDs, titles, severity, evidence and status. Posture is separate; references never fill an empty queue.

Queries, timeline and hunting results

Build the evidence timeline

In workspace Logs, set RunIdFilter and UTC window. WAF records use AzureDiagnostics; PHP events use AppServiceConsoleLogs. Run detections/waf-requests.kql and correlation.kql, replacing __APP_RESOURCE_ID__.

Check rule/action, request/reference, resource/time, synthetic: true. No DeviceProcessEvents/DeviceNetworkEvents. Ingestion does not make fixtures real.

Open all hunting queries, captured results and additional-value walkthroughs (central historical reference).

Defender posture and protection layers

Review the actual risk context

bash scripts/dojo.sh investigate --environment dev

Without paid Defender CSPM: configuration/recommendations. With paid Defender CSPM: actual graph/paths. Empty is not clearance; CVE keywords are not findings.

--enable-defender-cspm is billable subscription-wide Standard, not other plans. No recommendation export is configured.

Choose the right protection layer

Foundational CSPM: free recommendations. Paid CSPM: supported graph/paths. Serverless protection is separate; pricing is not scanning. App Service is not MDE. Verify plans/support in Environment settings; incident creation is disabled.

Defender and Sentinel status: not queried by this page. Omitted flags do not mean off; no extra app permissions are needed.

Optional Sentinel analytics

Correlation is not automatically an incident

Hunt manually without Sentinel. --enable-sentinel installs a disabled rule; --enable-analytics additionally enables evaluation. Review costs and plan/deploy flags.

CONFIGURED RULE / NOT AN OBSERVED ALERT

PHP to Proof - SYNTHETIC multi-signal training correlation

Severity: Informational. Source: Sentinel scheduled analytics, not Defender. Incident creation: disabled.

15-minute evaluation / one-hour window: WAF marker and at least two distinct synthetic app signals, same run, recent ingestion. Late data can miss; matches repeat. No timing guarantee. createIncident: false; no connector/playbook.

STEP 8 OF 10

8 / 10

Prevent the next request

Expected after an approved change in your own lab, not a captured after-state:

bash scripts/dojo.sh protect --environment dev --yes
bash scripts/dojo.sh simulate --environment dev --yes

Prevention: marker 403, four other signals 200. Normal traffic must work; origin stays403.

Official response and scripts: stage three request rules in Log first. Response rule99006001 has no WAF hits/logs. WAF mitigation is not a PHP patch.

STEP 9 OF 10

9 / 10

Resolve and verify

bash scripts/dojo.sh remediate --environment dev --yes
bash scripts/dojo.sh validate --environment dev

Fixed: five signals404 without the WAF marker; health200, origin403. Repair preserves fixed/Prevention/expiry. Fixture removal is not a PHP patch.

Resolution script and closure checks: verify running PHP8.4.27+, rebuild images, inspect findings. Approved window required; no after-state is claimed.

STEP 10 OF 10

10 / 10

Close the case

Save asset/run/UTC, WAF action, fixtures and real finding IDs. Verify isolation, unmarked fixed 404, ingestion and cleanup. Guide: docs/walkthrough.md.

Use Uninstall in the deployment wizard when finished. Expiry is not deletion.

Optional private container lab

A different lab, with real image evidence

docs/container-lab.md: private affected/fixed images, existing plans/sensors, verified findings. No exploit/public ingress/guaranteed alert. Separate costs/teardown.

Plain-language glossary

Logs, alerts and incidents

Log / alert / incident: record / detector conclusion / grouped case. Synthetic: fabricated, even if ingested.

Return to the story / Follow Ghostpaw again