-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathproc_modules.c
More file actions
126 lines (108 loc) · 3.95 KB
/
Copy pathproc_modules.c
File metadata and controls
126 lines (108 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// Retrieve virtual address for loadable kernel modules from
// /proc/modules.
//
// Kernel module addresses are masked (unless `kptr_restrict = 0`).
//
// Requires:
// - kernel.kptr_restrict = 0 (Default on Debian <= 9 systems)
//
// Leak primitive:
// Data leaked: kernel module virtual load addresses
// Kernel subsystem: kernel/module — /proc/modules
// Data structure: struct module → module_core (base address)
// Address type: virtual (kernel module text)
// Method: parsed (proc file read)
// Status: gated by design (kptr_restrict)
// Access check: m_show() checks kptr_restrict via restricted_pointer();
// requires CAP_SYSLOG
// Source:
// https://elixir.bootlin.com/linux/v6.12/source/kernel/module/procfs.c
//
// Mitigations:
// kernel.kptr_restrict >= 1 masks module addresses to 0x0.
// Bypass requires CAP_SYSLOG or CAP_SYS_ADMIN.
// ---
// <bcoles@gmail.com>
#define _GNU_SOURCE
#include "include/kasld/api.h"
#include "include/kasld/cli.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define module_range addr_range
KASLD_EXPLAIN(
"Reads kernel module virtual load addresses from /proc/modules. "
"Each line reports the module name, size, and base address. When "
"kernel.kptr_restrict is 0 (or the reader has CAP_SYSLOG), raw "
"addresses are shown. Module addresses fall in the modules region, "
"which on some architectures is at a fixed offset from kernel "
"text.");
KASLD_META("method:parsed\n"
"phase:inference\n"
"discloses:virtual\n"
"sysctl:kptr_restrict>=1\n"
"bypass:CAP_SYSLOG\n");
static struct module_range get_addr_proc_modules(void) {
FILE *f;
char *line = 0;
char *addr_buf;
size_t size = 0;
const char *path = "/proc/modules";
unsigned long module_addr = 0;
struct module_range range = {0, 0, 0};
kasld_info("reading %s ...", path);
f = kasld_fopen(path, "r");
if (f == NULL) {
perror("[-] fopen");
return range;
}
while ((getline(&line, &size, f)) != -1) {
addr_buf = strstr(line, " 0x");
if (addr_buf == NULL)
continue;
if (!kasld_addr_parse(addr_buf, 16, &module_addr, NULL) || !module_addr)
continue;
if (kasld_addr_is_module_band(module_addr)) {
if (!range.lo || module_addr < range.lo)
range.lo = module_addr;
if (module_addr > range.hi)
range.hi = module_addr;
}
}
free(line);
fclose(f);
return range;
}
int main(void) {
/* Pre-check: is /proc/modules readable? */
if (kasld_access("/proc/modules", R_OK) != 0)
return (errno == EACCES || errno == EPERM) ? KASLD_EXIT_NOPERM
: KASLD_EXIT_UNAVAILABLE;
struct module_range range = get_addr_proc_modules();
if (!range.lo) {
kasld_err("no kernel address found in /proc/modules");
return 0;
}
/* /proc/modules lists loaded module base addresses.
* The component aggregates them into a min/max range — both endpoints
* are within the module region. (A future version could enumerate
* each module by name with kasld_result_sample().) */
/* REGION_MODULE, not REGION_MODULE_BAND: each address is a loaded
* module's own base, read from a per-module record — the region is known
* structurally, not inferred from the address falling in a band. That is
* what lets module_text_bracket consume it on arches whose band is a wide
* multi-layout union (see the provenance note in api.h). */
kasld_info("lowest leaked module address: %lx", range.lo);
kasld_result_sample(KASLD_TYPE_VIRT, REGION_MODULE, range.lo, NULL,
CONF_PARSED);
if (range.hi != range.lo) {
kasld_info("highest leaked module address: %lx", range.hi);
kasld_result_sample(KASLD_TYPE_VIRT, REGION_MODULE, range.hi, NULL,
CONF_PARSED);
}
return 0;
}