KNJ Panel · Security

Security audits, run as we build

Every audit runs against the live dev build, not a cleaned-up snapshot before release. We publish what each pass covers and what it found, including the things that needed fixing. This is a habit, not a launch-day checklist.

26

Audits run

0

Open critical issues

255

Found and fixed same day, all-time

10

Issues found, latest audit (#26)

26

Commits covered, latest pass

Security notice 2026-10-21

Patched. No signs of compromise found.

Critical PHP flaws (PHP2Shell): patched the same day, everything checked

On 21 October PHP published fixes for two critical flaws, CVE-2026-109618 and CVE-2026-109619, that let anyone run code on a server that runs PHP, without logging in. The details had leaked the day before and attacks were reported before the fixes were public. Every server behind KNJ Panel and this website that uses PHP, including the production panel servers, this website, mail, DNS and licensing, was updated to a fixed release within about four hours of PHP publishing it. We then checked every one of them for signs that anyone had got in first, and found none.

What happened, and when

  1. 20 Oct — Details of the flaws leak ahead of PHP's planned release, according to public reports.
  2. 21 Oct, 14:00 UTC — PHP publishes the fixes: 8.5.12, 8.4.27, 8.3.36 and 8.2.35.
  3. 21 Oct, about 18:00 UTC — The fixed builds had not yet reached our usual update source, so we installed the official builds directly, after checking each file against its published checksum.
  4. 21 Oct, by about 18:10 UTC — Every server updated, PHP restarted, and each confirmed to be running the fixed version.
  5. 21 Oct, evening — A full compromise check of every server, looking back to 19 October.

What we checked afterwards

  • The KNJ Panel code on every production panel server matched the released build exactly: over a thousand files compared, no differences and nothing extra.
  • This website's code matched its repository, and its blog matched WordPress's own checksums.
  • No new or changed PHP files, no backdoor patterns and no planted scripts in any website folder or temporary area.
  • No PHP crashes or memory errors in the server logs, and no bursts of failed requests.
  • No new administrator accounts or API tokens, no new scheduled jobs, startup items, privileged accounts or login keys.
  • Every sign-in to our servers, and every administrator sign-in to the panel, came from our own addresses.

What this cannot show. A check like this shows what is there, not what might have been and gone. The flaws were reported as exploited before fixes were public, so we have treated the hours before our update as exposed, and we will keep watching. One internal system outside the panel and the website could not be reached for the check, and this notice will be updated when it has been done.

If you run KNJ Panel. If you run KNJ Panel, update PHP now: PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 or newer is fixed, and older releases are not. Open System, then Package Updates in the Controller and apply the PHP updates as soon as they are offered.

Last updated 2026-10-21.

Latest audit #26 · 2026-10-15

10 found 9 fixed 1 accepted 0 open

10 findings, all fixed or accepted, none open

A check of Deploy from Git's keys, how the panel's paths are shared with customer websites, the lock on the database tool and the update process itself. The most serious finding was an older one about how much reach the panel's everyday account had over the server's privileged steps. Everything was fixed the same day.

What this audit covered

Everything shipped since the penetration-test fixes, 26 commits: the Deploy from Git rebuild (one key per repository, deploying onto an existing domain), the change that stops the panel taking over a customer's own /account and /controller pages, the phpMyAdmin lock-down with its one-click sign-in bridge, and the update process that now applies a whole release in one run. We read the code for ownership gaps, key exposure, injection, path tricks and what the update process trusts, then fixed every finding, re-ran the full test suite (3,465 tests) and checked the result on a live server.

Audit days

26 rounds across 16 days. Each bar is a day; taller means more rounds that day.

In order, not to scale: quiet stretches were deliberate.

New: our penetration test

We attacked a fresh install from the outside and published every finding.

What audit #26 found

In plain language, without code or private server details.

Fixed

More Reach Than Needed Over the Server's Privileged Steps

A small part of the panel's housekeeping runs with elevated rights. The everyday account the panel runs as had more influence over that part than it needed, so a problem in the panel itself could have been made to reach further. Everything that runs with elevated rights now takes its instructions only from places that account cannot change, refreshed from the verified release on every update.

1 high, fixed on every code path

Fixed

Updates Are Now Signed

An update downloaded a release over a secure connection and unpacked it with full control, but nothing proved it was ours. If our download server were ever taken over, every server updating from it would have been handed attacker code. Every release is now signed with a key that never leaves our own workstation, and updates and fresh installs refuse anything that does not match, including an older release replayed to roll a server back.

1 medium, fixed

Fixed

Who Is Asking: Limiting Who May Vouch for a Visitor

The panel believes the visitor address its web server passes along, because customer webmail goes through it. That trust is now limited to the web server itself, and the address is read from the one value the web server always sets, so nothing else running on the machine can vouch for a visitor it has not seen.

1 medium, fixed in two layers

Fixed

Borrowing the Server's Own Login to List Private Repositories

The branch picker on Deploy from Git, when given a private repository address and no key of the customer's own, fell back to whatever login the server itself happened to hold. It now refuses without a key, uses no outside settings, never follows a redirect and no longer repeats the server's own error text, which could describe what lives at an internal address.

2 low, fixed

Fixed

A One-Time Sign-In That Could Be Used Twice

The one-click sign-in to the database tool is a single-use link. It was read and then deleted, so two requests arriving at the same instant could both use it. It is now claimed by renaming it first, which only one request can win.

1 low, fixed

Fixed

A Suspended Account Could Still Deploy From a Push

A push to a repository linked for automatic deploys still ran the customer's deploy script when their account was suspended, or when their package no longer included Deploy from Git. Both are now refused with the same answer as an unknown link, and the endpoint is rate limited.

1 low, fixed

Fixed

Small Tell-Tales Removed

The webmail shortcut now sets the forwarded port and prefix itself instead of passing along whatever the browser sent, and the database tool's sign-in address now answers a missing page in exactly the same way as any other address that does not exist.

1 low, 1 informational, fixed

Accepted

The Sign-In Link Sits in the Address Bar for a Moment

The database tool's one-click link carries its token in the address, so it can appear in the web server's access log for the minute it is valid. It is single-use and expires, and the alternative would put a secret somewhere less safe. Left as it is on purpose. A link sent to someone else could also sign them in to the sender's own database user, which the sender already controls.

1 informational, accepted

Pass

Keys and Ownership Held Up

One account could not reach another's keys, repositories, sites or database users through any route we tried. Private keys stay encrypted, are never shown again and are deleted after use, and every value passed to a privileged step is checked again there.

checked, nothing found

Pass

The Database Tool Lock Held Up

Encoded slashes, doubled slashes, dot paths, upper-case variants, extra path parts after a script name, the setup folder and static files all stayed behind the lock. The check never creates a session for a stranger, and the sign-in creates a fresh session so one cannot be planted.

checked, nothing found

Pass

Customers' Own Pages Stayed Theirs

The webmail address matches only that page and what is under it, no other panel path reaches a customer's domain, and the hostname behind it only ever comes from the administrator's own certificate step.

checked, nothing found

Pass: nothing found Concern found, fixed the same day Informational, accepted on purpose Critical and currently open

Audit history

The full write-up for each pass stays in our private engineering log. This is the shape of what we share publicly: what was covered and what came of it.

2026-10-15 · Audit #26

Everything shipped since the penetration-test fixes, 26 commits. Ten findings, all fixed the same day or accepted on purpose: the most serious was an older one about how much reach the panel's everyday account had over its privileged steps, now closed. Updates are now signed, only the web server may vouch for a visitor's address, and several smaller things were tightened in Deploy from Git and the database tool's sign-in. The full test suite (3,465 tests) passes.

2026-10-10 · Audit #25

The fourth round of the day, written from the point of view of one hosting customer trying to see another customer's data. Found three more things, all fixed immediately: any customer could read the command line of every running process on the server (which can briefly carry an installer's password), the traffic reports and per-site settings of every other site were readable, and removing an account left its FTP configuration behind. A final re-run of every automated check and a final look from the customer's side came back clean. 0 open.

