diff --git a/c2/channel/channel.go b/c2/channel/channel.go index e22b324..a91d333 100644 --- a/c2/channel/channel.go +++ b/c2/channel/channel.go @@ -80,7 +80,7 @@ func (c *Channel) AddSession(conn *net.Conn, addr string) bool { return true } -// Updates the LastSeen value for provided connection to the provided time +// Updates the LastSeen value for provided connection to the provided time. func (c *Channel) UpdateLastSeenByConn(conn net.Conn, timeStamp time.Time) bool { id, ok := c.GetSessionIDByConn(conn) if !ok { @@ -100,7 +100,7 @@ func (c *Channel) UpdateLastSeenByConn(conn net.Conn, timeStamp time.Time) bool return true } -// Returns the session ID that contains a given connection +// Returns the session ID that contains a given connection. func (c *Channel) GetSessionIDByConn(conn net.Conn) (string, bool) { if len(c.Sessions) == 0 { output.PrintFrameworkDebug("No sessions exist") @@ -119,7 +119,6 @@ func (c *Channel) GetSessionIDByConn(conn net.Conn) (string, bool) { return "", false } - // RemoveSession removes a specific session ID and if a connection exists, closes it. func (c *Channel) RemoveSession(id string) bool { if len(c.Sessions) == 0 { diff --git a/cli/commandline.go b/cli/commandline.go index 258ea06..ba8b423 100644 --- a/cli/commandline.go +++ b/cli/commandline.go @@ -15,6 +15,7 @@ import ( "github.com/vulncheck-oss/go-exploit/config" "github.com/vulncheck-oss/go-exploit/db" "github.com/vulncheck-oss/go-exploit/output" + "github.com/vulncheck-oss/go-exploit/payload" "github.com/vulncheck-oss/go-exploit/protocol" ) @@ -482,7 +483,10 @@ func printDetails(conf *config.Config) { for _, value := range conf.SupportedC2 { supportedC2Strings = append(supportedC2Strings, value.Name) } - + supportedPayloadsStrings := make([]string, 0) + for _, value := range conf.SupportedPayloads { + supportedPayloadsStrings = append(supportedPayloadsStrings, value.String()) + } customFlags := make([]CustomFlag, 0) for key, value := range conf.StringFlagsMap { customFlags = append(customFlags, CustomFlag{ @@ -519,6 +523,7 @@ func printDetails(conf *config.Config) { "VersionScanner", conf.Impl.VersionScanning, "Exploitation", conf.Impl.Exploitation, "SupportedC2", supportedC2Strings, + "SupportedPayloads", supportedPayloadsStrings, "Vendor", conf.Vendor, "Products", conf.Products, "CPE", conf.CPE, @@ -548,6 +553,7 @@ func CodeExecutionCmdLineParse(conf *config.Config) bool { exploitFunctionality(conf) sslFlags(conf) c2Flags(&c2Selection, conf) + addPayloadFlags(conf) detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit") flag.Usage = func() { @@ -612,6 +618,7 @@ func InformationDisclosureCmdLineParse(conf *config.Config) bool { localHostFlags(conf) exploitFunctionality(conf) sslFlags(conf) + addPayloadFlags(conf) detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit") flag.Usage = func() { @@ -654,6 +661,7 @@ func WebShellCmdLineParse(conf *config.Config) bool { localHostFlags(conf) exploitFunctionality(conf) sslFlags(conf) + addPayloadFlags(conf) detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit") flag.Usage = func() { @@ -726,6 +734,7 @@ func FormatFileCmdLineParse(conf *config.Config) bool { localHostFlags(conf) exploitFunctionality(conf) c2Flags(&c2Selection, conf) + addPayloadFlags(conf) detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit") flag.StringVar(&templateFile, "in", "", "The file format template to work with") flag.StringVar(&conf.FileFormatFilePath, "out", "", "The file to write the malicious file to") @@ -792,6 +801,7 @@ func LocalCmdLineParse(conf *config.Config) bool { localHostFlags(conf) exploitFunctionality(conf) c2Flags(&c2Selection, conf) + addPayloadFlags(conf) detailsFlag := flag.Bool("details", false, "Print the implementation details for this exploit") flag.Usage = func() { @@ -826,3 +836,83 @@ func LocalCmdLineParse(conf *config.Config) bool { return handleLogOptions(logFile, frameworkLogLevel, exploitLogLevel) } + +func addDefaultPayloadFlags(conf *config.Config) (string, string, map[payload.Type]int, []string, []string) { + if len(conf.SupportedPayloads) == 1 { + conf.SupportedPayloads[0].Default = payload.Default + } + hasDefault := false + defaultType := "" + defaultArch := "" + typeOptions := []string{} + archOptions := []string{} + count := map[payload.Type]int{} + for i, supported := range conf.SupportedPayloads { + switch supported.Type { + case payload.LinuxCommand, + payload.WindowsCommand, + payload.WindowsPowerShellCommand, + payload.MacCommand, + payload.GenericCommand: + _, exists := conf.StringFlagsMap["command"] + if !exists { + conf.CreateStringFlag("command", "", "Command to use for the exploit, an empty string will use the exploit default.") + } + case payload.LinuxELF, + payload.LinuxSO, + payload.WindowsEXE, + payload.WindowsDLL, + payload.Webshell: + _, exists := conf.StringFlagsMap["payload"] + if !exists { + conf.CreateStringFlag("payload", "", "Path to load custom payload from, an empty string will use the exploit default.") + } + case payload.UnspecifiedType: + output.PrintFrameworkError("Unspecified payload type used") + default: + output.PrintFrameworkError("Unexpected payload type used") + } + + count[supported.Type]++ + typeOptions = append(typeOptions, supported.Type.String()) + archOptions = append(archOptions, supported.Arch.String()) + if i == 0 && len(conf.SupportedPayloads) == 1 { + defaultType = supported.Type.String() + defaultArch = supported.Arch.String() + + continue + } + if hasDefault && supported.Default == payload.Default { + output.PrintfFrameworkWarn("Multiple default payloads selected, using the first and skipping: %s", supported.Type.String()) + + continue + } + if !hasDefault && supported.Default == payload.Default { + defaultType = supported.Type.String() + defaultArch = supported.Arch.String() + } + } + + return defaultType, defaultArch, count, typeOptions, archOptions +} + +// Adds default flags for payload types, this allows classes of payloads that are supported to +// use globally defined command line flags without having to redifine them each exploit. +func addPayloadFlags(conf *config.Config) { + if conf.PayloadFlags { + defaultType, defaultArch, count, typeOptions, archOptions := addDefaultPayloadFlags(conf) + if len(conf.SupportedPayloads) > 1 { + if defaultType == "" { + output.PrintFrameworkError("No default payload type was defined.") + } + conf.CreateStringFlag("payload-type", defaultType, "Payload type to use based on supported types: "+strings.Join(typeOptions, ", ")) + for _, v := range count { + if v > 1 { + conf.CreateStringFlag("payload-arch", defaultArch, "Payload architecture to use based on supported archs: "+strings.Join(archOptions, ", ")) + + break + } + } + } + } +} diff --git a/config/config.go b/config/config.go index 3804e7b..77f5bd1 100644 --- a/config/config.go +++ b/config/config.go @@ -10,6 +10,7 @@ import ( "github.com/vulncheck-oss/go-exploit/c2" "github.com/vulncheck-oss/go-exploit/c2/shelltunnel" "github.com/vulncheck-oss/go-exploit/output" + "github.com/vulncheck-oss/go-exploit/payload" "github.com/vulncheck-oss/go-exploit/protocol" ) @@ -67,6 +68,12 @@ type Config struct { ExType ExploitType // the c2 supported by the exploit SupportedC2 []c2.Impl + // the payload types that are supported by the exploit + SupportedPayloads []payload.Supported + // whether to parse payload flags + PayloadFlags bool + SelectedPayload payload.Supported + CustomPayload []byte // Some exploits need to define custom flags. Use the Create*Flag functions // to store them in the following data structures. They can then be fetched @@ -143,6 +150,7 @@ func New(extype ExploitType, supportedC2 []c2.Impl, product string, cve string, returnVal.Product = product returnVal.CVE = cve returnVal.Rport = defaultPort + returnVal.PayloadFlags = false return returnVal } @@ -154,6 +162,7 @@ func NewLocal(extype ExploitType, supportedC2 []c2.Impl, product string, cve str returnVal.SupportedC2 = supportedC2 returnVal.Product = product returnVal.CVE = cve + returnVal.PayloadFlags = false return returnVal } @@ -181,6 +190,7 @@ func NewRemoteExploit(implemented ImplementedFeatures, extype ExploitType, suppo newConf.CVE = cve newConf.Protocol = protocol newConf.Rport = defaultPort + newConf.PayloadFlags = false return newConf } @@ -209,6 +219,7 @@ func NewLocalExploit(implemented ImplementedFeatures, extype ExploitType, suppor newConf.C2AutoStart = true newConf.CPE = cpe newConf.CVE = cve + newConf.PayloadFlags = false return newConf } @@ -224,6 +235,10 @@ func (conf *Config) InitFlagsStructs() { // store the result locally. func (conf *Config) CreateStringFlag(name string, value string, usage string) { valuePtr := &value + _, exists := conf.StringFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } conf.StringFlagsMap[name] = valuePtr flag.StringVar(conf.StringFlagsMap[name], name, value, usage) } @@ -231,6 +246,10 @@ func (conf *Config) CreateStringFlag(name string, value string, usage string) { // Create a command line flag for the string var "name" with the default value of "value" and // store the result locally *using an external "param" pointer*. func (conf *Config) CreateStringVarFlag(param *string, name string, value string, usage string) { + _, exists := conf.StringFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } conf.StringFlagsMap[name] = param flag.StringVar(param, name, value, usage) } @@ -238,6 +257,10 @@ func (conf *Config) CreateStringVarFlag(param *string, name string, value string // Create a command line flag for the uint var "name" with the default value of "value" and // store the result locally. func (conf *Config) CreateUintFlag(name string, value uint, usage string) { + _, exists := conf.UintFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } valuePtr := &value conf.UintFlagsMap[name] = valuePtr flag.UintVar(conf.UintFlagsMap[name], name, value, usage) @@ -246,6 +269,10 @@ func (conf *Config) CreateUintFlag(name string, value uint, usage string) { // Create a command line flag for the uint var "name" with the default value of "value" and // store the result locally *using an external "param" pointer*. func (conf *Config) CreateUintVarFlag(param *uint, name string, value uint, usage string) { + _, exists := conf.UintFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } conf.UintFlagsMap[name] = param flag.UintVar(param, name, value, usage) } @@ -253,6 +280,10 @@ func (conf *Config) CreateUintVarFlag(param *uint, name string, value uint, usag // Create a command line flag for the int var "name" with the default value of "value" and // store the result locally. func (conf *Config) CreateIntFlag(name string, value int, usage string) { + _, exists := conf.IntFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } valuePtr := &value conf.IntFlagsMap[name] = valuePtr flag.IntVar(conf.IntFlagsMap[name], name, value, usage) @@ -261,6 +292,10 @@ func (conf *Config) CreateIntFlag(name string, value int, usage string) { // Create a command line flag for the int var "name" with the default value of "value" and // store the result locally *using an external "param" pointer*. func (conf *Config) CreateIntVarFlag(param *int, name string, value int, usage string) { + _, exists := conf.IntFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } conf.IntFlagsMap[name] = param flag.IntVar(param, name, value, usage) } @@ -268,6 +303,10 @@ func (conf *Config) CreateIntVarFlag(param *int, name string, value int, usage s // Create a command line flag for the bool var "name" with the default value of "value" and // store the result locally. func (conf *Config) CreateBoolFlag(name string, value bool, usage string) { + _, exists := conf.BoolFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } valuePtr := &value conf.BoolFlagsMap[name] = valuePtr flag.BoolVar(conf.BoolFlagsMap[name], name, value, usage) @@ -276,6 +315,10 @@ func (conf *Config) CreateBoolFlag(name string, value bool, usage string) { // Create a command line flag for the bool var "name" with the default value of "value" and // store the result locally *using an external "param" pointer*. func (conf *Config) CreateBoolVarFlag(param *bool, name string, value bool, usage string) { + _, exists := conf.BoolFlagsMap[name] + if exists { + output.PrintfFrameworkWarn("Command line flag '%s' already defined, flag may have unexpected effects", name) + } conf.BoolFlagsMap[name] = param flag.BoolVar(param, name, value, usage) } @@ -403,3 +446,101 @@ func (conf *Config) ResolveC2Payload() c2.Impl { return c2.SimpleShellServer } + +// AddPayload provides hints to an exploit about types of payloads that are +// supported and then sets up payload command line flags to allow user defined +// payload options. +// +// By default adding support for a payload sets up payload flags. This allows +// an exploit to automatically gain support for custom commands or files for +// payloads. See payload.Supported for details about which payload type adds +// which flags. +// +// Basic usage of adding support for specific payloads can be seen below: +// +// supportedPayload := []payload.Supported{ +// { +// Types: payload.GenericCommand, +// Arch: payload.None, +// Effects: payload.NoEffects, +// Default: true, +// }, +// { +// Types: payload.LinuxELF, +// Arch: payload.AMD64, +// Effects: payload.UnknownEffects, +// }, +// { +// Types: payload.LinuxELF, +// Arch: payload.ARM64, +// Effects: payload.Effects{ +// payload.FileCreate: []string{"/var/www/html/pwnt", "/var/www/html/pwnt2"}, +// }, +// }, +// } +// +// conf := config.NewRemoteExploit( +// config.ImplementedFeatures{AssetDetection: true, VersionScanning: false, Exploitation: true}, +// config.CodeExecution, supportedC2, +// "", []string{""}, +// []string{""}, "CVE-2023-28324", "HTTP", 80) +// conf.AddPayload(supportedPayload[:]...) +// +// Usage of payload supproted options requires some modification to payload +// generation logic to check for a custom payload, similarly to selecting C2 +// types. Below shows the payload selection type will automatically populate +// config.CustomPayload with the contents of the payload specific flag: +// +// switch conf.SelectedPayload.Types { +// case payload.GenericCommand: +// output.PrintfStatus("adding GenericCommand") +// if conf.HasCustomPayload() { +// output.PrintfStatus("using '%s' in place of default", string(conf.CustomPayload)) +// } +// // continue with non-custom payload generation +// case payload.LinuxELF: +// output.PrintfStatus("adding LinuxELF") +// if conf.HasCustomPayload() { +// output.PrintfStatus("using binary len %d in place of default", len(conf.CustomPayload)) +// } +// // continue with non-custom payload generation +// } +// +// Alternatively, simple payloads can utilize the payload type options +// during payload generations to substitute in the custom payloads by using +// the type specific checks: +// +// if conf.HasCustomPayload() { +// if conf.SelectedPayload.Type.IsCommand() { +// output.PrintfStatus("using '%s' in place of default", string(conf.CustomPayload)) +// } else { +// output.PrintfStatus("using binary len %d in place of default", len(string(conf.CustomPayload))) +// } +// } +func (conf *Config) AddPayload(p ...payload.Supported) { + conf.PayloadFlags = true + hasDefault := false + for _, pl := range p { + if pl.Default { + if hasDefault { + output.PrintFrameworkError("Cannot have multiple default payloads") + + return + } + conf.SelectedPayload = pl + hasDefault = true + } + conf.SupportedPayloads = append(conf.SupportedPayloads, pl) + } +} + +// HasCustomPayload checks if the supported payload has a custom flag defined and the data is +// populated by the framework. This is useful for payload generation wanting to explicitly +// check for if a user has provided a payload. +func (conf *Config) HasCustomPayload() bool { + if len(conf.SupportedPayloads) == 0 { + return false + } + + return len(conf.CustomPayload) > 0 +} diff --git a/dotnet/dotnetgadget.go b/dotnet/dotnetgadget.go index 3224263..f576c4e 100644 --- a/dotnet/dotnetgadget.go +++ b/dotnet/dotnetgadget.go @@ -166,7 +166,7 @@ func CreateAxHostStateDLL(dllBytes []byte, formatter string) (string, bool) { } } -// Serves a DLL in memory, used by CreateAxHostStateDLL +// Serves a DLL in memory, used by CreateAxHostStateDLL. func CreateDLLReflection(dllBytes []byte, formatter string) (string, bool) { // This one is so large that it makes more sense to just build the "final" gadget as we go, so that's what is going to happen with this one. var finalGadget string diff --git a/framework.go b/framework.go index 211dd3d..2cc3692 100644 --- a/framework.go +++ b/framework.go @@ -78,6 +78,7 @@ import ( "github.com/vulncheck-oss/go-exploit/config" "github.com/vulncheck-oss/go-exploit/db" "github.com/vulncheck-oss/go-exploit/output" + "github.com/vulncheck-oss/go-exploit/payload" "github.com/vulncheck-oss/go-exploit/protocol" ) @@ -471,6 +472,69 @@ func updateGoDebug() { } } +func addPayloadMetadata(conf *config.Config) { + cmd := conf.StringFlagsMap["command"] + p := conf.StringFlagsMap["payload"] + if p != nil && cmd != nil { + if *p != "" && *cmd != "" { + output.PrintFrameworkError("Both `-command` and `-payload` cannot be set at the same time.") + + return + } + } + payloadType := conf.StringFlagsMap["payload-type"] + if payloadType != nil { + p := payload.TypeFromString(*payloadType) + if p == payload.UnspecifiedType { + output.PrintfFrameworkError("payload type not supported: %s", *payloadType) + + return + } + for _, t := range conf.SupportedPayloads { + if t.Type == p { + conf.SelectedPayload = t + } + } + } + if cmd != nil { + if *cmd != "" { + conf.CustomPayload = []byte(*cmd) + } + } + if p != nil { + switch conf.SelectedPayload.Type { + case payload.LinuxSO, + payload.LinuxELF, + payload.WindowsEXE, + payload.WindowsDLL, + payload.Webshell: + if *p == "" { + output.PrintError("Selected payload type requires `-payload` to be used") + + return + } + case payload.GenericCommand, + payload.WindowsPowerShellCommand, + payload.WindowsCommand, + payload.MacCommand, + payload.LinuxCommand: + case payload.UnspecifiedType: + output.PrintFrameworkError("Unspecified payload type used") + default: + } + if *p != "" { + d, err := os.ReadFile(*p) + if err != nil { + output.PrintfFrameworkError("Could not read custom payload '%s': %s", *p, err.Error()) + + return + } + conf.CustomPayload = d + } + } + output.PrintfFrameworkDebug("selecting payload type: %s", *payloadType) +} + // Effectively the package main function. Parses configuration, starts command and control, // controls which targets are scanned, initiates call down into the exploits implementation // and is ultimately responsible for waiting for all c2 and attack threads to finish. @@ -484,6 +548,10 @@ func RunProgram(sploit Exploit, conf *config.Config) { if !parseCommandLine(conf) { return } + // Early start the payload flags + if conf.PayloadFlags { + addPayloadMetadata(conf) + } // create and init the db if the user provided a database if !db.InitializeDB(conf.DBName) { diff --git a/payload/dropper/unix.go b/payload/dropper/unix.go index aa70f3b..c3f8fd5 100644 --- a/payload/dropper/unix.go +++ b/payload/dropper/unix.go @@ -60,7 +60,7 @@ func (unix *UnixPayload) WgetHTTP(lhost string, lport int, ssl bool, downloadFil // // Mountv3Only("10.9.49.2","/tmp/nfsshare", "./b") // -// This function does not attempt to actually execute any files on the share +// This function does not attempt to actually execute any files on the share. func (unix *UnixPayload) Mountv3Only(lhost string, lshareDir string, rshareDir string) string { return fmt.Sprintf("mount -o vers=3,nolock,exec,tcp -t nfs %s:%s %s", lhost, lshareDir, rshareDir) } diff --git a/payload/payload.go b/payload/payload.go new file mode 100644 index 0000000..b7042fc --- /dev/null +++ b/payload/payload.go @@ -0,0 +1,460 @@ +package payload + +import ( + "fmt" + "strings" + "testing" + + "github.com/vulncheck-oss/go-exploit/output" +) + +type ( + // Type defines the different types of payload can be. These fall + // into 2 buckets: command and payload based. We define each of the + // types to be specific both for exploit payload metadata, but also + // because it is possible to support an exploit that targets + // different types depending on required behavior. + Type int + // Arch represents generalized architecture support for payload + // selection disambiguation and metadata. This allows a payload to + // explicitly declare what architectures they support and can be + // used by an exploit to change behavior dynamically if required. + Arch int + // Effect is the type of impact a portion of the exploit employs + // and any target system side effects. These are relatively loosely + // defined and focused on the combination between + // indicators-of-compromise, payload default behavior, and types of + // network traffic. The payload.Effects map provides a way to + // define multiple types of effects and define the metadata as + // arbitrary strings. + Effect int +) + +// Effects represents an exploits impact on the target, network, and +// potential side-effects caused by it's usage. An effect can happen +// multiple times and a human readable string describing the context can be +// used. For example, defining an effect for an exploit that creates 2 +// files can be defined as follows: +// +// payload.Effects{ +// payload.FileCreate: []string{"/var/www/html/pwnt", "/var/www/html/pwnt2"}, +// } +// +// These effects are currently only used for metadata definitions and +// details flags. +type Effects map[Effect][]string + +// Supported struct is passed to exploit definitions for calling RunExploit +// and informs the exploit of the types, architecture, effects, and whether +// a payload is the default type the exploit should use. An exploit +// developer can add support for a payload type to an exploit with +// config.AddPayload, which in turn enables enables the contextual flags +// for custom payload usage. +// +// If Default is set, the exploit will use the set payload. Additionally, +// if only a single payload is used Default does not have to be set and if +// no Default is set with multiple payloads the exploit logic will select +// the first processed supported type. +// +// Multiple payloads being defined enables the -payload-type flag that +// allows for string selection between the supported payloads. In addition, +// using multiple payloads of the same type but with different +// architectures will enable -payload-arch flag to further allow selection. +// +// An example of defining multiple payloads to support an exploit with a +// command execution or a payload upload with multiple architectures can be +// defined as follows: +// +// []payload.Supported{ +// { +// Type: payload.GenericCommand, +// Arch: payload.None, +// Effects: payload.NoEffects, +// Default: true, +// }, +// { +// Type: payload.LinuxELF, +// Arch: payload.AMD64, +// Effects: payload.NoEffects, +// }, +// { +// Type: payload.LinuxELF, +// Arch: payload.ARM64, +// Effects: payload.Effects{ +// payload.FileCreate: []string{"/var/www/html/pwnt", "/var/www/html/pwnt2"}, +// }, +// }, +// } +type Supported struct { + Type Type + Arch Arch + Effects Effects + Default bool +} + +const ( + // GenericCommand is used for arbitrary command line execution + // without OS specificity. + GenericCommand Type = iota + // WindowsCommand is used for command line execution, generally via + // cmd.exe or local execution on Windows systems. + WindowsCommand + // WindowsPowerShellCommand represents command line execution of PowerShell. + WindowsPowerShellCommand + // MacCommand is used command line execution on a macOS system. + MacCommand + // LinuxCommand is used for shell execution in a Linux environment. + LinuxCommand + // LinuxELF is used for payloads containing ELF binaries for execution. + LinuxELF + // LinuxSO is used for payloads containing ELF shared object + // binaries for execution via some library loading mechanism or via + // dropping. + LinuxSO + // WindowsEXE is used for Windows PE executable files. + WindowsEXE // PE + // WindowsDLL is used for Windows DLL files binaries for execution + // via some library loading mechanism or via dropping. + WindowsDLL + // Webshell is used for arbitrary web shells that represent + // payloads that get dropped to targets. + Webshell + UnspecifiedType +) + +const ( + NotDefault bool = false + Default = true +) + +const ( + None Arch = iota + AMD64 + I386 + ARMEL + ARMHF + ARM64 + MIPS + MIPSEL + MIPS64 + MIPS64EL + PPC + PPC64 + PPC64EL + S390X + + //nolint:revive // alias is not stylistically ok, but most X864 is not clear + X86_64 Arch = AMD64 + X86 Arch = I386 + POWER8 Arch = PPC64EL + POWER9 Arch = PPC64EL + AARCH64 Arch = ARM64 +) + +const ( + FileCreate Effect = 1 << iota + FileOverwrite + FileDelete + Execute + InMemory + ConfigChanges + IndicatorInLogs + AccountLockout + Physical + WebRequest + ReverseShellTCP + ReverseShellUDP + ReverseShellTLS + + Unknown + maxKey // Should be the largest/last iota. Used for stringifying + + ReverseShellSSL Effect = ReverseShellTLS +) + +var ( + NoEffects = Effects{} + UnknownEffects = Effects{ + Unknown: []string{"The effects of this exploit are unknown at this time"}, + } +) + +// String representation of the supported payload types. +func (s Supported) String() string { + a := []string{} + for effect := range s.Effects { + a = append(a, fmt.Sprintf("(%s: %s)", effect.String(), strings.Join(s.Effects[effect], ", "))) + } + + return fmt.Sprintf("%s (%s): Effects - %s", s.Type.String(), s.Arch.String(), strings.Join(a, ", ")) +} + +// String representation of the payload supported architecture. +func (a Arch) String() string { + switch a { + case None: + return "none" + case AMD64: + return "amd64" + case I386: + return "i386" + case ARMEL: + return "armel" + case ARMHF: + return "armhf" + case ARM64: + return "arm64" + case MIPS: + return "mips" + case MIPSEL: + return "mipsel" + case MIPS64: + return "mips64" + case MIPS64EL: + return "mips64el" + case PPC: + return "ppc" + case PPC64: + return "pp64" + case PPC64EL: + return "ppc64el" + case S390X: + return "s390x" + default: + output.PrintFrameworkError("Attempted to use an unknown architecture") + + return "" + } +} + +// Return the achitecture type from a string. +func ArchFromString(s string) Arch { + switch strings.ToLower(s) { + case "none": + return None + case "amd64", "x86_64": + return AMD64 + case "i386", "x86": + return I386 + case "armel": + return ARMEL + case "armhf": + return ARMHF + case "arm64": + return ARM64 + case "mips": + return MIPS + case "mipsel": + return MIPSEL + case "mips64": + return MIPS64 + case "mips64el": + return MIPS64EL + case "ppc": + return PPC + case "ppc64": + return PPC64 + case "ppc64el": + return PPC64EL + case "s390x": + return S390X + default: + output.PrintFrameworkError("Attempted to use an unknown architecture") + + return None + } +} + +// Payload type as represented by a string. +func (t Type) String() string { + switch t { + case LinuxCommand: + return "LinuxCommand" + case WindowsCommand: + return "WindowsCommand" + case WindowsPowerShellCommand: + return "WindowsPowerShellCommand" + case MacCommand: + return "MacCommand" + case GenericCommand: + return "GenericCommand" + case LinuxELF: + return "LinuxELF" + case LinuxSO: + return "LinuxSO" + case WindowsEXE: + return "WindowsEXE" + case WindowsDLL: + return "WindowsDLL" + case Webshell: + return "Webshell" + case UnspecifiedType: + return "Unspecified" + default: + output.PrintFrameworkError("Unexpected payload type used") + + return "" + } +} + +// If the payload type should be categorized as a command. This can be used +// to check if the selected type is a command payload type without doing +// type comparisons. An example of it's usages in combination with a custom +// payload can be seen below: +// +// if conf.HasCustomPayload() { +// if conf.SelectedPayload.Type.IsCommand() { +// output.PrintfStatus("using '%s' in place of default", string(conf.CustomPayload)) +// } else { +// output.PrintfStatus("using binary len %d in place of default", len(string(conf.CustomPayload))) +// } +// } +func (t Type) IsCommand() bool { + switch t { + case LinuxCommand: + return true + case WindowsCommand: + return true + case WindowsPowerShellCommand: + return true + case MacCommand: + return true + case GenericCommand: + return true + case LinuxELF: + return false + case LinuxSO: + return false + case WindowsEXE: + return false + case WindowsDLL: + return false + case Webshell: + return false + case UnspecifiedType: + return false + default: + output.PrintFrameworkError("Unexpected payload type used") + + return false + } +} + +// If the payload type should be categorized as a payload based type. This can be used to check if the selected type is a payload type without doing type comparisons. An example of it's usages in combination with a custom payload can be seen below: +// +// if conf.HasCustomPayload() { +// if conf.SelectedPayload.Type.IsPayload() { +// output.PrintfStatus("using binary len %d in place of default", len(string(conf.CustomPayload))) +// } else { +// output.PrintfStatus("using '%s' in place of default", string(conf.CustomPayload)) +// } +// } +func (t Type) IsPayload() bool { + switch t { + case LinuxCommand: + return false + case WindowsCommand: + return false + case WindowsPowerShellCommand: + return false + case MacCommand: + return false + case GenericCommand: + return false + case LinuxELF: + return true + case LinuxSO: + return true + case WindowsEXE: + return true + case WindowsDLL: + return true + case Webshell: + return true + case UnspecifiedType: + return false + default: + if !testing.Testing() { + output.PrintFrameworkError("Unexpected payload type used") + } + + return false + } +} + +func TypeFromString(s string) Type { + switch s { + case "LinuxCommand": + return LinuxCommand + case "WindowsCommand": + return WindowsCommand + case "WindowsPowerShellCommand": + return WindowsPowerShellCommand + case "MacCommand": + return MacCommand + case "GenericCommand": + return GenericCommand + case "LinuxELF": + return LinuxELF + case "LinuxSO": + return LinuxSO + case "WindowsEXE": + return WindowsEXE + case "WindowsDLL": + return WindowsDLL + case "Webshell": + return Webshell + case "Unspecified", "UnspecifiedType": + return UnspecifiedType + default: + if !testing.Testing() { + output.PrintFrameworkError("Unexpected payload type used for string parsing") + } + + return UnspecifiedType + } +} + +func (e Effect) String() string { + switch e { + case FileCreate: + return "FileCreate" + case FileOverwrite: + return "FileOverwrite" + case FileDelete: + return "FileDelete" + case Execute: + return "Execute" + case InMemory: + return "InMemory" + case ConfigChanges: + return "ConfigChanges" + case IndicatorInLogs: + return "IndicatorInLogs" + case AccountLockout: + return "AccountLockout" + case Physical: + return "Physical" + case WebRequest: + return "WebRequest" + case ReverseShellTCP: + return "ReverseShellTCP" + case ReverseShellUDP: + return "ReverseShellUDP" + case ReverseShellTLS: + return "ReverseShellTLS" + case Unknown: + return "Unknown" + case maxKey: + return "Unknown" + } + + var n []string + for key := FileCreate; key < maxKey; key <<= 1 { + if e&key != 0 { + n = append(n, e.String()) + } + } + + return strings.Join(n, "|") +} diff --git a/payload/payload_test.go b/payload/payload_test.go new file mode 100644 index 0000000..43cd63c --- /dev/null +++ b/payload/payload_test.go @@ -0,0 +1,103 @@ +package payload_test + +import ( + "testing" + + "github.com/vulncheck-oss/go-exploit/payload" +) + +func TestArchFromString(t *testing.T) { + if payload.None != payload.ArchFromString("none") { + t.Fatal(payload.None.String()) + } + if payload.AMD64 != payload.ArchFromString("amd64") { + t.Fatal(payload.AMD64.String()) + } + if payload.AMD64 != payload.ArchFromString("x86_64") { + t.Fatal(payload.AMD64.String()) + } + if payload.X86_64 != payload.ArchFromString("amd64") { + t.Fatal(payload.X86_64.String()) + } + if payload.I386 != payload.ArchFromString("i386") { + t.Fatal(payload.I386.String()) + } + if payload.I386 != payload.ArchFromString("x86") { + t.Fatal(payload.I386.String()) + } + if payload.ARMEL != payload.ArchFromString("armel") { + t.Fatal(payload.ARMEL.String()) + } + if payload.ARMHF != payload.ArchFromString("armhf") { + t.Fatal(payload.ARMHF.String()) + } + if payload.ARM64 != payload.ArchFromString("arm64") { + t.Fatal(payload.ARM64.String()) + } + if payload.MIPS != payload.ArchFromString("mips") { + t.Fatal(payload.MIPS.String()) + } + if payload.MIPS64 != payload.ArchFromString("mips64") { + t.Fatal(payload.MIPS64.String()) + } + if payload.MIPS64EL != payload.ArchFromString("mips64el") { + t.Fatal(payload.MIPS64EL.String()) + } + if payload.PPC != payload.ArchFromString("ppc") { + t.Fatal(payload.PPC.String()) + } + if payload.PPC64 != payload.ArchFromString("ppc64") { + t.Fatal(payload.PPC64.String()) + } + if payload.PPC64EL != payload.ArchFromString("ppc64el") { + t.Fatal(payload.PPC64EL.String()) + } + if payload.S390X != payload.ArchFromString("s390x") { + t.Fatal(payload.S390X.String()) + } + if payload.S390X == payload.ArchFromString("aaaa") { + t.Fatal(payload.S390X.String()) + } +} + +func TestTypeFromString(t *testing.T) { + if payload.LinuxCommand != payload.TypeFromString("LinuxCommand") { + t.Fatal(payload.LinuxCommand.String()) + } + if payload.WindowsCommand != payload.TypeFromString("WindowsCommand") { + t.Fatal(payload.WindowsCommand.String()) + } + if payload.WindowsPowerShellCommand != payload.TypeFromString("WindowsPowerShellCommand") { + t.Fatal(payload.WindowsPowerShellCommand.String()) + } + if payload.MacCommand != payload.TypeFromString("MacCommand") { + t.Fatal(payload.MacCommand.String()) + } + if payload.GenericCommand != payload.TypeFromString("GenericCommand") { + t.Fatal(payload.GenericCommand.String()) + } + if payload.LinuxELF != payload.TypeFromString("LinuxELF") { + t.Fatal(payload.LinuxELF.String()) + } + if payload.LinuxSO != payload.TypeFromString("LinuxSO") { + t.Fatal(payload.LinuxSO.String()) + } + if payload.WindowsEXE != payload.TypeFromString("WindowsEXE") { + t.Fatal(payload.WindowsEXE.String()) + } + if payload.WindowsDLL != payload.TypeFromString("WindowsDLL") { + t.Fatal(payload.WindowsDLL.String()) + } + if payload.Webshell != payload.TypeFromString("Webshell") { + t.Fatal(payload.Webshell.String()) + } + if payload.UnspecifiedType != payload.TypeFromString("Unspecified") { + t.Fatal(payload.UnspecifiedType.String()) + } + if payload.UnspecifiedType != payload.TypeFromString("UnspecifiedType") { + t.Fatal(payload.UnspecifiedType.String()) + } + if payload.UnspecifiedType != payload.TypeFromString("aaaaaaaaa") { + t.Fatal("Bad type returned different type than Unspecified") + } +} diff --git a/payload/reverse/bash.go b/payload/reverse/bash.go index a77c41d..be5ad23 100644 --- a/payload/reverse/bash.go +++ b/payload/reverse/bash.go @@ -29,5 +29,6 @@ func (bash *BashPayload) HTTPShellLoop(lhost string, lport int, ssl bool, auth s h = "https" k = `-k ` } + return fmt.Sprintf(BashHTTPShellLoop, k, auth, h, lhost, lport, k, auth, h, lhost, lport) }