v0.5
This commit is contained in:
193
typo3scan.py
Normal file
193
typo3scan.py
Normal file
@@ -0,0 +1,193 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#-------------------------------------------------------------------------------
|
||||
# Typo3Scan - Automatic Typo3 Enumeration Tool
|
||||
# Copyright (c) 2014-2020 Jan Rude
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/)
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
__version__ = '0.5'
|
||||
__program__ = 'Typo3Scan'
|
||||
__description__ = 'Automatic Typo3 enumeration tool'
|
||||
__author__ = 'https://github.com/whoot'
|
||||
|
||||
import sys
|
||||
import json
|
||||
import sqlite3
|
||||
import os.path
|
||||
import argparse
|
||||
from lib.domain import Domain
|
||||
from lib.update import Update
|
||||
from lib.initdb import DB_Init
|
||||
from lib.extensions import Extensions
|
||||
from colorama import Fore, init, deinit, Style
|
||||
init()
|
||||
|
||||
class Typo3:
|
||||
def __init__(self):
|
||||
self.__domain_list = []
|
||||
self.__path = os.path.dirname(os.path.abspath(__file__))
|
||||
self.__extensions = []
|
||||
|
||||
def print_help():
|
||||
print(
|
||||
"""\nUsage: python typo3scan.py [options]
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message and exit.
|
||||
|
||||
Target:
|
||||
At least one of these options has to be provided to define the target(s):
|
||||
|
||||
--domain | -d <target url> The Typo3 URL(s)/domain(s) to scan.
|
||||
--file | -f <file> Parse targets from file (one domain per line).
|
||||
|
||||
|
||||
Optional:
|
||||
You dont need to specify this arguments, but you may want to
|
||||
|
||||
--vuln Check for extensions with known vulnerabilities only.
|
||||
Default: all
|
||||
|
||||
--timeout TIMEOUT Request Timeout.
|
||||
Default: 10 seconds
|
||||
|
||||
--auth USER:PASS Username and Password for HTTP Basic Authorization.
|
||||
|
||||
--cookie NAME=VALUE Can be used for authenticiation based on cookies.
|
||||
|
||||
--threads THREADS The number of threads to use for enumerating extensions.
|
||||
Default: 5
|
||||
|
||||
General:
|
||||
-u | --update Update the database.
|
||||
-r | --reset Reset the database.
|
||||
""")
|
||||
|
||||
def run(self):
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
help = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('-f', '--file', dest='file')
|
||||
group.add_argument('-d', '--domain', dest='domain', type=str, nargs='+')
|
||||
group.add_argument('-u', '--update', dest='update', action='store_true')
|
||||
group.add_argument('-r', '--reset', dest='reset', action='store_true')
|
||||
parser.add_argument('--vuln', dest='vuln', action='store_true')
|
||||
parser.add_argument('--threads', dest='threads', type=int, default=5)
|
||||
parser.add_argument('--auth', dest='auth', type=str, default='')
|
||||
parser.add_argument('--cookie', dest='cookie', type=str, default='')
|
||||
parser.add_argument('--timeout', dest='timeout', type=int, default=10)
|
||||
help.add_argument( '-h', '--help', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.help or not len(sys.argv) > 1:
|
||||
Typo3.print_help()
|
||||
|
||||
elif args.reset:
|
||||
DB_Init()
|
||||
|
||||
elif args.update:
|
||||
Update()
|
||||
|
||||
else:
|
||||
database = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib', 'typo3scan.db')
|
||||
conn = sqlite3.connect(database)
|
||||
c = conn.cursor()
|
||||
c.execute('SELECT * FROM UserAgents ORDER BY RANDOM() LIMIT 1;')
|
||||
user_agent = c.fetchone()[0]
|
||||
c.close()
|
||||
config = {'threads': args.threads, 'timeout': args.timeout, 'cookie': args.cookie, 'auth': args.auth, 'User-Agent': user_agent}
|
||||
json.dump(config, open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'lib', 'config.json'), 'w'))
|
||||
try:
|
||||
if args.domain:
|
||||
for dom in args.domain:
|
||||
self.__domain_list.append(dom)
|
||||
elif args.file:
|
||||
if not os.path.isfile(args.file):
|
||||
print(Fore.RED + '\n[x] File not found: {}\n | Aborting...'.format(args.file) + Fore.RESET)
|
||||
sys.exit(-1)
|
||||
else:
|
||||
with open(args.file, 'r') as f:
|
||||
for line in f:
|
||||
self.__domain_list.append(line.strip())
|
||||
|
||||
for domain in self.__domain_list:
|
||||
print(Fore.CYAN + Style.BRIGHT + '\n\n[ Checking {} ]\n'.format(domain) + '-'* 73 + Fore.RESET + Style.RESET_ALL)
|
||||
check = Domain(domain)
|
||||
check.check_root()
|
||||
default_files = check.check_default_files()
|
||||
if not default_files:
|
||||
check_404 = check.check_404()
|
||||
if not check.is_typo3():
|
||||
print(Fore.RED + '\n[x] It seems that Typo3 is not used on this domain' + Fore.RESET)
|
||||
else:
|
||||
# print interesting headers
|
||||
print('\n[+] Interesting Headers')
|
||||
for key, value in check.get_interesting_headers().items():
|
||||
string = ' \u251c {}:'.format(key)
|
||||
print(string.ljust(30) + value)
|
||||
|
||||
# check for typo3 information
|
||||
print('\n[+] Typo3 Information')
|
||||
check.search_login()
|
||||
check.search_typo3_version()
|
||||
|
||||
# Search extensions
|
||||
print(' [+] Extension Search')
|
||||
if not self.__extensions:
|
||||
conn = sqlite3.connect(database)
|
||||
c = conn.cursor()
|
||||
if args.vuln:
|
||||
for row in c.execute('SELECT extensionkey FROM extension_vulns'):
|
||||
self.__extensions.append(row[0])
|
||||
self.__extensions = set(self.__extensions)
|
||||
else:
|
||||
for row in c.execute('SELECT extensionkey FROM extensions'):
|
||||
self.__extensions.append(row[0])
|
||||
conn.close()
|
||||
print (' \u251c Brute-Forcing {} extensions'.format(len(self.__extensions)))
|
||||
extensions = Extensions()
|
||||
ext_list = extensions.search_extension(check.get_path(), self.__extensions, args.threads)
|
||||
if ext_list:
|
||||
print ('\n \u251c Found {} extensions'.format(len(ext_list)))
|
||||
print (' |\n \u251c Brute-Forcing version information'.format(len(self.__extensions)))
|
||||
ext_list = extensions.search_ext_version(ext_list, args.threads)
|
||||
extensions.output(ext_list, database)
|
||||
else:
|
||||
print ('\n [!] No extensions found.')
|
||||
except KeyboardInterrupt:
|
||||
print('\nReceived keyboard interrupt.\nQuitting...')
|
||||
exit(-1)
|
||||
finally:
|
||||
deinit()
|
||||
print()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('\n' + 73*'=' + Style.BRIGHT)
|
||||
print(Fore.CYAN)
|
||||
print('________ ________ _________ '.center(73))
|
||||
print('\_ _/__ __ ______ _____\_____ \ / _____/ ____ _____ ___ '.center(73))
|
||||
print(' | | | | |\____ \| _ | _(__ < \_____ \_/ ___\\\\__ \ / \ '.center(73))
|
||||
print(' | | |___ || |_) | (_) |/ \/ \ \___ / __ \| | \ '.center(73))
|
||||
print(' |__| / ____|| __/|_____|________/_________/\_____|_____/|__|__/ '.center(73))
|
||||
print(' \/ |__| '.center(73))
|
||||
print(Fore.RESET + Style.RESET_ALL)
|
||||
print(__description__.center(73))
|
||||
print(('Version ' + __version__).center(73))
|
||||
print((__author__).center(73))
|
||||
print(73*'=')
|
||||
|
||||
main = Typo3()
|
||||
main.run()
|
||||
Reference in New Issue
Block a user