...
Creating a kernel module to privilege escalation
- Kernel Exploit시 반드시 알아야 하는 기본적인 함수는 prepare_kernel_cred(), commit_creds() 함수입니다.
- 이번 장에서는 해당 함수에 대한 이해와 해당 함수를 활용하여 권한상승을 일으키는 모듈을 만들어보겠습니다.
prepare_kernel_cred()
- prepare_kernel_cred()함수는 커널 서비스에 대한 자격 증명 세트 준비해당 함수는 증명을 준비하며, 다음과 같이 동작합니다.
- kmem_cache_alloc()에 의해 new 변수에 객체를 할당 합니다.
- daemon 인자의 값에 따라 다음과 같이 동작합니다.
- daemon 인자의 값이 0이 아닐 경우 get_task_cred()함수를 호출하여 전달된 프로세스의 자격 증명(credentials)을 old 변수에 저장합니다.
- daemon 인자의 값이 0일 경우 get_cred() 함수를 호출하여 init_cred 의 자격 증명(credentials)을 old 변수에 저장합니다.
validate_creds() 함수에 의해 전달된 자격 증명의 증명(old)의 유효성을 검사 합니다.
atomic_set()함수에 의해 "&new→usage" 영역에 1이 설정됩니다.
set_cred_subscribers() 함수를 이용하여 "&cred→subscribers" 영역에 0이 설정됩니다.
get_uid(), get_user_ns(), get_group_info() 새 자격 증명의 uid, user namespace, group info를 조회합니다.
security_prepare_creds()함수를 이용하여 현재 프로세스의 자격 증명을 변경합니다.
- put_cred()함수를 이용하여 현재 프로세스가 이전에 참조한 자격 증명을 해제합니다.
- validate_creds() 함수에 의해 전달된 자격 증명(new)의 유효성을 검사 합니다.
- kmem_cache_alloc()에 의해 new 변수에 객체를 할당 합니다.
Code Block | ||||
---|---|---|---|---|
Code Block | ||||
| ||||
struct cred *prepare_kernel_cred(struct task_struct *daemon) { const struct cred *old; struct cred *new; new = kmem_cache_alloc(cred_jar, GFP_KERNEL); if (!new) return NULL; kdebug("prepare_kernel_cred() alloc %p", new);subscribers if (daemon) old = get_task_cred(daemon); else old = get_cred(&init_cred); validate_creds(old); *new = *old; atomic_set(&new->usage, 1); set_cred_subscribers(new, 0); get_uid(new->user); get_user_ns(new->user_ns); get_group_info(new->group_info); #ifdef CONFIG_KEYS new->session_keyring = NULL; new->process_keyring = NULL; new->thread_keyring = NULL; new->request_key_auth = NULL; new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; #endif #ifdef CONFIG_SECURITY new->security = NULL; #endif if (security_prepare_creds(new, old, GFP_KERNEL) < 0) goto error; put_cred(old); validate_creds(new); return new; error: put_cred(new); put_cred(old); return NULL; } EXPORT_SYMBOL(prepare_kernel_cred); |
- init_cred 구조체는 다음과 같이 프로세스의 초기 자격 증명 정보를 가지고 있습니다.
- 여기서 중요한 것은 uid, gid, suid, sgid, 등의 값이 Root 권한으로 설정되어 있다는 것입니다.
- 즉, prepare_kernel_cred() 함수 호출시 인자 값으로 NULL(0)을 전달하면 Root권한의 자격 증명을 준비 할 수 있습니다.
Code Block | ||||
---|---|---|---|---|
| ||||
struct cred init_cred = { .usage = ATOMIC_INIT(4), #ifdef CONFIG_DEBUG_CREDENTIALS .subscribers = ATOMIC_INIT(2), .magic = CRED_MAGIC, #endif .uid = GLOBAL_ROOT_UID, .gid = GLOBAL_ROOT_GID, .suid = GLOBAL_ROOT_UID, .sgid = GLOBAL_ROOT_GID, .euid = GLOBAL_ROOT_UID, .egid = GLOBAL_ROOT_GID, .fsuid = GLOBAL_ROOT_UID, .fsgid = GLOBAL_ROOT_GID, .securebits = SECUREBITS_DEFAULT, .cap_inheritable = CAP_EMPTY_SET, .cap_permitted = CAP_FULL_SET, .cap_effective = CAP_FULL_SET, .cap_bset = CAP_FULL_SET, .user = INIT_USER, .user_ns = &init_user_ns, .group_info = &init_groups, }; |
Code Block | ||||
---|---|---|---|---|
| ||||
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 */
kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
struct key __rcu *session_keyring; /* keyring inherited over fork */
struct key *process_keyring; /* keyring private to this process */
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
#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 */
}; | ||||
#define GLOBAL_ROOT_UID KUIDT_INIT(0)
#define GLOBAL_ROOT_GID KGIDT_INIT(0) |
Code Block | ||||
---|---|---|---|---|
| ||||
typedef struct {
uid_t val;
} kuid_t;
typedef struct {
gid_t val;
} kgid_t;
#define KUIDT_INIT(value) (kuid_t){ value }
#define KGIDT_INIT(value) (kgid_t){ value } | ||||
Info | ||||
https://elixir.bootlin.com/linux/v4.3/source/kernel/cred.c#L594 |
commit_creds()
- 현재 작업에 새 자격 증명 설치
...
- https://en.wikipedia.org/wiki/Static_program_analysis
- https://www.kernel.org/doc/Documentation/security/credentials.txt
Panel |
---|