예제 #1
0
    def copysource(self, result_directory):
        # copies all the json, html, css and js files and saves them to the result directory
        # create content for the source.json file
        source_json = {}

        # Copies all the json, css, js files to the result directory for future reference
        for file in self.files:
            if file['type'] == 'json' or file['type'] == 'html' or file[
                    'type'] == 'css' or file['type'] == 'js':
                file_path = file['path']
                new_path = helper.fixpath(result_directory + '/' +
                                          file['name'] + '.src')
                file_name = file['name']
                if os.path.isfile(file_path):
                    # Checks if file present
                    shutil.copyfile(file_path, new_path)
                    core.updatelog('Copied ' + file_path + ' to ' + new_path)
                    # append this to source_json dict
                    rel_path = os.path.relpath(file_path, self.directory)
                    file_size = str(os.path.getsize(file_path) >> 10) + ' KB'
                    if file['type'] == 'js':
                        # Retire js scan
                        core.updatelog(
                            'Running retirejs vulnerability scan on: ' +
                            file_name)
                        try:
                            with open(file_path, 'r') as fc:
                                file_content = fc.read()
                                rjs_scan = retirejs.scan_file_content(
                                    file_content)
                                core.updatelog('Scan complete!')
                        except Exception as e:
                            core.updatelog(
                                'Error {0} while running retirejs scan on {1}'.
                                format(str(e), file_name))
                            rjs_scan = []
                        source_json[file['id']] = ({
                            'id': file['id'],
                            'file_name': file_name,
                            'location': new_path,
                            'relative_path': rel_path,
                            'file_size': file_size,
                            'retirejs_result': rjs_scan
                        })
                    else:
                        source_json[file['id']] = ({
                            'id': file['id'],
                            'file_name': file_name,
                            'location': new_path,
                            'relative_path': rel_path,
                            'file_size': file_size
                        })

        # write all the changes to source.json
        source_file = helper.fixpath(result_directory + '/source.json')
        sf = open(source_file, 'w+')
        sf.write(json.dumps(source_json, indent=4, sort_keys=True))
        sf.close()
        core.updatelog('Saved sources to: ' + source_file)
        return True
예제 #2
0
    def extract_chromium_plugins(self, parent_dir):
        ret_list = []

        extension_dirs = os.listdir(parent_dir)
        for extension_dir in extension_dirs:
            extension_path = os.path.join(parent_dir, extension_dir)

            if not os.path.isdir(extension_path):
                core.updatelog("Invalid extension directory: " +
                               extension_path)
                continue

            extension_vers = os.listdir(extension_path)

            for ver in extension_vers:
                manifest_file = helper.fixpath(extension_path + "/" + ver +
                                               "/manifest.json")
                if not os.path.isfile(manifest_file):
                    core.updatelog("Invalid extension directory: " +
                                   extension_path)
                    continue
                ext_name = core.GetNameFromManifest(manifest_file)
                if ext_name:
                    ext_version = ver.split('_')[0]
                    ext_name = ext_name + ' version ' + ext_version
                    # small hack to not let commas f**k around with the path
                    ext_name = ext_name.replace(",", ",")
                    ret_list.append(ext_name + ',' +
                                    helper.fixpath(extension_path + "/" + ver))

        return ret_list
예제 #3
0
def analyzelocalfirefoxextension(path):
    if os.path.isfile(path) and path.endswith('.xpi'):
        # Extract the .xpi file to a temp directory in lab directory
        # Analyze the extracted directory
        # delete the temp directory
        extract_directory = helper.fixpath(core.lab_path + '/temp_extract_directory')

        try:
            core.updatelog('Unzipping ' + path + ' to: ' + extract_directory)
            zip_contents = zipfile.ZipFile(path, 'r')
            zip_contents.extractall(extract_directory)
            zip_contents.close()
            core.updatelog('Unzipping complete')
        except Exception as e:
            helper.fixpath('Something went wrong while unzipping ' + path + ' to ' + extract_directory)
            logging.error(traceback.format_exc())
            return False
        
        analysis_status = analysis.analyze(extract_directory, 'Local Firefox Extension')

        if 'error:' in analysis_status:
            core.updatelog('Something went wrong while analysis... deleting temporary extract directory')
        else:
            core.updatelog('Scanning complete... Deleting temporary extract directory')
        
        shutil.rmtree(extract_directory)
        core.updatelog('Successfully deleted: ' + extract_directory)
        return analysis_status

    else:
        core.updatelog('[analyzelocalfirefoxextension] Invalid local firefox extension path: ' + path)
예제 #4
0
    def googlechrome(self):

        # TODO: add support for mac os

        chrome_directory = ""
        if self.os == 'windows':
            chrome_directory = helper.fixpath(
                self.user_directory +
                '\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Extensions'
            )
        elif self.os == 'linux':
            chrome_directory = helper.fixpath(
                self.user_directory +
                '/.config/google-chrome/Default/Extensions')
        elif self.os == 'osx':
            chrome_directory = helper.fixpath(
                self.user_directory +
                '/Library/Application Support/Google/Chrome/Profile 1/Extensions'
            )

        if chrome_directory != "":
            if os.path.isdir(chrome_directory):
                core.updatelog('Found Google chrome extension directory: ' +
                               chrome_directory)
                return self.extract_chromium_plugins(chrome_directory)
            else:
                core.updatelog('Could not find google chrome directory!')
                return False
        else:
            core.updatelog('Unsupported OS')
예제 #5
0
    def savereport(self):
        try:
            # Gen scan id
            report = core.report
            reportids = core.reportids

            curid = 'EXA' + time.strftime("%Y%j%H%M%S", time.gmtime())
            core.updatelog('Saving Analysis with ID: ' + curid)
            #saveas = curid + '.json'

            # create result directory
            result_directory = os.path.join(core.reports_path, curid)
            if not os.path.exists(result_directory):
                os.makedirs(result_directory)
                core.updatelog('Created Result directory: ' + result_directory)

            # create the basic report json file: extanalysis_report.json
            report_file = helper.fixpath(result_directory + '/extanalysis_report.json')
            f = open(report_file, 'w+')
            reportfinal = json.dumps(report, sort_keys=True, indent=4)
            f.write(reportfinal)
            f.close()
            core.updatelog('Saved report file: ' + report_file)

            # get file list and create the graph
            graph_data_stat = self.creategraphdata()
            if graph_data_stat != False:
                graph_file = helper.fixpath(result_directory + '/graph.data')
                graph_file_create = open(graph_file, 'w+')
                graph_file_create.write(self.nodes + '\n' + self.edges)
                core.updatelog('Saved graph data to: ' + graph_file)
            else:
                core.updatelog('Could not save graph data!')

            # save the source files
            # print(result_directory)
            self.copysource(result_directory)

            '''
            make an entry in the reports index file
            '''
            # use <reports_path> as a variable!
            relative_result_path = result_directory.replace(core.reports_path, '<reports_path>')
            reportindex = {"name":report['name'], "version":report['version'], "id": curid, "report_directory":relative_result_path, "time":time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())}
            reportids['reports'].append(reportindex)
            indexfile = core.report_index
            g = open(indexfile, 'w+')
            indexfinal = json.dumps(reportids, sort_keys=True, indent=4)
            g.write(indexfinal)
            g.close()
            core.updatelog('Updated report index')

            # Clear global report value for new scan
            core.report = {}

            return(curid)
        except Exception as e:
            logging.error(traceback.format_exc())
            print('Something went wrong while saving result. Error: ' + str(e))
예제 #6
0
    def googlechrome(self):

        # TODO: add support for mac os

        chrome_directory = ""
        if self.os == 'windows':
            chrome_directory = helper.fixpath(
                self.user_directory +
                '\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Extensions'
            )
        elif self.os == 'linux':
            chrome_directory = helper.fixpath(
                self.user_directory +
                '/.config/google-chrome/Default/Extensions')

        if chrome_directory != "":
            if os.path.isdir(chrome_directory):
                core.updatelog('Found Google chrome extension directory: ' +
                               chrome_directory)
                extension_dirs = os.listdir(chrome_directory)
                for extension_dir in extension_dirs:
                    # print('\n\n')
                    if os.path.isdir(
                            os.path.join(chrome_directory, extension_dir)):
                        # Every extension directory is like this: Extension/<id>/<version>/{contents}
                        extension_path = os.path.join(chrome_directory,
                                                      extension_dir)
                        extension_vers = os.listdir(extension_path)
                        for ver in extension_vers:
                            manifest_file = helper.fixpath(extension_path +
                                                           "/" + ver +
                                                           '/manifest.json')
                            if os.path.isfile(manifest_file):
                                ext_name = core.GetNameFromManifest(
                                    manifest_file)
                                if ext_name != False and ext_name != None:
                                    # append version with name
                                    ext_version = ver.split('_')[0]
                                    ext_name = ext_name + ' version ' + ext_version
                                    self.chrome_extensions.append(
                                        ext_name + ',' +
                                        helper.fixpath(extension_path + "/" +
                                                       ver))
                                else:
                                    core.updatelog(
                                        'Could not determine extension name.. skipping local chrome extension'
                                    )
                            else:
                                core.updatelog(
                                    'Invalid extension directory: ' +
                                    extension_path)
                return self.chrome_extensions
            else:
                core.updatelog('Could not find google chrome directory!')
                return False
        else:
            core.updatelog('Unsupported OS')
