Detection rules › Elastic

Potential Privilege Escalation via SUID Binary

Source
github.com/elastic/protections-artifacts

Identifies instances where a process is executed with user/group ID 0 (root), and a real user/group ID that is not 0. This is indicative of a process that has been granted SUID/SGID permissions, allowing it to run with elevated privileges. Attackers may leverage a misconfiguration for exploitation in order to escalate their privileges to root, or establish a backdoor for persistence.

MITRE ATT&CK coverage

Rule body

[rule]
description = """
Identifies instances where a process is executed with user/group ID 0 (root), and a real user/group ID that is not 0.
This is indicative of a process that has been granted SUID/SGID permissions, allowing it to run with elevated
privileges. Attackers may leverage a misconfiguration for exploitation in order to escalate their privileges to root, or
establish a backdoor for persistence.
"""
id = "8863c926-4811-4a5f-bdc3-58186bf7c5bb"
license = "Elastic License v2"
name = "Potential Privilege Escalation via SUID Binary"
os_list = ["linux"]
reference = ["https://gtfobins.github.io/#+suid"]
version = "1.0.10"

query = '''
process where event.type == "start" and event.action == "exec" and (
  (process.user.id == 0 and process.real_user.id != 0) or
  (process.group.id == 0 and process.real_group.id != 0)
) and process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish") and (
  process.name in (
    "aa-exec", "ab", "agetty", "alpine", "ar", "arj", "arp", "as", "ascii-xfr", "ash", "aspell",
    "atobm", "base32", "base64", "basenc", "basez", "bc", "bridge", "busctl",
    "busybox", "bzip2", "cabal", "capsh", "choom", "chroot", "clamscan", "cmp",
    "column", "comm", "cpio", "cpulimit", "csh", "csplit", "csvtool", "cupsfilter",
    "dd", "debugfs", "dialog", "diff", "dig", "distcc",
    "dosbox", "ed", "efax", "elvish", "emacs", "eqn", "espeak", "expand", "expect",
    "fish", "fmt", "fold", "gcore", "gdb", "genie", "genisoimage", "gimp",
    "gtester", "hd", "hexdump", "highlight", "hping3", "iconv", "install",
    "ionice", "ispell", "jjs", "join", "jrunscript", "julia", "ksshell",
    "ld.so", "less", "links", "logsave", "look", "lua", "make",
    "mosquitto", "msgattrib", "msgcat", "msgconv", "msgfilter", "msgmerge", "msguniq", "multitime",
    "nasm", "ncftp", "nft", "nl", "nm", "nmap", "nohup", "ntpdate",
    "od", "openssl", "openvpn", "pandoc", "paste", "perf", "pexec", "pg", "pidstat",
    "pr", "ptx", "python", "rc", "readelf", "restic", "rlwrap", "rsync", "rtorrent",
    "rview", "sash", "scanmem", "setarch", "setlock",
    "soelim", "softlimit", "sqlite3", "ss", "ssh-agent", "ssh-keygen", "ssh-keyscan",
    "sshpass", "start-stop-daemon", "stdbuf", "strace", "sysctl", "tac",
    "taskset", "tbl", "tclsh", "tee", "tftp", "tic", "time", "troff",
    "ul", "unexpand", "unshare", "unsquashfs", "unzip", "update-alternatives", "uudecode",
    "uuencode", "vagrant", "varnishncsa", "view", "vigr", "vipw", "w3m", "watch",
    "whiptail", "xdotool", "xmodmap", "xmore", "xxd", "xz", "yash", "zsh",
    "zsoelim"
  ) or
  process.name == "ip" and (
    (
      process.args == "-force" and process.args in ("-batch", "-b")) or (process.args == "exec")
    ) or
  (process.name in ("bash", "sh", "dash", "ksh") and process.args == "-p") or
  (process.name == "php" and process.args == "-r") or
  (process.name in ("rvim", "vim", "vimdiff") and process.args == "-c") or
  (process.name == "perl" and process.args == "-e") or
  (process.name == "minicom" and process.args == "-D") or
  (process.name == "dmsetup" and process.args == "--exec") or
  (process.name == "xargs" and process.args == "-a") or
  (process.name == "wget" and process.args like "--use-askpass=*") or
  (process.name == "docker" and process.args == "run" and process.args == "chroot") or
  (process.name in ("nawk", "gawk", "mawk") and process.command_line like "*system*")
) and not (
  process.parent.name in ("spine", "sudo", "gcc") or
  process.parent.executable like~ (
    "/usr/sbin/sshd", "/usr/lib/systemd/systemd", "/etc/aide/aide.conf.d/*", "/usr/lib/apt/apt.systemd.daily",
    "/usr/libexec/gdm-wayland-session", "/usr/bin/setup-policy-routes", "/usr/libexec/gnome-terminal-server",
    "/opt/microsoft/omsagent/plugin/omsbaseline", "/usr/bin/rkhunter", "/snap/microk8s/*"
  ) or
  process.args == "/usr/local/zeek/bin/zeekctl cron" or
  process.parent.args == "/usr/bin/supervisord" or
  process.working_directory == "/usr/local/spine/bin" or
  (process.name == "sort" and process.parent.args == "/usr/lib/grub/grub-multi-install") or
  (process.name == "minicom" and process.command_line == "minicom")
)
'''

