Skip to content

[Security] MEDIUM: Block list uses prefix-match on joined args — trivially bypassed via full path or argument reorder #4

@piyushsingariya

Description

@piyushsingariya

Summary

The block list in executor/intercept/callhook.go is implemented as:

joined := strings.Join(args, " ")
for _, pattern := range cfg.BlockList {
    if strings.HasPrefix(joined, pattern) {
        // blocked
    }
}

strings.HasPrefix on the joined argument string means the check is trivially bypassed by:

  • Using the full binary path instead of the base name
  • Reordering arguments
  • Using backslash-escaped command names

Affected code

Bypass demonstration (code-level)

BlockList: ["rm -rf /"]

BLOCKED:
  args = ["rm", "-rf", "/"]         → joined = "rm -rf /"        → HasPrefix = true ✓

NOT BLOCKED (bypasses):
  args = ["/bin/rm", "-rf", "/"]    → joined = "/bin/rm -rf /"   → HasPrefix = false ✗
  args = ["\\rm", "-rf", "/"]       → joined = "\\rm -rf /"      → HasPrefix = false ✗
  args = ["rm", "/", "-rf"]         → joined = "rm / -rf"        → HasPrefix = false ✗

Verified against Go's strings.HasPrefix semantics:

/bin/rm -rf / → blocked=False  ← BYPASS
\rm -rf /     → blocked=False  ← BYPASS
rm / -rf      → blocked=False  ← BYPASS
rm -rf /      → blocked=True   (correctly blocked)

Impact

Block lists intended as a last-resort safety net (e.g., blocking dd if=/dev/zero, mkfs, rm -rf /) can be bypassed by any caller who knows the full binary path. This gives a false sense of security to operators who configure block lists expecting them to actually block the operation.

Fix

Match filepath.Base(args[0]) separately from the arguments, or use a more structured matching approach:

// Match command basename + args separately
base := filepath.Base(args[0])
argsStr := strings.Join(args[1:], " ")

for _, pattern := range cfg.BlockList {
    parts := strings.SplitN(pattern, " ", 2)
    if base == parts[0] && (len(parts) == 1 || strings.HasPrefix(argsStr, parts[1])) {
        // blocked
    }
}

Or document explicitly that the block list is advisory/audit-only, not a security boundary.

Severity

Medium — the block list is not the primary security mechanism, but its bypass is non-obvious to operators who configure it expecting real protection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity vulnerability or audit

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions