2
2
mirror of https://github.com/Hilbis/Hilbish synced 2025-04-14 17:43:22 +00:00

chore: delete some unneeded code and files

This commit is contained in:
sammyette 2025-04-03 09:08:27 -04:00
parent 6cd294373c
commit 60edfc00ee
Signed by: sammyette
GPG Key ID: 904FC49417B44DCD
13 changed files with 0 additions and 2201 deletions

View File

@ -1,23 +0,0 @@
package completers
import (
"github.com/jessevdk/go-flags"
"github.com/maxlandon/readline"
)
// CompleteCommandArguments - Completes all values for arguments to a command.
// Arguments here are different from command options (--option).
// Many categories, from multiple sources in multiple contexts
func completeCommandArguments(cmd *flags.Command, arg string, lastWord string) (prefix string, completions []*readline.CompletionGroup) {
// the prefix is the last word, by default
prefix = lastWord
// SEE completeOptionArguments FOR A WAY TO ADD COMPLETIONS TO SPECIFIC ARGUMENTS ------------------------------
// found := argumentByName(cmd, arg)
// var comp *readline.CompletionGroup // This group is used as a buffer, to add groups to final completions
return
}

View File

@ -1,124 +0,0 @@
package completers
import (
"os"
"strings"
"github.com/maxlandon/readline"
)
// completeEnvironmentVariables - Returns all environment variables as suggestions
func completeEnvironmentVariables(lastWord string) (last string, completions []*readline.CompletionGroup) {
// Check if last input is made of several different variables
allVars := strings.Split(lastWord, "/")
lastVar := allVars[len(allVars)-1]
var evaluated = map[string]string{}
grp := &readline.CompletionGroup{
Name: "console OS environment",
MaxLength: 5, // Should be plenty enough
DisplayType: readline.TabDisplayGrid,
TrimSlash: true, // Some variables can be paths
}
for k, v := range clientEnv {
if strings.HasPrefix("$"+k, lastVar) {
grp.Suggestions = append(grp.Suggestions, "$"+k+"/")
evaluated[k] = v
}
}
completions = append(completions, grp)
return lastVar, completions
}
// clientEnv - Contains all OS environment variables, client-side.
// This is used for things like downloading/uploading files from localhost, etc.,
// therefore we need completion and parsing stuff, sometimes.
var clientEnv = map[string]string{}
// ParseEnvironmentVariables - Parses a line of input and replace detected environment variables with their values.
func ParseEnvironmentVariables(args []string) (processed []string, err error) {
for _, arg := range args {
// Anywhere a $ is assigned means there is an env variable
if strings.Contains(arg, "$") || strings.Contains(arg, "~") {
//Split in case env is embedded in path
envArgs := strings.Split(arg, "/")
// If its not a path
if len(envArgs) == 1 {
processed = append(processed, handleCuratedVar(arg))
}
// If len of the env var split is > 1, its a path
if len(envArgs) > 1 {
processed = append(processed, handleEmbeddedVar(arg))
}
} else if arg != "" && arg != " " {
// Else, if arg is not an environment variable, return it as is
processed = append(processed, arg)
}
}
return
}
// handleCuratedVar - Replace an environment variable alone and without any undesired characters attached
func handleCuratedVar(arg string) (value string) {
if strings.HasPrefix(arg, "$") && arg != "" && arg != "$" {
envVar := strings.TrimPrefix(arg, "$")
val, ok := clientEnv[envVar]
if !ok {
return envVar
}
return val
}
if arg != "" && arg == "~" {
return clientEnv["HOME"]
}
return arg
}
// handleEmbeddedVar - Replace an environment variable that is in the middle of a path, or other one-string combination
func handleEmbeddedVar(arg string) (value string) {
envArgs := strings.Split(arg, "/")
var path []string
for _, arg := range envArgs {
if strings.HasPrefix(arg, "$") && arg != "" && arg != "$" {
envVar := strings.TrimPrefix(arg, "$")
val, ok := clientEnv[envVar]
if !ok {
// Err will be caught when command is ran anyway, or completion will stop...
path = append(path, arg)
}
path = append(path, val)
} else if arg != "" && arg == "~" {
path = append(path, clientEnv["HOME"])
} else if arg != " " && arg != "" {
path = append(path, arg)
}
}
return strings.Join(path, "/")
}
// loadClientEnv - Loads all user environment variables
func loadClientEnv() error {
env := os.Environ()
for _, kv := range env {
key := strings.Split(kv, "=")[0]
value := strings.Split(kv, "=")[1]
clientEnv[key] = value
}
return nil
}

View File

