def search_login(self): """ This method requests the default login page and searches for a specific string in the title or the response. If the access is forbidden (403), extension search is still possible. """ print('[+] Backend Login') # maybe /typo3_src/typo3/index.php too? response = request.get_request('{}/typo3/index.php'.format( self.get_path())) searchTitle = re.search('<title>(.*)</title>', response['html']) if searchTitle and 'Login' in searchTitle.group(0): print(' \u251c {}'.format( Fore.GREEN + '{}/typo3/index.php'.format(self.get_path()) + Fore.RESET)) self.set_backend('{}/typo3/index.php'.format(self.get_path())) elif ('Backend access denied: The IP address of your client' in response['html']) or (response['status_code'] == 403): print(' \u251c {}'.format( Fore.GREEN + '{}/typo3/index.php'.format(self.get_path()) + Fore.RESET)) print(' \u251c {}'.format( Fore.YELLOW + 'But access is forbidden (IP Address Restriction)' + Fore.RESET)) self.set_backend('{}/typo3/index.php'.format(self.get_path())) else: print(' \u251c {}'.format(Fore.RED + 'Could not be found' + Fore.RESET))
def check_default_files(self): """ This method requests different files, which are generated on installation. Note: They are not accessible anymore on newer Typo3 installations """ files = { 'typo3_src/README.md': 'TYPO3 CMS', 'typo3_src/README.txt': 'TYPO3 CMS', 'typo3_src/INSTALL.md': 'INSTALLING TYPO3', 'typo3_src/INSTALL.txt': 'INSTALLING TYPO3', 'typo3_src/LICENSE.txt': 'TYPO3', 'typo3_src/CONTRIBUTING.md': 'TYPO3 CMS', 'typo3_src/composer.json': 'TYPO3' } for path, regex in files.items(): try: response = request.get_request('{}/{}'.format( self.get_path(), path)) regex = re.compile(regex) searchInstallation = regex.search(response['html']) installation = searchInstallation.groups() self.set_typo3() return True except: pass return False
def check_404(self): """ This method requests a site which is not available by using a random generated string. TYPO3 installations usually generate a default error page, which can be used as an indicator. """ random_string = ''.join(random.choice(string.ascii_lowercase) for i in range(10)) response = request.get_request('{}/{}'.format(self.get_path(), random_string)) search404 = re.search('[Tt][Yy][Pp][Oo]3 CMS', response['html']) if search404: self.set_typo3()
def check_root(self): """ This method requests the root page and searches for a specific string. Usually there are some TYPO3 notes in the HTML comments. If found, it searches for a Typo3 path reference in order to determine the Typo3 installation path. """ full_path = self.get_name() response = request.get_request('{}'.format(self.get_name())) if re.search('powered by TYPO3', response['html']): self.set_typo3() path = re.search( '="(?:{})/?(\S*?)/?(?:typo3temp|typo3conf)/'.format( self.get_name()), response['html']) if path and path.group(1) != '': full_path = '{}/{}'.format(self.get_name(), path) self.set_path(full_path)
def search_typo3_version(self): """ This method will aggressively search for version information by comparing file hashes """ version = None paths = set() hash_vers = dict() database = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'typo3scan.db') conn = sqlite3.connect(database) c = conn.cursor() c.execute('SELECT * FROM core_versions') data = c.fetchall() for entry in data: paths.add(entry[1]) hash_vers[entry[0]] = entry[2] for path in paths: response = request.get_request('{}/{}'.format( self.get_path(), path)) if response and response['status_code'] == 200: md5_hash = hashlib.md5() md5_hash.update(response['html'].encode()) digest = md5_hash.hexdigest() if digest in hash_vers: version = hash_vers[digest] if len(version) <= 4: continue else: break print(' | \n [+] Version Information') if version: self.set_typo3_version(version) print(' \u251c Identified Version: '.ljust(28) + '{}'.format(Style.BRIGHT + Fore.GREEN + version + Style.RESET_ALL)) if len(version) <= 4: print(' \u251c Could not identify exact version.') react = input( ' \u251c Do you want to print all vulnerabilities for branch {}? (y/n): ' .format(version)) if react.startswith('y'): version = version + '.0' else: return False c.execute( 'SELECT advisory, vulnerability, subcomponent, affected_version_max, affected_version_min FROM core_vulns WHERE (?<=affected_version_max AND ?>=affected_version_min)', ( version, version, )) data = c.fetchall() json_list = [] if data: for vulnerability in data: # maybe instead use this: https://zxq9.com/archives/797 if parse_version(version) <= parse_version( vulnerability[3]): json_list.append({ 'Advisory': vulnerability[0], 'Type': vulnerability[1], 'Subcomponent': vulnerability[2], 'Affected': '{} - {}'.format(vulnerability[3], vulnerability[4]), 'Advisory URL': 'https://typo3.org/security/advisory/{}'.format( vulnerability[0].lower()) }) if json_list: self.set_typo3_vulns(json_list) print(' \u2514 Known Vulnerabilities:\n') for vulnerability in json_list: print(Style.BRIGHT + ' [!] {}'.format(Fore.RED + vulnerability['Advisory'] + Style.RESET_ALL)) print(' \u251c Vulnerability Type:'.ljust(28) + vulnerability['Type']) print(' \u251c Subcomponent:'.ljust(28) + vulnerability['Subcomponent']) print(' \u251c Affected Versions:'.ljust(28) + vulnerability['Affected']) print(' \u2514 Advisory URL:'.ljust(28) + vulnerability['Advisory URL'] + '\n') if not json_list: print(' \u2514 No Known Vulnerabilities') else: print(' \u2514', Fore.RED + 'Could not be determined.' + Fore.RESET)