scan-symlinks: tighten DANGEROUS prefix list to actual destruction class
All checks were successful
cpanel-importer Build and Push / Build-and-Push (push) Successful in 1m27s

Previous version of scan-symlinks.php was a verbatim port of the panel's
scanTarballForDangerousSymlinks(), which flagged every symlink whose
target sits under /etc, /usr, /bin, /sbin, /lib, /lib64, /var/lib,
/var/log, /var/cache, or /var/spool. That's the right posture for the
panel's pre-extract scan in DIRECT mode — refuse before extract — but
it makes the container REFUSE every cpmove that comes from a real
cPanel source server, including totally clean ones. Standard cPanel
accounts ship with stock symlinks like:

  homedir/access-logs                  -> /usr/local/apache/domlogs/<user>
  homedir/var/cpanel/styled/current_style
                                       -> /usr/local/cpanel/base/frontend/...
  homedir/.cpanel/email                -> /usr/local/cpanel/...
  homedir/etc                          -> /var/cpanel/userhomes/<user>/etc

Every customer tarball has 5-20 of these. Treating them as DANGEROUS
made the container abort with verdict=refused before extract.sh ever
ran. Surfaced on darkside import to whp02: scan-symlinks found
homedir/access-logs (a textbook cPanel symlink) and the import bombed.

The real destruction class — what ALFA TEaM Shell uses, what we saw
brick whp02 in May — is symlinks whose target is the exact filesystem
root or under one of the genuinely catastrophic system trees that
either escape the customer account or clobber boot/config/proc state:

  /         exact root (the classic alfasymlink/root)
  /etc      config tampering, /etc/shadow exfil
  /root     root home dir
  /boot     bootloader / kernel
  /proc     process info / kernel knobs
  /sys      sysfs
  /dev      device nodes

Everything else (notably /usr, /var) becomes UNCERTAIN: reported in
the JSON output but doesn't refuse the tarball. With --cap-drop=ALL
--read-only --network none --user 999, a /usr-targeting symlink in
the container's sandbox can at worst dangle on extract; it can't
touch the host.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude (bootstrap)
2026-05-31 10:07:23 -07:00
parent 5e206edc50
commit 60a232c54a

View File

@@ -45,11 +45,45 @@ if (!is_file($tarPath) || !is_readable($tarPath)) {
exit(2); exit(2);
} }
// Same prefix list as the panel. // Threat model: an "ALFA TEaM Shell"-style payload links into a path that,
// when a recursive walker follows it (or when something writes through it),
// either ESCAPES the customer's account on the destination server OR
// CLOBBERS critical system state. The classification needs to be tight
// enough to catch those — and loose enough to NOT flag the dozens of
// standard cPanel-internal symlinks every customer tarball contains
// (access-logs -> /usr/local/apache/domlogs/<user>, var/cpanel/styled/...
// -> /usr/local/cpanel/base/frontend/..., mailman, etc.).
//
// Earlier versions of this file used the panel's broader list (everything
// under /etc, /usr, /bin, /sbin, /lib, /lib64, /var/lib, /var/log,
// /var/cache, /var/spool) which made the container REFUSE every cpmove
// from a real cPanel source server — including clean ones. The panel
// could afford to be permissive in UNCERTAIN handling because it never
// actually followed the links (removeDirectory now shell-rm's, not
// recursive PHP walk). The container is supposed to QUARANTINE the truly
// destructive ones and let the rest through.
//
// Real-world dangerous prefixes (escapes/clobbers):
// / exact root — ALFA "alfasymlink/root -> /"
// /etc config tampering, /etc/shadow exfil
// /root root home dir
// /boot bootloader / kernel
// /proc process info / kernel knobs
// /sys sysfs
// /dev device nodes
//
// Notably NOT in the list (cPanel-legitimate, kept as UNCERTAIN):
// /usr/local/apache/... access logs
// /usr/local/cpanel/... UI styling, plugins, mailman
// /var/log/... per-user mail logs
// /bin, /sbin customer "fix shell" symlinks (rare but seen)
$dangerousPrefixes = [ $dangerousPrefixes = [
'/etc', '/usr', '/bin', '/sbin', '/lib', '/lib64', '/etc',
'/boot', '/root', '/root',
'/var/lib', '/var/log', '/var/cache', '/var/spool', '/boot',
'/proc',
'/sys',
'/dev',
]; ];
$findings = []; $findings = [];