@ -1,180 +0,0 @@
package completers
import (
"strings"
"github.com/jessevdk/go-flags"
"github.com/maxlandon/readline"
)
// HintCompleter - Entrypoint to all hints in the Wiregost console
func (c *CommandCompleter) HintCompleter(line []rune, pos int) (hint []rune) {
// Format and sanitize input
// @args => All items of the input line
// @last => The last word detected in input line as []rune
// @lastWord => The last word detected in input as string
args, last, lastWord := formatInput(line)
// Detect base command automatically
var command = c.detectedCommand(args)
// Menu hints (command line is empty, or nothing recognized)
if noCommandOrEmpty(args, last, command) {
hint = MenuHint(args, last)
}
// Check environment variables
if envVarAsked(args, lastWord) {
return envVarHint(args, last)
}
// Command Hint
if commandFound(command) {
// Command hint by default (no space between cursor and last command character)
hint = CommandHint(command)
// Check environment variables
if envVarAsked(args, lastWord) {
return envVarHint(args, last)
}
// If options are asked for root command, return commpletions.
if len(command.Groups()) > 0 {
for _, grp := range command.Groups() {
if opt, yes := optionArgRequired(args, last, grp); yes {
hint = OptionArgumentHint(args, last, opt)
}
}
}
// If command has args, hint for args
if arg, yes := commandArgumentRequired(lastWord, args, command); yes {
hint = []rune(CommandArgumentHints(args, last, command, arg))
}
// Brief subcommand hint
if lastIsSubCommand(lastWord, command) {
hint = []rune(commandHint + command.Find(string(last)).ShortDescription)
}
// Handle subcommand if found
if sub, ok := subCommandFound(lastWord, args, command); ok {
return HandleSubcommandHints(args, last, sub)
}
}
// Handle system binaries, shell commands, etc...
if commandFoundInPath(args[0]) {
// hint = []rune(exeHint + util.ParseSummary(util.GetManPages(args[0])))
}
return
}
// CommandHint - Yields the hint of a Wiregost command
func CommandHint(command *flags.Command) (hint []rune) {
return []rune(commandHint + command.ShortDescription)
}
// HandleSubcommandHints - Handles hints for a subcommand and its arguments, options, etc.
func HandleSubcommandHints(args []string, last []rune, command *flags.Command) (hint []rune) {
// If command has args, hint for args
if arg, yes := commandArgumentRequired(string(last), args, command); yes {
hint = []rune(CommandArgumentHints(args, last, command, arg))
return
}
// Environment variables
if envVarAsked(args, string(last)) {
hint = envVarHint(args, last)
}
// If the last word in input is an option --name, yield argument hint if needed
if len(command.Groups()) > 0 {
for _, grp := range command.Groups() {
if opt, yes := optionArgRequired(args, last, grp); yes {
hint = OptionArgumentHint(args, last, opt)
}
}
}
// If user asks for completions with "-" or "--".
// (Note: This takes precedence on any argument hints, as it is evaluated after them)
if commandOptionsAsked(args, string(last), command) {
return OptionHints(args, last, command)
}
return
}
// CommandArgumentHints - Yields hints for arguments to commands if they have some
func CommandArgumentHints(args []string, last []rune, command *flags.Command, arg string) (hint []rune) {
found := argumentByName(command, arg)
// Base Hint is just a description of the command argument
hint = []rune(argHint + found.Description)
return
}
// ModuleOptionHints - If the option being set has a description, show it
func ModuleOptionHints(opt string) (hint []rune) {
return
}
// OptionHints - Yields hints for proposed options lists/groups
func OptionHints(args []string, last []rune, command *flags.Command) (hint []rune) {
return
}
// OptionArgumentHint - Yields hints for arguments to an option (generally the last word in input)
func OptionArgumentHint(args []string, last []rune, opt *flags.Option) (hint []rune) {
return []rune(valueHint + opt.Description)
}
// MenuHint - Returns the Hint for a given menu context
func MenuHint(args []string, current []rune) (hint []rune) {
return
}
// SpecialCommandHint - Shows hints for Wiregost special commands
func SpecialCommandHint(args []string, current []rune) (hint []rune) {
return current
}
// envVarHint - Yields hints for environment variables
func envVarHint(args []string, last []rune) (hint []rune) {
// Trim last in case its a path with multiple vars
allVars := strings.Split(string(last), "/")
lastVar := allVars[len(allVars)-1]
// Base hint
hint = []rune(envHint + lastVar)
envVar := strings.TrimPrefix(lastVar, "$")
if v, ok := clientEnv[envVar]; ok {
if v != "" {
hintStr := string(hint) + " => " + clientEnv[envVar]
hint = []rune(hintStr)
}
}
return
}
var (
// Hint signs
menuHint = readline.RESET + readline.DIM + readline.BOLD + " menu " + readline.RESET // Dim
envHint = readline.RESET + readline.GREEN + readline.BOLD + " env " + readline.RESET + readline.DIM + readline.GREEN // Green
commandHint = readline.RESET + readline.DIM + readline.BOLD + " command " + readline.RESET + readline.DIM + "\033[38;5;244m" // Cream
exeHint = readline.RESET + readline.DIM + readline.BOLD + " shell " + readline.RESET + readline.DIM // Dim
optionHint = "\033[38;5;222m" + readline.BOLD + " options " + readline.RESET + readline.DIM + "\033[38;5;222m" // Cream-Yellow
valueHint = readline.RESET + readline.DIM + readline.BOLD + " value " + readline.RESET + readline.DIM + "\033[38;5;244m" // Pink-Cream
// valueHint = "\033[38;5;217m" + readline.BOLD + " Value " + readline.RESET + readline.DIM + "\033[38;5;244m" // Pink-Cream
argHint = readline.DIM + "\033[38;5;217m" + readline.BOLD + " arg " + readline.RESET + readline.DIM + "\033[38;5;244m" // Pink-Cream
)

View File

@ -1,205 +0,0 @@
package completers
import (
"io/ioutil"
"os"
"os/user"
"path/filepath"
"strings"
"github.com/maxlandon/readline"
)
func completeLocalPath(last string) (string, *readline.CompletionGroup) {
// Completions
completion := &readline.CompletionGroup{
Name: "(console) local path",
MaxLength: 10, // The grid system is not yet able to roll on comps if > MaxLength
DisplayType: readline.TabDisplayGrid,
TrimSlash: true,
}
var suggestions []string
// Any parsing error is silently ignored, for not messing the prompt
processedPath, _ := ParseEnvironmentVariables([]string{last})
// Check if processed input is empty
var inputPath string
if len(processedPath) == 1 {
inputPath = processedPath[0]
}
// Add a slash if the raw input has one but not the processed input
if len(last) > 0 && last[len(last)-1] == '/' {
inputPath += "/"
}
var linePath string // curated version of the inputPath
var absPath string // absolute path (excluding suffix) of the inputPath
var lastPath string // last directory in the input path
if strings.HasSuffix(string(inputPath), "/") {
linePath = filepath.Dir(string(inputPath))
absPath, _ = expand(string(linePath)) // Get absolute path
} else if string(inputPath) == "" {
linePath = "."
absPath, _ = expand(string(linePath))
} else {
linePath = filepath.Dir(string(inputPath))
absPath, _ = expand(string(linePath)) // Get absolute path
lastPath = filepath.Base(string(inputPath)) // Save filter
}
// 2) We take the absolute path we found, and get all dirs in it.
var dirs []string
files, _ := ioutil.ReadDir(absPath)
for _, file := range files {
if file.IsDir() {
dirs = append(dirs, file.Name())
}
}
switch lastPath {
case "":
for _, dir := range dirs {
if strings.HasPrefix(dir, lastPath) || lastPath == dir {
tokenized := addSpaceTokens(dir)
suggestions = append(suggestions, tokenized+"/")
}
}
default:
filtered := []string{}
for _, dir := range dirs {
if strings.HasPrefix(dir, lastPath) {
filtered = append(filtered, dir)
}
}
for _, dir := range filtered {
if !hasPrefix([]rune(lastPath), []rune(dir)) || lastPath == dir {
tokenized := addSpaceTokens(dir)
suggestions = append(suggestions, tokenized+"/")
}
}
}
completion.Suggestions = suggestions
return string(lastPath), completion
}
func addSpaceTokens(in string) (path string) {
items := strings.Split(in, " ")
for i := range items {
if len(items) == i+1 { // If last one, no char, add and return
path += items[i]
return
}
path += items[i] + "\\ " // By default add space char and roll
}
return
}
func completeLocalPathAndFiles(last string) (string, *readline.CompletionGroup) {
// Completions
completion := &readline.CompletionGroup{
Name: "(console) local directory/files",
MaxLength: 10, // The grid system is not yet able to roll on comps if > MaxLength
DisplayType: readline.TabDisplayGrid,
TrimSlash: true,
}
var suggestions []string
// Any parsing error is silently ignored, for not messing the prompt
processedPath, _ := ParseEnvironmentVariables([]string{last})
// Check if processed input is empty
var inputPath string
if len(processedPath) == 1 {
inputPath = processedPath[0]
}
// Add a slash if the raw input has one but not the processed input
if len(last) > 0 && last[len(last)-1] == '/' {
inputPath += "/"
}
var linePath string // curated version of the inputPath
var absPath string // absolute path (excluding suffix) of the inputPath
var lastPath string // last directory in the input path
if strings.HasSuffix(string(inputPath), "/") {
linePath = filepath.Dir(string(inputPath)) // Trim the non needed slash
absPath, _ = expand(string(linePath)) // Get absolute path
} else if string(inputPath) == "" {
linePath = "."
absPath, _ = expand(string(linePath))
} else {
linePath = filepath.Dir(string(inputPath))
absPath, _ = expand(string(linePath)) // Get absolute path
lastPath = filepath.Base(string(inputPath)) // Save filter
}
// 2) We take the absolute path we found, and get all dirs in it.
var dirs []string
files, _ := ioutil.ReadDir(absPath)
for _, file := range files {
if file.IsDir() {
dirs = append(dirs, file.Name())
}
}
switch lastPath {
case "":
for _, file := range files {
if strings.HasPrefix(file.Name(), lastPath) || lastPath == file.Name() {
if file.IsDir() {
suggestions = append(suggestions, file.Name()+"/")
} else {
suggestions = append(suggestions, file.Name())
}
}
}
default:
filtered := []os.FileInfo{}
for _, file := range files {
if strings.HasPrefix(file.Name(), lastPath) {
filtered = append(filtered, file)
}
}
for _, file := range filtered {
if !hasPrefix([]rune(lastPath), []rune(file.Name())) || lastPath == file.Name() {
if file.IsDir() {
suggestions = append(suggestions, file.Name()+"/")
} else {
suggestions = append(suggestions, file.Name())
}
}
}
}
completion.Suggestions = suggestions
return string(lastPath), completion
}
// 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))
}

