-
Notifications
You must be signed in to change notification settings - Fork 14
/
misc.go
297 lines (265 loc) · 8.92 KB
/
misc.go
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
package main
import (
"fmt"
"io/ioutil"
"os"
)
func RWFile(path string) (bool) {
// Attempts to read a file and write its contents back to the file
// Used to check for read/write permissions
// Returns whether successful
data, err := ioutil.ReadFile(path)
if err == nil {
err = ioutil.WriteFile(path, data, 0644)
if err == nil {
return true
}
}
return false
}
func SeccompMode(state *scanState) (string, error) {
if val, ok := state.ProcStatus["Seccomp"]; ok {
if val == "0" {
return "disabled", nil
} else if val == "1" {
return "strict", nil
} else if val == "2" {
return "filtered", nil
}
}
return "", fmt.Errorf("error scanning for seccomp profile")
}
func ScanMisc(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
var result []*ScanResult
var err error
// Scan Runtime
result, err = ScanRuntime(state)
if err != nil {
InfoLog.Printf("error scanning runtime: %s", err)
} else {
results = append(results, result...)
}
// Check for user namespacing
result, err = ScanUserNamespacing(state)
if err != nil {
InfoLog.Printf("error checking for user namespacing: %s", err)
} else {
results = append(results, result...)
}
// Check for proc namespacing
result, err = ScanProcessNamespacing(state)
if err != nil {
InfoLog.Printf("error checking for process namespacing: %s", err)
} else {
results = append(results, result...)
}
// Check for core_pattern
result, err = ScanProcVars(state)
if err != nil {
InfoLog.Printf("error checking for namespacing: %s", err)
} else {
results = append(results, result...)
}
// Check for kcore
result, err = ScanKcore(state)
if err != nil {
InfoLog.Printf("error checking for kcore: %s", err)
} else {
results = append(results, result...)
}
// Check for AppArmor
result, err = ScanAppArmor(state)
if err != nil {
InfoLog.Printf("error checking for AppArmor profile: %s", err)
} else {
results = append(results, result...)
}
// Check for seccomp
result, err = ScanSeccompEnabled(state)
if err != nil {
InfoLog.Printf("error checking if seccomp is enabled: %s", err)
} else {
results = append(results, result...)
}
// Scan CPUInfo
result, err = ScanProcCPUInfo(state)
if err != nil {
InfoLog.Printf("error scanning CPU info: %s", err)
} else {
results = append(results, result...)
}
// get kernel version
versionresults, err := ScanVersion(state)
if err != nil {
InfoLog.Printf("error finding kernel version: %s", err)
} else {
results = append(results, versionresults...)
}
return results, nil
}
func ScanVersion(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
data, err := ioutil.ReadFile("/proc/version")
if err != nil {
InfoLog.Printf("could not open /proc/version: %s. Trying /proc/sys/kernel/version", err)
data, err = ioutil.ReadFile("/proc/sys/kernel/version")
if err != nil {
InfoLog.Printf("could not open /proc/sys/kernel/version: %s, giving up on finding version info", err)
return results, nil
}
}
result := NewResult("Kernel Version Info", string(data), SEV_INFO)
results = append(results, result)
return results, nil
}
func ScanProcessNamespacing(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
hasNs, err := HasNamespace("pid")
if err != nil {
InfoLog.Printf("error checking pid namespacing: %s\n", err)
return results, nil
}
if !hasNs {
result := NewResult("Container not using process namespaces", "Container is not using process namespaces. This allows contained processes to interact with uncontained processes", SEV_HIGH)
results = append(results, result)
}
return results, nil
}
func ScanSeccompEnabled(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
mode, _ := SeccompMode(state)
if mode == "disabled" {
result := NewResult("Seccomp is Disabled", "Seccomp is disabled in container", SEV_LOW)
results = append(results, result)
}
return results, nil
}
func ScanProcVars(state *scanState) ([]*ScanResult, error) {
// Checks if we can read/write to various /proc files
results := make([]*ScanResult, 0)
if RWFile("/proc/sys/kernel/core_pattern") {
result := NewResult("/proc/sys/kernel/core_pattern is writable", "/proc/sys/kernel/core_pattern is writable which allows for container escape", SEV_CRITICAL)
results = append(results, result)
}
if RWFile("/proc/sys/kernel/modprobe") {
result := NewResult("/proc/sys/kernel/modprobe is writable", "/proc/sys/kernel/modprobe is writable which allows for container escape", SEV_CRITICAL)
results = append(results, result)
}
if RWFile("/proc/sys/vm/panic_on_oom") {
result := NewResult("/proc/sys/vm/panic_on_oom is writable", "/proc/sys/vm/panic_on_oom is writable which could allow a contained process to crash the host", SEV_LOW)
results = append(results, result)
}
return results, nil
}
func ScanKcore(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
// Open kcore, kmem, mem RW then confirm we can actually get data out of it
f, err := os.OpenFile("/proc/kcore", os.O_RDWR, 0644)
if err == nil {
b := make([]byte, 3)
n, err := f.Read(b)
if err == nil && n > 0 {
f.Close()
result := NewResult("/proc/kcore is writable", "/proc/kcore is writable which allows for container escape", SEV_CRITICAL)
results = append(results, result)
}
}
// kmem
f, err = os.OpenFile("/proc/kmem", os.O_RDWR, 0644)
if err == nil {
b := make([]byte, 3)
n, err := f.Read(b)
if err == nil && n > 0 {
f.Close()
result := NewResult("/proc/kmem is writable", "/proc/kmem is writable which allows for container escape", SEV_CRITICAL)
results = append(results, result)
}
}
// mem
f, err = os.OpenFile("/proc/mem", os.O_RDWR, 0644)
if err == nil {
b := make([]byte, 3)
n, err := f.Read(b)
if err == nil && n > 0 {
f.Close()
result := NewResult("/proc/mem is writable", "/proc/mem is writable which allows for container escape", SEV_CRITICAL)
results = append(results, result)
}
}
return results, nil
}
func ScanProcCPUInfo(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
bugstr := ""
isFinding := false
for _, p := range state.ProcCPUInfo {
var ok bool
var bugline string
if bugline, ok = p["bugs"]; !ok {
continue
}
var proc string
if proc, ok = p["processor"]; !ok {
proc = "??"
}
isFinding = true
var mname string
if mname, ok = p["model name"]; ok {
bugstr += fmt.Sprintf("Processor %s (%s) bugs: %s\n", proc, mname, bugline)
} else {
bugstr += fmt.Sprintf("Processor %s bugs: %s\n", proc, bugline)
}
}
if isFinding {
desc := "The following processors have bugs which may be exploited. More information about each of the processors can be found by reading /proc/cpuinfo.\n\n" + bugstr
result := NewResult("Processor Vulnerable to Hardware Attacks", desc, SEV_LOW)
results = append(results, result)
}
return results, nil
}
func ScanRuntime(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
desc := fmt.Sprintf("Container runtime: %s", state.Runtime)
result := NewResult("Detected Container Runtime", desc, SEV_INFO)
results = append(results, result)
return results, nil
}
func ScanAppArmor(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
f := readFile("/proc/self/attr/current")
if f == "unconfined" || f == "" {
result := NewResult("Container Running Unconfined AppArmor Profile",
"Container is not enforcing an AppArmor profile on contained processes",
SEV_LOW)
results = append(results, result)
} else {
desc := fmt.Sprintf("AppArmor Profile: %s", f)
result := NewResult("AppArmor Profile", desc, SEV_INFO)
results = append(results, result)
}
return results, nil
}
func ScanUserNamespacing(state *scanState) ([]*ScanResult, error) {
results := make([]*ScanResult, 0)
isNamespaced, mappings := UserNamespace()
if isNamespaced {
var desc string
if mappings == nil {
desc = "User namespacing enabled but not initialized therefore there are no mappings."
} else {
desc = "User namespacing enabled. The following mappings were detected:\n"
for _, mapping := range mappings {
desc += fmt.Sprintf("\nContainer -> %d / Host -> %d / Range -> %d", mapping.ContainerID, mapping.HostID, mapping.Range)
}
}
result := NewResult("User Namespace Mappings", desc, SEV_INFO)
results = append(results, result)
} else {
result := NewResult("User Namespace Disabled",
"User namespacing is not enabled. As a result, if a contained process is running with uid=0 it will be running as a privileged user if it gains access to resources outside of the container.",
SEV_MEDIUM)
results = append(results, result)
}
return results, nil
}