2026-10-10 · Audit #24

The third round of the day, fixing everything the previous two rounds found and looking again. Found that a hosting customer's own website code could read the panel's source and logs and every other customer's web traffic logs, that removing an account left its backups behind forever, that the programming interface accepted domains the web form refused, and that a shortcut in a folder listing revealed details of another customer's file. All fixed and re-verified live on the dev server, with each upgrade path also closing the exposure on existing servers. 0 open.

2026-10-10 · Audit #23

The second round of the day, and the one that found the most serious issue in this run: a folder the malware scanner used for quarantined files sat somewhere a customer could redirect, letting a privileged step act anywhere on the server — reproduced, fixed, and re-verified by planting real shortcuts on the live dev build. The same class of problem turned up in the File Manager and in six places where the panel writes its own small files into customers' folders, and was then closed for about thirty privileged actions at once by running them where the operating system refuses to follow a shortcut at all. Also tightened: invitation links are no longer stored in a recoverable form, login timing no longer reveals which usernames exist, and the panel now asks browsers to insist on a secure connection. 0 open.

2026-10-10 · Audit #22

A full sweep of the 256 commits since the last published audit, using the same bug-class technique as before plus a live inspection of the dev server and a scan of every repository's full history for secrets (clean). Found and fixed five things: a password-reset email whose link could be pointed at an attacker's server, an account whose name could collide with one of the server's own and take it with it when removed, a redirect address that could stop the web server loading for every customer, a way for one customer to claim a name inside another's domain, and out-of-date third-party libraries. 0 open.