View File

@ -1,77 +0,0 @@
package completers
import (
"strings"
"github.com/jessevdk/go-flags"
"github.com/maxlandon/readline"
)
// completeOptionArguments - Completes all values for arguments to a command. Arguments here are different from command options (--option).
// Many categories, from multiple sources in multiple contexts
func completeOptionArguments(cmd *flags.Command, opt *flags.Option, lastWord string) (prefix string, completions []*readline.CompletionGroup) {
// By default the last word is the prefix
prefix = lastWord
var comp *readline.CompletionGroup // This group is used as a buffer, to add groups to final completions
// First of all: some options, no matter their contexts and subject, have default values.
// When we have such an option, we don't bother analyzing context, we just build completions and return.
if len(opt.Choices) > 0 {
comp = &readline.CompletionGroup{
Name: opt.ValueName, // Value names are specified in struct metadata fields
DisplayType: readline.TabDisplayGrid,
}
for _, choice := range opt.Choices {
if strings.HasPrefix(choice, lastWord) {
comp.Suggestions = append(comp.Suggestions, choice)
}
}
completions = append(completions, comp)
return
}
// EXAMPLE OF COMPLETING ARGUMENTS BASED ON THEIR NAMES -----------------------------------------------------------------------
// We have 3 words, potentially different, with which we can filter:
//
// 1) '--option-name' is the string typed as input.
// 2) 'OptionName' is the name of the struct/type for this option.
// 3) 'ValueName' is the name of the value we expect.
// var match = func(name string) bool {
// if strings.Contains(opt.Field().Name, name) {
// return true
// }
// return false
// }
//
// // Sessions
// if match("ImplantID") || match("SessionID") {
// completions = append(completions, sessionIDs(lastWord))
// }
//
// // Any arguments with a path name. Often we "save" files that need paths, certificates, etc
// if match("Path") || match("Save") || match("Certificate") || match("PrivateKey") {
// switch cmd.Name {
// case constants.WebContentTypeStr, constants.WebUpdateStr, constants.AddWebContentStr, constants.RmWebContentStr:
// // Make an exception for WebPath option in websites commands.
// default:
// switch opt.ValueName {
// case "local-path", "path":
// prefix, comp = completeLocalPath(lastWord)
// completions = append(completions, comp)
// case "local-file", "file":
// prefix, comp = completeLocalPathAndFiles(lastWord)
// completions = append(completions, comp)
// default:
// // We always have a default searching for files, locally
// prefix, comp = completeLocalPathAndFiles(lastWord)
// completions = append(completions, comp)
// }
//
// }
// }
//
return
}

View File

