Kernel Exploit을 공부하다보니 커널 영역의 UAF나 Heap Overflow 같은 공격이 매우 유용하다고 느꼈다. 하지만 취약한 드라이버에 있는 기능 외에 커널이나 다른 드라이버가 제공하는 기능을 이용하는 경우가 많기 때문에 폭넓은 지식이 많지 않으면 Exploit Chain을 쉽게 짜기 힘들다고 생각한다. 예를 들어 kUAF는 매우 유용하지만, UAF에서 할당되는 크기가 고정된 경우 kmalloc 크기가 일치하고 사용 가능한 오브젝트를 찾아야 한다. 이번 글에서는 그런 쓸만한 오프젝트를 알아보고 공부해보려고 한다. 하지만 Kernel Exploit을 공부한지 얼마 되지 않았기 때문에 틀린 부분이나 놓친 부분이 있을 수도 있기 때문에 찾으면 댓글로 알려주시면 매우 감사하겠습니다...(_ _)
실험 환경과 작성한 코드는 아래 리포지토리에서 받았습니다.
리눅스 커널 버전은 Linux Kernel 4.19.98을 사용했습니다.
구조체는 정말 많지만, 우선 크기별로 사용할 수 있는 구조체를 최대한 정리해보려 한다.
struct shm_file_data {
int id;
struct ipc_namespace *ns;
struct file *file;
const struct vm_operations_struct *vm_ops;
};
Size : 0x20 (kmalloc-32)
Leakable
ns, vm_ops가 커널 데이터 영역을 가리키므로 커널 주소를 Leak 가능.file이 힙 영역을 가리키므로 힙 주소를 Leak 가능.RIP Control
할당 :shmat로 공유 메모리를 맵핑하면 shm_file_data 구조체가 할당된다.
해제 :shmctl 등의 함수로 공유 메모리를 제거하면 구조체가 해제될 가능성이 있다.
참고 코드: https://elixir.bootlin.com/linux/v4.19.98/source/ipc/shm.c#L74

struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};
Size:0x20 (kmalloc-32)
Leakable
RIP Control
할당 : single_open을 사용하는 파일을 열면 할당 된다(ex: /proc/self/stat).
해제 : close 하기
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/seq_file.h#L32

struct msg_msg {
struct list_head m_list;
long m_type;
size_t m_ts; /* message text size */
struct msg_msgseg *next;
void *security;
/* the actual message follows immediately */
};
Size : 0x31〜0x1000 (kmalloc-64)
Leakable
RIP Control
할당 : msgget -> msgsnd
해제 : msgrcv
기타
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/msg.h#L9

struct subprocess_info {
struct work_struct work;
struct completion *complete;
const char *path;
char **argv;
char **envp;
struct file *file;
int wait;
int retval;
pid_t pid;
int (*init)(struct subprocess_info *info, struct cred *new);
void (*cleanup)(struct subprocess_info *info);
void *data;
} __randomize_layout;
Size : 0x60 (kmalloc-128)
Leakable
work.func가 call_usermodehelper_exec_work를 가리키고 있어서 가능RIP Control
cleanup을 조작하면 control 가능 할당 : 확인된 것은 socket(22, AF_INET,0);으로 알 수 없는 프로토콜을 지정
해제 : 할당과 같은 방법으로 해제
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/umh.h#L19


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 */
/* RCU deletion */
union {
int non_rcu; /* Can we skip RCU deletion? */
struct rcu_head rcu; /* RCU deletion hook */
};
} __randomize_layout;
Size : 0xa8 (kmalloc-192)
Leakable
session_keyring으로 유출 가능RIP Control
할당 : fork() 호출
해제 : 생성한 프로세스 종료
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/cred.h#L116
Size : ? (kmalloc-256)
Leakable
f_op이 커널의 데이터 영역을 가리키고 있기 때문에 leak 가능RIP Control
f_op을 다시 조작하고 shmctl 등을 호출하면 RIP 제어 가능. 하지만 UAF 후 file 구조체가 왜인지 overlap 되지 않아서 검증에는 실패.할당 : shmget으로 공유 메모리 생성
해제 : shmctl로 삭제
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/fs.h#L891
struct timerfd_ctx {
union {
struct hrtimer tmr;
struct alarm alarm;
} t;
ktime_t tintv;
ktime_t moffs;
wait_queue_head_t wqh;
u64 ticks;
int clockid;
short unsigned expired;
short unsigned settime_flags; /* to show in fdinfo */
struct rcu_head rcu;
struct list_head clist;
spinlock_t cancel_lock;
bool might_cancel;
};
크기: ? (kmalloc-256) base : tmr.function이 timerfd_tmrproc를 가리키고 있어 유출 가능.heap: tmr.base 등에서 유출 가능.stack: 누출되지 않음.RIP: 걸릴 것 같고 안 걸릴 것 같다.확보: timerfd_create를 호출한다.해제: tfd를 close?참고 : https://elixir.bootlin.com/linux/v4.19.98/source/fs/timerfd.c#L30
Size : ? (kmalloc-256)
Leakable
tmr.function이 timerfd_tmrproc을 가리치고 있어 leak 가능tmr.base에서 leak 가능RIP Control
할당 : timerfd_create를 호출
해제 : tfd를 close
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/fs/timerfd.c#L30

Size : 0x2e0 (kmalloc-1024)
Leakable
ops가 ptm_unix98_ops를 가리켜서 leak 가능dev, driver 등 많은 오브젝트가 힙이나 자신의 멤버를 가리켜서 leak 가능RIP Control
ops를 다시 조작해서 Control rksmd할당 : /dev/ptmx 열기
해제 : 열린 ptmx 닫기
참고 코드 : https://elixir.bootlin.com/linux/v4.19.98/source/include/linux/tty.h#L283
0x0000: 0x0000000100005401
0x0008: 0x0000000000000000
0x0010: 0xffff88800f1ed840
0x0018: 0xffffffff81e65900
0x0020: 0x0000000000000000
...
[+] kbase = 0xffffffff81000000
[+] kheap = 0xffff88800f1ed840
Press enter to continue...
[ 5.413411] BUG: unable to handle kernel paging request at 00000000deadbeef
Size : <= 65536
할당 : setxattr을 호출 후 value에 사용하고 싶은 값의 포인터를 입력 후 size 지정
해제 : 할당과 같은 방법으로 해제
userfaultfd와 함께 사용하며,msgsnd에서는 맨 앞 48byte를 다시 덮을 수 없기 때문에 이에 대응할 방법으로 유용하다. Heap Spray에도 사용 가능
크기: 임의(>=2) 확보: sendmsg를 호출하여 msg.msg_control에 포인터, msg.msg_controllen에 크기를 넣는다.해제: 확보와 동일한 경로로 해제한다.비고: setxattr과 동일하게 userfaultfd와 결합한다.
Size : >=2
할당 : sendmsg를 호출 후 msg.msg_control에 원하는 값 포인터, msg.msg_controllen에 size 지정
해제 : 할당과 같은 방법으로 해제
setxattr과 동일하게userdefaultfd와 퓨전