...
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 |
|---|