@ -1,548 +0,0 @@
package completers
import (
"os/exec"
"reflect"
"strings"
"unicode"
"github.com/jessevdk/go-flags"
)
// These functions are just shorthands for checking various conditions on the input line.
// They make the main function more readable, which might be useful, should a logic error pop somewhere.
// [ Parser Commands & Options ] --------------------------------------------------------------------------
// ArgumentByName Get the name of a detected command's argument
func argumentByName(command *flags.Command, name string) *flags.Arg {
args := command.Args()
for _, arg := range args {
if arg.Name == name {
return arg
}
}
return nil
}
// optionByName - Returns an option for a command or a subcommand, identified by name
func optionByName(cmd *flags.Command, option string) *flags.Option {
if cmd == nil {
return nil
}
// Get all (root) option groups.
groups := cmd.Groups()
// For each group, build completions
for _, grp := range groups {
// Add each option to completion group
for _, opt := range grp.Options() {
if opt.LongName == option {
return opt
}
}
}
return nil
}
// [ Menus ] --------------------------------------------------------------------------------------------
// Is the input line is either empty, or without any detected command ?
func noCommandOrEmpty(args []string, last []rune, command *flags.Command) bool {
if len(args) == 0 || len(args) == 1 && command == nil {
return true
}
return false
}
// [ Commands ] -------------------------------------------------------------------------------------
// detectedCommand - Returns the base command from parser if detected, depending on context
func (c *CommandCompleter) detectedCommand(args []string) (command *flags.Command) {
arg := strings.TrimSpace(args[0])
command = c.parser.Find(arg)
return
}
// is the command a special command, usually not handled by parser ?
func isSpecialCommand(args []string, command *flags.Command) bool {
// If command is not nil, return
if command == nil {
// Shell
if args[0] == "!" {
return true
}
// Exit
if args[0] == "exit" {
return true
}
return false
}
return false
}
// The commmand has been found
func commandFound(command *flags.Command) bool {
if command != nil {
return true
}
return false
}
// Search for input in $PATH
func commandFoundInPath(input string) bool {
_, err := exec.LookPath(input)
if err != nil {
return false
}
return true
}
// [ SubCommands ]-------------------------------------------------------------------------------------
// Does the command have subcommands ?
func hasSubCommands(command *flags.Command, args []string) bool {
if len(args) < 2 || command == nil {
return false
}
if len(command.Commands()) != 0 {
return true
}
return false
}
// Does the input has a subcommand in it ?
func subCommandFound(lastWord string, raw []string, command *flags.Command) (sub *flags.Command, ok bool) {
// First, filter redundant spaces. This does not modify the actual line
args := ignoreRedundantSpaces(raw)
if len(args) <= 1 || command == nil {
return nil, false
}
sub = command.Find(args[1])
if sub != nil {
return sub, true
}
return nil, false
}
// Is the last input PRECISELY a subcommand. This is used as a brief hint for the subcommand
func lastIsSubCommand(lastWord string, command *flags.Command) bool {
if sub := command.Find(lastWord); sub != nil {
return true
}
return false
}
// [ Arguments ]-------------------------------------------------------------------------------------
// Does the command have arguments ?
func hasArgs(command *flags.Command) bool {
if len(command.Args()) != 0 {
return true
}
return false
}
// commandArgumentRequired - Analyses input and sends back the next argument name to provide completion for
func commandArgumentRequired(lastWord string, raw []string, command *flags.Command) (name string, yes bool) {
// First, filter redundant spaces. This does not modify the actual line
args := ignoreRedundantSpaces(raw)
// Trim command and subcommand args
var remain []string
if args[0] == command.Name {
remain = args[1:]
}
if len(args) > 1 && args[1] == command.Name {
remain = args[2:]
}
// The remain may include a "" as a last element,
// which we don't consider as a real remain, so we move it away
switch lastWord {
case "":
case command.Name:
return "", false
}
// Trim all --option flags and their arguments if they have
remain = filterOptions(remain, command)
// For each argument, check if needs completion. If not continue, if yes return.
// The arguments remainder is popped according to the number of values expected.
for i, arg := range command.Args() {
// If it's required and has one argument, check filled.
if arg.Required == 1 && arg.RequiredMaximum == 1 {
// If last word is the argument, and we are
// last arg in: line keep completing.
if len(remain) < 1 {
return arg.Name, true
}
// If the we are still writing the argument
if len(remain) == 1 {
if lastWord != "" {
return arg.Name, true
}
}
// If filed and we are not last arg, continue
if len(remain) > 1 && i < (len(command.Args())-1) {
remain = remain[1:]
continue
}
continue
}
// If we need more than one value and we knwo the maximum,
// either return or pop the remain.
if arg.Required > 0 && arg.RequiredMaximum > 1 {
// Pop the corresponding amount of arguments.
var found int
for i := 0; i < len(remain) && i < arg.RequiredMaximum; i++ {
remain = remain[1:]
found++
}
// If we still need values:
if len(remain) == 0 && found <= arg.RequiredMaximum {
if lastWord == "" { // We are done, no more completions.
break
} else {
return arg.Name, true
}
}
// Else go on with the next argument
continue
}
// If has required arguments, with no limit of needs, return true
if arg.Required > 0 && arg.RequiredMaximum == -1 {
return arg.Name, true
}
// Else, if no requirements and the command has subcommands,
// return so that we complete subcommands
if arg.Required == -1 && len(command.Commands()) > 0 {
continue
}
// Else, return this argument
// NOTE: This block is after because we always use []type arguments
// AFTER individual argument fields. Thus blocks any args that have
// not been processed.
if arg.Required == -1 {
return arg.Name, true
}
}
// Once we exited the loop, it means that none of the arguments require completion:
// They are all either optional, or fullfiled according to their required numbers.
// Thus we return none
return "", false
}
// getRemainingArgs - Filters the input slice from commands and detected option:value pairs, and returns args
func getRemainingArgs(args []string, last []rune, command *flags.Command) (remain []string) {
var input []string
// Clean subcommand name
if args[0] == command.Name && len(args) >= 2 {
input = args[1:]
} else if len(args) == 1 {
input = args
}
// For each each argument
for i := 0; i < len(input); i++ {
// Check option prefix
if strings.HasPrefix(input[i], "-") || strings.HasPrefix(input[i], "--") {
// Clean it
cur := strings.TrimPrefix(input[i], "--")
cur = strings.TrimPrefix(cur, "-")
// Check if option matches any command option
if opt := command.FindOptionByLongName(cur); opt != nil {
boolean := true
if opt.Field().Type == reflect.TypeOf(boolean) {
continue // If option is boolean, don't skip an argument
}
i++ // Else skip next arg in input
continue
}
}
// Safety check
if input[i] == "" || input[i] == " " {
continue
}
remain = append(remain, input[i])
}
return
}
// [ Options ]-------------------------------------------------------------------------------------
// commandOptionsAsked - Does the user asks for options in a root command ?
func commandOptionsAsked(args []string, lastWord string, command *flags.Command) bool {
if len(args) >= 2 && (strings.HasPrefix(lastWord, "-") || strings.HasPrefix(lastWord, "--")) {
return true
}
return false
}
// commandOptionsAsked - Does the user asks for options in a subcommand ?
func subCommandOptionsAsked(args []string, lastWord string, command *flags.Command) bool {
if len(args) > 2 && (strings.HasPrefix(lastWord, "-") || strings.HasPrefix(lastWord, "--")) {
return true
}
return false
}
// Is the last input argument is a dash ?
func isOptionDash(args []string, last []rune) bool {
if len(args) > 2 && (strings.HasPrefix(string(last), "-") || strings.HasPrefix(string(last), "--")) {
return true
}
return false
}
// optionIsAlreadySet - Detects in input if an option is already set
func optionIsAlreadySet(args []string, lastWord string, opt *flags.Option) bool {
return false
}
// Check if option type allows for repetition
func optionNotRepeatable(opt *flags.Option) bool {
return true
}
// [ Option Values ]-------------------------------------------------------------------------------------
// Is the last input word an option name (--option) ?
func optionArgRequired(args []string, last []rune, group *flags.Group) (opt *flags.Option, yes bool) {
var lastItem string
var lastOption string
var option *flags.Option
// If there is argument required we must have 1) command 2) --option inputs at least.
if len(args) <= 2 {
return nil, false
}
// Check for last two arguments in input
if strings.HasPrefix(args[len(args)-2], "-") || strings.HasPrefix(args[len(args)-2], "--") {
// Long opts
if strings.HasPrefix(args[len(args)-2], "--") {
lastOption = strings.TrimPrefix(args[len(args)-2], "--")
if opt := group.FindOptionByLongName(lastOption); opt != nil {
option = opt
}
// Short opts
} else if strings.HasPrefix(args[len(args)-2], "-") {
lastOption = strings.TrimPrefix(args[len(args)-2], "-")
if len(lastOption) > 0 {
if opt := group.FindOptionByShortName(rune(lastOption[0])); opt != nil {
option = opt
}
}
}
}
// If option is found, and we still are in writing the argument
if (lastItem == "" && option != nil) || option != nil {
// Check if option is a boolean, if yes return false
boolean := true
if option.Field().Type == reflect.TypeOf(boolean) {
return nil, false
}
return option, true
}
// Check for previous argument
if lastItem != "" && option == nil {
if strings.HasPrefix(args[len(args)-2], "-") || strings.HasPrefix(args[len(args)-2], "--") {
// Long opts
if strings.HasPrefix(args[len(args)-2], "--") {
lastOption = strings.TrimPrefix(args[len(args)-2], "--")
if opt := group.FindOptionByLongName(lastOption); opt != nil {
option = opt
return option, true
}
// Short opts
} else if strings.HasPrefix(args[len(args)-2], "-") {
lastOption = strings.TrimPrefix(args[len(args)-2], "-")
if opt := group.FindOptionByShortName(rune(lastOption[0])); opt != nil {
option = opt
return option, true
}
}
}
}
return nil, false
}
// [ Other ]-------------------------------------------------------------------------------------
// Does the user asks for Environment variables ?
func envVarAsked(args []string, lastWord string) bool {
// Check if the current word is an environment variable, or if the last part of it is a variable
if len(lastWord) > 1 && strings.HasPrefix(lastWord, "$") {
if strings.LastIndex(lastWord, "/") < strings.LastIndex(lastWord, "$") {
return true
}
return false
}
// Check if env var is asked in a path or something
if len(lastWord) > 1 {
// If last is a path, it cannot be an env var anymore
if lastWord[len(lastWord)-1] == '/' {
return false
}
if lastWord[len(lastWord)-1] == '$' {
return true
}
}
// If we are at the beginning of an env var
if len(lastWord) > 0 && lastWord[len(lastWord)-1] == '$' {
return true
}
return false
}
// filterOptions - Check various elements of an option and return a list
func filterOptions(args []string, command *flags.Command) (processed []string) {
for i := 0; i < len(args); i++ {
arg := args[i]
// --long-name options
if strings.HasPrefix(arg, "--") {
name := strings.TrimPrefix(arg, "--")
if opt := optionByName(command, name); opt != nil {
var boolean = true
if opt.Field().Type == reflect.TypeOf(boolean) {
continue
}
// Else skip the option argument (next item)
i++
}
continue
}
// -s short options
if strings.HasPrefix(arg, "-") {
name := strings.TrimPrefix(arg, "-")
if opt := optionByName(command, name); opt != nil {
var boolean = true
if opt.Field().Type == reflect.TypeOf(boolean) {
continue
}
// Else skip the option argument (next item)
i++
}
continue
}
processed = append(processed, arg)
}
return
}
// Other Functions -------------------------------------------------------------------------------------------------------------//
// formatInput - Formats & sanitize the command line input
func formatInput(line []rune) (args []string, last []rune, lastWord string) {
args = strings.Split(string(line), " ") // The readline input as a []string
last = trimSpaceLeft([]rune(args[len(args)-1])) // The last char in input
lastWord = string(last)
return
}
// FormatInput - Formats & sanitize the command line input
func formatInputHighlighter(line []rune) (args []string, last []rune, lastWord string) {
args = strings.SplitN(string(line), " ", -1)
last = trimSpaceLeft([]rune(args[len(args)-1])) // The last char in input
lastWord = string(last)
return
}
// ignoreRedundantSpaces - We might have several spaces between each real arguments.
// However these indivual spaces are counted as args themselves.
// For each space arg found, verify that no space args follow,
// and if some are found, delete them.
func ignoreRedundantSpaces(raw []string) (args []string) {
for i := 0; i < len(raw); i++ {
// Catch a space argument.
if raw[i] == "" {
// The arg evaulated is always kept, because we just adjusted
// the indexing to avoid the ones we don't need
// args = append(args, raw[i])
for y, next := range raw[i:] {
if next != "" {
i += y - 1
break
}
// If we come to the end while not breaking
// we push the outer loop straight to the end.
if y == len(raw[i:])-1 {
i += y
}
}
} else {
// The arg evaulated is always kept, because we just adjusted
// the indexing to avoid the ones we don't need
args = append(args, raw[i])
}
}
return
}
func trimSpaceLeft(in []rune) []rune {
firstIndex := len(in)
for i, r := range in {
if unicode.IsSpace(r) == false {
firstIndex = i
break
}
}
return in[firstIndex:]
}
func equal(a, b []rune) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
func hasPrefix(r, prefix []rune) bool {
if len(r) < len(prefix) {
return false
}
return equal(r[:len(prefix)], prefix)
}