예제 #7
0
def home():
    core.updatelog('Accessed Main page')
    lic = open(helper.fixpath(core.path + '/LICENSE'), 'r')
    license_text = lic.read()
    cred = open(helper.fixpath(core.path + '/CREDITS'), 'r')
    credits_text = cred.read()
    return render_template("index.html",
                           report_dir=core.reports_path,
                           lab_dir=core.lab_path,
                           license_text=license_text,
                           credits_text=credits_text,
                           virustotal_api=core.virustotal_api)
예제 #8
0
def downloadFirefox(url):
    if 'addons.mozilla.org' not in url:
        core.updatelog('Invalid Firefox addon URL')
        return False
    else:
        try:
            test = urllib.request.Request(url)
            test.add_header(
                'User-Agent',
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0'
            )
            source = urllib.request.urlopen(test)
            source_code = source.read().decode('utf-8')
            xpi_file = re.findall(
                '<a class="Button Button--action AMInstallButton-button Button--puffy" href="(.*?).xpi?',
                source_code)[0]
            core.updatelog('Found link for xpi file: ' + xpi_file + '.xpi')
            name = xpi_file.split('/')[-1]
            xpi_file += '.xpi'
            save_path = helper.fixpath(core.lab_path + '/' + name + '.xpi')
            core.updatelog("Downloader says: save_path is " + save_path)
            try:
                urllib.request.urlretrieve(xpi_file, save_path)
                core.updatelog("Extension downloaded successfully: " +
                               save_path)
                return name
            except Exception as e:
                core.updatelog("Error while downloading xpi file: " + xpi_file)
                print(e)
                return False
        except Exception:
            core.updatelog(
                'Something went wrong while getting download link for xpi file'
            )
예제 #9
0
def get_result_info(analysis_id):
    '''
    GET INFO ABOUT A SPECIFIC ANALYSIS INFO
    RESPONSE = [TRUE/FALSE, JSON_LOADED_RESULT/ERROR_MSG]
    '''
    global reportids, report_index, reports_path
    if reportids == {}:
        # index not loaded.. let's load it up
        indexs = open(report_index, 'r')
        indexs = json.loads(indexs.read())
        reportids = indexs

    reports = reportids['reports']
    if analysis_id in str(reports):
        for report in reports:
            if report['id'] == analysis_id:
                report['report_directory'] = helper.fixpath(
                    report['report_directory'].replace('<reports_path>',
                                                       reports_path).replace(
                                                           '\\', '/'))
                return [True, report]
        return [False, 'Analysis ID mismatch: {0}'.format(analysis_id)]
    else:
        return [
            False,
            'Analysis ID {0} not found in result index!'.format(analysis_id)
        ]
예제 #10
0
def sort_files(extension_dir):
    try:
        extract_dir = helper.fixpath(core.lab_path + '/' + extension_dir)
        html_files = []
        js_files = []
        css_files = []
        static_files = []
        other_files = [] # File extensions that are not listed above
        for root, dirs, files in os.walk(extract_dir):
            for file in files:
                filepath = os.path.join(root, file)
                file = file.lower()
                if file.endswith('.html'):
                    core.updatelog('Discovered html file: ' + file)
                    html_files.append(filepath)
                elif file.endswith('.js'):
                    core.updatelog('Discovered js file: ' + file)
                    js_files.append(filepath)
                elif file.endswith('.css'):
                    core.updatelog('Discovered css file: ' + file)
                    css_files.append(filepath)
                elif file.endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.svg')):
                    core.updatelog('Discovered static file: ' + file)
                    static_files.append(filepath)
                else:
                    other_files.append(filepath)
        core.updatelog('Sorted files: ' + extension_dir)
        #core.updatelog('HTML Files: {0}, JS Files: {1}, CSS Files: {2}, Static Files: {3}, Other Files: {4}'.format(str(count(html_files)), str(count(js_files)), str(count(css_files)), str(count(static_files)), str(count(other_files)))
        r = {'html_files': html_files, 'js_files': js_files, 'css_files': css_files, 'static_files': static_files, 'other_files': other_files}
        return r
    except Exception as e:
        core.updatelog('Error while sorting files: ' + str(e))
        return False
예제 #11
0
def get_country(ip):
    '''
    Gets Country and country code from given IP.
    Parameters = ip = ip address for lookup
    Response = [True, {country_code}, {country_name}] or [False, {ERR_MSG}]
    Needs maxminddb for fast performance
    '''
    core.updatelog('Getting country from IP: ' + ip)
    try:
        # If maxminddb module is installed we don't have to query online services to get the country code hence saving a lot of time
        import maxminddb
        try:
            core.updatelog('Getting country from local DB')
            reader = maxminddb.open_database(helper.fixpath(core.path + '/db/geoip.mmdb'))
            ip_info = reader.get(ip)
            iso_code = ip_info['country']['iso_code'].lower()
            country = ip_info['country']['names']['en']
            return [True, iso_code, country]
        except Exception as e:
            core.updatelog('Something went wrong while getting country from ip {0}! Error: {1}'.format(ip, str(e)))
            logging.error(traceback.format_exc())
            return [False, str(e)]
    except:
        core.updatelog('maxminddb module not installed! Using online service to get Country from IP')
        core.updatelog('To save time in future analysis; install maxminddb by: pip3 install maxminddb')
        import core.scans as scan
        gip = scan.geoip(ip)
        if gip[0]:
           geoip = gip[1]
           return [True, geoip['country'].lower(), geoip['country_name']] 
        else:
            return [False, gip[1]]
예제 #12
0
def update():
    '''
    Updates ExtAnalysis
    1. Create the updater child script and save it to temp directory
    2. End self process and start the child script
    '''
    print("\n[i] Creating Updater file")

    child_script = open(helper.fixpath(core.path + '/db/updater.py'), 'r')
    child_script = child_script.read()

    src = child_script.replace('<current_extanalysis_directory>',
                               core.path.replace('\\', '\\\\'))
    src = src.replace('<github_zip_url>', core.github_zip)

    print('[i] Moving updater file to temp directory')
    temp_dir = tempfile.gettempdir()

    updater_script = helper.fixpath(temp_dir + '/update_extanalysis.py')
    f = open(updater_script, 'w+')
    f.write(src)
    f.close()

    python_loc = sys.executable

    print('[i] Starting Updater script')

    if sys.platform == 'win32':
        os.chdir(temp_dir)
        command = [python_loc, 'update_extanalysis.py']
        subprocess.Popen(command,
                         creationflags=subprocess.CREATE_NEW_CONSOLE,
                         shell=False)
        print(
            '[i] Killing self... Next time we meet I will be a better version of myself ;)'
        )
        exit()
    else:
        os.chdir(temp_dir)
        command = ['x-terminal-emulator', '-e', python_loc, updater_script]
        subprocess.Popen(command, shell=False)
        print(
            '[i] Killing self... Next time we meet I will be a better version of myself ;)'
        )
        exit()
예제 #13
0
    def braveLocalExtensionsCheck(self):
        brave_directory = ""

        if self.os == 'windows':
            brave_directory = helper.fixpath(
                self.user_directory +
                '\\AppData\\Local\\BraveSoftware\\Brave-Browser\\User Data\\Default\\Extensions'
            )
        elif self.os == 'linux':
            brave_directory = helper.fixpath(
                self.user_directory +
                '/.config/BraveSoftware/Brave-Browser/Default/Extensions')

        if brave_directory != "":
            if os.path.isdir(brave_directory):
                core.updatelog('Found Brave extension directory: ' +
                               brave_directory)
                return self.extract_chromium_plugins(brave_directory)
            else:
                core.updatelog('Could not find Brave extension directory!')
                return False
        else:
            core.updatelog('Unsupported OS')
예제 #14
0
def extract_urls(file_path):
    updatelog('Extracting URLs From: ' + file_path)
    urls = []
    try:
        cnt = open(helper.fixpath(file_path), 'r', encoding='utf8')
        contents = cnt.read()
        curls = re.findall('(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?', contents)
        for url in curls:
            urls.append(url[0]+'://'+url[1]+url[2])
            updatelog('Found url: ' + url[0]+'://'+url[1]+url[2])
        urls = list(set(urls))
        return(urls)
    except Exception as e:
        updatelog('error: Something went wrong while reading file')
        updatelog('ERROR: ' + str(e))
        logging.error(traceback.format_exc())
        return []
예제 #15
0
    def vivaldi_local_extensions_check(self):
        vivaldi_dir = ""
        if self.os == 'windows':
            vivaldi_dir = helper.fixpath(
                self.user_directory +
                '\\AppData\\Local\\Vivaldi\\User Data\\Default\\Extensions')

        if vivaldi_dir == "":
            core.updatelog('Unsupported OS')
            return

        if not os.path.isdir(vivaldi_dir):
            core.updatelog("Couldn't find Vivaldi Extension directory!")
            return

        # --
        core.updatelog('Found Vivaldi extension directory: ' + vivaldi_dir)
        return self.extract_chromium_plugins(vivaldi_dir)
예제 #16
0
def download(id, name=""):
    ext_id = id
    if name == "":
        save_name = ext_id
    else:
        save_name = name
    save_path = helper.fixpath(core.lab_path + '/' + save_name + '.crx')
    core.updatelog("Downloader says: save_path is " + save_path)
    dl_url = "https://clients2.google.com/service/update2/crx?response=redirect&x=id%3D" + ext_id + "%26uc&prodversion=32"
    print("Download URL: " + dl_url)

    try:
        urllib.request.urlretrieve(dl_url, save_path)
        core.updatelog("Extension downloaded successfully: " + save_path)
        return save_name
    except Exception as e:
        core.updatelog("Error in downloader.py")
        print(e)
        return False
