fix(security): close shell deny gaps for halt, su, doas, pkexec, runuser

Add missing deny patterns discovered during security audit:
- destructive_ops: halt, init/telinit 0|6, systemctl suspend/hibernate
- privilege_escalation: broaden su to block all forms (bare su, su root),
  add doas, pkexec (CVE-2021-4034), runuser
This commit is contained in:
viettranx committed 2026-03-23 10:59:26 +07:00
1 parent 0973b842a2
commit 94f6bf2445
2 files changed
+97 -3

No files matched your search

+8 -3
View File
@@ -28,8 +28,10 @@ var DenyGroupRegistry = map[string]*DenyGroup{
regexp.MustCompile(`\b(mkfs|diskpart)\b|\bformat\s`),
regexp.MustCompile(`\bdd\s+if=`),
regexp.MustCompile(`>\s*/dev/sd[a-z]\b`),
regexp.MustCompile(`\b(shutdown|reboot|poweroff)\b`),
regexp.MustCompile(`:\(\)\s*\{.*\};\s*:`), // fork bomb
regexp.MustCompile(`\b(shutdown|reboot|poweroff|halt)\b`),
regexp.MustCompile(`\b(init|telinit)\s+[06]\b`), // SysV shutdown/reboot
regexp.MustCompile(`\bsystemctl\s+(suspend|hibernate)\b`), // power management
regexp.MustCompile(`:\(\)\s*\{.*\};\s*:`), // fork bomb
},
},
"data_exfiltration": {
@@ -81,7 +83,10 @@ var DenyGroupRegistry = map[string]*DenyGroup{
Default: true,
Patterns: []*regexp.Regexp{
regexp.MustCompile(`\bsudo\b`),
regexp.MustCompile(`\bsu\s+-`),
regexp.MustCompile(`\bsu\b`),
regexp.MustCompile(`\bdoas\b`),
regexp.MustCompile(`\bpkexec\b`),
regexp.MustCompile(`\brunuser\b`),
regexp.MustCompile(`\bnsenter\b`),
regexp.MustCompile(`\bunshare\b`),
regexp.MustCompile(`\b(mount|umount)\b`),
+89
View File
@@ -1,6 +1,7 @@
package tools
import (
"regexp"
"testing"
)
@@ -49,6 +50,94 @@ func TestBase64DecodeShellDeny(t *testing.T) {
}
}
// mustDeny asserts all commands match at least one pattern.
func mustDeny(t *testing.T, patterns []*regexp.Regexp, commands ...string) {
t.Helper()
for _, cmd := range commands {
matched := false
for _, p := range patterns {
if p.MatchString(cmd) {
matched = true
break
}
}
if !matched {
t.Errorf("expected deny for %q", cmd)
}
}
}
// mustAllow asserts no command matches any pattern.
func mustAllow(t *testing.T, patterns []*regexp.Regexp, commands ...string) {
t.Helper()
for _, cmd := range commands {
for _, p := range patterns {
if p.MatchString(cmd) {
t.Errorf("unexpected deny for %q (matched %s)", cmd, p.String())
break
}
}
}
}
func TestDestructiveOpsGaps(t *testing.T) {
patterns := DenyGroupRegistry["destructive_ops"].Patterns
mustDeny(t, patterns,
// existing
"shutdown", "reboot", "poweroff",
"shutdown -h now", "reboot -f",
// new: halt
"halt", "halt -p", "systemctl halt",
// new: init/telinit
"init 0", "init 6", "telinit 0", "telinit 6",
// new: systemctl suspend/hibernate
"systemctl suspend", "systemctl hibernate",
)
mustAllow(t, patterns,
"halting the process", // "halt" inside word
"initialize", // "init" inside word
"initial setup", // "init" inside word
"init_db", // no space+digit after init
"init 1", // only 0 and 6 are blocked
"systemctl status", // not suspend/hibernate
"systemctl start nginx",
)
}
func TestPrivilegeEscalationGaps(t *testing.T) {
patterns := DenyGroupRegistry["privilege_escalation"].Patterns
mustDeny(t, patterns,
// existing
"sudo ls", "sudo -i",
// su: all forms now blocked
"su", "su -", "su root", "su -l postgres", "su admin",
// new: doas
"doas reboot", "doas ls /root", "doas -u www sh",
// new: pkexec
"pkexec vim /etc/passwd", "pkexec /bin/bash",
// new: runuser
"runuser -l postgres", "runuser -u nobody -- /bin/sh",
// existing
"nsenter --target 1", "unshare -m", "mount /dev/sda1 /mnt",
)
mustAllow(t, patterns,
"summit", // not "su"
"sugar", // not "su"
"surplus", // not "su"
"issue", // not "su"
"result", // not "su"
"resume", // not "su"
"visual", // not "su"
"sushi", // not "su"
"doaspkg", // not "doas" (no word boundary)
"pkexecute", // not "pkexec" (no word boundary)
)
}
func TestLimitedBuffer(t *testing.T) {
t.Run("under limit", func(t *testing.T) {
lb := &limitedBuffer{max: 100}