View File

@ -1,151 +0,0 @@
package completers
import (
"fmt"
"strings"
"github.com/jessevdk/go-flags"
"github.com/maxlandon/readline"
)
// SyntaxHighlighter - Entrypoint to all input syntax highlighting in the Wiregost console
func (c *CommandCompleter) SyntaxHighlighter(input []rune) (line string) {
// Format and sanitize input
args, last, lastWord := formatInputHighlighter(input)
// Remain is all arguments that have not been highlighted, we need it for completing long commands
var remain = args
// Detect base command automatically
var command = c.detectedCommand(args)
// Return input as is
if noCommandOrEmpty(remain, last, command) {
return string(input)
}
// Base command
if commandFound(command) {
line, remain = highlightCommand(remain, command)
// SubCommand
if sub, ok := subCommandFound(lastWord, args, command); ok {
line, remain = highlightSubCommand(line, remain, sub)
}
}
line = processRemain(line, remain)
return
}
func highlightCommand(args []string, command *flags.Command) (line string, remain []string) {
line = readline.BOLD + args[0] + readline.RESET + " "
remain = args[1:]
return
}
func highlightSubCommand(input string, args []string, command *flags.Command) (line string, remain []string) {
line = input
line += readline.BOLD + args[0] + readline.RESET + " "
remain = args[1:]
return
}
func processRemain(input string, remain []string) (line string) {
// Check the last is not the last space in input
if len(remain) == 1 && remain[0] == " " {
return input
}
line = input + strings.Join(remain, " ")
// line = processEnvVars(input, remain)
return
}
// processEnvVars - Highlights environment variables. NOTE: Rewrite with logic from console/env.go
func processEnvVars(input string, remain []string) (line string) {
var processed []string
inputSlice := strings.Split(input, " ")
// Check already processed input
for _, arg := range inputSlice {
if arg == "" || arg == " " {
continue
}
if strings.HasPrefix(arg, "$") { // It is an env var.
if args := strings.Split(arg, "/"); len(args) > 1 {
for _, a := range args {
fmt.Println(a)
if strings.HasPrefix(a, "$") && a != " " { // It is an env var.
processed = append(processed, "\033[38;5;108m"+readline.DIM+a+readline.RESET)
continue
}
}
}
processed = append(processed, "\033[38;5;108m"+readline.DIM+arg+readline.RESET)
continue
}
processed = append(processed, arg)
}
// Check remaining args (non-processed)
for _, arg := range remain {
if arg == "" {
continue
}
if strings.HasPrefix(arg, "$") && arg != "$" { // It is an env var.
var full string
args := strings.Split(arg, "/")
if len(args) == 1 {
if strings.HasPrefix(args[0], "$") && args[0] != "" && args[0] != "$" { // It is an env var.
full += "\033[38;5;108m" + readline.DIM + args[0] + readline.RESET
continue
}
}
if len(args) > 1 {
var counter int
for _, arg := range args {
// If var is an env var
if strings.HasPrefix(arg, "$") && arg != "" && arg != "$" {
if counter < len(args)-1 {
full += "\033[38;5;108m" + readline.DIM + args[0] + readline.RESET + "/"
counter++
continue
}
if counter == len(args)-1 {
full += "\033[38;5;108m" + readline.DIM + args[0] + readline.RESET
counter++
continue
}
}
// Else, if we are not at the end of array
if counter < len(args)-1 && arg != "" {
full += arg + "/"
counter++
}
if counter == len(args)-1 {
full += arg
counter++
}
}
}
// Else add first var
processed = append(processed, full)
}
}
line = strings.Join(processed, " ")
// Very important, keeps the line clear when erasing
// line += " "
return
}