예제 #17
0
def download(id, name=""):
    ext_id = id
    if name == "":
        save_name = ext_id
    else:
        save_name = name
    save_path = helper.fixpath(core.lab_path + '/' + save_name + '.crx')
    core.updatelog("Downloader says: save_path is " + save_path)
    # dl_url = "https://clients2.google.com/service/update2/crx?response=redirect&x=id%3D" + ext_id + "%26uc&prodversion=32"
    # new download URL, issue #13
    dl_url = "https://clients2.google.com/service/update2/crx?response=redirect&os=win&arch=x86-64&os_arch=x86-64&nacl_arch=x86-64&prod=chromecrx&prodchannel=unknown&prodversion=81.0.4044.138&acceptformat=crx2,crx3&x=id%3D" + ext_id + "%26uc"
    print("Download URL: " + dl_url)

    try:
        urllib.request.urlretrieve(dl_url, save_path)
        core.updatelog("Extension downloaded successfully: " + save_path)
        return save_name
    except Exception as e:
        core.updatelog("Error in downloader.py")
        print(e)
        return False
예제 #18
0
def download(id, name=""):  #chrome
    ext_id = id
    if name == "":
        save_name = ext_id
    else:
        save_name = name
    save_path = helper.fixpath(core.lab_path + '/' + save_name + '.crx')
    core.updatelog("Downloader says: save_path is " + save_path)
    if sys.platform == 'win32':
        os = 'windows'
        file = open(
            ' '
        )  #path or command to find where windows installed latest version is located (wmi or registry seems to be easiest)
        version = file.readline()  #grabs the chrome version
        #dl_url = 'http://clients2.google.com/service/update2/crx?response=redirect&prodversion=' + version + '&acceptformat=crx2,crx3&x=id%3D' + ext_id + '%26uc'
    elif sys.platform == 'darwin':
        os = 'osx'
        username = getpass.getuser(
        )  #this gets the current users username as it is in the system path on MAC
        file = open(
            '/Users/' + username +
            '/Library/Application Support/Google/Chrome/Last Version', 'r'
        )  # combining the username into the macOS path so we can open the file.
        version = file.readline(
        )  # this grabs the version of chrome out of the local file
        dl_url = 'http://clients2.google.com/service/update2/crx?response=redirect&prodversion=' + version + '&acceptformat=crx2,crx3&x=id%3D' + ext_id + '%26uc'
    elif sys.platform == 'linux' or sys.platform == 'linux2':
        os = 'linux'
        dl_url = ''  #need to craft linux url specific to finding the latest version of chrome
    print("Download URL: " + dl_url)

    try:
        urllib.request.urlretrieve(dl_url, save_path)
        core.updatelog("Extension downloaded successfully: " + save_path)
        return save_name
    except Exception as e:
        core.updatelog("Error in downloader.py")
        print(e)
        return False
예제 #19
0
    def firefox(self):
        # read the profiles.ini
        # check for previous list and create new if not found [list = extanalysis.json]
        # Get a list of all the xpi files
        # Unzip them
        # Get all their names from manifest.json
        # update the list
        firefox_directory = ""
        default_profile_path = ""

        if self.os == 'windows':
            firefox_directory = helper.fixpath(
                self.user_directory + '\\AppData\\Roaming\\Mozilla\\Firefox')

            if os.path.isdir(firefox_directory):
                # firfox installed
                firefox_profile = helper.fixpath(firefox_directory +
                                                 '\\profiles.ini')
                if os.path.isfile(firefox_profile):
                    # found firefox profiles.ini
                    try:
                        firefox_config = configparser.SafeConfigParser()
                        with open(firefox_profile, 'rU') as ini_source:
                            firefox_config.readfp(ini_source)
                        default_profile_path = os.path.normpath(
                            os.path.join(
                                firefox_directory,
                                firefox_config.get('Profile0', 'Path')))
                        core.updatelog('Found firefox profile path: ' +
                                       default_profile_path)
                    except Exception as e:
                        core.updatelog(
                            'Something went wrong while reading firefox profiles.ini'
                        )
                        logging.error(traceback.format_exc())
                        return False
                else:
                    core.updatelog(
                        'Could not find profiles.ini ExtAnalysis can\'t analyze local firefox extensions'
                    )
                    return False
            else:
                # Could not find firefox directory
                core.updatelog('Firefox installation could not be detected')
                return False
        elif self.os == 'linux':
            firefox_directory = helper.fixpath(self.user_directory +
                                               '/.mozilla/firefox/')
            if os.path.isdir(firefox_directory):
                # firfox installed
                firefox_profile = helper.fixpath(firefox_directory +
                                                 '/profiles.ini')
                if os.path.isfile(firefox_profile):
                    # found firefox profiles.ini
                    try:
                        firefox_config = configparser.SafeConfigParser()
                        with open(firefox_profile, 'rU') as ini_source:
                            firefox_config.readfp(ini_source)
                        default_profile_path = os.path.normpath(
                            os.path.join(
                                firefox_directory,
                                firefox_config.get('Profile0', 'Path')))
                        core.updatelog('Found firefox profile path: ' +
                                       default_profile_path)
                    except Exception as e:
                        core.updatelog(
                            'Something went wrong while reading firefox profiles.ini'
                        )
                        logging.error(traceback.format_exc())
                        return False
                else:
                    core.updatelog(
                        'Could not find profiles.ini ExtAnalysis can\'t analyze local firefox extensions'
                    )
                    return False
            else:
                # Could not find firefox directory
                core.updatelog('Firefox installation could not be detected')
                return False

        if default_profile_path != "":
            if os.path.isdir(default_profile_path):
                # profile path is valid
                firefox_extension_directory = os.path.join(
                    default_profile_path, 'extensions')
                if os.path.join(firefox_extension_directory):
                    unfiltered_files = os.listdir(firefox_extension_directory)
                    xpi_files = []
                    for afile in unfiltered_files:
                        if afile.endswith('.xpi') and os.path.isfile(
                                os.path.join(firefox_extension_directory,
                                             afile)):
                            xpi_files.append(afile)
                    core.updatelog('xpi list generated')
                else:
                    core.updatelog(
                        'extensions directory could not be found inside firefox default profile'
                    )
                    return False
            else:
                core.updatelog(
                    'Invalid firefox profile path... Can\'t get local firefox extensions'
                )
                return False
        else:
            core.updatelog('Could not find default profile path for firefox')
            return False

        if xpi_files != []:
            exta_firefox_list = os.path.join(firefox_extension_directory,
                                             'extanalysis.json')
            if os.path.isfile(exta_firefox_list):
                # found previous list
                core.updatelog(
                    'Found previous analysis log.. updating with current extensions'
                )
                listed_extensions = []
                list_file = open(exta_firefox_list, 'r')
                list_files = json.loads(list_file.read())
                for list_file in list_files['extensions']:
                    listed_extensions.append(list_file)
                for xpi_file in xpi_files:
                    if xpi_file not in listed_extensions:
                        core.updatelog('Inserting ' + xpi_file + ' into list')
                        self.createFirefoxListing(firefox_extension_directory,
                                                  xpi_file)
                # return True
            else:
                core.updatelog('Creating ExtAnalysis list file')
                list_file = open(exta_firefox_list, 'w+')
                list_file.write('{"extensions":{}}')
                list_file.close()
                core.updatelog('Updating list file with all xpi file infos')
                for xpi_file in xpi_files:
                    core.updatelog('Inserting ' + xpi_file + ' into list')
                    self.createFirefoxListing(firefox_extension_directory,
                                              xpi_file)
                # return True
        else:
            core.updatelog('No installed firefox extensions found!')
            return False

        # Read the final list and then create return list and return it
        firefox_extensions_list = []
        read_list = open(exta_firefox_list, 'r')
        read_list = json.loads(read_list.read())
        if read_list['extensions'] != {}:
            # There are some extensions
            for fext in read_list['extensions']:
                prepare_to_insert = read_list['extensions'][fext][
                    'name'] + ',' + read_list['extensions'][fext]['file']
                firefox_extensions_list.append(prepare_to_insert)
            return firefox_extensions_list
        else:
            core.updatelog(
                'ExtAnalysis could not find any local firefox extensions')
