# [CRIT] [GHSA / CRITICAL] CVE-2026-45721: Algernon: handler.lua discovery walks parent directories above the server root

**Source:** GitHub Security Advisories
**Published:** 2026-05-19
**Article:** https://github.com/advisories/GHSA-xwcr-wm99-g9jc

## Threat Profile

Algernon: handler.lua discovery walks parent directories above the server root

### Summary

When Algernon is asked for any URL path that resolves to a directory *without* an index file, `DirPage` walks **upward through parent directories — past the configured server root** — looking for a file named `handler.lua` to execute as the request handler. The loop terminates only after 100 ancestor steps or when `filepath.Dir` returns `.`, so on any absolute server-root path the search reaches the file…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-45721`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1204.002** — User Execution: Malicious File
- **T1059.004** — Command and Scripting Interpreter: Unix Shell
- **T1505.003** — Server Software Component: Web Shell
- **T1547** — Boot or Logon Autostart Execution
- **T1574** — Hijack Execution Flow
- **T1592.002** — Gather Victim Host Information: Software

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### Algernon web server spawning shell child process (CVE-2026-45721 handler.lua RCE)

`UC_262_2` · phase: **exploit** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Processes.process) as cmdline values(Processes.process_path) as child_path from datamodel=Endpoint.Processes where Processes.parent_process_name="algernon" Processes.parent_process_name!="algernon-dev" (Processes.process_name IN ("sh","bash","dash","zsh","ash","cmd.exe","powershell.exe","pwsh.exe")) by host Processes.parent_process_name Processes.process_name Processes.user | `drop_dm_object_name(Processes)` | rename firstTime as first_seen lastTime as last_seen
```

**Defender KQL:**
```kql
// CVE-2026-45721 — Algernon parent process spawning a shell. Stock Algernon never forks /bin/sh for request handling; this is the run3()/os.execute() primitive that handler.lua exposes.
DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName =~ "algernon"
   or InitiatingProcessFolderPath has "/algernon"
   or InitiatingProcessFileName matches regex @"(?i)^algernon(\.exe)?$"
| where FileName in~ ("sh","bash","dash","zsh","ash","cmd.exe","powershell.exe","pwsh.exe")
| where AccountName !endswith "$"
// classic exploit shape: sh -c <attacker payload> launched by web server
| extend ShellArgs = iff(ProcessCommandLine has_any ("-c ","/c ","-Command","-EncodedCommand"), "shell-with-inline-cmd", "interactive-or-script")
| project Timestamp, DeviceName, AccountName,
          ParentImage = InitiatingProcessFolderPath,
          ParentCmd   = InitiatingProcessCommandLine,
          ChildImage  = FolderPath,
          ChildCmd    = ProcessCommandLine,
          ShellArgs, SHA256, ReportId
| order by Timestamp desc
```

### handler.lua dropped outside Algernon's configured web root (CVE-2026-45721 backdoor stage)

`UC_262_3` · phase: **install** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Filesystem.user) as user values(Filesystem.process_name) as writer from datamodel=Endpoint.Filesystem where Filesystem.file_name="handler.lua" (Filesystem.file_path IN ("/handler.lua","/srv/handler.lua","/var/handler.lua","/var/www/handler.lua","/home/handler.lua","/tmp/handler.lua","/dev/shm/handler.lua","/opt/handler.lua","C:\\handler.lua") OR Filesystem.file_path="/home/*/handler.lua" OR Filesystem.file_path="/srv/*/../handler.lua") by host Filesystem.file_path Filesystem.process_name | `drop_dm_object_name(Filesystem)` | rename firstTime as first_seen lastTime as last_seen
```

**Defender KQL:**
```kql
// CVE-2026-45721 — handler.lua written into a parent of any plausible Algernon web root. The article's exploit places it at /srv/handler.lua, /var/handler.lua, /home/<user>/handler.lua, /dev/shm/handler.lua, or filesystem root.
let WebRootAncestors = dynamic([
    "/handler.lua",
    "/srv/handler.lua",
    "/var/handler.lua",
    "/var/www/handler.lua",
    "/home/handler.lua",
    "/tmp/handler.lua",
    "/dev/shm/handler.lua",
    "/opt/handler.lua",
    "/usr/local/handler.lua",
    "/run/handler.lua"
]);
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileRenamed","FileModified")
| where FileName =~ "handler.lua"
| extend FullPath = strcat(FolderPath, iff(FolderPath endswith "/" or FolderPath endswith "\\", "", "/"), FileName)
| where FullPath in~ (WebRootAncestors)
   or FolderPath matches regex @"^/home/[^/]+/?$"
   or FolderPath matches regex @"^/srv/?$"
   or FolderPath matches regex @"^/var(/www)?/?$"
   or FolderPath in~ ("/","/tmp","/dev/shm","/run","/opt","/usr/local")