min_endpoint_version = "8.2.0"
optional_actions = []
[[actions]]
action = "kill_process"
field = "process.entity_id"
state = 0

[[actions]]
action = "kill_process"
field = "process.parent.entity_id"
state = 0

[[threat]]
framework = "MITRE ATT&CK"

[threat.tactic]
id = "TA0003"
name = "Persistence"
reference = "https://attack.mitre.org/tactics/TA0003/"
[[threat]]
framework = "MITRE ATT&CK"
[[threat.technique]]
id = "T1068"
name = "Exploitation for Privilege Escalation"
reference = "https://attack.mitre.org/techniques/T1068/"

[[threat.technique]]
id = "T1548"
name = "Abuse Elevation Control Mechanism"
reference = "https://attack.mitre.org/techniques/T1548/"
[[threat.technique.subtechnique]]
id = "T1548.001"
name = "Setuid and Setgid"
reference = "https://attack.mitre.org/techniques/T1548/001/"



[threat.tactic]
id = "TA0004"
name = "Privilege Escalation"
reference = "https://attack.mitre.org/tactics/TA0004/"

[internal]
min_endpoint_version = "8.2.0"

Stages and Predicates

Stage 1: process

process where event.type == "start" and event.action == "exec" and (
  (process.user.id == 0 and process.real_user.id != 0) or
  (process.group.id == 0 and process.real_group.id != 0)
) and process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish") and (
  process.name in (
    "aa-exec", "ab", "agetty", "alpine", "ar", "arj", "arp", "as", "ascii-xfr", "ash", "aspell",
    "atobm", "base32", "base64", "basenc", "basez", "bc", "bridge", "busctl",
    "busybox", "bzip2", "cabal", "capsh", "choom", "chroot", "clamscan", "cmp",
    "column", "comm", "cpio", "cpulimit", "csh", "csplit", "csvtool", "cupsfilter",
    "dd", "debugfs", "dialog", "diff", "dig", "distcc",
    "dosbox", "ed", "efax", "elvish", "emacs", "eqn", "espeak", "expand", "expect",
    "fish", "fmt", "fold", "gcore", "gdb", "genie", "genisoimage", "gimp",
    "gtester", "hd", "hexdump", "highlight", "hping3", "iconv", "install",
    "ionice", "ispell", "jjs", "join", "jrunscript", "julia", "ksshell",
    "ld.so", "less", "links", "logsave", "look", "lua", "make",
    "mosquitto", "msgattrib", "msgcat", "msgconv", "msgfilter", "msgmerge", "msguniq", "multitime",
    "nasm", "ncftp", "nft", "nl", "nm", "nmap", "nohup", "ntpdate",
    "od", "openssl", "openvpn", "pandoc", "paste", "perf", "pexec", "pg", "pidstat",
    "pr", "ptx", "python", "rc", "readelf", "restic", "rlwrap", "rsync", "rtorrent",
    "rview", "sash", "scanmem", "setarch", "setlock",
    "soelim", "softlimit", "sqlite3", "ss", "ssh-agent", "ssh-keygen", "ssh-keyscan",
    "sshpass", "start-stop-daemon", "stdbuf", "strace", "sysctl", "tac",
    "taskset", "tbl", "tclsh", "tee", "tftp", "tic", "time", "troff",
    "ul", "unexpand", "unshare", "unsquashfs", "unzip", "update-alternatives", "uudecode",
    "uuencode", "vagrant", "varnishncsa", "view", "vigr", "vipw", "w3m", "watch",
    "whiptail", "xdotool", "xmodmap", "xmore", "xxd", "xz", "yash", "zsh",
    "zsoelim"
  ) or
  process.name == "ip" and (
    (
      process.args == "-force" and process.args in ("-batch", "-b")) or (process.args == "exec")
    ) or
  (process.name in ("bash", "sh", "dash", "ksh") and process.args == "-p") or
  (process.name == "php" and process.args == "-r") or
  (process.name in ("rvim", "vim", "vimdiff") and process.args == "-c") or
  (process.name == "perl" and process.args == "-e") or
  (process.name == "minicom" and process.args == "-D") or
  (process.name == "dmsetup" and process.args == "--exec") or
  (process.name == "xargs" and process.args == "-a") or
  (process.name == "wget" and process.args like "--use-askpass=*") or
  (process.name == "docker" and process.args == "run" and process.args == "chroot") or
  (process.name in ("nawk", "gawk", "mawk") and process.command_line like "*system*")
) and not (
  process.parent.name in ("spine", "sudo", "gcc") or
  process.parent.executable like~ (
    "/usr/sbin/sshd", "/usr/lib/systemd/systemd", "/etc/aide/aide.conf.d/*", "/usr/lib/apt/apt.systemd.daily",
    "/usr/libexec/gdm-wayland-session", "/usr/bin/setup-policy-routes", "/usr/libexec/gnome-terminal-server",
    "/opt/microsoft/omsagent/plugin/omsbaseline", "/usr/bin/rkhunter", "/snap/microk8s/*"
  ) or
  process.args == "/usr/local/zeek/bin/zeekctl cron" or
  process.parent.args == "/usr/bin/supervisord" or
  process.working_directory == "/usr/local/spine/bin" or
  (process.name == "sort" and process.parent.args == "/usr/lib/grub/grub-multi-install") or
  (process.name == "minicom" and process.command_line == "minicom")
)

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
process.command_lineeqminicomexcludes:process.command_line field:"process.command_line" value:"minicom"
process.nameeqminicomexcludes:process.name field:"process.name" value:"minicom"
process.nameeqsortexcludes:process.name field:"process.name" value:"sort"
process.parent.argseq/usr/lib/grub/grub-multi-installexcludes:process.parent.args field:"process.parent.args" value:"/usr/lib/grub/grub-multi-install"
process.argseq/usr/local/zeek/bin/zeekctl cronexcludes:process.args field:"process.args" value:"/usr/local/zeek/bin/zeekctl cron"
process.parent.argseq/usr/bin/supervisordexcludes:process.parent.args field:"process.parent.args" value:"/usr/bin/supervisord"
process.parent.executablewildcard/usr/sbin/sshd, /usr/lib/systemd/systemd, /etc/aide/aide.conf.d/*, /usr/lib/apt/apt.systemd.daily, /usr/libexec/gdm-wayland-session, /usr/bin/setup-policy-routes, /usr/libexec/gnome-terminal-server, /opt/microsoft/omsagent/plugin/omsbaseline, /usr/bin/rkhunter, /snap/microk8s/*excludes:process.parent.executable
process.parent.nameingcc, spine, sudoexcludes:process.parent.name field:"process.parent.name" value:"gcc" field:"process.parent.name" value:"spine" field:"process.parent.name" value:"sudo"
process.working_directoryeq/usr/local/spine/binexcludes:process.working_directory field:"process.working_directory" value:"/usr/local/spine/bin"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
event.actioneq
  • exec
field:"EventType" kind:eq value:"exec"
event.typeeq
  • start
field:"event.type" kind:eq value:"start"
process.argseq
  • --exec
  • -D
  • -a
  • -c
  • -e
  • -force
  • -p
  • -r
  • chroot
  • exec
  • run
field:"process.args" kind:eq
process.argsin
  • -b
  • -batch
field:"process.args" kind:in
process.argswildcard
  • --use-askpass=*
field:"process.args" kind:wildcard value:"--use-askpass=*"
process.command_linewildcard
  • *system*
field:"CommandLine" kind:wildcard value:"*system*"
process.group.ideq
  • 0 transforms: number
field:"process.group.id" kind:eq value:"0"
process.nameeq
  • dmsetup
  • docker
  • ip
  • minicom
  • perl
  • php
  • wget
  • xargs
field:"process_name" kind:eq
process.namein
  • aa-exec
  • ab
  • agetty
  • alpine
  • ar
  • arj
  • arp
  • as
  • ascii-xfr
  • ash
  • aspell
  • atobm
  • base32
  • base64
  • basenc
  • basez
  • bash
  • bc
  • bridge
  • busctl
  • busybox
  • bzip2
  • cabal
  • capsh
  • choom
  • chroot
  • clamscan
  • cmp
  • column
  • comm
  • cpio
  • cpulimit
  • csh
  • csplit
  • csvtool
  • cupsfilter
  • dash
  • dd
  • debugfs
  • dialog
  • +133 more values (see full rule source)
field:"process_name" kind:in
process.parent.namein
  • bash
  • csh
  • dash
  • fish
  • ksh
  • sh
  • tcsh
  • zsh
field:"parent_process_name" kind:in
process.real_group.idne
  • 0 transforms: number
field:"process.real_group.id" kind:ne value:"0"
process.real_user.idne
  • 0 transforms: number
field:"process.real_user.id" kind:ne value:"0"
process.user.ideq
  • 0 transforms: number
field:"process.user.id" kind:eq value:"0"