2026-08-27 · Audit #21

The smallest pass in this run of exhaustive, bug-class-organized sweeps across the whole codebase — the seventh confirmation pass using this technique, interrupted partway through by a personal usage-limit reset and picked back up a few hours later with no code left in a broken state. 3 issues found and fixed: a certificate-retry button missing the same one-at-a-time safeguard every other retry action already has, a narrow window where a background configuration rebuild could quietly undo an admin's just-saved setting, and an app-install failure case that had correctly been left un-auto-fixed by an earlier pass but never given the admin alert its safer sibling gets. Five of the eight sweep categories came back completely clean, including the cross-account access check for a fifth pass running. Full test suite (2,446 tests, up from 2,440) re-run clean. 0 open.

2026-08-27 · Audit #20

The biggest sweep yet in this series — roughly 65 issues found, collapsing to about a dozen real underlying causes once traced back, three of them wide-reaching enough that fixing the shared root cause closed somewhere between a dozen and nineteen separate symptoms each in one move, a meaningfully different shape of progress than earlier rounds' one-file-at-a-time fixes. The most serious: a background helper script that ran a privileged system command but never actually checked whether it succeeded — an entire class of 'this quietly failed' risk an earlier design had assumed was only theoretical turned out, in the real code, to be guaranteed to go undetected every time it happened. That finding fed directly into a deliberate, from-scratch redesign of account removal: it no longer deletes anything until the real system-level teardown is confirmed to have actually succeeded, and a failed removal now shows clearly in the admin dashboard with a retry option, instead of the database quietly disagreeing with reality. The cross-account access check came back clean for a fourth pass running. Full test suite (2,440 tests, up from 2,343) re-run clean. 0 open.

2026-08-27 · Audit #19

A fresh morning's re-run of the same eight-category technique against the previous pass's own fixes, explicitly testing whether two passes in a row could come back clean. They couldn't — but every one of the roughly 24 issues found was the same shape as something already fixed in an earlier pass, just in one more file, not a newly discovered kind of bug. Included two more unlocked concurrency windows and a background-alert wiring gap traced back to a decision made two commits into an earlier fixing session — but the most notable was a real permission bypass, reported independently by a second review running the same technique in parallel and cross-checked before being folded in: an action correctly blocked by the ordinary admin interface but not by its own API twin, letting a permission an admin had explicitly revoked keep working through the back door. The cross-account access check came back clean for a fourth pass running, and one reviewer caught and openly retracted its own false-positive finding before it reached a fix queue. Full test suite (2,343 tests, up from 2,302) re-run clean. 0 open.

2026-08-26 · Audit #18

