From dea460c81396d2bf99723768437f414db318090a Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Mon, 17 Sep 2018 11:00:51 -0500 Subject: [PATCH 1/8] golang module loader support --- lib/msf/core/modules/external/bridge.rb | 16 +++++++++++++++- lib/msf/core/modules/loader/base.rb | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/modules/external/bridge.rb b/lib/msf/core/modules/external/bridge.rb index fddb4c62db3d..6b9b2ed8528c 100644 --- a/lib/msf/core/modules/external/bridge.rb +++ b/lib/msf/core/modules/external/bridge.rb @@ -185,17 +185,31 @@ def self.applies?(module_name) def initialize(module_path, framework: nil) super - ruby_path = File.expand_path('../ruby', __FILE__) self.cmd = [[Gem.ruby, 'ruby'], "-I#{ruby_path}", self.path] end end +class Msf::Modules::External::GoBridge < Msf::Modules::External::Bridge + def self.applies?(module_name) + $stderr.puts module_name + module_name.match? /\.go$/ + end + + def initialize(module_path, framework: nil) + super + gopath = ENV['GOPATH'] || '' + self.env = self.env.merge({ 'GOPATH' => gopath + File::PATH_SEPARATOR + File.expand_path('../go', __FILE__) }) + self.cmd = ['go', 'run', self.path] + end +end + class Msf::Modules::External::Bridge LOADERS = [ Msf::Modules::External::PyBridge, Msf::Modules::External::RbBridge, + Msf::Modules::External::GoBridge, Msf::Modules::External::Bridge ] diff --git a/lib/msf/core/modules/loader/base.rb b/lib/msf/core/modules/loader/base.rb index eddad094afdb..105a54da3879 100644 --- a/lib/msf/core/modules/loader/base.rb +++ b/lib/msf/core/modules/loader/base.rb @@ -479,10 +479,11 @@ def module_path?(path) module_path end + # Tries to determine if a file might be executable, def script_path?(path) File.executable?(path) && !File.directory?(path) && - File.read(path, 2) == "#!" + ['#!', '//'].include?(File.read(path, 2)) end # Changes a file name path to a canonical module reference name. From b3704773261356799b65d93a032604d859df19ae Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sat, 6 Oct 2018 05:11:56 -0500 Subject: [PATCH 2/8] add example of launching a go 'script' --- modules/exploits/multi/test.go | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 modules/exploits/multi/test.go diff --git a/modules/exploits/multi/test.go b/modules/exploits/multi/test.go new file mode 100755 index 000000000000..b00e8c78c681 --- /dev/null +++ b/modules/exploits/multi/test.go @@ -0,0 +1,7 @@ +//usr/bin/env go run "$0" "$@"; exit "$?" +package main +import "fmt" + +func main() { + fmt.Println("Hello") +} From cfbc0a9a0c044a1c45157751e6ac0b1290986f01 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sat, 6 Oct 2018 05:52:34 -0500 Subject: [PATCH 3/8] properly bubble up errors on external module load --- lib/msf/core/modules/external.rb | 1 + lib/msf/core/modules/external/shim.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/modules/external.rb b/lib/msf/core/modules/external.rb index 3b5775a58cf2..83357aabc389 100644 --- a/lib/msf/core/modules/external.rb +++ b/lib/msf/core/modules/external.rb @@ -47,5 +47,6 @@ def describe exec method: :describe do |msg| return msg.params if msg.method == :reply end + return nil end end diff --git a/lib/msf/core/modules/external/shim.rb b/lib/msf/core/modules/external/shim.rb index 495e93d9d17e..a83fdf05f629 100644 --- a/lib/msf/core/modules/external/shim.rb +++ b/lib/msf/core/modules/external/shim.rb @@ -4,7 +4,7 @@ class Msf::Modules::External::Shim def self.generate(module_path, framework) mod = Msf::Modules::External.new(module_path, framework: framework) - return '' unless mod.meta + return nil unless mod.meta case mod.meta['type'] when 'remote_exploit' remote_exploit(mod) From df43b372fa519c76b0ee12be1d040dd302c4814d Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sat, 6 Oct 2018 08:14:47 -0500 Subject: [PATCH 4/8] initial golang module support --- .../external/go/src/metasploit/module.go | 185 ++++++++++++++++++ modules/exploits/multi/test.go | 26 ++- 2 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 lib/msf/core/modules/external/go/src/metasploit/module.go diff --git a/lib/msf/core/modules/external/go/src/metasploit/module.go b/lib/msf/core/modules/external/go/src/metasploit/module.go new file mode 100644 index 000000000000..0b17386b6e18 --- /dev/null +++ b/lib/msf/core/modules/external/go/src/metasploit/module.go @@ -0,0 +1,185 @@ +package metasploit + +import ( + "bufio" + "encoding/json" + "log" + "os" +) + +type response struct { + Jsonrpc string `json:"jsonrpc"` + Id string `json:"id"` +} + +func rpc_send(res interface{}) { + res_str, _ := json.Marshal(res) + f := bufio.NewWriter(os.Stdout) + defer f.Flush() + f.Write(res_str) +} + +type ( + logparams struct { + Level string `json:"level"` + Message string `json:"message"` + } + + LogRequest struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params logparams `json:"params"` + } + +) + +// 'debug' +func Log(message string, level string) { + req := &LogRequest {"2.0", "message", logparams{level, message}} + rpc_send(req) +} + +type ( + reportparams struct { + Type string `json:"type"` + Data map[string]string `json:"data"` + } + + ReportRequest struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params reportparams `json:"params"` + } +) + +func report(kind string, base map[string]string, opts map[string]string) { + for k, v := range base { + opts[k] = v + } + req := &ReportRequest {"2.0", "report", reportparams{kind, opts}} + rpc_send(req) +} + +func ReportHost(ip string, opts map[string]string) { + base := map[string]string{"host": ip} + report("host", base, opts) +} + +func ReportService(ip string, opts map[string]string) { + base := map[string]string{"host": ip} + report("service", base, opts) +} + +func ReportVuln(ip string, name string, opts map[string]string) { + base := map[string]string{"host": ip, "name": name} + report("vuln", base, opts) +} + +func ReportCorrectPassword(username string, password string, opts map[string]string) { + base := map[string]string{"username": username, "password": password} + report("correct_password", base, opts) +} + +func ReportWrongPassword(username string, password string, opts map[string]string) { + base := map[string]string{"username": username, "password": password} + report("wrong_password", base, opts) +} + +type ( + Reference struct { + Type string `json:"type"` + Ref string `json:"ref"` + } + + Target struct { + Platform string `json:"platform"` + Arch string `json:"arch"` + } + + Option struct { + Type string `json:"type"` + Description string `json:"description"` + Required bool `json:"required"` + Default string `json:"default"` + } + + Metadata struct { + Name string `json:"name"` + Description string `json:"description"` + Authors []string `json:"authors"` + Date string `json:"date"` + References []Reference `json:"references"` + Type string `json:"type"` + Rank string `json:"rank"` + WFSDelay int `json:"wfsdelay"` + Privileged bool `json:"privileged"` + Targets []Target `json:"targets",omitempty` + Capabilities []string `json:"capabilities"` + Payload map[string]string `json:"payload",omitempty` + Options map[string]Option `json:"options",omitempty` + Notes map[string][]string `json:"notes",omitempty` + } + + Request struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Id string `json:"id"` + } + + MetadataResponse struct { + Jsonrpc string `json:"jsonrpc"` + Id string `json:"id"` + Result *Metadata `json:"result"` + } + + RunResult struct { + Message string `json:"message"` + Return string `json:"return"` + } + + RunResponse struct { + Jsonrpc string `json:"jsonrpc"` + Id string `json:"id"` + Result RunResult `json:"result"` + } +) + +type RunCallback func(req *Request) string + +func Run(metadata *Metadata, callback RunCallback) { + var req Request + + err := json.NewDecoder(os.Stdin).Decode(&req) + if err != nil { + log.Fatal(err) + } + if req.Method == "describe" { + metadata.Capabilities = []string{"run"} + res := &MetadataResponse{"2.0", req.Id, metadata} + rpc_send(res) + } + + if req.Method == "run" { + ret := callback(&req) + res := &RunResponse{"2.0", req.Id, RunResult{"Module complete", ret}} + rpc_send(res) + } +} +/* + //type RunCallback func(struct) string + def run(metadata, callback, soft_check: nil) + elsif req[:method] == 'run' + cb = callback + end + + if cb + ret = cb.call req[:params] + rpc_send({ + jsonrpc: '2.0', id: req[:id], result: { + message: 'Module completed', + 'return' => ret + } + }) + end +*/ +//} diff --git a/modules/exploits/multi/test.go b/modules/exploits/multi/test.go index b00e8c78c681..ad05de16fef6 100755 --- a/modules/exploits/multi/test.go +++ b/modules/exploits/multi/test.go @@ -1,7 +1,29 @@ //usr/bin/env go run "$0" "$@"; exit "$?" package main -import "fmt" +import ( + "metasploit" +) + +func exploit(req *metasploit.Request) string { + return "Success!" +} func main() { - fmt.Println("Hello") + m := &metasploit.Metadata { + Name: "Test Module", + Description: "This is a test", + Authors: []string{ "bcook-r7" }, + Date: "2018-10-6", + Type: "remote_exploit", + Rank: "excellent", + Privileged: false, + Targets: []metasploit.Target { + {Platform: "linux", Arch: "x64"}, + {Platform: "linux", Arch: "x86"}}, + References: []metasploit.Reference { + {Type: "URL", Ref: "https://golang.org"}}, + Options: map[string]metasploit.Option { + "Test": {Type: "string", Description: "This is a test", Required: false, Default: ""}}} + + metasploit.Run(m, exploit) } From 97bee891cee541d7302bbca34a601e508633109d Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sat, 6 Oct 2018 12:06:40 -0500 Subject: [PATCH 5/8] remove some ruby vestiges --- .../external/go/src/metasploit/module.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/msf/core/modules/external/go/src/metasploit/module.go b/lib/msf/core/modules/external/go/src/metasploit/module.go index 0b17386b6e18..67bdc082d7e1 100644 --- a/lib/msf/core/modules/external/go/src/metasploit/module.go +++ b/lib/msf/core/modules/external/go/src/metasploit/module.go @@ -165,21 +165,3 @@ func Run(metadata *Metadata, callback RunCallback) { rpc_send(res) } } -/* - //type RunCallback func(struct) string - def run(metadata, callback, soft_check: nil) - elsif req[:method] == 'run' - cb = callback - end - - if cb - ret = cb.call req[:params] - rpc_send({ - jsonrpc: '2.0', id: req[:id], result: { - message: 'Module completed', - 'return' => ret - } - }) - end -*/ -//} From 76531cb818e78ccfd38914730e9c5bfe19001d4f Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sat, 6 Oct 2018 16:04:46 -0400 Subject: [PATCH 6/8] gofmt all the things --- .../external/go/src/metasploit/module.go | 81 +++++++++---------- modules/exploits/multi/test.go | 21 ++--- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/lib/msf/core/modules/external/go/src/metasploit/module.go b/lib/msf/core/modules/external/go/src/metasploit/module.go index 67bdc082d7e1..ec5fadc03b1e 100644 --- a/lib/msf/core/modules/external/go/src/metasploit/module.go +++ b/lib/msf/core/modules/external/go/src/metasploit/module.go @@ -9,7 +9,7 @@ import ( type response struct { Jsonrpc string `json:"jsonrpc"` - Id string `json:"id"` + Id string `json:"id"` } func rpc_send(res interface{}) { @@ -21,34 +21,33 @@ func rpc_send(res interface{}) { type ( logparams struct { - Level string `json:"level"` + Level string `json:"level"` Message string `json:"message"` } LogRequest struct { - Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Params logparams `json:"params"` + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params logparams `json:"params"` } - ) // 'debug' func Log(message string, level string) { - req := &LogRequest {"2.0", "message", logparams{level, message}} + req := &LogRequest{"2.0", "message", logparams{level, message}} rpc_send(req) } type ( reportparams struct { - Type string `json:"type"` + Type string `json:"type"` Data map[string]string `json:"data"` } ReportRequest struct { - Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Params reportparams `json:"params"` + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params reportparams `json:"params"` } ) @@ -56,7 +55,7 @@ func report(kind string, base map[string]string, opts map[string]string) { for k, v := range base { opts[k] = v } - req := &ReportRequest {"2.0", "report", reportparams{kind, opts}} + req := &ReportRequest{"2.0", "report", reportparams{kind, opts}} rpc_send(req) } @@ -88,59 +87,59 @@ func ReportWrongPassword(username string, password string, opts map[string]strin type ( Reference struct { Type string `json:"type"` - Ref string `json:"ref"` + Ref string `json:"ref"` } Target struct { Platform string `json:"platform"` - Arch string `json:"arch"` + Arch string `json:"arch"` } Option struct { - Type string `json:"type"` + Type string `json:"type"` Description string `json:"description"` - Required bool `json:"required"` - Default string `json:"default"` + Required bool `json:"required"` + Default string `json:"default"` } Metadata struct { - Name string `json:"name"` - Description string `json:"description"` - Authors []string `json:"authors"` - Date string `json:"date"` - References []Reference `json:"references"` - Type string `json:"type"` - Rank string `json:"rank"` - WFSDelay int `json:"wfsdelay"` - Privileged bool `json:"privileged"` - Targets []Target `json:"targets",omitempty` - Capabilities []string `json:"capabilities"` - Payload map[string]string `json:"payload",omitempty` - Options map[string]Option `json:"options",omitempty` - Notes map[string][]string `json:"notes",omitempty` - } + Name string `json:"name"` + Description string `json:"description"` + Authors []string `json:"authors"` + Date string `json:"date"` + References []Reference `json:"references"` + Type string `json:"type"` + Rank string `json:"rank"` + WFSDelay int `json:"wfsdelay"` + Privileged bool `json:"privileged"` + Targets []Target `json:"targets",omitempty` + Capabilities []string `json:"capabilities"` + Payload map[string]string `json:"payload",omitempty` + Options map[string]Option `json:"options",omitempty` + Notes map[string][]string `json:"notes",omitempty` + } Request struct { Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Id string `json:"id"` + Method string `json:"method"` + Id string `json:"id"` } MetadataResponse struct { - Jsonrpc string `json:"jsonrpc"` - Id string `json:"id"` - Result *Metadata `json:"result"` + Jsonrpc string `json:"jsonrpc"` + Id string `json:"id"` + Result *Metadata `json:"result"` } RunResult struct { Message string `json:"message"` - Return string `json:"return"` + Return string `json:"return"` } RunResponse struct { - Jsonrpc string `json:"jsonrpc"` - Id string `json:"id"` - Result RunResult `json:"result"` + Jsonrpc string `json:"jsonrpc"` + Id string `json:"id"` + Result RunResult `json:"result"` } ) diff --git a/modules/exploits/multi/test.go b/modules/exploits/multi/test.go index ad05de16fef6..c29576188616 100755 --- a/modules/exploits/multi/test.go +++ b/modules/exploits/multi/test.go @@ -1,5 +1,6 @@ //usr/bin/env go run "$0" "$@"; exit "$?" package main + import ( "metasploit" ) @@ -9,20 +10,20 @@ func exploit(req *metasploit.Request) string { } func main() { - m := &metasploit.Metadata { - Name: "Test Module", + m := &metasploit.Metadata{ + Name: "Test Module", Description: "This is a test", - Authors: []string{ "bcook-r7" }, - Date: "2018-10-6", - Type: "remote_exploit", - Rank: "excellent", - Privileged: false, - Targets: []metasploit.Target { + Authors: []string{"bcook-r7"}, + Date: "2018-10-6", + Type: "remote_exploit", + Rank: "excellent", + Privileged: false, + Targets: []metasploit.Target{ {Platform: "linux", Arch: "x64"}, {Platform: "linux", Arch: "x86"}}, - References: []metasploit.Reference { + References: []metasploit.Reference{ {Type: "URL", Ref: "https://golang.org"}}, - Options: map[string]metasploit.Option { + Options: map[string]metasploit.Option{ "Test": {Type: "string", Description: "This is a test", Required: false, Default: ""}}} metasploit.Run(m, exploit) From 407a9f3de1404981233d439f2ba117bba489792c Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Sat, 6 Oct 2018 16:07:54 -0400 Subject: [PATCH 7/8] remove debug --- lib/msf/core/modules/external/bridge.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/msf/core/modules/external/bridge.rb b/lib/msf/core/modules/external/bridge.rb index 6b9b2ed8528c..8fc3f038aaa2 100644 --- a/lib/msf/core/modules/external/bridge.rb +++ b/lib/msf/core/modules/external/bridge.rb @@ -192,7 +192,6 @@ def initialize(module_path, framework: nil) class Msf::Modules::External::GoBridge < Msf::Modules::External::Bridge def self.applies?(module_name) - $stderr.puts module_name module_name.match? /\.go$/ end From ab8ca0581e465fcf70e307bb9aabe6f4e25f4541 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Tue, 6 Nov 2018 11:14:06 -0600 Subject: [PATCH 8/8] remove test module --- modules/exploits/multi/test.go | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100755 modules/exploits/multi/test.go diff --git a/modules/exploits/multi/test.go b/modules/exploits/multi/test.go deleted file mode 100755 index c29576188616..000000000000 --- a/modules/exploits/multi/test.go +++ /dev/null @@ -1,30 +0,0 @@ -//usr/bin/env go run "$0" "$@"; exit "$?" -package main - -import ( - "metasploit" -) - -func exploit(req *metasploit.Request) string { - return "Success!" -} - -func main() { - m := &metasploit.Metadata{ - Name: "Test Module", - Description: "This is a test", - Authors: []string{"bcook-r7"}, - Date: "2018-10-6", - Type: "remote_exploit", - Rank: "excellent", - Privileged: false, - Targets: []metasploit.Target{ - {Platform: "linux", Arch: "x64"}, - {Platform: "linux", Arch: "x86"}}, - References: []metasploit.Reference{ - {Type: "URL", Ref: "https://golang.org"}}, - Options: map[string]metasploit.Option{ - "Test": {Type: "string", Description: "This is a test", Required: false, Default: ""}}} - - metasploit.Run(m, exploit) -}