Skip to content

Defense-in-depth: 13 path-injection findings in backup code (CodeQL/GHAS) #2

@Rene-Roscher

Description

@Rene-Roscher

Kontext

GitHub Advanced Security (CodeQL) hat in #1 13 path-injection-Findings (high severity) im Backup-Code gemeldet. Build/Test (5 Matrix-Jobs) und unser eigener Analyze (go, autobuild)-Workflow sind sauber durchgelaufen — das hier ist ein separater GHAS-Scan-Befund, der die PR-Diff gegen develop analysiert.

Risiko-Einschätzung

Aktuell nicht akut ausnutzbar. Alle betroffenen Pfade werden aus dem Backup-Identifier (UUID) gebildet, der vom Panel via JWT signiert übergeben wird. Solange der Panel-Auth-Pfad hält, ist der Identifier kontrolliert. Aber: Defense in depth fehlt — kein explizites filepath.Clean + Prefix-Containment-Check vor os.Stat / os.Remove / os.ReadDir, daher kann CodeQL die Sicherheit nicht beweisen.

Ursprung (git blame)

Findings Eingeführt durch
12 Rene-Roscher Fork-Commits 2025-09 (Backup-Restore-Erweiterungen dieses Forks)
1 backup_local.go:91 os.Remove(b.Path()) — Dane Everitt 2020-04-17 (reines Upstream-Pterodactyl-Code)

→ Das Pattern existiert auch im pterodactyl-Upstream, ist dort nur nie von einem CodeQL-Run gesehen worden.

Liste der Findings

  • server/server.go:475os.ReadDir(backupDir)
  • server/server.go:529os.Remove(filePath)
  • server/backup.go:775os.Stat(backupPath)
  • server/backup.go:786os.Open(backupPath)
  • server/backup.go:846os.Open(backupPath)
  • server/backup.go:937os.Open(backupPath)
  • server/backup.go:1044os.Stat(b.Path())
  • server/backup/backup_s3.go:98os.Stat(s.Path())
  • server/backup/backup_local.go:64os.Stat(backupPath)
  • server/backup/backup_local.go:91os.Remove(b.Path()) (upstream-Code)
  • server/backup/backup_local.go:177os.ReadDir(backupDir)
  • server/backup/backup_local.go:231os.Remove(filePath)
  • router/router_server_backup.go:453os.Stat(s3Backup.Path())

Alle Rule: go/path-injection, Severity: high.

Empfohlene Lösung

Zentrale Helper-Funktion einziehen, die für Backup-Pfade folgendes erzwingt:

func safeBackupPath(identifier string) (string, error) {
    if !validBackupIdentifier(identifier) {     // z.B. UUID-Regex
        return "", errors.New("invalid backup identifier")
    }
    clean := filepath.Clean(filepath.Join(config.BackupDirectory, identifier))
    if !strings.HasPrefix(clean, config.BackupDirectory+string(os.PathSeparator)) {
        return "", errors.New("path escapes backup directory")
    }
    return clean, nil
}

Anwenden überall wo b.Path() / backupPath direkt in os.*-Calls fließt. Erfüllt CodeQL UND liefert echte Defense-in-depth, falls jemals der JWT-Layer kompromittiert wird.

Referenzen

Priorität

Niedrig (P3) — kein akut ausnutzbarer Pfad bekannt. Adressieren wenn das Backup-Modul ohnehin angefasst wird, oder im Rahmen eines defensiven Security-Sweeps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity-related findings or hardening

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions