Qinglong (whyour/qinglong) is a popular open-source Node.js/Express panel for scheduling and running automation scripts, widely deployed by developers — particularly in China-based automation communities — to manage cron-style jobs on a self-hosted server. In early 2026, security researchers and outlets including BleepingComputer, Snyk, and GBHackers confirmed that two chainable vulnerabilities in versions up to and including 2.20.1 let an unauthenticated attacker reach admin-only endpoints and, in the worst case, execute arbitrary shell commands with no credentials at all. In-the-wild exploitation reportedly began around February 7, 2026, weeks before the root cause was publicly disclosed on February 27, and attackers used it to plant multi-architecture cryptominer binaries disguised as garbage-collection processes. Victims saw CPU utilization spike to 85–100%. The fix landed in PR #2941. This is a case study in how a routing quirk — not a memory-safety bug, not a deserialization gadget — becomes full remote code execution, and what that means for anyone running internet-facing self-hosted tooling.
What were the actual vulnerabilities?
Two separate bugs, each usable alone but more dangerous chained. CVE-2026-3965 is a URL-rewrite bypass: Qinglong's reverse-proxy layer rewrote any request to /open/* into /api/$1, and /open endpoints were treated as public while /api endpoints held the admin logic — including credential reinitialization. A single unauthenticated request through the rewrite path let an attacker reset admin credentials outright. CVE-2026-4047 is subtler and more severe: the authentication middleware checked whether a request path started with /api/ using a case-sensitive string comparison, but Express (like most Node HTTP routers) resolves routes case-insensitively. A request to /aPi/system/command-run matched the routing table but skipped the auth check entirely, handing an attacker direct, unauthenticated command execution — no credential reset step needed at all.
Why does a case-sensitivity check like this keep happening?
Because auth middleware and route matching are frequently implemented as two independent systems that are assumed to agree on case-folding, and in Express-based apps they don't by default. This is the same failure class documented for years in web frameworks generally: a security control written against one normalization of a string (exact-case path prefix) sits in front of a router that resolves a different normalization (case-insensitive path matching). The middleware developer tested /api/system/command-run, saw it blocked, and moved on — never testing /Api/, /aPi/, or /API/, each of which reaches the same handler. Any self-hosted tool that layers custom auth middleware in front of a framework's native router, rather than using the framework's built-in guard/permission system, is exposed to this exact pattern regardless of language or stack.
How did the exploitation actually play out?
Once an attacker reached the unauthenticated system/command-run endpoint (directly via CVE-2026-4047, or indirectly by first resetting admin credentials via CVE-2026-3965), they modified Qinglong's config.sh startup script to inject a shell command. That command downloaded a multi-architecture cryptominer binary from file.551911.xyz and saved it to /ql/data/db/.fullgc — a filename deliberately chosen to resemble a benign Java "Full GC" garbage-collection log or process, so it would blend into process listings and not draw operator attention during a casual review. Because config.sh runs on every container/service start, the miner persisted across restarts. Administrators typically noticed only after CPU utilization climbed to 85–100% sustained load, at which point the hidden .fullgc process was the giveaway during investigation.
What is the patch status right now?
The fix shipped in PR #2941 upstream in the whyour/qinglong repository, and any instance running a version after that merge is not vulnerable to either chain. The practical risk today is entirely in unpatched, still-running instances: self-hosted tools like Qinglong are typically deployed once by an individual developer or a small team and then left alone for months or years, with no forced update mechanism and no vendor pushing patches to them. Unlike a SaaS product where the vendor patches the server, a self-hosted scheduler's security posture is only as current as the last time someone manually pulled a new image or ran a git update — which for personal-automation tooling is often "never" after initial setup.
What should teams running self-hosted schedulers check for?
Confirm the running version is newer than PR #2941, and if not, patch or take the instance off the public internet immediately — there is no reason a personal task scheduler needs to be reachable unauthenticated from the open web in the first place. Independent of patch status, check for compromise indicators directly: look for a hidden process or file at /ql/data/db/.fullgc, review config.sh for injected commands you didn't write, and check outbound network connections for traffic to unfamiliar domains like file.551911.xyz or unexplained sustained CPU load. More broadly, this incident is a reminder that "internal" or "personal" self-hosted tools accumulate real exposure over time: they rarely get the same patch cadence as production infrastructure, yet they often run with full shell access on a box that also holds other credentials.
How Safeguard helps
Self-hosted panels like Qinglong rarely show up in a typical asset inventory because nobody thinks of a personal automation scheduler as "production," which is exactly why they go unpatched. Safeguard's asset discovery and external exposure surface mapping are built to catch this blind spot — surfacing internet-facing services and self-hosted tooling that live outside your primary SCM and registry footprint, so an unauthenticated admin panel doesn't sit unnoticed for months. Continuous scanning re-evaluates every tracked asset the moment a new CVE or KEV entry lands, so a fix like PR #2941 gets flagged against any instance in inventory within minutes rather than waiting for the next manual audit. And the runtime collector's behavioral monitoring is designed to catch exactly the pattern this campaign relied on — a renamed process consuming abnormal CPU and reaching out to a domain with no prior history — even when the process itself is deliberately disguised to look benign.