View File

@ -1,289 +0,0 @@
package completers
import (
"errors"
"fmt"
"strings"
"github.com/jessevdk/go-flags"
"github.com/maxlandon/readline"
)
// CommandCompleter - A completer using a github.com/jessevdk/go-flags Command Parser, in order
// to build completions for commands, arguments, options and their arguments as well.
// This completer needs to be instantiated with its constructor, in order to ensure the parser is not nil.
type CommandCompleter struct {
parser *flags.Parser
}
// NewCommandCompleter - Instantiate a new tab completer using a github.com/jessevdk/go-flags Command Parser.
func NewCommandCompleter(parser *flags.Parser) (completer *CommandCompleter, err error) {
if parser == nil {
return nil, errors.New("command completer was instantiated with a nil parser")
}
return &CommandCompleter{parser: parser}, nil
}
// TabCompleter - A default tab completer working with a github.com/jessevdk/go-flags parser.
func (c *CommandCompleter) TabCompleter(line []rune, pos int, dtc readline.DelayedTabContext) (lastWord string, completions []*readline.CompletionGroup) {
// Format and sanitize input
// @args => All items of the input line
// @last => The last word detected in input line as []rune
// @lastWord => The last word detected in input as string
args, last, lastWord := formatInput(line)
// Detect base command automatically
var command = c.detectedCommand(args)
// Propose commands
if noCommandOrEmpty(args, last, command) {
return c.completeMenuCommands(lastWord, pos)
}
// Check environment variables
if envVarAsked(args, lastWord) {
completeEnvironmentVariables(lastWord)
}
// Base command has been identified
if commandFound(command) {
// Check environment variables again
if envVarAsked(args, lastWord) {
return completeEnvironmentVariables(lastWord)
}
// If options are asked for root command, return commpletions.
if len(command.Groups()) > 0 {
for _, grp := range command.Groups() {
if opt, yes := optionArgRequired(args, last, grp); yes {
return completeOptionArguments(command, opt, lastWord)
}
}
}
// Then propose subcommands. We don't return from here, otherwise it always skips the next steps.
if hasSubCommands(command, args) {
completions = completeSubCommands(args, lastWord, command)
}
// Handle subcommand if found (maybe we should rewrite this function and use it also for base command)
if sub, ok := subCommandFound(lastWord, args, command); ok {
return handleSubCommand(line, pos, sub)
}
// If user asks for completions with "-" / "--", show command options.
// We ask this here, after having ensured there is no subcommand invoked.
// This prevails over command arguments, even if they are required.
if commandOptionsAsked(args, lastWord, command) {
return completeCommandOptions(args, lastWord, command)
}
// Propose argument completion before anything, and if needed
if arg, yes := commandArgumentRequired(lastWord, args, command); yes {
return completeCommandArguments(command, arg, lastWord)
}
}
return
}
// [ Main Completion Functions ] -----------------------------------------------------------------------------------------------------------------
// completeMenuCommands - Selects all commands available in a given context and returns them as suggestions
// Many categories, all from command parsers.
func (c *CommandCompleter) completeMenuCommands(lastWord string, pos int) (prefix string, completions []*readline.CompletionGroup) {
prefix = lastWord // We only return the PREFIX for readline to correctly show suggestions.
// Check their namespace (which should be their "group" (like utils, core, Jobs, etc))
for _, cmd := range c.parser.Commands() {
// If command matches readline input
if strings.HasPrefix(cmd.Name, lastWord) {
// Check command group: add to existing group if found
var found bool
for _, grp := range completions {
if grp.Name == cmd.Aliases[0] {
found = true
grp.Suggestions = append(grp.Suggestions, cmd.Name)
grp.Descriptions[cmd.Name] = readline.Dim(cmd.ShortDescription)
}
}
// Add a new group if not found
if !found {
grp := &readline.CompletionGroup{
Name: cmd.Aliases[0],
Suggestions: []string{cmd.Name},
Descriptions: map[string]string{
cmd.Name: readline.Dim(cmd.ShortDescription),
},
}
completions = append(completions, grp)
}
}
}
// Make adjustments to the CompletionGroup list: set maxlength depending on items, check descriptions, etc.
for _, grp := range completions {
// If the length of suggestions is too long and we have
// many groups, use grid display.
if len(completions) >= 10 && len(grp.Suggestions) >= 7 {
grp.DisplayType = readline.TabDisplayGrid
} else {
// By default, we use a map of command to descriptions
grp.DisplayType = readline.TabDisplayList
}
}
return
}
// completeSubCommands - Takes subcommands and gives them as suggestions
// One category, from one source (a parent command).
func completeSubCommands(args []string, lastWord string, command *flags.Command) (completions []*readline.CompletionGroup) {
group := &readline.CompletionGroup{
Name: command.Name,
Suggestions: []string{},
Descriptions: map[string]string{},
DisplayType: readline.TabDisplayList,
}
for _, sub := range command.Commands() {
if strings.HasPrefix(sub.Name, lastWord) {
group.Suggestions = append(group.Suggestions, sub.Name)
group.Descriptions[sub.Name] = readline.DIM + sub.ShortDescription + readline.RESET
}
}
completions = append(completions, group)
return
}
// handleSubCommand - Handles completion for subcommand options and arguments, + any option value related completion
// Many categories, from many sources: this function calls the same functions as the ones previously called for completing its parent command.
func handleSubCommand(line []rune, pos int, command *flags.Command) (lastWord string, completions []*readline.CompletionGroup) {
args, last, lastWord := formatInput(line)
// Check environment variables
if envVarAsked(args, lastWord) {
completeEnvironmentVariables(lastWord)
}
// Check argument options
if len(command.Groups()) > 0 {
for _, grp := range command.Groups() {
if opt, yes := optionArgRequired(args, last, grp); yes {
return completeOptionArguments(command, opt, lastWord)
}
}
}
// If user asks for completions with "-" or "--". This must take precedence on arguments.
if subCommandOptionsAsked(args, lastWord, command) {
return completeCommandOptions(args, lastWord, command)
}
// If command has non-filled arguments, propose them first
if arg, yes := commandArgumentRequired(lastWord, args, command); yes {
return completeCommandArguments(command, arg, lastWord)
}
return
}
// completeCommandOptions - Yields completion for options of a command, with various decorators
// Many categories, from one source (a command)
func completeCommandOptions(args []string, lastWord string, cmd *flags.Command) (prefix string, completions []*readline.CompletionGroup) {
prefix = lastWord // We only return the PREFIX for readline to correctly show suggestions.
// Get all (root) option groups.
groups := cmd.Groups()
// Append command options not gathered in groups
groups = append(groups, cmd.Group)
// For each group, build completions
for _, grp := range groups {
_, comp := completeOptionGroup(lastWord, grp, "")
// No need to add empty groups, will screw the completion system.
if len(comp.Suggestions) > 0 {
completions = append(completions, comp)
}
}
// Do the same for global options, which are not part of any group "per-se"
_, gcomp := completeOptionGroup(lastWord, cmd.Group, "global options")
if len(gcomp.Suggestions) > 0 {
completions = append(completions, gcomp)
}
return
}
// completeOptionGroup - make completions for a single group of options. Title is optional, not used if empty.
func completeOptionGroup(lastWord string, grp *flags.Group, title string) (prefix string, compGrp *readline.CompletionGroup) {
compGrp = &readline.CompletionGroup{
Name: grp.ShortDescription,
Descriptions: map[string]string{},
DisplayType: readline.TabDisplayList,
Aliases: map[string]string{},
}
// An optional title for this comp group.
// Used by global flag options, added to all commands.
if title != "" {
compGrp.Name = title
}
// Add each option to completion group
for _, opt := range grp.Options() {
// Check if option is already set, next option if yes
// if optionNotRepeatable(opt) && optionIsAlreadySet(args, lastWord, opt) {
// continue
// }
// Depending on the current last word, either build a group with option longs only, or with shorts
if strings.HasPrefix("--"+opt.LongName, lastWord) {
optName := "--" + opt.LongName
compGrp.Suggestions = append(compGrp.Suggestions, optName)
// Add short if there is, and that the prefix is only one dash
if strings.HasPrefix("-", lastWord) {
if opt.ShortName != 0 {
compGrp.Aliases[optName] = "-" + string(opt.ShortName)
}
}
// Option default value if any
var def string
if len(opt.Default) > 0 {
def = " (default:"
for _, d := range opt.Default {
def += " " + d + ","
}
def = strings.TrimSuffix(def, ",")
def += ")"
}
desc := fmt.Sprintf(" -- %s%s%s", opt.Description, def, readline.RESET)
compGrp.Descriptions[optName] = desc
}
}
return
}
// RecursiveGroupCompletion - Handles recursive completion for nested option groups
// Many categories, one source (a command's root option group). Called by the function just above.
func RecursiveGroupCompletion(args []string, last []rune, group *flags.Group) (lastWord string, completions []*readline.CompletionGroup) {
return
}