An evening confirmation pass that turned up the most severe individual finding of this whole run: a password-change action for a file-access feature that persisted the new password to the database and reported success, but the actual system-level file write it depended on had no check on whether it worked at all — a disk-full or permissions failure meant the account owner believed their password had changed while the real access credential silently kept the old one, with nothing anywhere signaling anything was wrong. Also found: the same file-locking gap already fixed once, propagated into six more files outside that fix's original scope; roughly 30 more sites where a real safety net existed but surfaced a raw error page instead of a friendly message when two people acted at the same moment; and about 15 more places where one method in a file had a safeguard its neighbor in the same file didn't. Full test suite (2,302 tests, up from 2,230) re-run clean. 0 open.

2026-08-26 · Audit #17

A same-day confirmation pass against the previous audit's own fixes, which surfaced a real propagation gap: the exact 'stop two people acting at once' fix applied to one background action had never been copied to nine other actions with the identical shape. Also found and closed: a cross-feature naming collision that could let two different account features silently claim the same address at once, four background configuration-rebuild processes missing the same one-at-a-time protection, a missing uniqueness check on server registration, a small pagination bug in the new calendar-sync feature, and one gap in the safety net built in the previous audit, found by testing it against a case it hadn't been extended to yet. Full test suite (2,230 tests, up from 2,197) re-run clean. 0 open.

2026-08-26 · Audit #16

A deliberate methodology change, triggered by the previous audit's own finding that its detection technique had a structural blind spot — instead of reviewing one feature area at a time, seven parallel passes each swept the entire codebase for one specific bug shape and checked every match fresh. The most serious finding: a self-service backup-restore upload accepted an archive containing a symbolic link, which a crafted upload could use to write a file onto a completely different customer's website — fixed with the same archive-safety check already used everywhere else archives are extracted, just never applied to this one unprivileged path. Also found and fixed: a webmail pagination bug that silently skipped one message every time a customer turned the page, a missing safety lock on account backup/restore that let two near-simultaneous requests corrupt each other's output, and nine more instances of a background job silently getting stuck instead of failing visibly when a privileged operation timed out. A same-day follow-up built a general safety net for that last category — any background job now gets automatically marked failed and flagged for an admin, rather than sitting stuck forever, if the process running it gets killed outright rather than failing on its own. Full test suite (2,197 tests, up from 2,153) re-run clean. 0 open.

2026-08-25 · Audit #15

A pre-production readiness sweep, the first scoped explicitly to "are we ready to move off the current setup" rather than just a diff since the last pass — everything shipped since #14 (a web application firewall, calendar and contact syncing, and several mail-security features) plus, for the first time, a full compromise check across every server we currently operate, not only the one we build on. 7 issues found and fixed, the most notable a spelling mismatch that meant a virus-scanning toggle had silently never actually turned on since the day it shipped, while the on-screen status kept reading as enabled. Also closed: the new calendar-sync feature missing the same brute-force logging every other login already has, a spreadsheet-formula injection risk in a new export feature, and four smaller consistency gaps matching this project's own established patterns. The full-fleet check found zero signs of unauthorized access anywhere. Full test suite (1,914 tests, up from 1,907) re-run clean. 0 open.

2026-08-22 · Audit #14

The second audit in a row scoped to one whole piece of the system rather than a diff since the last pass: the Mail Only satellite role, reviewed end to end — role gating, the command channel between the main server and a linked satellite, every mail-related action in the install script, authorization across every mail-touching feature, and the cross-server admin login plus webmail session handling. 4 issues found and fixed, the most serious a command channel that checked who was sending a command but not what commands it would accept — closed with an explicit allowlist of the exact 33 commands that channel is meant to carry. Also closed: a licensing guard that covered one limited-purpose server type but not its twin, one login form missing the same brute-force protection every other login already has, and two install-script checks tightened to rule out a path-traversal shape by construction rather than by hoping it never appears. Cross-account authorization across every mail feature came back fully clean. Full test suite (1,693 tests) re-run clean. 0 open.

2026-08-22 · Audit #13

The first audit in this series scoped to one whole piece of the system rather than a diff since the last pass: the DNS-only satellite role, reviewed end to end — role gating, server-to-server sync in both directions, the trust-establishment flow, and the privileged install script's actions for that role. 4 issues found and fixed, the most serious a fallback connection path that, if intercepted once during a narrow bootstrap window, could leave a permanent address redirect behind — closed by narrowing which connection a self-reported address update is trusted from, without touching the underlying design question already flagged as open two audits ago. Also closed: a whole panel area relying on two facts holding forever instead of checking directly (not currently exploitable, added anyway), and two small consistency cleanups. Full test suite (1,688 tests) re-run clean. 0 open.

