スティッキービット下のexec
Rev.1を表示中。最新版はこちら。
サンプル
[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);}