View File

@ -1,109 +0,0 @@
package main
// This file defines a few argument choices for commands
import (
"github.com/jessevdk/go-flags"
)
// Command/option argument choices
var (
// Logs & components
logLevels = []string{"trace", "debug", "info", "warning", "error"}
loggers = []string{"client", "comm"}
// Stages / Stagers
implantOS = []string{"windows", "linux", "darwin"}
implantArch = []string{"amd64", "x86"}
implantFmt = []string{"exe", "shared", "service", "shellcode"}
stageListenerProtocols = []string{"tcp", "http", "https"}
// MSF
msfStagerProtocols = []string{"tcp", "http", "https"}
msfTransformFormats = []string{
"bash",
"c",
"csharp",
"dw",
"dword",
"hex",
"java",
"js_be",
"js_le",
"num",
"perl",
"pl",
"powershell",
"ps1",
"py",
"python",
"raw",
"rb",
"ruby",
"sh",
"vbapplication",
"vbscript",
}
msfEncoders = []string{
"x86/shikata_ga_nai",
"x64/xor_dynamic",
}
msfPayloads = map[string][]string{
"windows": windowsMsfPayloads,
"linux": linuxMsfPayloads,
"osx": osxMsfPayloads,
}
// ValidPayloads - Valid payloads and OS combos
windowsMsfPayloads = []string{
"meterpreter_reverse_http",
"meterpreter_reverse_https",
"meterpreter_reverse_tcp",
"meterpreter/reverse_tcp",
"meterpreter/reverse_http",
"meterpreter/reverse_https",
}
linuxMsfPayloads = []string{
"meterpreter_reverse_http",
"meterpreter_reverse_https",
"meterpreter_reverse_tcp",
}
osxMsfPayloads = []string{
"meterpreter_reverse_http",
"meterpreter_reverse_https",
"meterpreter_reverse_tcp",
}
// Comm network protocols
portfwdProtocols = []string{"tcp", "udp"}
transportProtocols = []string{"tcp", "udp", "ip"}
applicationProtocols = []string{"http", "https", "mtls", "quic", "http3", "dns", "named_pipe"}
)
// loadArgumentCompletions - Adds a bunch of choices for command arguments (and their completions.)
func loadArgumentCompletions(parser *flags.Parser) {
if parser == nil {
return
}
serverCompsAddtional(parser)
}
// Additional completion mappings for command in the server context
func serverCompsAddtional(parser *flags.Parser) {
// Stage options
g := parser.Find("generate")
g.FindOptionByLongName("os").Choices = implantOS
g.FindOptionByLongName("arch").Choices = implantArch
g.FindOptionByLongName("format").Choices = implantFmt
// Stager options (mostly MSF)
gs := g.Find("stager")
gs.FindOptionByLongName("os").Choices = implantOS
gs.FindOptionByLongName("arch").Choices = implantArch
gs.FindOptionByLongName("protocol").Choices = msfStagerProtocols
gs.FindOptionByLongName("msf-format").Choices = msfTransformFormats
}