예제 #20
0
def view(query, allargs):

    if query == 'dlanalysis':
        try:
            extension_id = allargs.get('extid')
            saveas = ""
            try:
                saveas = allargs.get('savedir')
                if saveas == "" or saveas == " ":
                    saveas = extension_id
            except Exception as e:
                print('Save name not specified')
            try:
                download_log = download_extension.download(
                    extension_id, saveas)
                if download_log:
                    aok = analysis.analyze(saveas + '.crx',
                                           'Remote Google Chrome Extension')
                    return (aok)
                else:
                    return (
                        'error: Something went wrong while downloading extension'
                    )
            except Exception as e:
                core.updatelog(
                    'Something went wrong while downloading extension: ' +
                    str(e))
                return (
                    'error: Something went wrong while downloading extension, check log for more information'
                )

        except Exception as e:
            core.updatelog('Something went wrong: ' + str(e))
            return (
                'error: Something went wrong while downloading extension, check log for more information'
            )

    elif query == 'firefoxaddon':
        try:
            addonurl = allargs.get('addonurl')
            try:
                download_log = download_extension.downloadFirefox(addonurl)
                if download_log:
                    aok = analysis.analyze(download_log + '.xpi',
                                           'Remote Firefox Addon')
                    return (aok)
                else:
                    return (
                        'error: Something went wrong while downloading extension'
                    )
            except Exception as e:
                core.updatelog(
                    'Something went wrong while downloading extension: ' +
                    str(e))
                return (
                    'error: Something went wrong while downloading extension, check log for more information'
                )
        except Exception as e:
            core.updatelog('Something went wrong: ' + str(e))
            return (
                'error: Something went wrong while downloading extension, check log for more information'
            )

    elif query == 'results':
        reportids = core.reportids
        if reportids == {}:
            # Result index not loaded so let's load it and show em results
            core.updatelog('Reading report index and loading json')
            ridfile = core.report_index
            ridcnt = open(ridfile, 'r', encoding='utf8')
            ridcnt = ridcnt.read()
            reportids = json.loads(ridcnt)

        rd = "<table class='result-table' id='result-table'><thead><tr><th>Name</th><th>Version</th><th>Date</th><th>Actions</th></tr></thead><tbody>"
        for areport in reportids['reports']:
            report_name = areport['name']
            report_id = areport['id']
            report_date = areport['time']
            report_version = areport['version']
            rd += '<tr><td>' + report_name + '</td><td>' + report_version + '</td><td>' + report_date + '</td><td><button class="bttn-fill bttn-xs bttn-primary" onclick=viewResult(\'' + report_id + '\')><i class="fas fa-eye"></i> View</button> <button class="bttn-fill bttn-xs bttn-danger" onclick=deleteResult(\'' + report_id + '\')><i class="fas fa-trash"></i> Delete</button></td></tr>'
        return (rd + '</tbody></table><br>')

    elif query == 'getlocalextensions':
        try:
            browser = allargs.get('browser')
            if browser == 'googlechrome':
                import core.localextensions as localextensions
                lexts = localextensions.GetLocalExtensions()
                exts = ""
                exts = lexts.googlechrome()
                if exts != False and exts != [] and exts != None:
                    return_html = "<table class='result-table' id='result-table'><thead><tr><th>Extension Name</th><th>Action</th></tr></thead><tbody>"
                    for ext in exts:
                        ext_info = ext.split(',')
                        return_html += '<tr><td>' + ext_info[
                            0] + '</td><td><button class="bttn-fill bttn-xs bttn-success" onclick="analyzeLocalExtension(\'' + ext_info[
                                1].replace(
                                    '\\', '\\\\'
                                ) + '\', \'googlechrome\')"><i class="fas fa-bolt"></i> Analyze</button></td></tr>'
                    return (return_html + '</tbody></table>')
                else:
                    return (
                        'error: Something went wrong while getting local Google Chrome extensions! Check log for more information'
                    )
            elif browser == 'firefox':
                import core.localextensions as localextensions
                lexts = localextensions.GetLocalExtensions()
                exts = lexts.firefox()
                if exts != False and exts != [] and exts != None:
                    return_html = "<table class='result-table' id='result-table'><thead><tr><th>Extension Name</th><th>Action</th></tr></thead><tbody>"
                    for ext in exts:
                        ext_info = ext.split(',')
                        return_html += '<tr><td>' + ext_info[
                            0] + '</td><td><button class="bttn-fill bttn-xs bttn-success" onclick="analyzeLocalExtension(\'' + ext_info[
                                1].replace(
                                    '\\', '\\\\'
                                ) + '\', \'firefox\')"><i class="fas fa-bolt"></i> Analyze</button></td></tr>'
                    return (return_html + '</tbody></table>')
                else:
                    return (
                        'error: Something went wrong while getting local firefox extensions! Check log for more information'
                    )
            else:
                return ('error: Invalid Browser!')
        except Exception:
            logging.error(traceback.format_exc())
            return ('error: Incomplete Query')

    elif query == 'analyzelocalextension':
        try:
            browser = allargs.get('browser')
            path_to_local = allargs.get('path')
            path = helper.fixpath(path_to_local)
            if browser == 'firefox' and os.path.isfile(path):
                # valid firefox extension
                import core.localextensions as localextensions
                analysis_stat = localextensions.analyzelocalfirefoxextension(
                    path)
                return (analysis_stat)

            elif browser == 'googlechrome' and os.path.isdir(path):
                if os.path.isfile(os.path.join(path, 'manifest.json')):
                    analysis_stat = analysis.analyze(
                        path, 'Local Google Chrome Extension')
                    return (analysis_stat)
                else:
                    return ('error: Invalid Google Chrome Extension Directory')
            else:
                return ('error: Malformed Query')
        except Exception:
            logging.error(traceback.format_exc())
            return ('error: Incomplete Query')

    elif query == 'deleteAll':
        '''
        DELETES ALL RESULTS
        RESPONSE = SUCCESS / ERROR
        '''
        import core.result as result
        delete_status = result.clearAllResults()
        if delete_status:
            return "success"
        else:
            return (
                'There were some errors while deleting all analysis reports... refer to log for more information'
            )

    elif query == 'clearLab':
        '''
        Deletes all the contents of lab
        RESPONSE = SUCCESS / ERROR
        '''
        clear_lab = core.clear_lab()
        if clear_lab[0]:
            # Successful
            return (clear_lab[1])
        else:
            # Unsuccessful
            return ('error: ' + clear_lab[1])

    elif query == 'deleteResult':
        '''
        DELETES A SPECIFIC RESULT
        PARAMETER = resultID
        RESPONSE = SUCCESS_MSG / 'error: ERROR_MSG'
        '''
        try:
            result_id_to_delete = allargs.get('resultID')
            import core.result as result
            delete_status = result.clearResult(result_id_to_delete)
            if delete_status:
                return "success"
            else:
                return "Something went wrong while deleting result! Check log for more information"
        except Exception:
            return ('Invalid Query')

    elif query == 'vtDomainReport':
        try:
            domain = allargs.get('domain')
            analysis_id = allargs.get('analysis_id')
            ranalysis = core.get_result_info(analysis_id)
            if ranalysis[0]:
                # if ranalysis[0] is True then ranalysis[1] contains the details
                analysis_dir = ranalysis[1]['report_directory']
                analysis_report = os.path.join(analysis_dir,
                                               'extanalysis_report.json')
                if os.path.isfile(analysis_report):
                    report = open(analysis_report, 'r')
                    domains = json.loads(report.read())['domains']
                    for adomain in domains:
                        if adomain['name'] == domain:
                            vtjson = json.dumps(adomain['virustotal'],
                                                indent=4,
                                                sort_keys=False)
                            #return_html = '<div id="vt_info"></div><script>var wrapper1 = document.getElementById("vt_info");var data = '+vtjson+' try {var data = JSON.parse(dataStr);} catch (e) {} var tree = jsonTree.create(data, wrapper1);tree.expand(function(node) {   return node.childNodes.length < 2 || node.label === "phoneNumbers";});</script>'
                            return vtjson
                    return ('error: Domain info not found in analysis report!')
                else:
                    return ('error: Analysis report for #{0} not found'.format(
                        analysis_id))
            else:
                # ranalysis[1] is the error msg when ranalysis[0] = False
                return ('error: ' + ranalysis[1])
        except:
            logging.error(traceback.format_exc())
            return ('error: Malformed api call')

    elif query == 'retirejsResult':
        '''
        GET RETIREJS SCAN RESULTS FOR FILE
        REQUIRED PARAMETER: file = FILE_ID
        '''
        try:
            file_id = allargs.get('file')
            analysis_id = allargs.get('analysis_id')
            ranalysis = core.get_result_info(analysis_id)
            if ranalysis[0]:
                # if ranalysis[0] is True then ranalysis[1] contains the details
                analysis_dir = ranalysis[1]['report_directory']
                source_json = os.path.join(analysis_dir, 'source.json')
                if os.path.isfile(source_json):
                    report = open(source_json, 'r')
                    files = json.loads(report.read())
                    for _file in files:
                        if _file == file_id:
                            retirejs_result = files[_file]['retirejs_result']
                            if retirejs_result == []:
                                ret = 'none'
                            else:
                                ret = json.dumps(retirejs_result,
                                                 indent=4,
                                                 sort_keys=False)
                            return ret
                    return ('error: File ID not found in report!')
                else:
                    return ('error: Analysis report for #{0} not found'.format(
                        analysis_id))
            else:
                # ranalysis[1] is the error msg when ranalysis[0] = False
                return ('error: ' + ranalysis[1])
        except:
            logging.error(traceback.format_exc())
            return ('error: Malformed api call')

    elif query == 'whois':
        '''
        GET WHOIS REPORT OF DOMAIN
        REQUIRES 'python-whois' module
        RESPONSE = HTML DIV WITH FORMATTED WHOIS INFO
        '''
        try:
            domain = allargs.get('domain')
            try:
                import whois
            except:
                return (
                    "error: python-whois module not installed! install it using `pip3 install python-whois` or `pip3 install -r requirements.txt`"
                )
            whois_result = whois.whois(domain)
            whois_html = '<div class="whois-data" style="overflow-y: scroll; max-height:500px; text-align: left;">'
            for data in whois_result:
                proper_data = data.replace('_', ' ').capitalize()
                if isinstance(whois_result[data], list):
                    for subdata in whois_result[data]:
                        whois_html += '<b style="color:#89ff00;">{0} : </b>{1}<br>'.format(
                            proper_data, subdata)
                else:
                    whois_html += '<b style="color:#89ff00;">{0} : </b>{1}<br>'.format(
                        proper_data, whois_result[data])
            whois_html += '</div>'
            if whois_result:
                return (
                    '<center><h4>Whois Results For {0}</h4></center><br>{1}'.
                    format(domain, whois_html))
            else:
                return (
                    "error: Something went wrong while checking whois information of: "
                    + domain)
        except Exception:
            logging.error(traceback.format_exc())
            return ('error: Invalid Query')

    elif query == 'geoip':
        '''
        GEO-IP LOOKUP OF AN IP ADDRESS
        PARAMETERS -> IP = CONTAINS IP ADDRESS TO BE LOOKED UP
        RETURNS A HTML TO BE SHOWN
        '''
        try:
            ip_address = allargs.get('ip')
            geo_ip = scan.geoip(ip_address)
            if geo_ip[0]:
                gip = geo_ip[1]
                rethtml = '<div class="whois-data" style="overflow-y: scroll; max-height:500px; text-align: left;">'
                for g in gip:
                    name = str(g).replace('_', ' ').capitalize()
                    val = str(gip[g])
                    rethtml += '<b style="color:#89ff00;">{0} : </b>{1}<br>'.format(
                        name, val)
                rethtml += '</div>'
                return (
                    '<center><h4>Geo-IP Lookup Results For {0}</h4></center><br>{1}'
                    .format(ip_address, rethtml))

            else:
                # in case of geo_ip[0] being false element 1 has the error msg
                return ('error: ' + geo_ip[1])

        except Exception as e:
            logging.error(traceback.format_exc())
            return ('error: Invalid Query')

    elif query == 'HTTPHeaders':
        '''
        HTTP HEADERS OF AN URL
        PARAMETERS -> URL -> BASE64 ENCODED URL
        RETURNS HTML
        '''
        try:
            url = allargs.get('url')
            url = base64.b64decode(url).decode('ascii')
            headers_status = scan.http_headers(url)
            if headers_status[0]:
                rethtml = '<div class="whois-data" style="overflow-y: scroll; max-height:500px; text-align: left;">'
                headers = headers_status[1]
                for header in headers:
                    hval = headers[header]
                    rethtml += '<b style="color:#89ff00;">{0} : </b>{1}<br>'.format(
                        header, hval)
                rethtml += '</div>'
                return (
                    '<center><h4>Showing HTTP Headers of: {0}</h4></center><br>{1}'
                    .format(url, rethtml))
            else:
                return ('error: ' + headers_status[1])
        except Exception as e:
            logging.error(traceback.format_exc())
            return ('error: Invalid Query')

    elif query == 'SourceCode':
        '''
        GET SOURCE CODE OF AN URL
        PARAMETERS -> URL -> BASE64 ENCODED URL
        RETURNS HTML
        '''
        try:
            url = allargs.get('url')
            rurl = base64.b64decode(url).decode('ascii')
            headers_status = scan.source_code(rurl)
            if headers_status[0]:
                rethtml = '<textarea id="src_code" class="source_code" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">'
                headers = headers_status[1]
                rethtml += headers
                rethtml += '</textarea><br><br><center><a href="{0}" target="_blank" class="start_scan"><i class="fas fa-external-link-alt"></i> View Full Screen</a>'.format(
                    '/source-code/' + url)
                return ('<center><h4>Source Code of: {0}</h4></center><br>{1}'.
                        format(rurl, rethtml))
            else:
                return ('error: ' + headers_status[1])
        except Exception as e:
            logging.error(traceback.format_exc())
            return ('error: Invalid Query')

    elif query == 'clearlogs':
        '''
        CLEARS LOG
        '''
        core.clearlog()
        return ('Logs cleared successfully!')

    elif query == 'changeReportsDir':
        '''
        CHANGES THE REPORT DIRECTORY
        RESPONSE = SUCCESS / 'error: ERROR_MSG'
        '''
        try:
            newpath = allargs.get('newpath')
            if os.path.isdir(newpath):
                # valid directory.. let's get the absolute path and set it
                absolute_path = os.path.abspath(newpath)
                import core.settings as settings
                change = settings.changedir(absolute_path)
                if change[0]:
                    return (change[1])
                else:
                    return ('error: ' + change[1])
            else:
                return ('error: Invalid directory path!')
        except:
            logging.error(traceback.format_exc())
            return ('error: Invalid request for directory change!')

    elif query == 'changeVTapi':
        '''
        CHANGE VIRUSTOTAL API
        RESPONSE = SUCCESS_MSG / 'error: ERROR_MSG'
        '''
        try:
            new_api = allargs.get('api')
            import core.settings as settings
            change = settings.change_vt_api(new_api)
            if change[0]:
                return (change[1])
            else:
                return ('error: ' + change[1])
        except:
            logging.error(traceback.format_exc())
            return ('error: Invalid request!')

    elif query == 'changelabDir':
        '''
        CHANGES THE LAB DIRECTORY
        RESPONSE = SUCCESS / 'error : ERROR_MSG'
        '''
        try:
            newpath = allargs.get('newpath')
            if os.path.isdir(newpath):
                # valid directory.. let's get the absolute path and set it
                absolute_path = os.path.abspath(newpath)
                import core.settings as settings
                change = settings.changelabdir(absolute_path)
                if change[0]:
                    return (change[1])
                else:
                    return ('error: ' + change[1])
            else:
                return ('error: Invalid directory path!')
        except:
            logging.error(traceback.format_exc())
            return ('error: Invalid request for directory change!')

    elif query == 'updateIntelExtraction':
        '''
        UPDATES INTELS TO BE EXTRACTED
        RESPONSE = SUCCESS_MSG / 'error: ' + ERROR_MSG
        '''
        try:
            # Create the dict with all values and keys
            parameters = {}
            parameters["extract_comments"] = str(
                allargs.get('extract_comments'))
            parameters["extract_btc_addresses"] = str(
                allargs.get('extract_btc_addresses'))
            parameters["extract_base64_strings"] = str(
                allargs.get('extract_base64_strings'))
            parameters["extract_email_addresses"] = str(
                allargs.get('extract_email_addresses'))
            parameters["extract_ipv4_addresses"] = str(
                allargs.get('extract_ipv4_addresses'))
            parameters["extract_ipv6_addresses"] = str(
                allargs.get('extract_ipv6_addresses'))
            parameters["ignore_css"] = str(allargs.get('ignore_css'))

            import core.settings as settings
            status_code = settings.update_settings_batch(parameters)
            # 0 = failed, 1 = success, 2 = some updated some not!
            if status_code == '0':
                return (
                    'error: Settings could not be updated! Check log for more information'
                )
            elif status_code == '1':
                return (
                    'Settings updated successfully... Please restart ExtAnalysis for them to take effect!'
                )
            elif status_code == '2':
                return (
                    'Some settings were updated and some were not... Please restart ExtAnalysis for them to take effect!'
                )
            else:
                return (
                    'error: Invalid response from "update_settings_batch". please report it here: https://github.com/Tuhinshubhra/ExtAnalysis/issues/new'
                )
        except:
            logging.error(traceback.format_exc())
            return ('error: Incomplete Request!')

    else:
        return ('error: Invalid Query!')
