スティッキービット下の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 root
uid=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
[root@north dir]# su -c ./fork.out aaa
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
[root@north dir]# su -c ./fork.out aaa
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
[root@north dir]]# su -c ./fork.out aaa
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);
}

最終更新 2018/03/09 15:32:58 - north
(2018/03/09 15:26:22 作成)