From b3406fe7469ec1511d08d6d4c7461a3714247de9 Mon Sep 17 00:00:00 2001 From: VG Date: Tue, 8 Mar 2016 15:59:20 +0100 Subject: add demo showing privileged run from a runner only --- readme.rst | 9 ++++ run_with_inherited_caps.py | 115 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 readme.rst create mode 100755 run_with_inherited_caps.py diff --git a/readme.rst b/readme.rst new file mode 100644 index 0000000..f7820b7 --- /dev/null +++ b/readme.rst @@ -0,0 +1,9 @@ +The first example drops caps except setuid/gid, then change to a user, then +regain a specific capability. + +The second example sets the inheritable caps and drops all caps except +setuid/gid, then change to a user, then execve a program which is assumed to +have same set of inheritable caps sets in its xattrs + effective flag. Thus +the result is the launched program has only a specific capability and nobody +can automatically gain (as opposed to effective + permited file caps) the +allowed capability. Only the runner can do it. diff --git a/run_with_inherited_caps.py b/run_with_inherited_caps.py new file mode 100755 index 0000000..063860c --- /dev/null +++ b/run_with_inherited_caps.py @@ -0,0 +1,115 @@ +#!/usr/bin/python3 + +# resources for cffi: +# http://eli.thegreenplace.net/2013/03/09/python-ffi-with-ctypes-and-cffi + +import cffi +import os +import time +import pwd + +pid = os.getpid() +print('pid:', pid) + +ffi = cffi.FFI() +libcap = ffi.dlopen('libcap.so.2') +ffi.cdef(''' +typedef struct _cap_struct *cap_t; +typedef int cap_value_t; +typedef enum { + CAP_EFFECTIVE=0, + CAP_PERMITTED=1, + CAP_INHERITABLE=2 +} cap_flag_t; +typedef enum { + CAP_CLEAR=0, + CAP_SET=1 +} cap_flag_value_t; + +cap_t cap_get_proc(void); +int cap_set_flag(cap_t cap_p, cap_flag_t flag, int ncap, + const cap_value_t *caps, cap_flag_value_t value); +cap_t cap_init(void); +int cap_free(void *obj_d); +int cap_set_proc(cap_t cap_p); +''') + +CAP_EFFECTIVE = 0 +CAP_PERMITTED = 1 +CAP_INHERITABLE = 2 +CAP_CLEAR = 0 +CAP_SET = 1 + +# generated list with command line below: +# sed -n 's/^#define \(CAP_.*\)\s\+\([0-9]\+\).*$/\1 = \2/p' /usr/include/linux/capability.h +CAP_CHOWN = 0 +CAP_DAC_OVERRIDE = 1 +CAP_DAC_READ_SEARCH = 2 +CAP_FOWNER = 3 +CAP_FSETID = 4 +CAP_KILL = 5 +CAP_SETGID = 6 +CAP_SETUID = 7 +CAP_SETPCAP = 8 +CAP_LINUX_IMMUTABLE = 9 +CAP_NET_BIND_SERVICE = 10 +CAP_NET_BROADCAST = 11 +CAP_NET_ADMIN = 12 +CAP_NET_RAW = 13 +CAP_IPC_LOCK = 14 +CAP_IPC_OWNER = 15 +CAP_SYS_MODULE = 16 +CAP_SYS_RAWIO = 17 +CAP_SYS_CHROOT = 18 +CAP_SYS_PTRACE = 19 +CAP_SYS_PACCT = 20 +CAP_SYS_ADMIN = 21 +CAP_SYS_BOOT = 22 +CAP_SYS_NICE = 23 +CAP_SYS_RESOURCE = 24 +CAP_SYS_TIME = 25 +CAP_SYS_TTY_CONFIG = 26 +CAP_MKNOD = 27 +CAP_LEASE = 28 +CAP_AUDIT_WRITE = 29 +CAP_AUDIT_CONTROL = 30 +CAP_SETFCAP = 31 +CAP_MAC_OVERRIDE = 32 +CAP_MAC_ADMIN = 33 +CAP_SYSLOG = 34 +CAP_WAKE_ALARM = 35 +CAP_BLOCK_SUSPEND = 36 + + +uid, gid = 1000, 1000 + + +print("before droping caps") +os.system("cat /proc/{}/status | grep Cap".format(pid)) +os.system("cp /bin/sleep /tmp/sleep") +os.system("setcap cap_net_bind_service=ei /tmp/sleep") + +cap_values = (CAP_NET_BIND_SERVICE, ) +cap_values_temp = (CAP_SETUID, CAP_SETGID,) +ccap_values = ffi.new('cap_value_t[]', cap_values) +ccap_values_temp = ffi.new('cap_value_t[]', cap_values_temp) + +print('len cap_values:', len(cap_values)) +caps = libcap.cap_init() +libcap.cap_set_flag(caps, CAP_INHERITABLE, len(cap_values), ccap_values, CAP_SET) +libcap.cap_set_flag(caps, CAP_PERMITTED, len(cap_values_temp), ccap_values_temp, CAP_SET) +libcap.cap_set_flag(caps, CAP_EFFECTIVE, len(cap_values_temp), ccap_values_temp, CAP_SET) +libcap.cap_set_proc(caps) +libcap.cap_free(caps) + +print("after dropping caps") +os.system("cat /proc/{}/status | grep Cap".format(pid)) + +os.setgroups(os.getgrouplist(pwd.getpwuid(uid)[0], gid)) +os.setgid(gid) +os.setuid(uid) + +print("after setgid/setuid/setgroups") +os.system("cat /proc/{}/status | grep Cap".format(pid)) + +os.execve("/tmp/sleep", ["/tmp/sleep", "9999"], {}) -- cgit v1.2.3