スティッキービット下のexec
サンプル
[root@north dir]# cat cmd.c#include <stdio.h> #include <unistd.h> void get_id(); void main() { get_id("cmd"); } void get_id(char* name) { printf("%-7s ", name); printf("pid :%-4d ", getpid()); printf("uid :%-4d ", getuid()); printf("euid:%-4d ", geteuid()); printf("gid :%-4d ", getgid()); printf("egid:%-4d\n", getegid()); }[root@north dir]# cat fork.c
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> void get_id(char* name); void main(int argc, char *argv[]) { int pid, stat; get_id("parent"); pid = fork(); if (!pid) { get_id("child"); execlp("./cmd.out", "cmd", NULL); printf("no exec\n"); } else { waitpid(pid, &stat, 0); } } void get_id(char* name) { printf("%-7s ", name); printf("pid :%-4d ", getpid()); printf("uid :%-4d ", getuid()); printf("euid:%-4d ", geteuid()); printf("gid :%-4d ", getgid()); printf("egid:%-4d\n", getegid()); }
ユーザ/グループID
[root@north dir]# su -c id rootuid=0(root) gid=0(root) groups=0(root)
[root@north dir]# su -c id aaa
uid=200(aaa) gid=1003(aaa) groups=1003(aaa)
[root@north dir]# su -c id bbb
uid=1004(bbb) gid=1004(bbb) groups=1004(bbb)
root:x:0 :0 aaa :x:1003:1003 bbb :x:1004:1004
ファイルグループ変更
[root@north dir]# ls -l cmd.out- rwxr-xr-x 1 root root 7560 3月 8 14:07 cmd.out
[root@north dir]# chgrp bbb cmd.out
[root@north dir]# ls -l cmd.out
- rwxr-xr-x 1 root bbb 7560 3月 8 14:07 cmd.out
[root@north dir]# su -c ./fork.out aaa
parent pid :2783 uid :200 euid:200 gid :1003 egid:1003
child pid :2784 uid :200 euid:200 gid :1003 egid:1003
cmd pid :2784 uid :200 euid:200 gid :1003 egid:1003 <-cmdでのidはcmdを起動したプロセス(child)のid
S_ISUID
[root@north dir]# chmod u+s cmd.out[root@north dir]# ls -l cmd.out
- rwsr-xr-x 1 root bbb 7560 3月 8 14:34 cmd.out
parent pid :2839 uid :200 euid:200 gid :1003 egid:1003
child pid :2840 uid :200 euid:200 gid :1003 egid:1003
cmd pid :2840 uid :200 euid:0 gid :1003 egid:1003 <-euid:cmd.outユーザrootのinode->i_uid=0
S_ISGID
[root@north dir]# chmod g+xs cmd.out[root@north dir]# ls -l cmd.out
- rwsr-sr-x 1 root bbb 7560 3月 8 14:34 cmd.out
parent pid :2851 uid :200 euid:200 gid :1003 egid:1003
child pid :2852 uid :200 euid:200 gid :1003 egid:1003
cmd pid :2852 uid :200 euid:0 gid :1003 egid:1004 <-egid:cmdファイルのinode->i_gid
グループ実行不可ならS_ISGIDの効果無し
[root@north dir]# chmod g-xs cmd.out[root@north dir]# chmod g+s cmd.out
[root@north dir]# ls -l cmd.out
- rwsr-Sr-x 1 root bbb 7560 3月 8 14:34 cmd.out
parent pid :3285 uid :200 euid:200 gid :1003 egid:1003
child pid :3286 uid :200 euid:200 gid :1003 egid:1003
cmd pid :3286 uid :200 euid:0 gid :1003 egid:1003 <-g+sであってもg-xなら実行でのinode->i_gidは設定されない
捕捉
ファイル参照はeuid/egid下での実装となる。 MNT_NOSUIDのマウントファイルシステムはS_ISUID/S_ISGIDでも、inodeのidは設定されない。カーネル
struct task_struct { : const struct cred __rcu *cred; : } current; struct cred { atomic_t usage; #ifdef CONFIG_DEBUG_CREDENTIALS atomic_t subscribers; /* number of processes subscribed */ void *put_addr; unsigned magic; #define CRED_MAGIC 0x43736564 #define CRED_MAGIC_DEAD 0x44656144 #endif kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ #ifdef CONFIG_KEYS unsigned char jit_keyring; /* default keyring to attach requested * keys to */ struct key *thread_keyring; /* keyring private to this thread */ struct key *request_key_auth; /* assumed request_key authority */ struct thread_group_cred *tgcred; /* thread-group shared credentials */ #endif #ifdef CONFIG_SECURITY void *security; /* subjective LSM security */ #endif struct user_struct *user; /* real user ID subscription */ struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ struct rcu_head rcu; /* RCU deletion hook */ }; int do_execve(const char *filename, const char __user *const __user *__argv, const char __user *const __user *__envp, struct pt_regs *regs) { struct user_arg_ptr argv = { .ptr.native = __argv }; struct user_arg_ptr envp = { .ptr.native = __envp }; return do_execve_common(filename, argv, envp, regs); } static int do_execve_common(const char *filename, struct user_arg_ptr argv, struct user_arg_ptr envp, struct pt_regs *regs) { struct linux_binprm *bprm; struct file *file; struct files_struct *displaced; bool clear_in_exec; int retval; const struct cred *cred = current_cred(); if ((current->flags & PF_NPROC_EXCEEDED) && atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) { retval = -EAGAIN; goto out_ret; } current->flags &= ~PF_NPROC_EXCEEDED; retval = unshare_files(&displaced); if (retval) goto out_ret; retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) goto out_files; retval = prepare_bprm_creds(bprm); if (retval) goto out_free; retval = check_unsafe_exec(bprm); if (retval < 0) goto out_free; clear_in_exec = retval; current->in_execve = 1; file = open_exec(filename); retval = PTR_ERR(file); if (IS_ERR(file)) goto out_unmark; sched_exec(); bprm->file = file; bprm->filename = filename; bprm->interp = filename; retval = bprm_mm_init(bprm); if (retval) goto out_file; bprm->argc = count(argv, MAX_ARG_STRINGS); if ((retval = bprm->argc) < 0) goto out; bprm->envc = count(envp, MAX_ARG_STRINGS); if ((retval = bprm->envc) < 0) goto out; retval = prepare_binprm(bprm); if (retval < 0) goto out; retval = copy_strings_kernel(1, &bprm->filename, bprm); if (retval < 0) goto out; bprm->exec = bprm->p; retval = copy_strings(bprm->envc, envp, bprm); if (retval < 0) goto out; retval = copy_strings(bprm->argc, argv, bprm); if (retval < 0) goto out; retval = search_binary_handler(bprm,regs); if (retval < 0) goto out; current->fs->in_exec = 0; current->in_execve = 0; acct_update_integrals(current); free_bprm(bprm); if (displaced) put_files_struct(displaced); return retval; out: if (bprm->mm) { acct_arg_size(bprm, 0); mmput(bprm->mm); } out_file: if (bprm->file) { allow_write_access(bprm->file); fput(bprm->file); } out_unmark: if (clear_in_exec) current->fs->in_exec = 0; current->in_execve = 0; out_free: free_bprm(bprm); out_files: if (displaced) reset_files_struct(displaced); out_ret: return retval; } int prepare_binprm(struct linux_binprm *bprm) { umode_t mode; struct inode * inode = bprm->file->f_path.dentry->d_inode; int retval; mode = inode->i_mode; if (bprm->file->f_op == NULL) return -EACCES; bprm->cred->euid = current_euid(); bprm->cred->egid = current_egid(); if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && !current->no_new_privs) { if (mode & S_ISUID) { if (!kuid_has_mapping(bprm->cred->user_ns, inode->i_uid)) return -EPERM; bprm->per_clear |= PER_CLEAR_ON_SETID; bprm->cred->euid = inode->i_uid; } if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { if (!kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) return -EPERM; bprm->per_clear |= PER_CLEAR_ON_SETID; bprm->cred->egid = inode->i_gid; } } retval = security_bprm_set_creds(bprm); if (retval) return retval; bprm->cred_prepared = 1; memset(bprm->buf, 0, BINPRM_BUF_SIZE); return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE); }