mirror of https://github.com/Hilbis/Hilbish
316 lines
11 KiB
Go
316 lines
11 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/jessevdk/go-flags"
|
|
|
|
"github.com/maxlandon/readline"
|
|
)
|
|
|
|
// This file declares a go-flags parser and a few commands.
|
|
|
|
var (
|
|
// commandParser - The command parser used by the example console.
|
|
commandParser = flags.NewNamedParser("example", flags.IgnoreUnknown)
|
|
)
|
|
|
|
func bindCommands() (err error) {
|
|
|
|
// core console
|
|
// ----------------------------------------------------------------------------------------
|
|
ex, err := commandParser.AddCommand("exit", // Command string
|
|
"Exit from the client/server console", // Description (completions, help usage)
|
|
"", // Long description
|
|
&Exit{}) // Command implementation
|
|
ex.Aliases = []string{"core"}
|
|
|
|
cd, err := commandParser.AddCommand("cd",
|
|
"Change client working directory",
|
|
"",
|
|
&ChangeClientDirectory{})
|
|
cd.Aliases = []string{"core"}
|
|
|
|
ls, err := commandParser.AddCommand("ls",
|
|
"List directory contents",
|
|
"",
|
|
&ListClientDirectories{})
|
|
ls.Aliases = []string{"core"}
|
|
|
|
// Log
|
|
log, err := commandParser.AddCommand("log",
|
|
"Manage log levels of one or more components",
|
|
"",
|
|
&Log{})
|
|
log.Aliases = []string{"core"}
|
|
|
|
// Implant generation
|
|
// ----------------------------------------------------------------------------------------
|
|
g, err := commandParser.AddCommand("generate",
|
|
"Configure and compile an implant (staged or stager)",
|
|
"",
|
|
&Generate{})
|
|
g.Aliases = []string{"builds"}
|
|
g.SubcommandsOptional = true
|
|
|
|
_, err = g.AddCommand("stager",
|
|
"Generate a stager shellcode payload using MSFVenom, (to file: --save, to stdout: --format",
|
|
"",
|
|
&GenerateStager{})
|
|
|
|
r, err := commandParser.AddCommand("regenerate",
|
|
"Recompile an implant by name, passed as argument (completed)",
|
|
"",
|
|
&Regenerate{})
|
|
r.Aliases = []string{"builds"}
|
|
|
|
// Add choices completions (and therefore completions) to some of these commands.
|
|
loadArgumentCompletions(commandParser)
|
|
|
|
return
|
|
}
|
|
|
|
// Exit - Kill the current client console
|
|
type Exit struct{}
|
|
|
|
// Execute - Run
|
|
func (e *Exit) Execute(args []string) (err error) {
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
fmt.Print("Confirm exit (Y/y): ")
|
|
text, _ := reader.ReadString('\n')
|
|
answer := strings.TrimSpace(text)
|
|
|
|
if (answer == "Y") || (answer == "y") {
|
|
os.Exit(0)
|
|
}
|
|
|
|
fmt.Println()
|
|
return
|
|
}
|
|
|
|
// ChangeClientDirectory - Change the working directory of the client console
|
|
type ChangeClientDirectory struct {
|
|
Positional struct {
|
|
Path string `description:"local path" required:"1-1"`
|
|
} `positional-args:"yes" required:"yes"`
|
|
}
|
|
|
|
// Execute - Handler for ChangeDirectory
|
|
func (cd *ChangeClientDirectory) Execute(args []string) (err error) {
|
|
|
|
dir, err := expand(cd.Positional.Path)
|
|
|
|
err = os.Chdir(dir)
|
|
if err != nil {
|
|
fmt.Printf(CommandError+"%s \n", err)
|
|
} else {
|
|
fmt.Printf(Info+"Changed directory to %s \n", dir)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// ListClientDirectories - List directory contents
|
|
type ListClientDirectories struct {
|
|
Positional struct {
|
|
Path []string `description:"local directory/file"`
|
|
} `positional-args:"yes"`
|
|
}
|
|
|
|
// Execute - Command
|
|
func (ls *ListClientDirectories) Execute(args []string) error {
|
|
|
|
base := []string{"ls", "--color", "-l"}
|
|
|
|
if len(ls.Positional.Path) == 0 {
|
|
ls.Positional.Path = []string{"."}
|
|
}
|
|
|
|
fullPaths := []string{}
|
|
for _, path := range ls.Positional.Path {
|
|
full, _ := expand(path)
|
|
fullPaths = append(fullPaths, full)
|
|
}
|
|
base = append(base, fullPaths...)
|
|
|
|
// Print output
|
|
out, err := shellExec(base[0], base[1:])
|
|
if err != nil {
|
|
fmt.Printf(CommandError+"%s \n", err.Error())
|
|
return nil
|
|
}
|
|
|
|
// Print output
|
|
fmt.Println(out)
|
|
|
|
return nil
|
|
}
|
|
|
|
// shellExec - Execute a program
|
|
func shellExec(executable string, args []string) (string, error) {
|
|
path, err := exec.LookPath(executable)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
cmd := exec.Command(path, args...)
|
|
|
|
// Load OS environment
|
|
cmd.Env = os.Environ()
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return strings.Trim(string(out), "/"), nil
|
|
}
|
|
|
|
// Generate - Configure and compile an implant
|
|
type Generate struct {
|
|
StageOptions // Command makes use of full stage options
|
|
}
|
|
|
|
// StageOptions - All these options, regrouped by area, are used by any command that needs full
|
|
// configuration information for a stage Sliver implant.
|
|
type StageOptions struct {
|
|
// CoreOptions - All options about OS/arch, files to save, debugs, etc.
|
|
CoreOptions struct {
|
|
OS string `long:"os" short:"o" description:"target host operating system" default:"windows" value-name:"stage OS"`
|
|
Arch string `long:"arch" short:"a" description:"target host CPU architecture" default:"amd64" value-name:"stage architectures"`
|
|
Format string `long:"format" short:"f" description:"output formats (exe, shared (DLL), service (see 'psexec' for info), shellcode (Windows only)" default:"exe" value-name:"stage formats"`
|
|
Profile string `long:"profile-name" description:"implant profile name to use (use with generate-profile)"`
|
|
Name string `long:"name" short:"N" description:"implant name to use (overrides random name generation)"`
|
|
Save string `long:"save" short:"s" description:"directory/file where to save binary"`
|
|
Debug bool `long:"debug" short:"d" description:"enable debug features (incompatible with obfuscation, and prevailing)"`
|
|
} `group:"core options"`
|
|
|
|
// TransportOptions - All options pertaining to transport/RPC matters
|
|
TransportOptions struct {
|
|
MTLS []string `long:"mtls" short:"m" description:"mTLS C2 domain(s), comma-separated (ex: mtls://host:port)" env-delim:","`
|
|
DNS []string `long:"dns" short:"n" description:"DNS C2 domain(s), comma-separated (ex: dns://mydomain.com)" env-delim:","`
|
|
HTTP []string `long:"http" short:"h" description:"HTTP(S) C2 domain(s)" env-delim:","`
|
|
NamedPipe []string `long:"named-pipe" short:"p" description:"Named pipe transport strings, comma-separated" env-delim:","`
|
|
TCPPivot []string `long:"tcp-pivot" short:"i" description:"TCP pivot transport strings, comma-separated" env-delim:","`
|
|
Reconnect int `long:"reconnect" short:"j" description:"attempt to reconnect every n second(s)" default:"60"`
|
|
MaxErrors int `long:"max-errors" short:"k" description:"max number of transport errors" default:"10"`
|
|
} `group:"transport options"`
|
|
|
|
// SecurityOptions - All security-oriented options like restrictions.
|
|
SecurityOptions struct {
|
|
LimitDatetime string `long:"limit-datetime" short:"w" description:"limit execution to before datetime"`
|
|
LimitDomain bool `long:"limit-domain-joined" short:"D" description:"limit execution to domain joined machines"`
|
|
LimitUsername string `long:"limit-username" short:"U" description:"limit execution to specified username"`
|
|
LimitHosname string `long:"limit-hostname" short:"H" description:"limit execution to specified hostname"`
|
|
LimitFileExits string `long:"limit-file-exists" short:"F" description:"limit execution to hosts with this file in the filesystem"`
|
|
} `group:"security options"`
|
|
|
|
// EvasionOptions - All proactive security options (obfuscation, evasion, etc)
|
|
EvasionOptions struct {
|
|
Canary []string `long:"canary" short:"c" description:"DNS canary domain strings, comma-separated" env-delim:","`
|
|
SkipSymbols bool `long:"skip-obfuscation" short:"b" description:"skip binary/symbol obfuscation"`
|
|
Evasion bool `long:"evasion" short:"e" description:"enable evasion features"`
|
|
} `group:"evasion options"`
|
|
}
|
|
|
|
// Execute - Configure and compile an implant
|
|
func (g *Generate) Execute(args []string) (err error) {
|
|
save := g.CoreOptions.Save
|
|
if save == "" {
|
|
save, _ = os.Getwd()
|
|
}
|
|
|
|
fmt.Println("Executed 'generate' command. ")
|
|
return
|
|
}
|
|
|
|
// Regenerate - Recompile an implant by name, passed as argument (completed)
|
|
type Regenerate struct {
|
|
Positional struct {
|
|
ImplantName string `description:"Name of Sliver implant to recompile" required:"1-1"`
|
|
} `positional-args:"yes" required:"yes"`
|
|
Save string `long:"save" short:"s" description:"Directory/file where to save binary"`
|
|
}
|
|
|
|
// Execute - Recompile an implant with a given profile
|
|
func (r *Regenerate) Execute(args []string) (err error) {
|
|
fmt.Println("Executed 'regenerate' command. ")
|
|
return
|
|
}
|
|
|
|
// GenerateStager - Generate a stager payload using MSFVenom
|
|
type GenerateStager struct {
|
|
PayloadOptions struct {
|
|
OS string `long:"os" short:"o" description:"target host operating system" default:"windows" value-name:"stage OS"`
|
|
Arch string `long:"arch" short:"a" description:"target host CPU architecture" default:"amd64" value-name:"stage architectures"`
|
|
Format string `long:"msf-format" short:"f" description:"output format (MSF Venom formats). List is auto-completed" default:"raw" value-name:"MSF Venom transform formats"`
|
|
BadChars string `long:"badchars" short:"b" description:"bytes to exclude from stage shellcode"`
|
|
Save string `long:"save" short:"s" description:"directory to save the generated stager to"`
|
|
} `group:"payload options"`
|
|
TransportOptions struct {
|
|
LHost string `long:"lhost" short:"l" description:"listening host address" required:"true"`
|
|
LPort int `long:"lport" short:"p" description:"listening host port" default:"8443"`
|
|
Protocol string `long:"protocol" short:"P" description:"staging protocol (tcp/http/https)" default:"tcp" value-name:"stager protocol"`
|
|
} `group:"transport options"`
|
|
}
|
|
|
|
// Execute - Generate a stager payload using MSFVenom
|
|
func (g *GenerateStager) Execute(args []string) (err error) {
|
|
fmt.Println("Executed 'generate stager' subcommand. ")
|
|
return
|
|
}
|
|
|
|
// Log - Log management commands. Sets log level by default.
|
|
type Log struct {
|
|
Positional struct {
|
|
Level string `description:"log level to filter by" required:"1-1"`
|
|
Components []string `description:"components on which to apply log filter" required:"1"`
|
|
} `positional-args:"yes" required:"true"`
|
|
}
|
|
|
|
// Execute - Set the log level of one or more components
|
|
func (l *Log) Execute(args []string) (err error) {
|
|
fmt.Println("Executed 'log' command. ")
|
|
return
|
|
}
|
|
|
|
var (
|
|
Info = fmt.Sprintf("%s[-]%s ", readline.BLUE, readline.RESET)
|
|
Warn = fmt.Sprintf("%s[!]%s ", readline.YELLOW, readline.RESET)
|
|
Error = fmt.Sprintf("%s[!]%s ", readline.RED, readline.RESET)
|
|
Success = fmt.Sprintf("%s[*]%s ", readline.GREEN, readline.RESET)
|
|
|
|
Infof = fmt.Sprintf("%s[-] ", readline.BLUE) // Infof - formatted
|
|
Warnf = fmt.Sprintf("%s[!] ", readline.YELLOW) // Warnf - formatted
|
|
Errorf = fmt.Sprintf("%s[!] ", readline.RED) // Errorf - formatted
|
|
Sucessf = fmt.Sprintf("%s[*] ", readline.GREEN) // Sucessf - formatted
|
|
|
|
RPCError = fmt.Sprintf("%s[RPC Error]%s ", readline.RED, readline.RESET)
|
|
CommandError = fmt.Sprintf("%s[Command Error]%s ", readline.RED, readline.RESET)
|
|
ParserError = fmt.Sprintf("%s[Parser Error]%s ", readline.RED, readline.RESET)
|
|
DBError = fmt.Sprintf("%s[DB Error]%s ", readline.RED, readline.RESET)
|
|
)
|
|
|
|
// expand will expand a path with ~ to the $HOME of the current user.
|
|
func expand(path string) (string, error) {
|
|
if path == "" {
|
|
return path, nil
|
|
}
|
|
home := os.Getenv("HOME")
|
|
if home == "" {
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
home = usr.HomeDir
|
|
}
|
|
return filepath.Abs(strings.Replace(path, "~", home, 1))
|
|
}
|