1#!/usr/bin/python 2# @lint-avoid-python-3-compatibility-imports 3# 4# capable Trace security capabilitiy checks (cap_capable()). 5# For Linux, uses BCC, eBPF. Embedded C. 6# 7# USAGE: capable [-h] [-v] [-p PID] 8# 9# ToDo: add -s for kernel stacks. 10# 11# Copyright 2016 Netflix, Inc. 12# Licensed under the Apache License, Version 2.0 (the "License") 13# 14# 13-Sep-2016 Brendan Gregg Created this. 15 16from __future__ import print_function 17from bcc import BPF 18import argparse 19from time import strftime 20import ctypes as ct 21 22# arguments 23examples = """examples: 24 ./capable # trace capability checks 25 ./capable -v # verbose: include non-audit checks 26 ./capable -p 181 # only trace PID 181 27""" 28parser = argparse.ArgumentParser( 29 description="Trace security capability checks", 30 formatter_class=argparse.RawDescriptionHelpFormatter, 31 epilog=examples) 32parser.add_argument("-v", "--verbose", action="store_true", 33 help="include non-audit checks") 34parser.add_argument("-p", "--pid", 35 help="trace this PID only") 36args = parser.parse_args() 37debug = 0 38 39# capabilities to names, generated from (and will need updating): 40# awk '/^#define.CAP_.*[0-9]$/ { print " " $3 ": \"" $2 "\"," }' \ 41# include/uapi/linux/capability.h 42capabilities = { 43 0: "CAP_CHOWN", 44 1: "CAP_DAC_OVERRIDE", 45 2: "CAP_DAC_READ_SEARCH", 46 3: "CAP_FOWNER", 47 4: "CAP_FSETID", 48 5: "CAP_KILL", 49 6: "CAP_SETGID", 50 7: "CAP_SETUID", 51 8: "CAP_SETPCAP", 52 9: "CAP_LINUX_IMMUTABLE", 53 10: "CAP_NET_BIND_SERVICE", 54 11: "CAP_NET_BROADCAST", 55 12: "CAP_NET_ADMIN", 56 13: "CAP_NET_RAW", 57 14: "CAP_IPC_LOCK", 58 15: "CAP_IPC_OWNER", 59 16: "CAP_SYS_MODULE", 60 17: "CAP_SYS_RAWIO", 61 18: "CAP_SYS_CHROOT", 62 19: "CAP_SYS_PTRACE", 63 20: "CAP_SYS_PACCT", 64 21: "CAP_SYS_ADMIN", 65 22: "CAP_SYS_BOOT", 66 23: "CAP_SYS_NICE", 67 24: "CAP_SYS_RESOURCE", 68 25: "CAP_SYS_TIME", 69 26: "CAP_SYS_TTY_CONFIG", 70 27: "CAP_MKNOD", 71 28: "CAP_LEASE", 72 29: "CAP_AUDIT_WRITE", 73 30: "CAP_AUDIT_CONTROL", 74 31: "CAP_SETFCAP", 75 32: "CAP_MAC_OVERRIDE", 76 33: "CAP_MAC_ADMIN", 77 34: "CAP_SYSLOG", 78 35: "CAP_WAKE_ALARM", 79 36: "CAP_BLOCK_SUSPEND", 80 37: "CAP_AUDIT_READ", 81} 82 83# define BPF program 84bpf_text = """ 85#include <uapi/linux/ptrace.h> 86#include <linux/sched.h> 87 88struct data_t { 89 // switch to u32s when supported 90 u64 pid; 91 u64 uid; 92 int cap; 93 int audit; 94 char comm[TASK_COMM_LEN]; 95}; 96 97BPF_PERF_OUTPUT(events); 98 99int kprobe__cap_capable(struct pt_regs *ctx, const struct cred *cred, 100 struct user_namespace *targ_ns, int cap, int audit) 101{ 102 u32 pid = bpf_get_current_pid_tgid(); 103 FILTER1 104 FILTER2 105 106 u32 uid = bpf_get_current_uid_gid(); 107 struct data_t data = {.pid = pid, .uid = uid, .cap = cap, .audit = audit}; 108 bpf_get_current_comm(&data.comm, sizeof(data.comm)); 109 events.perf_submit(ctx, &data, sizeof(data)); 110 111 return 0; 112}; 113""" 114if args.pid: 115 bpf_text = bpf_text.replace('FILTER1', 116 'if (pid != %s) { return 0; }' % args.pid) 117if not args.verbose: 118 bpf_text = bpf_text.replace('FILTER2', 'if (audit == 0) { return 0; }') 119bpf_text = bpf_text.replace('FILTER1', '') 120bpf_text = bpf_text.replace('FILTER2', '') 121if debug: 122 print(bpf_text) 123 124# initialize BPF 125b = BPF(text=bpf_text) 126 127TASK_COMM_LEN = 16 # linux/sched.h 128 129class Data(ct.Structure): 130 _fields_ = [ 131 ("pid", ct.c_ulonglong), 132 ("uid", ct.c_ulonglong), 133 ("cap", ct.c_int), 134 ("audit", ct.c_int), 135 ("comm", ct.c_char * TASK_COMM_LEN) 136 ] 137 138# header 139print("%-9s %-6s %-6s %-16s %-4s %-20s %s" % ( 140 "TIME", "UID", "PID", "COMM", "CAP", "NAME", "AUDIT")) 141 142# process event 143def print_event(cpu, data, size): 144 event = ct.cast(data, ct.POINTER(Data)).contents 145 146 if event.cap in capabilities: 147 name = capabilities[event.cap] 148 else: 149 name = "?" 150 print("%-9s %-6d %-6d %-16s %-4d %-20s %d" % (strftime("%H:%M:%S"), 151 event.uid, event.pid, event.comm.decode('utf-8', 'replace'), 152 event.cap, name, event.audit)) 153 154# loop with callback to print_event 155b["events"].open_perf_buffer(print_event) 156while 1: 157 b.perf_buffer_poll() 158