diff --git a/CHANGELOG b/CHANGELOG index 8f6d97cf..c877705f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +Version 1.0.4 [17-07-2018] + - added joomla deep scan and version detection + - minor core update + - removed some junk code from wordpress deepscan Version 1.0.3 [06-07-2018] - clear-result argument added - fixed all bruteforce modules diff --git a/README.md b/README.md index b8dc6029..b7d14e8a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

Logo
- version + version Python Version License

@@ -10,6 +10,7 @@ ## Release History ``` +- Version 1.0.4 [17-07-2018] - Version 1.0.3 [06-07-2018] - Version 1.0.2 [06-07-2018] - Version 1.0.1 [19-06-2018] @@ -23,6 +24,7 @@ - Detects Version - Detects Users (3 Detection Methods) - Looks for Version Vulnerabilities and much more! +- Advance Joomla Scans - Modular bruteforce system - Use pre made bruteforce modules or create your own and integrate with it diff --git a/VersionDetect/detect.py b/VersionDetect/detect.py index bb156bc1..d79a4f46 100644 --- a/VersionDetect/detect.py +++ b/VersionDetect/detect.py @@ -2,5 +2,9 @@ def start(id, url, ua, ga, source): if id == "wp": # trust me more will be added soon import VersionDetect.wp as wpverdetect - wpver = wpverdetect.start(id, url, ua, ga, source): + wpver = wpverdetect.start(id, url, ua, ga, source) return wpver + elif id == 'joom': + import VersionDetect.joom as joomverdetect + joomver = joomverdetect.start(id, url, ua, ga, source) + return joomver diff --git a/VersionDetect/joom.py b/VersionDetect/joom.py new file mode 100644 index 00000000..bd3a0317 --- /dev/null +++ b/VersionDetect/joom.py @@ -0,0 +1,96 @@ +## Joomla version detection +## Rev 1 + +import cmseekdb.basic as cmseek +import re +def start(id, url, ua, ga, source): + version = '0' + cmseek.info('detecting joomla version') + + # version detection stats here + if ga == '1': + # Detect version via generator meta tag + cmseek.statement('Detecting version using generator meta tag [Method 1 of 4]') + regex_1 = re.findall(r'content=(?:\"|\')Joomla! (.*?) - Open Source Content Management(?:\"|\')', source) + if regex_1 != []: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + regex_1[0] + cmseek.cln) + return regex_1[0] + + if version == '0': + # Detections using the xml files + xml_files = ['administrator/manifests/files/joomla.xml','language/en-GB/en-GB.xml','administrator/components/com_content/content.xml','administrator/components/com_plugins/plugins.xml','administrator/components/com_media/media.xml','mambots/content/moscode.xml'] + cmseek.statement('Detecting version using xml files [Method 2 of 4]') + for xml_file in xml_files: + xml_source = cmseek.getsource(url + '/' + xml_file, ua) + if xml_source[0] == '1': + regex_2 = re.findall(r'(.*?)', xml_source[1]) + if regex_2 != []: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + regex_2[0] + cmseek.cln) + return regex_2[0] + + # Detection method 3 + if version == '0': + other_files = ['language/en-GB/en-GB.xml','templates/system/css/system.css','media/system/js/mootools-more.js','language/en-GB/en-GB.ini','htaccess.txt','language/en-GB/en-GB.com_media.ini'] + cmseek.statement('Detecting version using advanced fingerprinting [Method 3 of 4]') + for file in other_files: + file_source = cmseek.getsource(url + '/' + file, ua) + if file_source[0] == '1': + # Regex find + regex_3 = re.findall(r'', file_source[1]) + if regex_3 != []: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + regex_3[0] + cmseek.cln) + return regex_3[0] + + # Joomla version 1.6 + j16 = ['system.css 20196 2011-01-09 02:40:25Z ian','MooTools.More={version:"1.3.0.1"','en-GB.ini 20196 2011-01-09 02:40:25Z ian','en-GB.ini 20990 2011-03-18 16:42:30Z infograf768','20196 2011-01-09 02:40:25Z ian'] + for j in j16: + rsearch = re.search(j,file_source[1]) + if rsearch is not None: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + '1.6' + cmseek.cln) + return '1.6' + + # Joomla version 1.5 + j15 = ['Joomla! 1.5','MooTools={version:\'1.12\'}','11391 2009-01-04 13:35:50Z ian'] + for j in j15: + rsearch = re.search(j,file_source[1]) + if rsearch is not None: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + '1.5' + cmseek.cln) + return '1.5' + + # Joomla version 1.7 + j17 = ['system.css 21322 2011-05-11 01:10:29Z dextercowley','MooTools.More={version:"1.3.2.1"','22183 2011-09-30 09:04:32Z infograf768','21660 2011-06-23 13:25:32Z infograf768'] + for j in j17: + rsearch = re.search(j,file_source[1]) + if rsearch is not None: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + '1.7' + cmseek.cln) + return '1.7' + + # Joomla version 1.0 + j10 = ['(Copyright (C) 2005 - 200(6|7))','47 2005-09-15 02:55:27Z rhuk','423 2005-10-09 18:23:50Z stingrey','1005 2005-11-13 17:33:59Z stingrey','1570 2005-12-29 05:53:33Z eddieajau','2368 2006-02-14 17:40:02Z stingrey','1570 2005-12-29 05:53:33Z eddieajau','4085 2006-06-21 16:03:54Z stingrey','4756 2006-08-25 16:07:11Z stingrey','5973 2006-12-11 01:26:33Z robs','5975 2006-12-11 01:26:33Z robs'] + for j in j10: + rsearch = re.search(j,file_source[1]) + if rsearch is not None: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + '1.0' + cmseek.cln) + return '1.0' + + # Joomla version 2.5 + j25 = ['Copyright (C) 2005 - 2012 Open Source Matters','MooTools.More={version:"1.4.0.1"'] + for j in j25: + rsearch = re.search(j,file_source[1]) + if rsearch is not None: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + '2.5' + cmseek.cln) + return '2.5' + + # Detection using README file + if version == '0': + cmseek.statement('Detecting version from README file [Method 4 of 4]') + readme_file = url + '/README.txt' + readme_source = cmseek.getsource(readme_file, ua) + if readme_source[0] == '1': + regex_4 = re.findall(r'package to version (.*?)', readme_source[1]) + if regex_4 != []: + cmseek.success('Joomla version detected, version: ' + cmseek.bold + regex_4[0] + cmseek.cln) + return regex_4[0] + + # if we fail ¯\_(ツ)_/¯ + return version diff --git a/VersionDetect/wp.py b/VersionDetect/wp.py index d0d21de4..fe9ad9b8 100644 --- a/VersionDetect/wp.py +++ b/VersionDetect/wp.py @@ -1,3 +1,6 @@ +## WordPress version detection +## Rev 1 + import cmseekdb.basic as cmseek import re diff --git a/cmseek.py b/cmseek.py index 8a64f2a5..540d64ff 100644 --- a/cmseek.py +++ b/cmseek.py @@ -23,7 +23,7 @@ parser.add_argument('-v', '--verbose', help="increase output verbosity", action="store_true") parser.add_argument("--version", help="Show CMSeeK version", action="store_true") parser.add_argument("--update", help="Update CMSeeK", action="store_true") -parser.add_argument("--random-agent", help="Use a random user agent", action="store_true") +parser.add_argument('-r', "--random-agent", help="Use a random user agent", action="store_true") parser.add_argument('--user-agent', help='Specify custom user agent') parser.add_argument('-u', '--url', help='Target Url') parser.add_argument('--clear-result', action='store_true') diff --git a/cmseekdb/basic.py b/cmseekdb/basic.py index ba5349a3..7f953876 100644 --- a/cmseekdb/basic.py +++ b/cmseekdb/basic.py @@ -68,7 +68,7 @@ def banner (txt): print(whitebg + black + bold) print(" [+] " + txt + " [+] " + cln) else: - print(cln + bold + lbluebg + black + " Author: " + cln + bold + " https://twitter.com/r3dhax0r" + blackbg + white + "\n GitHub: " + cln + bold + " https://github.com/Tuhinshubhra \n" + cln + bold + violetbg + white + " Group : " + cln + bold + " Virtual Unvoid Defensive @virtuallyunvoid" + cln + '\n') + print(cln + bold + lbluebg + black + " Author: " + cln + bold + " https://twitter.com/r3dhax0r" + blackbg + white + "\n GitHub: " + cln + bold + " https://github.com/Tuhinshubhra \n" + cln + '\n') print(cln + "\n") return @@ -102,22 +102,22 @@ def help(): # The help screen print( """ - CMSeeK Version {0} - Coded By: @r3dhax0r - - Usage: cmseek.py (for a guided scanning) OR cmseek.py -u [...] - - Arguments: - - -u URL, --url URL Target Url - -h, --help Show this help message and exit - -v, --verbose Increase output verbosity - --version Show CMSeeK version and exit - --update Update CMSeeK (Requires git) - --random-agent Use a random user agent - --user-agent USER_AGENT Specify custom user agent - --clear-result Delete all the scan result - """.format(cmseek_version)) +CMSeeK Version {0} +Coded By:{1} @r3dhax0r {2} + +Usage: cmseek.py (for a guided scanning) OR cmseek.py -u [...] + +Arguments: + + -u URL, --url URL Target Url + -h, --help Show this help message and exit + -v, --verbose Increase output verbosity + --version Show CMSeeK version and exit + --update Update CMSeeK (Requires git) + -r, --random-agent Use a random user agent + --user-agent USER_AGENT Specify custom user agent + --clear-result Delete all the scan result + """.format(cmseek_version,red, cln)) bye() def signal_handler(signal, frame): @@ -146,7 +146,7 @@ def statement(msg): print("[+] " + msg) def error(msg): - print(bold + red + "[❌] " + msg) + print(bold + red + "[x] " + msg) # switched to x from ❌ .. def warning(msg): print(bold + yellow + "[!] " + cln + msg) @@ -433,7 +433,7 @@ def getsource(url, ua): ## (url, useragent) return type: ({0/1/2},{error/source ) cj = CookieJar() opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) - with opener.open(ckreq, timeout=4) as response: + with opener.open(ckreq, timeout=8) as response: scode = response.read().decode() headers = str(response.info()) rurl = response.geturl() @@ -444,6 +444,16 @@ def getsource(url, ua): ## (url, useragent) return type: ({0/1/2},{error/source r = ['2', e, '', ''] ## 'error code', 'error message', 'empty' return r +def check_url(url,ua): + request = urllib.request.Request(url) + request.add_header('User-Agent', ua) + request.get_method = lambda: 'HEAD' + try: + urllib.request.urlopen(request) + return '1' + except urllib.request.HTTPError: + return '0' + def wpbrutesrc(url, user, pwd): redirecto = url + '/wp-admin/' url = url + '/wp-login.php' diff --git a/cmseekdb/cmss.py b/cmseekdb/cmss.py index 8b979d9a..4ad844c0 100644 --- a/cmseekdb/cmss.py +++ b/cmseekdb/cmss.py @@ -153,8 +153,8 @@ joom = { 'name':'Joomla', 'url':'https://joomla.org', - 'vd':'0', - 'deeps':'0' + 'vd':'1', + 'deeps':'1' } oc = { 'name':'OpenCart', diff --git a/cmseekdb/core.py b/cmseekdb/core.py index ed17ea20..abd937fc 100644 --- a/cmseekdb/core.py +++ b/cmseekdb/core.py @@ -6,6 +6,7 @@ import importlib from datetime import datetime +import VersionDetect.detect as version_detect # Version detection import deepscans.core as advanced # Deep scan and Version Detection functions import cmseekdb.basic as cmseek # All the basic functions import cmseekdb.sc as source # Contains function to detect cms from source code @@ -49,9 +50,13 @@ def main_proc(site,cua): else: cmseek.statement("CMS Version is detectable, detecting CMS Version") ### Detect version + cms_version = version_detect.start(c1[1], site, cua, '1', scode) print('\n') cmseek.result('',"CMS Name: " + cmseek.bold + cmseek.fgreen + cka['name'] + cmseek.cln) cmseek.update_log('cms_name',cka['name']) # update log + if cms_version != '0': + cmseek.result('',"CMS Version: " + cmseek.bold + cmseek.fgreen + cms_version + cmseek.cln) + cmseek.update_log('cms_version',cms_version) # update log cmseek.result('',"CMS Link: " + cmseek.bold + cmseek.fgreen + cka['url'] + cmseek.cln) cmseek.update_log('cms_url',cka['url']) # update log # return @@ -79,9 +84,13 @@ def main_proc(site,cua): else: cmseek.statement("CMS Version is detectable, detecting CMS Version") ### Detect version + cms_version = version_detect.start(c21[1], site, cua, '1', scode) print('\n') cmseek.result('',"CMS Name: " + cmseek.bold + cmseek.fgreen + cka['name'] + cmseek.cln) cmseek.update_log('cms_name',cka['name']) # update log + if cms_version != '0': + cmseek.result('',"CMS Version: " + cmseek.bold + cmseek.fgreen + cms_version + cmseek.cln) + cmseek.update_log('cms_version',cms_version) # update log cmseek.result('',"CMS Link: " + cmseek.bold + cmseek.fgreen + cka['url'] + cmseek.cln) cmseek.update_log('cms_url',cka['url']) # update log # return @@ -107,10 +116,14 @@ def main_proc(site,cua): cmseek.update_log('cms_url',cka['url']) # update log else: cmseek.statement("CMS Version is detectable, detecting CMS Version") + cms_version = version_detect.start(c22[1], site, cua, '1', scode) ### Detect version print('\n') cmseek.result('',"CMS Name: " + cmseek.bold + cmseek.fgreen + cka['name'] + cmseek.cln) cmseek.update_log('cms_name',cka['name']) # update log + if cms_version != '0': + cmseek.result('',"CMS Version: " + cmseek.bold + cmseek.fgreen + cms_version + cmseek.cln) + cmseek.update_log('cms_version',cms_version) # update log cmseek.result('',"CMS Link: " + cmseek.bold + cmseek.fgreen + cka['url'] + cmseek.cln) cmseek.update_log('cms_url',cka['url']) # update log return @@ -139,11 +152,15 @@ def main_proc(site,cua): cmseek.result('',"CMS Link: " + cmseek.bold + cmseek.fgreen + cka['url'] + cmseek.cln) cmseek.update_log('cms_url',cka['url']) # update log else: + cms_version = version_detect.start(c22[1], site, cua, '0', scode) cmseek.statement("CMS Version is detectable, detecting CMS Version") ### Detect version print('\n') cmseek.result('',"CMS Name: " + cmseek.bold + cmseek.fgreen + cka['name'] + cmseek.cln) cmseek.update_log('cms_name',cka['name']) # update log + if cms_version != '0': + cmseek.result('',"CMS Version: " + cmseek.bold + cmseek.fgreen + cms_version + cmseek.cln) + cmseek.update_log('cms_version',cms_version) # update log cmseek.result('',"CMS Link: " + cmseek.bold + cmseek.fgreen + cka['url'] + cmseek.cln) cmseek.update_log('cms_url',cka['url']) # update log return diff --git a/cmseekdb/sc.py b/cmseekdb/sc.py index 8e215b93..7364a83e 100644 --- a/cmseekdb/sc.py +++ b/cmseekdb/sc.py @@ -78,7 +78,7 @@ def generator(s): ## CMS Check using generator meta tags # Ametys CMS r = ['1','amcms'] return r - elif '' in file_check[1]: + cmseek.success('Directory listing enabled in: ' + cmseek.bold + cmseek.fgreen + file + cmseek.cln) + joom_dir_found += 1 + joom_dirs.append(file) + +def start(url, ua): + directory_files = ['administrator/components','components','administrator/modules','modules','administrator/templates','templates','cache','images','includes','language','media','templates','tmp','images/stories','images/banners'] + threads = [threading.Thread(target=check_directory, args=(url, file ,ua)) for file in directory_files] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + global joom_dir_found, joom_dirs + return [joom_dir_found, joom_dirs] diff --git a/deepscans/joom/init.py b/deepscans/joom/init.py new file mode 100644 index 00000000..32ba25df --- /dev/null +++ b/deepscans/joom/init.py @@ -0,0 +1,146 @@ +# Joomla DeepScan +# Rev 1 +# Props to joomscan.. big time! https://github.com/rezasp/joomscan + +import os +import cmseekdb.basic as cmseek +import VersionDetect.joom as version_detect +import deepscans.joom.backups as backup_finder +import deepscans.joom.config_leak as config_check +import deepscans.joom.core_vuln as core_vuln +import deepscans.joom.admin_finder as admin_finder +import deepscans.joom.check_debug as check_debug +import deepscans.joom.dir_list as dir_list +import deepscans.joom.check_reg as user_registration + +def start(id, url, ua, ga, source): + + # init variables + vuln_detection = '0' + vuln_count = 0 + joom_vulns = [] + + # Version Detection + version = version_detect.start(id, url, ua, ga, source) + + # Detecting joomla core vulnerabilities + jcv = core_vuln.start(version) + vuln_detection = jcv[0] + vuln_count = jcv[1] + joom_vulns = jcv[2] + + # README.txt + readmesrc = cmseek.getsource(url + '/README.txt', ua) + if readmesrc[0] != '1': ## something went wrong while getting the source codes + cmseek.statement("Couldn't get readme file's source code most likely it's not present") + readmefile = '0' + elif 'This is a Joomla!' in readmesrc[1]: + cmseek.info('README.txt file found') + readmefile = '1' # Readme file present + else: + readmefile = '2' # Readme file found but most likely it's not of joomla + + # Debug Mode + cmseek.info('Checking debug mode status') + debug_mode = check_debug.start(source) + + # Check user registration status + cmseek.statement('Checking if user registration is enabled') + registration = user_registration.start(url,ua) + + # Find admin url + cmseek.info('Locating admin url') + admin = admin_finder.start(url,ua) + + # Backups check + cmseek.info('Checking for common Backups') + backups = backup_finder.start(url,ua) + + # Check Potential configuration file leak + cmseek.info('Looking for potential config leak') + configs = config_check.start(url,ua) + + # Checking for directory listing + cmseek.statement('Checking for directory listing') + directories = dir_list.start(url, ua) + + ### THE RESULTS START FROM HERE + + cmseek.clearscreen() + cmseek.banner("Deep Scan Results") + cmseek.result('Target: ',url) + cmseek.result("Detected CMS: ", 'Joomla') + cmseek.update_log('cms_name','joomla') # update log + cmseek.result("CMS URL: ", "https://joomla.org") + cmseek.update_log('cms_url', "https://joomla.org") # update log + + if version != '0': + cmseek.result("Joomla Version: ", version) + cmseek.update_log('joomla_version', version) + + if registration[0] == '1': + cmseek.result('User registration enabled: ', registration[1]) + cmseek.update_log('user_registration_url', registration[1]) + + if debug_mode =='1': + cmseek.result('Debug mode enabled', '') + cmseek.update_log('joomla_debug_mode', 'enabled') + else: + cmseek.update_log('joomla_debug_mode', 'disabled') + + if readmefile == '1': + cmseek.result('Readme file: ', url + '/README.txt') + cmseek.update_log('joomla_readme_file', url + '/README.txt') + + if admin[0] > 0: + cmseek.result('Admin URL: ', url + admin[1][0]) + admin_log = '' + for adm in admin[1]: + admin_log += url+ adm + ',' + # print(cmseek.bold + cmseek.fgreen + " [B] " + cmseek.cln + url+ adm) + cmseek.update_log('joomla_backup_files', admin_log) + print('\n') + + if directories[0] > 0: + cmseek.result('Open directories: ', str(directories[0])) + cmseek.success('Open directory url: ') + dirs = '' + for dir in directories[1]: + dirs += url + dir + ',' + print(cmseek.bold + cmseek.fgreen + " [>] " + cmseek.cln + url + dir) + cmseek.update_log('directory_listing', dirs) + print('\n') + + if backups[0] > 0: + cmseek.result('Found potential backup file: ', str(backups[0])) + cmseek.success('Backup URLs: ') + bkup_log = '' + for backup in backups[1]: + bkup_log += url+ backup + ',' + print(cmseek.bold + cmseek.fgreen + " [B] " + cmseek.cln + url+ backup) + cmseek.update_log('joomla_backup_files', bkup_log) + print('\n') + + if configs[0] > 0: + cmseek.result('Found potential Config file: ', str(configs[0])) + cmseek.success('Config URLs: ') + conf_log = '' + for config in configs[1]: + conf_log += url+ config + ',' + print(cmseek.bold + cmseek.fgreen + " [c] " + cmseek.cln + url+ config) + cmseek.update_log('joomla_config_files', conf_log) + print('\n') + + if vuln_detection == '1' and vuln_count > 0: + cmseek.result('Total joomla core vulnerabilities: ', str(vuln_count)) + cmseek.info('Vulnerabilities found: \n') + for vuln in joom_vulns: + vuln = vuln.replace('\\n', cmseek.cln + '\n ') + print(cmseek.bold + cmseek.red + '[v] ' + vuln) + print('\n') + elif vuln_detection == '2': + cmseek.warning('Couldn\'t find core vulnerabilities, No VERSION detected') + elif vuln_detection == '3': + cmseek.error('Core vulnerability databse not found!') + else: + cmseek.warning('No core vulnerabilities detected!') diff --git a/deepscans/wp/init.py b/deepscans/wp/init.py index f50e73b0..4f6f3c4f 100644 --- a/deepscans/wp/init.py +++ b/deepscans/wp/init.py @@ -1,36 +1,12 @@ ### All WordPress DeepScan stuffs goes here import cmseekdb.basic as cmseek ## Good old module -import re ## Comes in handy while detecting version -import json ## For parsing the wpvulndb result -import multiprocessing ## Let's speed things up a lil bit (actually a hell lot faster) shell we? -from functools import partial ## needed somewhere :/ - import VersionDetect.wp as wordpress_version_detect import deepscans.wp.userenum as wp_user_enum import deepscans.wp.vuln as wp_vuln_scan import deepscans.wp.pluginsdetect as wp_plugins_enum import deepscans.wp.themedetect as wp_theme_enum -def wpauthorenum(ua, url, param): - ## WordPress function for Collecting usernames from author Parameter - ## Had to create a different function to avoid some pickle issues - param = param + 1 - i = str(param) - # cmseek.statement('Checking for ?author=' + i) # Looks Ugly.. enable if you want over verbose result - authorsrc = cmseek.getsource(url + '/?author=' + i, ua) - if authorsrc[0] == '1' and '/author/' in authorsrc[3]: ## Detection using the url redirection - author = re.findall(r'/author/(.*?)/', str(authorsrc[3])) - if author != []: - cmseek.success('Found user from redirection: ' + cmseek.bold + author[0] + cmseek.cln) - return author[0] - elif authorsrc[0] == '1' and '/author/' in authorsrc[1]: - author = re.findall(r'/author/(.*?)/', str(authorsrc[1])) - if author != []: - cmseek.success('Found user from source code: ' + cmseek.bold + author[0] + cmseek.cln) - return author[0] - - def start(id, url, ua, ga, source): ## ({ID of the cms}, {url of target}, {User Agent}, {is Generator Meta tag available [0/1]}, {Source code}) ## Do shits later [update from later: i forgot what shit i had to do ;___;] if id == "wp": @@ -88,7 +64,7 @@ def start(id, url, ua, ga, source): ## ({ID of the cms}, {url of target}, {User plugins_found = plug_enum[0] plugins = plug_enum[1] - ## Plugins Enumeration + ## Themes Enumeration theme_enum = wp_theme_enum.start(source) themes_found = theme_enum[0] themes = theme_enum[1]