예제 #21
0
def analyze(ext_name, ext_type='local'):
    core.updatelog('analyze func: ' + ext_name)
    # extension_extracted = False
    if ext_name.endswith('.crx') or ext_name.endswith('.xpi') or ext_name.endswith('.zip'):
        '''
        EXTENSION NAME / PACKED PATH 
        UNZIP THE EXTENSION FOR FURTHER ANALYSIS
        '''

        if ext_name.endswith('.crx'):
            file_extension = '.crx'
        elif ext_name.endswith('.xpi'):
            file_extension = '.xpi'
        elif ext_name.endswith('.zip'):
            file_extension = '.zip'
        else:
            file_extension = ''

        if os.path.isfile(ext_name):
            '''
            Full extension path sent, we unzip it to the lab directroy
            Used mostly to pass local firefox extensions
            '''
            ext_path = ext_name
            ext_name = os.path.basename(ext_name).split(file_extension)[0]
            extract_dir = helper.fixpath(core.lab_path + '/' + ext_name)
        else:
            '''
            Only the extension name is sent.. 
            In this case we assume that the extension is located inside the lab directory.
            Used while scanning an uploaded or downloaded extension
            '''
            ext_path = helper.fixpath(core.lab_path + '/' + ext_name)
            extract_dir = helper.fixpath(core.lab_path + '/' + ext_name.split(file_extension)[0])
        
        
        core.updatelog('Trying to unzip {0} to {1}'.format(ext_path, extract_dir))
        try:
            if os.path.exists(extract_dir):
                
                if os.path.exists(extract_dir + '_old'):
                    # there is already a _old directory we have to delete so that we can rename the last directory to this name
                    core.updatelog('Found previously created _old directory... deleting that')
                    try:
                        shutil.rmtree(extract_dir + '_old')
                    except Exception as e:
                        core.updatelog('Error while deleting: {0} . Error: {1}'.format(extract_dir + '_old', str(e)))
                        logging.error(traceback.format_exc())
                        return ('error: Something went wrong while deleting old scan directory {0}'.format(extract_dir + '_old'))
                
                new_name = os.path.basename(extract_dir) + '_old'
                core.updatelog('Renaming old extract directory {0} as {1}'.format(extract_dir, new_name))
                os.rename(extract_dir, extract_dir + '_old')
                core.updatelog('Old directory successfully renamed')
            zip_contents = zipfile.ZipFile(ext_path, 'r')
            zip_contents.extractall(extract_dir)
            zip_contents.close()
            core.updatelog('Zip Extracted Successfully')
            # extension_extracted = True
        except Exception as e:
            logging.error(traceback.format_exc())
            core.updatelog('Something went wrong while unzipping extension\nError: ' + str(e))
            return ('error: Something went wrong while unzipping extension. Check log for more information')
    
    
    elif os.path.isdir(ext_name):
        # if ext_name is a directory most likely it's a local extension
        ext_path = 'Local'
        extract_dir = ext_name
    else:
        return('error: [analyze.py] Unsupported input!')


    core.updatelog('======== Analysis Begins ========')
    try:
        core.updatelog('Reading manifest.json')
        manifest_file = helper.fixpath(extract_dir + '/manifest.json')
        manifest_load = open(manifest_file, 'r')
        manifest_content = manifest_load.read()
        manifest_content = json.loads(manifest_content)
        rinit = core.initreport(manifest_content, extract_dir, ext_type)
        if not rinit:
            return('error: Something went wrong while parsing manifest.json... analysis stopped')
        core.report['crx'] = ext_path
        core.report['extracted'] = extract_dir

        #####################################################################
        ##### PERMISSION CHECKS AND OTHER STUFFS RELATED TO PERMISSIONS #####
        #####################################################################
        perm_file = helper.fixpath(core.path + '/db/permissions.json')
        perms = open(perm_file, 'r')
        perms = perms.read()
        perms = json.loads(perms)
        try:
            for permission in manifest_content['permissions']:
                if permission != "":
                    permarray = {'name':permission}
                    core.updatelog('Discoverd Permission: ' + helper.escape(permission))
                    if permission in perms:
                        permarray['description'] = perms[permission]['description']
                        permarray['badge'] = perms[permission]['badge']
                        permarray['risk'] = perms[permission]['risk']
                        if perms[permission]['warning'] != 'none':
                            permarray['warning'] = perms[permission]['warning']
                            # core.updatelog('Warning: ' + perms[permission]['warning'])
                        else:
                            permarray['warning'] = 'na'
                    else:
                        permarray['description'] = 'na'
                        permarray['warning'] = 'na'
                        permarray['risk'] = 'none'
                        permarray['badge'] = '<i class="fas fa-question"></i>'
                    core.insertpermission(permarray)
        except Exception as e:
            core.updatelog('No permissions found')
            core.updatelog(str(e))

        #####################################################################
        #####     GET ALL FIELS AND STORE THEM FOR FURTHER ANALYSIS      ####
        #####################################################################
        html_files = []
        js_files = []
        json_files = []
        css_files = []
        static_files = []
        other_files = [] # File extensions that are not listed above

        for root, dirs, files in os.walk(extract_dir):
            for file in files:
                filepath = os.path.join(root, file)
                relpath = os.path.relpath(filepath, extract_dir)
                fname = file
                file = file.lower()
                if file.endswith(('.html', '.htm')):
                    html_files.append(filepath)
                    core.report['files']['html'].append({fname : relpath})
                elif file.endswith('.js'):
                    js_files.append(filepath)
                    core.report['files']['js'].append({fname : relpath})
                elif file.endswith('.json'):
                    json_files.append(filepath)
                    core.report['files']['json'].append({fname : relpath})
                elif file.endswith('.css'):
                    css_files.append(filepath)
                    core.report['files']['css'].append({fname : relpath})
                elif file.endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.svg', '.gif')):
                    core.report['files']['static'].append({fname : relpath})
                    static_files.append(filepath)
                else:
                    core.report['files']['other'].append({fname : relpath})
                    other_files.append(filepath)


        ######################################################################
        ## EXTRACT INTELS FROM  FILES (url, email, ip address, btc address) ##
        ######################################################################

        urls = []
        domains = []
        for allfiles in (js_files, html_files, json_files, css_files):
            for file in allfiles:
                try:
                    cnt = open(file, 'r', encoding="utf8")
                    contents = cnt.read()
                    relpath = os.path.relpath(file, extract_dir)
                    core.updatelog('Extracting intels from: ' + file)
                    intels = intel.extract(contents, relpath)

                    ## Parse the intels and add them to result
                    found_urls = intels['urls']
                    found_mail = intels['mails']
                    found_btcs = intels['btc']
                    found_ipv4 = intels['ipv4']
                    found_ipv6 = intels['ipv6']
                    found_b64s = intels['base64']
                    found_cmnt = intels['comments']

                    for u in found_urls:
                        urls.append(u)
                    for m in found_mail:
                        core.report['emails'].append(m)
                    for b in found_btcs:
                        core.report['bitcoin_addresses'].append(b)
                    for i in found_ipv4:
                        core.report['ipv4_addresses'].append(i)
                    for i in found_ipv6:
                        core.report['ipv6_addresses'].append(i)
                    for b in found_b64s:
                        core.report['base64_strings'].append(b)
                    for c in found_cmnt:
                        core.report['comments'].append(c)

                except Exception as e:
                    core.updatelog('Skipped reading file: {0} -- Error: {1}'.format(file, str(e)))
                    logging.error(traceback.format_exc())
        #urls = list(set(urls)) [NOTE TO SELF] we are tracking all urls in all files so set isn't used here
        

        ######################################################################
        ## APPEND URLS, DOMAINS TO REPORT AND DO VIRUSTOTAL SCAN ON DOMAINS ##
        ######################################################################      
        
        for url in urls:
            core.updatelog('Found URL: ' + url['url'])
            domain = re.findall('^(?:https?:\/\/)?(?:[^@\/\\n]+@)?(?:www\.)?([^:\/?\\n]+)', url['url'])[0]
            url['domain'] = domain
            core.report['urls'].append(url) # add url to the report file
            domains.append(domain)

        if virustotal.pub_vt == []:
            # No extra virustotal apis added hence the slow scan
            core.updatelog('Starting virustotal analysis of domains. [SLOW MODE]')
            virustotal_scans = virustotal.domain_batch_scan(set(domains))
        
        for domain in set(domains):
            core.updatelog('getting virustotal Scan results for domain: ' + domain)
            if virustotal.pub_vt != []:
                # the faster scan!
                virustotal_report = virustotal.scan_domain(domain)
                if not virustotal_report[0]:
                    core.updatelog('Error getting virustotal result... Error: ' + virustotal_report[1])
                    domain_vt = {"error":"Either rate limited or something else went wrong while getting domain report from virustotal"}
                else:
                    core.updatelog('Virustotal result successfully acquired!')
                    domain_vt = virustotal_report[1]
            else:
                domain_vt = virustotal_scans[domain][1]
                core.updatelog('Virustotal result successfully acquired!')
            try:
                ip = socket.gethostbyname(domain)
            except:
                ip = 'unknown'
            if ip != 'unknown':
                ip_info = ip2country.get_country(ip)
                if ip_info[0]:
                    country = ip_info[2]
                    country_code = ip_info[1]
                else:
                    country = 'unknown'
                    country_code = 'unknown'
            else:
                country = 'unknown'
                country_code = 'unknown'
                
            domainarr = {"name":domain, "ip":ip, "country_code":country_code, "country":country, "virustotal":domain_vt}
            core.report['domains'].append(domainarr)

        save_result = saveresult.createresult(extract_dir)
        cid = save_result.savereport()
        """
        if extension_extracted:
            # if the extension was extracted we delete the directory it was extracted to
            # don't want any local extensions to be deleted hence extension_extracted boolean
            core.updatelog('Deleting extraction directory: ' + extract_dir)
            try:
                shutil.rmtree(extract_dir)
                core.updatelog('Extraction directory successfully deleted')
            except Exception as e:
                core.updatelog('Something went wrong while deleting extraction directory: ' + str(e))
                logging.error(traceback.format_exc())
        """
        if cid != False and cid != None:
            return('Extension analyzed and report saved under ID: ' + cid)
        else:
            return('error:Something went wrong with the analysis!')
    except Exception as e:
        core.updatelog('Something went wrong while reading source of manifest.json file')
        print(e)
        core.updatelog(logging.error(traceback.format_exc()))