| where InitiatingProcessAccountName !endswith "$"
// .alg archive extraction by algernon itself is a documented vector — keep but tag it
| extend ExtractedByAlgernon = iff(InitiatingProcessFileName =~ "algernon", true, false)
| project Timestamp, DeviceName, FullPath, FolderPath,
          WriterImage = InitiatingProcessFolderPath,
          WriterCmd   = InitiatingProcessCommandLine,
          WriterUser  = InitiatingProcessAccountName,
          ExtractedByAlgernon, SHA256, ReportId
| order by Timestamp desc
```

### Algernon vulnerable installation discovery (CVE-2026-45721 exposure inventory)

`UC_262_4` · phase: **recon** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as first_seen max(_time) as last_seen values(Processes.process) as cmdlines values(Processes.user) as users values(Processes.parent_process_name) as parents from datamodel=Endpoint.Processes where Processes.process_name="algernon" OR Processes.process_path="*algernon*" by host Processes.process_name | `drop_dm_object_name(Processes)` | eval days_observed=round((last_seen-first_seen)/86400,1) | table host process_name first_seen last_seen days_observed cmdlines users parents
```

**Defender KQL:**
```kql
// CVE-2026-45721 — discover every host with Algernon installed or running. Two pivots: TVM inventory (slower, accurate) + recent process executions (faster, covers running instances).
let TvmHits =
    DeviceTvmSoftwareInventory
    | where SoftwareName has "algernon" or SoftwareVendor =~ "xyproto"
    | project Timestamp, DeviceId, DeviceName, OSPlatform, SoftwareVendor, SoftwareName, SoftwareVersion, Source="TVM";
let ProcessHits =
    DeviceProcessEvents
    | where Timestamp > ago(30d)
    | where FileName =~ "algernon" or FolderPath matches regex @"(?i)(^|/)algernon(\.exe)?$"
    | summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp),
                CmdLines=make_set(ProcessCommandLine, 20),
                ParentBins=make_set(InitiatingProcessFileName, 10),
                RunAsUsers=make_set(AccountName, 10)
                by DeviceId, DeviceName, FileName, SHA256
    | extend Timestamp=LastSeen, OSPlatform="unknown", SoftwareVendor="xyproto", SoftwareName="algernon", SoftwareVersion=tostring(SHA256), Source="ProcessExec"
    | project Timestamp, DeviceId, DeviceName, OSPlatform, SoftwareVendor, SoftwareName, SoftwareVersion, Source, FirstSeen, LastSeen, CmdLines, ParentBins, RunAsUsers;
union isfuzzy=true TvmHits, ProcessHits
| order by DeviceName asc, Timestamp desc
```

### Article-specific behavioural hunt — [GHSA / CRITICAL] CVE-2026-45721: Algernon: handler.lua discovery walks parent d

`UC_262_1` · phase: **install** · confidence: **High**

**Splunk SPL (CIM):**
```spl
``` Article-specific bespoke detection — [GHSA / CRITICAL] CVE-2026-45721: Algernon: handler.lua discovery walks parent d ```
| tstats `summariesonly` count
    from datamodel=Endpoint.Filesystem
    where Filesystem.action IN ("created","modified")
      AND (Filesystem.file_path="*/var/www/example.com*" OR Filesystem.file_path="*/var/www/handler.lua*" OR Filesystem.file_path="*/var/handler.lua*" OR Filesystem.file_path="*/home/alice/handler.lua*" OR Filesystem.file_path="*/home/handler.lua*" OR Filesystem.file_path="*/dev/shm*" OR Filesystem.file_path="*/etc/shadow*" OR Filesystem.file_path="*/dev/shm/handler.lua*")
    by Filesystem.dest, Filesystem.user, Filesystem.process_name,
       Filesystem.file_path, Filesystem.file_name
| `drop_dm_object_name(Filesystem)`
```

**Defender KQL:**
```kql
// Article-specific bespoke detection — [GHSA / CRITICAL] CVE-2026-45721: Algernon: handler.lua discovery walks parent d
// Hunts the actual binaries / paths / commandline fragments named
// in the article instead of a generic technique-class template.

// File-creation events for the named binaries / paths
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileCreated","FileModified")
| where (FolderPath has_any ("/var/www/example.com", "/var/www/handler.lua", "/var/handler.lua", "/home/alice/handler.lua", "/home/handler.lua", "/dev/shm", "/etc/shadow", "/dev/shm/handler.lua"))
| project Timestamp, DeviceName, AccountName, FolderPath,
          FileName, ActionType, InitiatingProcessFileName,
          InitiatingProcessCommandLine
| order by Timestamp desc
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-45721`


## Why this matters

Severity classified as **CRIT** based on: CVE present, 5 use case(s) fired, 7 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