2026-08-22 · Audit #12

Everything shipped since #11 — 112 commits, the largest single window this project has covered: the DNS-only role's remaining buildout, the first-run setup wizard, the two-key mutual-auth server linking system, and the full Mail Only satellite-server epic. Reviewed as five parallel passes rather than one linear read. 5 issues found and fixed, the most serious a step-ordering gap in the setup wizard that could have permanently locked a fresh install out of ever creating an admin account — closed by checking the real invariant (does an admin exist) rather than a session flag that didn't guarantee it. Also closed: the same gap made it possible to register a forged server-trust record before any admin existed; a signing key reused for two different purposes in the cross-server login feature, now domain-separated; and a setup-link expiry whose first implementation was silently broken by a date-library behaviour change, caught by the test suite before it ever shipped. Full test suite (1,683 tests) re-run clean. 0 open.

2026-08-17 · Audit #11

A pre-release "check every line" pass, ordered before the audit itself: fix everything found, then run the audit clean. Dedicated sweeps covered every controller, the whole services layer, all 182 views, all 62 models, and the full ~6,000-line provisioning script line by line. 14 issues found and fixed — the largest was one root-run symlink-safety bug class (11 instances, found by re-sweeping the whole script after the first two turned up) — plus config injection via a File Manager folder name, several passwords briefly visible via the server's process list, and two access tokens moved from plaintext to encrypted storage. Full test suite (1,414 tests) re-run clean, every fix live-verified on the real server, and the audit re-run afterward found 0 new issues. 0 open.

2026-08-14 · Audit #10

A targeted review of everything shipped since #09: Server-Side Cron Jobs, real AWStats, Feature Manager, SSL/TLS, self-service Perl Modules, and Upgrade Database Version — the highest-risk addition this window. Secret scan across all three repos clean; every new route's actual middleware chain checked directly. The database upgrade tool was genuinely live-tested on a disposable server built for exactly that, which found and fixed one real bug before it could ever reach a live customer install. 0 issues open.

2026-08-12 · Audit #09

A 5-day break, then a full intrusion-detection sweep instead of a code review — no new commits in scope. Every server's complete login history traced, every access control tested live, one database restart investigated and explained. 0 issues found, 0 open.

2026-08-07 · Audit #08

Everything shipped since #07: the App Installer's rebuild into a five-app catalog (WordPress, Drupal, Nextcloud, MediaWiki, phpBB). 3 issues found, all fixed the same day. 0 open.

2026-08-06 · Audit #07

Everything shipped since #06: SSL/TLS, Databases, OS package management, Packages & Resellers, Server Configuration, the full Email section, Domains, Files, account-side Databases, Metrics, DNS Lookup, Team Access, and real phpMyAdmin. 13 issues found, all fixed the same day. 0 open.

2026-08-04 · Audit #06

Everything shipped since #05: the Security section's closing items (Access Control, Password Policy, API Tokens), Service Configuration, and Accounts. 5 issues found, all fixed the same day. 0 open.

2026-08-02 · Audit #05

Everything shipped since #04: the release pipeline's first real-world test, trial-licensing reinstall protection, and a fresh look at the panel update mechanism's privilege boundary. 4 issues found, all fixed the same day. 0 open.

2026-08-01 · Audit #04

Everything shipped since #03: FTP Settings, Backups, Login Security, Cron Jobs, and Licensing — the first pass to span three separate live servers, including our license server's entire history for the first time. 2 issues found on the license server, both fixed same day. 0 open.

2026-07-30 · Audit #03

Everything shipped since #02 (~159 commits): Security Center, Packages & Resellers, KNJ Webmail, account creation/suspension, disk quota & bandwidth. 2 issues found, both fixed same day. 0 open.

2026-07-25 · Audit #02

The second server bring-up and everything unique to it. 1 informational note, no live exposure.

2026-07-25 · Audit #01

First full pass across everything built through the core provisioning loop, SSL, database, mail, and DNS management. 4 issues found, all fixed same day.