예제 #22
0
import os
import time
import traceback
import logging
import json
import zipfile
import re
import shutil
import signal
import core.helper as helper

## all the paths
path = os.path.dirname(os.path.abspath(__file__)).replace('/core', '').replace(
    '\core', '')
lab_path = helper.fixpath(path + '/lab')
reports_path = helper.fixpath(path + '/reports')

# Version
with open(os.path.join(path, 'current_version')) as vf:
    version = vf.read()

# All the variables
quiet = False
log = ""
raw_log = "\n\n\n"
report = {
}  #'{"name":"","version":"","author":"","permissions":[{"name":"","description":"","warning":""}],"urls":"","files":{"html":"","js":"","css":"","static":"","other":""},"content-scripts":[],"background-scripts":[],"pageaction-popup":[],"browseraction-popup":[]}'
reportids = {}
virustotal_api = ''
ignore_css = True
예제 #23
0
def view(analysis_id):
    # so the result ids are in this format : EXA<some digits> so we can try to replace 'EXTA' and convert the rest to int if it passes it's a valid type
    try:
        int(analysis_id.replace('EXA', ''))  # Check

        analysis_info = core.get_result_info(analysis_id)

        if not analysis_info[0]:
            # Could not get analysis_info
            error_txt = 'Something went wrong while getting analysis info!<br>Error: ' + analysis_info[
                1]
            return render_template(
                'error.html',
                error_title="Invalid Result ID",
                error_head="Invalid Result ID: {0}".format(analysis_id),
                error_txt=error_txt)
        else:
            result_directory = analysis_info[1]['report_directory'].replace(
                '<reports_path>', core.reports_path)

            if os.path.isdir(result_directory):
                # result directory found let's check for all necessary files
                graph_file = os.path.join(result_directory, 'graph.data')
                report_json = os.path.join(result_directory,
                                           'extanalysis_report.json')
                source_file = os.path.join(result_directory, 'source.json')

                if all([
                        os.path.isfile(the_file)
                        for the_file in [graph_file, report_json, source_file]
                ]):
                    core.updatelog('Viewing Analysis {0}'.format(analysis_id))
                    graph_data = open(graph_file, 'r')
                    graph_data = graph_data.read()
                    source_data = open(source_file, 'r')
                    source_data = json.loads(source_data.read())
                    report_data = open(report_json, 'r')
                    report_data = json.loads(report_data.read())

                    # prepare data to be sent to result page
                    basic_info_t = [
                        report_data['name'], report_data['version'],
                        report_data['author'], report_data['description'],
                        analysis_info[1]['time']
                    ]

                    # extension type
                    extension_type = report_data['type']

                    # URL Table
                    if report_data['urls'] != []:
                        urls_table = '<table class="result-table" id="urls-table"><thead><tr><th>URL</th><th>Domain</th><th>File</th><th>Actions</th></tr></thead><tbody>'
                        extjs_table = '<table class="result-table" id="extjs-table"><thead><tr><th>URL</th><th>Domain</th><th>File</th><th>Actions</th></tr></thead><tbody>'
                        done_urls = []
                        done_ejss = []
                        urls_count = 0
                        extjs_count = 0
                        for aurl in report_data['urls']:
                            if aurl['url'].endswith('.js'):
                                if aurl['url'] not in done_ejss:
                                    done_ejss.append(aurl['url'])
                                    aurl_href = '<a href="{0}" class="ext_url" target="_blank"><i class="fas fa-external-link-alt" style="font-size:12px;"></i> {0}</a>'.format(
                                        aurl['url'])
                                    extjs_table += '<tr><td>' + aurl_href + '</td>'
                                    b64url = "'" + base64.b64encode(
                                        aurl['url'].encode(
                                            'ascii',
                                            'ignore')).decode('ascii') + "'"
                                    extjs_table += '<td>{0}</td><td>{1}</td>'.format(
                                        aurl['domain'], aurl['file'])
                                    extjs_table += '<td><button class="bttn-fill bttn-xs bttn-primary" onclick=whois(\'{1}\')><i class="fab fa-searchengin"></i> WHOIS</button> <button class="bttn-fill bttn-xs bttn-success" onclick="getSource({0})"><i class="fas fa-code"></i> Source</button> <button class="bttn-fill bttn-xs bttn-danger" onclick="getHTTPHeaders({0})"><i class="fas fa-stream"></i> HTTP Headers</button></td></tr>'.format(
                                        b64url, aurl['url'])
                                    extjs_count += 1
                            else:
                                if aurl['url'] not in done_urls:
                                    done_urls.append(aurl['url'])
                                    aurl_href = '<a href="{0}" class="ext_url" target="_blank"><i class="fas fa-external-link-alt" style="font-size:12px;"></i> {0}</a>'.format(
                                        aurl['url'])
                                    urls_table += '<tr><td>' + aurl_href + '</td>'
                                    urls_table += '<td>{0}</td><td>{1}</td>'.format(
                                        aurl['domain'], aurl['file'])
                                    b64url = "'" + base64.b64encode(
                                        aurl['url'].encode(
                                            'ascii',
                                            'ignore')).decode('ascii') + "'"
                                    urls_table += '<td><button class="bttn-fill bttn-xs bttn-primary" onclick=whois(\'{1}\')><i class="fab fa-searchengin"></i> WHOIS</button> <button class="bttn-fill bttn-xs bttn-success" onclick="getSource({0})"><i class="fas fa-code"></i> Source</button> <button class="bttn-fill bttn-xs bttn-danger" onclick="getHTTPHeaders({0})"><i class="fas fa-stream"></i> HTTP Headers</button></td></tr>'.format(
                                        b64url, aurl['url'])
                                    urls_count += 1

                        if done_urls != []:
                            urls_table += '</tbody></table>'
                        else:
                            urls_table = '<h3 class="nothing"> No URLs Found </h3>'

                        if done_ejss != []:
                            extjs_table += '</tbody></table>'
                        else:
                            extjs_table = '<h3 class="nothing"> No External JavaScript Found in any files! </h3>'
                    else:
                        urls_table = '<h3 class="nothing"> No URLs Found </h3>'
                        extjs_table = '<h3 class="nothing"> No External JavaScript Found in any files! </h3>'
                        extjs_count = 0
                        urls_count = 0

                    # Domains div
                    if report_data['domains'] != []:
                        domains_table = '<table class="result-table" id="domains-table"><thead><tr><th>Country</th><th>Domain</th><th>IP Address</th><th>Virustotal Score</th><th>Actions</th></tr></thead><tbody>'
                        for domain in report_data['domains']:
                            domain_flag = helper.fixpath(
                                core.path + '/static/images/flags/' +
                                domain['country_code'] + '.png')
                            if os.path.isfile(domain_flag):
                                flag_path = url_for('static',
                                                    filename='images/flags/' +
                                                    domain['country_code'] +
                                                    '.png')
                            else:
                                flag_path = url_for(
                                    'static',
                                    filename='images/flags/unknown.png')
                            country_html = '<img src="{0}" class="country_flag"> {1}'.format(
                                flag_path, domain['country'])
                            domains_table += '<tr><td>{4}</td><td>{0}</td><td>{2}</td><td>{1}</td><td><button class="bttn-fill bttn-xs bttn-danger" onclick=whois("{0}")><i class="fab fa-searchengin"></i> WHOIS</button> <button class="bttn-fill bttn-xs bttn-primary" onclick="domainvt(\'{0}\', \'{3}\')"><i class="fas fa-hourglass-end"></i> VT Report</button> <button class="bttn-fill bttn-xs bttn-success" onclick=geoip("{2}")><i class="fas fa-globe-americas"></i> Geo-IP Lookup</button></td></tr>'.format(
                                domain['name'], '0/66', domain['ip'],
                                analysis_id, country_html)
                        domains_table += '</tbody></table>'
                    else:
                        domains_table = '<h3 class="nothing"> No Domains Extracted! </h3>'
                    unique_domains = len(report_data['domains'])

                    # Permissions div containing all permissions accordions
                    permissions_div = ""
                    for perm in report_data['permissions']:
                        #perm_html = '<div class="perm"><div class="perm-name">{0}</div> <div class="perm-desc">{1}</div> <div class="perm-warn">{2}</div></div>'.format(perm['name'], perm['description'], (perm['warning'] if perm['warning'] != 'na' else ''))
                        perm_html = '<div class="accordion"><div class="accordion__item"><div class="accordion__question">{0} {1} <div class="risk-pill {4}">{4}</div></div><div class="accordion__answer">{2} <div class="warning"> {3} </div></div></div></div>'.format(
                            perm['badge'], helper.escape(perm['name']),
                            perm['description'],
                            (perm['warning']
                             if perm['warning'] != 'na' else ''), perm['risk'])
                        permissions_div += perm_html
                    permissions_count = len(report_data['permissions'])

                    # table consisting of all the viewable source files
                    files_table = '<table class="result-table" id="files-table"><thead><tr><th>File Name</th><th>Path</th><th>Size</th><th>Actions</th></tr></thead><tbody>'
                    for file_info in source_data:
                        file_name = source_data[file_info]['file_name']
                        rel_path = source_data[file_info]['relative_path']
                        file_id = source_data[file_info]['id']
                        file_size = source_data[file_info]['file_size']
                        file_action = '<button class="bttn-fill bttn-xs bttn-primary" onclick="viewfile(\'' + analysis_id + '\', \'' + file_id + '\')"><i class="fas fa-code"></i> View Source</button>'
                        if file_name.endswith('.js'):
                            # Add button for viewing retirejs vulnerability scan results
                            file_action += ' <button class="bttn-fill bttn-xs bttn-danger" onclick="retirejsResult({0}, {1}, {2})"><i class="fas fa-spider"></i> Vulnerabilities</button>'.format(
                                "'" + file_id + "'", "'" + analysis_id + "'",
                                "'" + file_name + "'")
                        file_type = helper.fixpath(core.path +
                                                   '/static/images/' +
                                                   file_name.split('.')[-1] +
                                                   '1.png')
                        if os.path.isfile(file_type):
                            file_type = file_name.split('.')[-1] + '1.png'
                        else:
                            file_type = 'other1.png'
                        file_type = url_for('static',
                                            filename='images/' + file_type)
                        file_type = '<img src="{0}" class="ft_icon">'.format(
                            file_type)
                        file_html = "<tr><td>{2} {0}</td><td>{1}</td><td>{4}</td><td>{3}</td></tr>".format(
                            file_name, rel_path, file_type, file_action,
                            file_size)
                        files_table += file_html
                    files_table += '</tbody></table>'

                    # table consisting of ipv6 and ipv4 addresses
                    if report_data['ipv4_addresses'] == [] and report_data[
                            'ipv6_addresses'] == []:
                        ips_table = '<h3 class="nothing">No IPv4 or IPv6 addresses found!</h3>'
                    else:
                        ips_table = '<table class="result-table" id="ips_table"><thead><tr><th>IP Address</th><th>Type</th><th>File</th></tr></thead><tbody>'
                        for ip in report_data['ipv4_addresses']:
                            ips_table += '<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>'.format(
                                ip['address'], 'IPv4', ip['file'])
                        for ip in report_data['ipv6_addresses']:
                            ips_table += '<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>'.format(
                                ip['address'], 'IPv6', ip['file'])
                        ips_table += '</tbody></table>'

                    # table consisting of emails
                    if report_data['emails'] != []:
                        mails_table = '<table class="result-table" id="mails_table"><thead><tr><th>Email Address</th><th>File</th></tr></thead><tbody>'
                        for mail in report_data['emails']:
                            mails_table += '<tr><td>{0}</td><td>{1}</td></tr>'.format(
                                mail['mail'], mail['file'])
                        mails_table += '</tbody></table>'
                    else:
                        mails_table = '<h3 class="nothing">No email addresses found in any of the files!</h3>'

                    # table containing btc addresses
                    if report_data['bitcoin_addresses'] != []:
                        btc_table = '<table class="result-table" id="btc_table"><thead><tr><th>BTC Address</th><th>File</th></tr></thead><tbody>'
                        for mail in report_data['bitcoin_addresses']:
                            btc_table += '<tr><td>{0}</td><td>{1}</td></tr>'.format(
                                mail['address'], mail['file'])
                        btc_table += '</tbody></table>'
                    else:
                        btc_table = '<h3 class="nothing">No Bitcoin Address found!</h3>'

                    # table containing comments
                    if report_data['comments'] != []:
                        comments_table = '<table class="result-table" id="comments_table"><thead><tr><th>Comment</th><th>File</th></tr></thead><tbody>'
                        for comment in report_data['comments']:
                            comments_table += '<tr><td>{0}</td><td>{1}</td></tr>'.format(
                                helper.escape(comment['comment']),
                                comment['file'])
                        comments_table += '</tbody></table>'
                    else:
                        comments_table = '<h3 class="nothing">No comments found in any js/html/css files!</h3>'

                    # table containing base64 encoded strings
                    if report_data['base64_strings'] != []:
                        base64_table = '<table class="result-table" id="base64_table"><thead><tr><th>Base64 Encoded String</th><th>File</th></tr></thead><tbody>'
                        for b64 in report_data['base64_strings']:
                            base64_table += '<tr><td>{0}</td><td>{1}</td></tr>'.format(
                                b64['string'], b64['file'])
                        base64_table += '</tbody></table>'
                    else:
                        base64_table = '<h3 class="nothing">No base64 encoded string found in any js/html/css files!</h3>'

                    manifest_content = json.dumps(report_data['manifest'])
                    '''
                    Files count
                    '''
                    js_files_count = len(report_data['files']['js'])
                    css_files_count = len(report_data['files']['css'])
                    html_files_count = len(report_data['files']['html'])
                    json_files_count = len(report_data['files']['json'])
                    other_files_count = len(report_data['files']['other'])
                    static_files_count = len(report_data['files']['static'])

                    return render_template(
                        "report.html",
                        extension_type=extension_type,
                        graph_data=graph_data,
                        basic_info=basic_info_t,
                        urls_table=urls_table,
                        permissions_div=permissions_div,
                        analysis_id=analysis_id,
                        files_table=files_table,
                        manifest_content=manifest_content,
                        domains_table=domains_table,
                        base64_table=base64_table,
                        comments_table=comments_table,
                        ips_table=ips_table,
                        btc_table=btc_table,
                        mails_table=mails_table,
                        extjs_table=extjs_table,
                        urls_count=urls_count,
                        extjs_count=extjs_count,
                        permissions_count=permissions_count,
                        unique_domains=unique_domains,
                        js_files_count=js_files_count,
                        css_files_count=css_files_count,
                        html_files_count=html_files_count,
                        json_files_count=json_files_count,
                        other_files_count=other_files_count,
                        static_files_count=static_files_count)

                else:
                    error_txt = 'All the result files are not found.. Try scanning the extension again! and don\'t mess with the result files this time'
                    return render_template('error.html',
                                           error_title="Malformed Result",
                                           error_head="Incomplete Result",
                                           error_txt=error_txt)

            else:
                error_txt = 'The result directory corresponding to result id {0} could not be found... hence ExtAnalysis has nothing to show'.format(
                    analysis_id)
                return render_template(
                    'error.html',
                    error_title="Result Directory Not Found",
                    error_head="Result Directory Not Foundt",
                    error_txt=error_txt)

    except:
        logging.error(traceback.format_exc())
        return render_template(
            'error.html',
            error_title="Invalid Result ID",
            error_head="Invalid Result ID",
            error_txt=
            'There seems to be no result corresponding to the provided ID. Did you delete the result? or maybe you did some weird shit with the parameter?'
        )