View File

@ -1,315 +0,0 @@
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))
}

View File

@ -1,171 +0,0 @@
package main
import (
"fmt"
"strings"
"github.com/jessevdk/go-flags"
"github.com/maxlandon/readline"
"github.com/maxlandon/readline/completers"
)
// This file shows a typical way of using readline in a loop.
func main() {
// Instantiate a console object
console := newConsole()
// Bind commands to the console
bindCommands()
// Setup the console completers, prompts, and input modes
console.setup()
// Start the readline loop (blocking)
console.Start()
}
// newConsole - Instantiates a new console with some default behavior.
// We modify/add elements of behavior later in setup.
func newConsole() *console {
console := &console{
shell: readline.NewInstance(),
parser: commandParser,
}
return console
}
// console - A simple console example.
type console struct {
shell *readline.Instance
parser *flags.Parser
}
// setup - The console sets up various elements such as the completion system, hints,
// syntax highlighting, prompt system, commands binding, and client environment loading.
func (c *console) setup() (err error) {
// Input mode & defails
c.shell.InputMode = readline.Vim // Could be readline.Emacs for emacs input mode.
c.shell.ShowVimMode = true
c.shell.VimModeColorize = true
// Prompt: we want a two-line prompt, with a custom indicator after the Vim status
c.shell.SetPrompt("readline ")
c.shell.Multiline = true
c.shell.MultilinePrompt = " > "
// Instantiate a default completer associated with the parser
// declared in commands.go, and embedded into the console struct.
// The error is muted, because we don't pass an nil parser, therefore no problems.
defaultCompleter, _ := completers.NewCommandCompleter(c.parser)
// Register the completer for command/option completions, hints and syntax highlighting.
// The completer can handle all of them.
c.shell.TabCompleter = defaultCompleter.TabCompleter
c.shell.HintText = defaultCompleter.HintCompleter
c.shell.SyntaxHighlighter = defaultCompleter.SyntaxHighlighter
// History: by default the history is in-memory, use it with Ctrl-R
return
}
// Start - The console has a working RPC connection: we setup all
// things pertaining to the console itself, and start the input loop.
func (c *console) Start() (err error) {
// Setup console elements
err = c.setup()
if err != nil {
return fmt.Errorf("Console setup failed: %s", err)
}
// Start input loop
for {
// Read input line
line, _ := c.Readline()
// Split and sanitize input
sanitized, empty := sanitizeInput(line)
if empty {
continue
}
// Process various tokens on input (environment variables, paths, etc.)
// These tokens will be expaneded by completers anyway, so this is not absolutely required.
envParsed, _ := completers.ParseEnvironmentVariables(sanitized)
// Other types of tokens, needed by commands who expect a certain type
// of arguments, such as paths with spaces.
tokenParsed := c.parseTokens(envParsed)
// Execute the command and print any errors
if _, parserErr := c.parser.ParseArgs(tokenParsed); parserErr != nil {
fmt.Println(readline.RED + "[Error] " + readline.RESET + parserErr.Error() + "\n")
}
}
}
// Readline - Add an empty line between input line and command output.
func (c *console) Readline() (line string, err error) {
line, err = c.shell.Readline()
fmt.Println()
return
}
// sanitizeInput - Trims spaces and other unwished elements from the input line.
func sanitizeInput(line string) (sanitized []string, empty bool) {
// Assume the input is not empty
empty = false
// Trim border spaces
trimmed := strings.TrimSpace(line)
if len(line) < 1 {
empty = true
return
}
unfiltered := strings.Split(trimmed, " ")
// Catch any eventual empty items
for _, arg := range unfiltered {
if arg != "" {
sanitized = append(sanitized, arg)
}
}
return
}
// parseTokens - Parse and process any special tokens that are not treated by environment-like parsers.
func (c *console) parseTokens(sanitized []string) (parsed []string) {
// PATH SPACE TOKENS
// Catch \ tokens, which have been introduced in paths where some directories have spaces in name.
// For each of these splits, we concatenate them with the next string.
// This will also inspect commands/options/arguments, but there is no reason why a backlash should be present in them.
var pathAdjusted []string
var roll bool
var arg string
for i := range sanitized {
if strings.HasSuffix(sanitized[i], "\\") {
// If we find a suffix, replace with a space. Go on with next input
arg += strings.TrimSuffix(sanitized[i], "\\") + " "
roll = true
} else if roll {
// No suffix but part of previous input. Add it and go on.
arg += sanitized[i]
pathAdjusted = append(pathAdjusted, arg)
arg = ""
roll = false
} else {
// Default, we add our path and go on.
pathAdjusted = append(pathAdjusted, sanitized[i])
}
}
parsed = pathAdjusted
// Add new function here, act on parsed []string from now on, not sanitized
return
}

View File

@ -1,9 +0,0 @@
package main
import (
rt "github.com/arnodel/golua/runtime"
)
func Loader(rtm *rt.Runtime) rt.Value {
return rt.StringValue("hello world!")
}

Binary file not shown.