예제 #24
0
def GetDescriptionFromManifest(manifest_file):
    # Get's the desc of an extension from manifest file
    if os.path.isfile(manifest_file):
        # Path is valid and it's a manifest file
        extension_path = helper.fixpath(
            manifest_file.replace('manifest.json', ''))
        manifest_content = open(manifest_file, 'r', encoding="utf8")
        manifest_content = manifest_content.read()

        try:
            # load the json data
            manifest_json = json.loads(manifest_content)

            try:
                manifest_desc = manifest_json['description']
                if '__MSG_' in manifest_desc:
                    # This is the whole reason i created this function...
                    updatelog(
                        'Getting manifest description from locale file: ' +
                        extension_path)
                    manifest_message = re.findall('__MSG_(.*?)__',
                                                  manifest_desc)[0]
                    locale_dir = helper.fixpath(extension_path + '/_locales/')
                    if os.path.isdir(locale_dir):
                        # locale directory exists let' s grab our thing
                        try:
                            # get the default locale from manifest
                            default_locale = manifest_json['default_locale']
                            en_locale_file = helper.fixpath(locale_dir + '/' +
                                                            default_locale +
                                                            '/messages.json')
                            updatelog('Default Locale: ' + default_locale)
                        except Exception as e:
                            # use hardcoded en
                            en_locale_file = helper.fixpath(
                                locale_dir + '/en/messages.json')
                        if os.path.isfile(en_locale_file):
                            # en locale file found let's grab the desc
                            en_locale_content = open(en_locale_file,
                                                     'r',
                                                     encoding="utf8")
                            try:
                                en_locale_content = json.loads(
                                    en_locale_content.read())
                                string_content = str(en_locale_content)
                                ext_desc = ''
                                if manifest_message in string_content:
                                    ext_desc = en_locale_content[
                                        manifest_message]['message']
                                    updatelog(
                                        'Extension description grabbed from default locale file.. description: '
                                        + ext_desc)
                                    return ext_desc
                                else:
                                    updatelog('Could not find description')
                                    return False

                            except Exception as e:
                                updatelog(
                                    'Something went wrong while reading or parsing default locale file...'
                                )
                                logging.error(traceback.format_exc())
                                return False
                        else:
                            # en locale not found let's just get the first one we find and be done with it
                            ldirs = os.listdir(locale_dir)
                            for dir in ldirs:
                                if os.path.isfile(
                                        helper.fixpath(locale_dir + '/' + dir +
                                                       '/messages.json')):
                                    locale_content = open(
                                        helper.fixpath(locale_dir + '/' + dir +
                                                       '/messages.json'),
                                        'r',
                                        encoding="utf8")
                                    try:
                                        en_locale_content = json.loads(
                                            locale_content.read())
                                        if manifest_message in locale_content:
                                            ext_desc = en_locale_content[
                                                manifest_message]['message']
                                            updatelog(
                                                'Extension description grabbed from '
                                                + dir +
                                                ' locale file.. description: '
                                                + ext_desc)
                                            return ext_desc
                                        else:
                                            updatelog(
                                                'Could not find description')
                                            return False

                                    except Exception as e:
                                        updatelog(
                                            'Something went wrong while reading or parsing en locale file...'
                                        )
                                        logging.error(traceback.format_exc())
                                        return False
                    else:
                        # _locale dir doesn't exist let's just go...
                        updatelog('_locale directory doesn\'t exist.. dir: ' +
                                  locale_dir)
                        return False
                else:
                    return manifest_desc
            except Exception as e:
                updatelog(
                    'No description in on manifest.json ... maybe an invalid extension?'
                )
                logging.error(traceback.format_exc())
                return False
        except Exception as e:
            updatelog(
                'Something went wrong while loading manifest json [GetDescriptionFromManifest]'
            )
            updatelog('Error: ' + str(e))
            logging.error(traceback.format_exc())
            return False