def take_screenshot(request):
    """Take Screenshot"""
    logger.info("Taking Screenshot")
    try:
        if request.method == 'POST':
            md5_hash = request.POST['md5']
            if re.match('^[0-9a-f]{32}$', md5_hash):
                data = {}
                rand_int = random.randint(1, 1000000)
                base_dir = settings.BASE_DIR
                # make sure that list only png from this directory
                screen_dir = os.path.join(
                    settings.UPLD_DIR, md5_hash + '/screenshots-apk/')
                if not os.path.exists(screen_dir):
                    os.makedirs(screen_dir)
                adb_command(
                    ["screencap", "-p", "/data/local/screen.png"], True)
                adb_command(["pull", "/data/local/screen.png",
                             screen_dir + "screenshot-" + str(rand_int) + ".png"])
                logger.info("Screenshot Taken")
                data = {'screenshot': 'yes'}
                return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                return print_n_send_error_response(request, "Invalid Scan Hash", True)
        else:
            return print_n_send_error_response(request, "Only POST allowed", True)
    except:
        PrintException("Taking Screenshot")
        return print_n_send_error_response(request, "Error Taking Screenshot", True)
def dump_data(request):
    """Downloading Application Data from Device"""
    logger.info("Downloading Application Data from Device")
    try:
        if request.method == 'POST':
            data = {}
            package = request.POST['pkg']
            md5_hash = request.POST['md5']
            if re.match('^[0-9a-f]{32}$', md5_hash):
                if re.findall(r";|\$\(|\|\||&&", package):
                    return print_n_send_error_response(request, "Possible RCE Attack", True)
                base_dir = settings.BASE_DIR
                apk_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/')
                # Let's try to close Proxy a bit early as we don't have much
                # control on the order of thread execution
                stop_capfuzz(settings.PORT)
                logger.info("Deleting Dump Status File")
                adb_command(["rm", "/sdcard/mobsec_status"], True)
                logger.info("Creating TAR of Application Files.")
                adb_command(["am", "startservice", "-a", package,
                             "opensecurity.ajin.datapusher/.GetPackageLocation"], True)
                logger.info("Waiting for TAR dump to complete...")
                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_REAL_DEVICE":
                    timeout = settings.DEVICE_TIMEOUT
                else:
                    timeout = settings.VM_TIMEOUT
                start_time = time.time()
                while True:
                    current_time = time.time()
                    if b"MOBSEC-TAR-CREATED" in adb_command(["cat", "/sdcard/mobsec_status"], shell=True):
                        break
                    if (current_time - start_time) > timeout:
                        logger.error(
                            "TAR Generation Failed. Process timed out.")
                        break
                logger.info("Dumping Application Files from Device/VM")
                adb_command(["pull", "/data/local/" + package +
                             ".tar", apk_dir + package + ".tar"])
                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                    logger.info("Removing package")
                    adb_command(["uninstall", package])
                    stop_avd()
                logger.info("Stopping ADB")
                adb_command(["kill-server"])
                data = {'dump': 'yes'}
                return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                return print_n_send_error_response(request, "Invalid Scan Hash", True)
        else:
            return print_n_send_error_response(request, "Only POST allowed", True)
    except:
        PrintException("Downloading Application Data from Device")
        return print_n_send_error_response(request, "Application Data Dump from Device failed", True)
def mobsf_ca(request):
    """Install and Remove MobSF Proxy RootCA"""
    try:
        if request.method == 'POST':
            data = {}
            act = request.POST['action']
            rootca = get_ca_dir()
            adb = getADB()
            if act == "install":
                logger.info("Installing MobSF RootCA")
                adb_command(
                    ["push", rootca, "/data/local/tmp/" + settings.ROOT_CA])
                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                    # For some reason, avd emulator does not have cp binary
                    adb_command(["/data/local/tmp/busybox", "cp",
                                 "/data/local/tmp/" + settings.ROOT_CA,
                                 "/system/etc/security/cacerts/" + settings.ROOT_CA], True)
                    adb_command(["chmod", "644",
                                 "/system/etc/security/cacerts/" + settings.ROOT_CA], True)
                else:
                    adb_command(["su",
                                 "-c",
                                 "cp",
                                 "/data/local/tmp/" + settings.ROOT_CA,
                                 "/system/etc/security/cacerts/" + settings.ROOT_CA], True)
                    adb_command(["su",
                                 "-c",
                                 "chmod",
                                 "644",
                                 "/system/etc/security/cacerts/" + settings.ROOT_CA], True)
                adb_command(
                    ["rm", "/data/local/tmp/" + settings.ROOT_CA], True)
                data = {'ca': 'installed'}
            elif act == "remove":
                logger.info("Removing MobSF RootCA")
                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                    adb_command(
                        ["rm", "/system/etc/security/cacerts/" + settings.ROOT_CA], True)
                else:
                    adb_command(["su",
                                 "-c",
                                 "rm",
                                 "/system/etc/security/cacerts/" + settings.ROOT_CA], True)
                data = {'ca': 'removed'}
            return HttpResponse(json.dumps(data), content_type='application/json')
        else:
            return print_n_send_error_response(request, "Only POST allowed", True)
    except:
        PrintException("MobSF RootCA Handler")
        return print_n_send_error_response(request, "Error in RootCA Handler", True)
def touch(request):
    """Sending Touch Events"""
    logger.info("Sending Touch Events")
    try:
        data = {}
        if (request.method == 'POST') and (is_number(request.POST['x'])) and (is_number(request.POST['y'])):
            x_axis = request.POST['x']
            y_axis = request.POST['y']
            adb = getADB()
            args = ["input",
                    "tap",
                    x_axis,
                    y_axis]
            data = {'status': 'success'}
            try:
                adb_command(args, True)
            except:
                data = {'status': 'error'}
                PrintException("Performing Touch Action")
        else:
            data = {'status': 'failed'}
        return HttpResponse(json.dumps(data), content_type='application/json')
    except:
        PrintException("Sending Touch Events")
        return print_n_send_error_response(request, "Error Sending Touch Events", True)
def compare_apps(request, first_hash: str, second_hash: str):
    if first_hash == second_hash:
        error_msg = "Results with same hash cannot be compared"
        return print_n_send_error_response(request, error_msg, False)
    logger.info(
        "Starting App compare for - {} and {}".format(first_hash, second_hash))
    return generic_compare(request, first_hash, second_hash)
def delete_scan(request, api=False):
    """
    Delete Scan from DB and remove the scan related files
    """
    try:
        if request.method == 'POST':
            if api:
                md5_hash = request.POST['hash']
            else:
                md5_hash = request.POST['md5']
            data = {'deleted': 'no'}
            if re.match('[0-9a-f]{32}', md5_hash):
                # Delete DB Entries
                scan = RecentScansDB.objects.filter(MD5=md5_hash)
                if scan.exists():
                    RecentScansDB.objects.filter(MD5=md5_hash).delete()
                    ScopeURLSandTests.objects.filter(MD5=md5_hash).delete()
                    StaticAnalyzerAndroid.objects.filter(MD5=md5_hash).delete()
                    StaticAnalyzerIPA.objects.filter(MD5=md5_hash).delete()
                    StaticAnalyzerIOSZIP.objects.filter(MD5=md5_hash).delete()
                    StaticAnalyzerWindows.objects.filter(MD5=md5_hash).delete()
                    # Delete Upload Dir Contents
                    app_upload_dir = os.path.join(settings.UPLD_DIR, md5_hash)
                    if isDirExists(app_upload_dir):
                        shutil.rmtree(app_upload_dir)
                    # Delete Download Dir Contents
                    dw_dir = settings.DWD_DIR
                    for item in os.listdir(dw_dir):
                        item_path = os.path.join(dw_dir, item)
                        # Delete all related files
                        if isFileExists(item_path) and item.startswith(md5_hash + "-"):
                            os.remove(item_path)
                        # Delete related directories
                        if isDirExists(item_path) and item.startswith(md5_hash + "-"):
                            shutil.rmtree(item_path)
                    data = {'deleted': 'yes'}
            if api:
                return data
            else:
                return HttpResponse(json.dumps(data), content_type='application/json; charset=utf-8')
    except Exception as exp:
        msg = str(exp)
        exp_doc = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
def final_test(request):
    """Collecting Data and Cleanup"""
    global TCP_SERVER_MODE
    logger.info("Collecting Data and Cleaning Up")
    try:
        if request.method == 'POST':
            data = {}
            md5_hash = request.POST['md5']
            package = request.POST['pkg']
            if re.findall(r";|\$\(|\|\||&&", package):
                return print_n_send_error_response(request, "Possible RCE Attack", True)
            if re.match('^[0-9a-f]{32}$', md5_hash):
                # Stop ScreenCast Client if it is running
                TCP_SERVER_MODE = "off"
                base_dir = settings.BASE_DIR
                apk_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/')
                adb = getADB()
                # Change to check output of subprocess when analysis is done
                # Can't RCE
                os.system(adb + ' -s ' + get_identifier() +
                          ' logcat -d dalvikvm:W ActivityManager:I > "' + apk_dir + 'logcat.txt"')
                logger.info("Downloading Logcat logs")
                adb_command(["pull", "/data/data/de.robv.android.xposed.installer/log/error.log",
                             apk_dir + "x_logcat.txt"])

                logger.info("Downloading Droidmon API Monitor Logcat logs")
                # Can't RCE
                os.system(adb + ' -s ' + get_identifier() +
                          ' shell dumpsys > "' + apk_dir + 'dump.txt"')
                logger.info("Downloading Dumpsys logs")

                adb_command(["am", "force-stop", package], True)
                logger.info("Stopping Application")

                adb_command(
                    ["am", "force-stop", "opensecurity.screencast"], True)
                logger.info("Stopping ScreenCast Service")

                data = {'final': 'yes'}
                return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                return print_n_send_error_response(request, "Invalid Scan Hash", True)
        else:
            return print_n_send_error_response(request, "Only POST allowed", True)
    except:
        PrintException("Data Collection & Clean Up")
        return print_n_send_error_response(request, "Data Collection & Clean Up failed", True)
def get_env(request):
    """Get Dynamic Analysis Environment for Android"""
    logger.info("Setting up Dynamic Analysis Environment")
    try:
        if request.method == 'POST':
            data = {}
            md5_hash = request.POST['md5']
            package = request.POST['pkg']
            launcher = request.POST['lng']
            if re.findall(r";|\$\(|\|\||&&", package) or re.findall(r";|\$\(|\|\||&&", launcher):
                return print_n_send_error_response(request, "Possible RCE Attack", True)
            if re.match('^[0-9a-f]{32}$', md5_hash):
                base_dir = settings.BASE_DIR
                app_dir = os.path.join(
                    settings.UPLD_DIR, md5_hash + '/')  # APP DIRECTORY
                app_file = md5_hash + '.apk'  # NEW FILENAME
                app_path = app_dir + app_file  # APP PATH
                adb = getADB()
                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                    proxy_ip = '127.0.0.1'
                else:
                    proxy_ip = settings.PROXY_IP  # Proxy IP
                start_proxy(settings.PORT, package)
                # vm needs the connect function
                try:
                    if not settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                        connect()
                except Exception as exp:
                    data = {'ready': 'no',
                            'msg': 'Cannot Connect to the VM/Device.',
                            'error': str(exp)}
                    return HttpResponse(json.dumps(data), content_type='application/json')
                # Change True to support non-activity components
                install_and_run(app_path, package, launcher, True)
                screen_width, screen_width = get_res()
                data = {'ready': 'yes',
                        'screen_witdth': screen_width,
                        'screen_height': screen_width, }
                return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                return print_n_send_error_response(request, "Invalid Scan Hash", True)
        else:
            return print_n_send_error_response(request, "Only POST allowed", True)
    except:
        PrintException("Setting up Dynamic Analysis Environment")
        return print_n_send_error_response(request, "Environment Setup Failed", True)
def execute_adb(request):
    """Execute ADB Commands"""
    logger.info("Executing ADB Commands")
    try:
        if request.method == 'POST':
            data = {}
            cmd = request.POST['cmd']
            resp = "error"
            try:
                resp = adb_command(cmd.split(' '))
            except:
                PrintException("Executing ADB Commands")
            data = {'cmd': 'yes', 'resp': resp.decode("utf8", "ignore")}
            return HttpResponse(json.dumps(data), content_type='application/json')
        else:
            return print_n_send_error_response(request, "Only POST allowed", True)
    except:
        PrintException("Executing ADB Commands")
        return print_n_send_error_response(request, "Error running ADB commands", True)
def search(request):
    """
    Search Scan by MD5 Route
    """
    md5 = request.GET['md5']
    if re.match('[0-9a-f]{32}', md5):
        db_obj = RecentScansDB.objects.filter(MD5=md5)
        if db_obj.exists():
            return HttpResponseRedirect('/' + db_obj[0].URL)
        else:
            return HttpResponseRedirect('/not_found')
    return print_n_send_error_response(request, "Invalid Scan Hash")
def view(request):
    """View File"""
    logger.info("Viewing File")
    try:
        typ = ''
        fil = ''
        rtyp = ''
        dat = ''
        if re.match('^[0-9a-f]{32}$', request.GET['md5']):
            fil = request.GET['file']
            md5_hash = request.GET['md5']
            typ = request.GET['type']
            src = os.path.join(settings.UPLD_DIR,
                               md5_hash + '/DYNAMIC_DeviceData/')
            sfile = os.path.join(src, fil)
            # Prevent Directory Traversal Attacks
            if ("../" in fil) or ("%2e%2e" in fil) or (".." in fil) or ("%252e" in fil):
                return print_n_send_error_response(request, "Path Traversal Attack Detected")
            else:
                with io.open(sfile, mode='r', encoding="utf8", errors="ignore") as flip:
                    dat = flip.read()
                if (fil.endswith('.xml')) and (typ == 'xml'):
                    rtyp = 'xml'
                elif typ == 'db':
                    dat = handle_sqlite(sfile)
                    rtyp = 'asciidoc'
                elif typ == 'others':
                    rtyp = 'asciidoc'
                else:
                    return print_n_send_error_response(request, "File Type not supported")
                context = {'title': escape(ntpath.basename(fil)), 'file': escape(
                    ntpath.basename(fil)), 'dat': dat, 'type': rtyp, }
                template = "general/view.html"
                return render(request, template, context)
        else:
            return print_n_send_error_response(request, "Invalid Scan Hash")
    except:
        PrintException("Viewing File")
        return print_n_send_error_response(request, "ERROR Viewing File")
def download(request):
    """
    Download from MobSF Route
    """
    msg = "Error Downloading File "
    if request.method == 'GET':
        allowed_exts = settings.ALLOWED_EXTENSIONS
        filename = request.path.replace("/download/", "", 1)
        # Security Checks
        if "../" in filename:
            return print_n_send_error_response(request, "Path Traversal Attack detected")
        ext = os.path.splitext(filename)[1]
        if ext in allowed_exts:
            dwd_file = os.path.join(settings.DWD_DIR, filename)
            if os.path.isfile(dwd_file):
                wrapper = FileWrapper(open(dwd_file, "rb"))
                response = HttpResponse(
                    wrapper, content_type=allowed_exts[ext])
                response['Content-Length'] = os.path.getsize(dwd_file)
                return response
    msg += filename
    return print_n_send_error_response(request, msg)
def capfuzz_start(request):
    """Start CapFuzz UI"""
    logger.info("Starting CapFuzz Web UI")
    try:
        stop_capfuzz(settings.PORT)
        start_fuzz_ui(settings.PORT)
        time.sleep(3)
        logger.info("CapFuzz UI Started")
        if request.GET['project']:
            project = request.GET['project']
        else:
            project = ""
        return HttpResponseRedirect('http://localhost:' + str(settings.PORT) + "/dashboard/" + project)
    except:
        PrintException("Starting CapFuzz Web UI")
        return print_n_send_error_response(request, "Error Starting CapFuzz UI")
def android_dynamic_analyzer(request):
    """Android Dynamic Analyzer View"""
    logger.info("Dynamic Analysis Started")
    try:
        if request.method == 'POST':
            md5_hash = request.POST['md5']
            package = request.POST['pkg']
            launcher = request.POST['lng']
            if re.findall(r';|\$\(|\|\||&&', package) or re.findall(r';|\$\(|\|\||&&', launcher):
                return print_n_send_error_response(request, "Possible RCE Attack")
            if re.match('^[0-9a-f]{32}$', md5_hash):
                # Delete ScreenCast Cache
                screen_file = os.path.join(settings.SCREEN_DIR, 'screen.png')
                if os.path.exists(screen_file):
                    os.remove(screen_file)
                # Delete Contents of Screenshot Dir
                screen_dir = os.path.join(
                    settings.UPLD_DIR, md5_hash + '/screenshots-apk/')
                if os.path.isdir(screen_dir):
                    shutil.rmtree(screen_dir)
                else:
                    os.makedirs(screen_dir)
                # Start DM
                stop_capfuzz(settings.PORT)
                adb = getADB()
                is_avd = False
                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_REAL_DEVICE":
                    logger.info(
                        "MobSF will perform Dynamic Analysis on real Android Device")
                elif settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                    # adb, avd_path, reference_name, dup_name, emulator
                    is_avd = True
                    if not os.path.exists(settings.AVD_EMULATOR):
                        return print_n_send_error_response(request, "Cannot Find AVD Emulator")
                    if not refresh_avd():
                        return print_n_send_error_response(request, "Cannot Refresh AVD")
                else:
                    # Refersh VM
                    refresh_vm(settings.UUID, settings.SUUID, settings.VBOX)
                context = {'md5': md5_hash,
                           'pkg': package,
                           'lng': launcher,
                           'title': 'Start Testing',
                           'AVD': is_avd, }
                template = "dynamic_analysis/start_test.html"
                return render(request, template, context)
            else:
                return print_n_send_error_response(request, "Invalid Scan Hash")
        else:
            return print_n_send_error_response(request, "Only POST allowed")
    except:
        PrintException("DynamicAnalyzer")
        return print_n_send_error_response(request, "Dynamic Analysis Failed.")
def screen_cast(request):
    """Start or Stop ScreenCast Feature"""
    logger.info("Invoking ScreenCast Service in VM/Device")
    try:
        global TCP_SERVER_MODE
        data = {}
        if request.method == 'POST':
            mode = request.POST['mode']
            if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                ip_address = '10.0.2.2'
            else:
                ip_address = settings.SCREEN_IP
            port = str(settings.SCREEN_PORT)
            if mode == "on":
                args = ["am",
                        "startservice",
                        "-a",
                        ip_address + ":" + port,
                        "opensecurity.screencast/.StartScreenCast"]
                data = {'status': 'on'}
                TCP_SERVER_MODE = "on"
            elif mode == "off":
                args = ["am",
                        "force-stop",
                        "opensecurity.screencast"]
                data = {'status': 'off'}
                TCP_SERVER_MODE = "off"
            if (mode in ["on", "off"]):
                try:
                    adb_command(args, True)
                    screen_trd = threading.Thread(target=screencast_service)
                    screen_trd.setDaemon(True)
                    screen_trd.start()
                except:
                    PrintException("Casting Screen")
                    data = {'status': 'error'}
                    return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                data = {'status': 'failed'}
        else:
            data = {'status': 'failed'}
        return HttpResponse(json.dumps(data), content_type='application/json')
    except:
        PrintException("Casting Screen")
        return print_n_send_error_response(request, "Error Casting Screen", True)
def clip_dump(request):
    """Dump Android ClipBoard"""
    logger.info("Starting Clipboard Dump Service in VM/Device")
    try:
        data = {}
        if request.method == 'POST':
            adb = getADB()
            args = ["am",
                    "startservice",
                    "opensecurity.clipdump/.ClipDumper"]
            try:
                adb_command(args, True)
                data = {'status': 'success'}
            except:
                PrintException("Dumping Clipboard")
                data = {'status': 'error'}
        else:
            data = {'status': 'failed'}
        return HttpResponse(json.dumps(data), content_type='application/json')
    except:
        PrintException("Dumping Clipboard")
        return print_n_send_error_response(request, "Error Dumping Clipboard", True)
def pdf(request, api=False, jsonres=False):
    try:
        if api:
            checksum = request.POST['hash']
        else:
            checksum = request.GET['md5']
        hash_match = re.match('^[0-9a-f]{32}$', checksum)
        if hash_match:
            android_static_db = StaticAnalyzerAndroid.objects.filter(
                MD5=checksum)
            ios_static_db = StaticAnalyzerIOS.objects.filter(MD5=checksum)
            win_static_db = StaticAnalyzerWindows.objects.filter(MD5=checksum)

            if android_static_db.exists():
                context, template = handle_pdf_android(android_static_db)
            elif ios_static_db.exists():
                context, template = handle_pdf_ios(ios_static_db)
            elif win_static_db.exists():
                context, template = handle_pdf_win(win_static_db)
            else:
                if api:
                    return {'report': 'Report not Found'}
                else:
                    return HttpResponse(
                        json.dumps({'report': 'Report not Found'}),
                        content_type='application/json; charset=utf-8',
                        status=500)
            context['virus_total'] = None
            if settings.VT_ENABLED:
                file_extension = os.path.splitext(
                    context['file_name'].lower())[1]
                app_dir = os.path.join(settings.UPLD_DIR, checksum + '/')
                vt = VirusTotal.VirusTotal()
                if file_extension.lower() == '.zip':
                    context['virus_total'] = None
                else:
                    context['virus_total'] = vt.get_result(
                        os.path.join(app_dir, checksum) +
                        file_extension.lower(), checksum)
            try:
                if api and jsonres:
                    return {'report_dat': context}
                else:
                    options = {
                        'page-size': 'A4',
                        'quiet': '',
                        'no-collate': '',
                        'margin-top': '0.50in',
                        'margin-right': '0.50in',
                        'margin-bottom': '0.50in',
                        'margin-left': '0.50in',
                        'encoding': 'UTF-8',
                        'custom-header': [
                            ('Accept-Encoding', 'gzip'),
                        ],
                        'no-outline': None,
                    }
                    html = template.render(context)
                    pdf_dat = pdfkit.from_string(html, False, options=options)
                    if api:
                        return {'pdf_dat': pdf_dat}
                    return HttpResponse(pdf_dat,
                                        content_type='application/pdf')
            except Exception as exp:
                logger.exception('Error Generating PDF Report')
                if api:
                    return {
                        'error': 'Cannot Generate PDF/JSON',
                        'err_details': str(exp)
                    }
                else:
                    return HttpResponse(
                        json.dumps({
                            'pdf_error': 'Cannot Generate PDF',
                            'err_details': str(exp)
                        }),
                        content_type='application/json; charset=utf-8',
                        status=500)

        else:
            if api:
                return {'error': 'Invalid scan hash'}
            else:
                return HttpResponse(
                    json.dumps({'md5': 'Invalid MD5'}),
                    content_type='application/json; charset=utf-8',
                    status=500)
    except Exception as exp:
        logger.exception('Error Generating PDF Report')
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
Example #18
0
def static_analyzer_ios(request, api=False):
    """Module that performs iOS IPA/ZIP Static Analysis"""
    try:
        print "[INFO] iOS Static Analysis Started"
        if api:
            file_type = request.POST['scan_type']
            checksum = request.POST['hash']
            rescan = str(request.POST.get('re_scan', 0))
            filename = request.POST['file_name']
        else:
            file_type = request.GET['type']
            checksum = request.GET['checksum']
            rescan = str(request.GET.get('rescan', 0))
            filename = request.GET['name']

        md5_match = re.match('^[0-9a-f]{32}$', checksum)
        if ((md5_match) and (filename.lower().endswith('.ipa')
                             or filename.lower().endswith('.zip'))
                and (file_type in ['ipa', 'ios'])):
            app_dict = {}
            app_dict["directory"] = settings.BASE_DIR  # BASE DIR
            app_dict["app_name"] = filename  # APP ORGINAL NAME
            app_dict["md5_hash"] = checksum  # MD5
            app_dict["app_dir"] = os.path.join(
                settings.UPLD_DIR, app_dict["md5_hash"] + '/')  # APP DIRECTORY
            tools_dir = os.path.join(app_dict["directory"],
                                     'StaticAnalyzer/tools/mac/')  # TOOLS DIR
            if file_type == 'ipa':
                # DB
                ipa_db = StaticAnalyzerIPA.objects.filter(
                    MD5=app_dict["md5_hash"])
                if ipa_db.exists() and rescan == '0':
                    context = get_context_from_db_entry_ipa(ipa_db)
                else:
                    print "[INFO] iOS Binary (IPA) Analysis Started"
                    app_dict["app_file"] = app_dict[
                        "md5_hash"] + '.ipa'  # NEW FILENAME
                    app_dict["app_path"] = app_dict["app_dir"] + \
                        app_dict["app_file"]  # APP PATH
                    app_dict["bin_dir"] = os.path.join(app_dict["app_dir"],
                                                       "Payload/")
                    app_dict["size"] = str(file_size(
                        app_dict["app_path"])) + 'MB'  # FILE SIZE
                    app_dict["sha1"], app_dict["sha256"] = hash_gen(
                        app_dict["app_path"])  # SHA1 & SHA256 HASHES
                    print "[INFO] Extracting IPA"
                    # EXTRACT IPA
                    unzip(app_dict["app_path"], app_dict["app_dir"])
                    # Get Files, normalize + to x,
                    # and convert binary plist -> xml
                    files, sfiles = ios_list_files(app_dict["bin_dir"],
                                                   app_dict["md5_hash"], True,
                                                   'ipa')
                    infoplist_dict = plist_analysis(app_dict["bin_dir"], False)
                    bin_analysis_dict = binary_analysis(
                        app_dict["bin_dir"], tools_dir, app_dict["app_dir"])
                    # Saving to DB
                    print "\n[INFO] Connecting to DB"
                    if rescan == '1':
                        print "\n[INFO] Updating Database..."
                        update_db_entry_ipa(app_dict, infoplist_dict,
                                            bin_analysis_dict, files, sfiles)
                    elif rescan == '0':
                        print "\n[INFO] Saving to Database"
                        create_db_entry_ipa(app_dict, infoplist_dict,
                                            bin_analysis_dict, files, sfiles)
                    context = get_context_from_analysis_ipa(
                        app_dict, infoplist_dict, bin_analysis_dict, files,
                        sfiles)

                context['VT_RESULT'] = None
                if settings.VT_ENABLED:
                    vt = VirusTotal.VirusTotal()
                    context['VT_RESULT'] = vt.get_result(
                        os.path.join(app_dict['app_dir'], app_dict['md5_hash'])
                        + '.ipa', app_dict['md5_hash'])

                template = "static_analysis/ios_binary_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif file_type == 'ios':
                ios_zip_db = StaticAnalyzerIOSZIP.objects.filter(
                    MD5=app_dict["md5_hash"])
                if ios_zip_db.exists() and rescan == '0':
                    context = get_context_from_db_entry_ios(ios_zip_db)
                else:
                    print "[INFO] iOS Source Code Analysis Started"
                    app_dict["app_file"] = app_dict[
                        "md5_hash"] + '.zip'  # NEW FILENAME
                    app_dict["app_path"] = app_dict["app_dir"] + \
                        app_dict["app_file"]  # APP PATH
                    # ANALYSIS BEGINS - Already Unzipped
                    print "[INFO] ZIP Already Extracted"
                    app_dict["size"] = str(file_size(
                        app_dict["app_path"])) + 'MB'  # FILE SIZE
                    app_dict["sha1"], app_dict["sha256"] = hash_gen(
                        app_dict["app_path"])  # SHA1 & SHA256 HASHES
                    files, sfiles = ios_list_files(app_dict["app_dir"],
                                                   app_dict["md5_hash"], False,
                                                   'ios')
                    infoplist_dict = plist_analysis(app_dict["app_dir"], True)
                    code_analysis_dic = ios_source_analysis(
                        app_dict["app_dir"])
                    # Saving to DB
                    print "\n[INFO] Connecting to DB"
                    if rescan == '1':
                        print "\n[INFO] Updating Database..."
                        update_db_entry_ios(app_dict, infoplist_dict,
                                            code_analysis_dic, files, sfiles)
                    elif rescan == '0':
                        print "\n[INFO] Saving to Database"
                        create_db_entry_ios(app_dict, infoplist_dict,
                                            code_analysis_dic, files, sfiles)
                    context = get_context_from_analysis_ios(
                        app_dict, infoplist_dict, code_analysis_dic, files,
                        sfiles)
                template = "static_analysis/ios_source_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                msg = "File Type not supported!"
                if api:
                    return print_n_send_error_response(request, msg, True)
                else:
                    return print_n_send_error_response(request, msg, False)
        else:
            msg = "Hash match failed or Invalid file extension or file type"
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)
    except Exception as exp:
        msg = str(exp)
        exp_doc = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
def run(request, api=False):
    """View iOS Files"""
    try:
        print("[INFO] View iOS Source File")
        file_format = "cpp"
        if api:
            fil = request.POST['file']
            md5_hash = request.POST['hash']
            mode = request.POST['type']
            viewsource_form = ViewSourceIOSApiForm(request.POST)
        else:
            fil = request.GET['file']
            md5_hash = request.GET['md5']
            mode = request.GET['type']
            viewsource_form = ViewSourceIOSForm(request.GET)
        typ = set_ext_api(fil)
        if not viewsource_form.is_valid():
            err = FormUtil.errors_message(viewsource_form)
            if api:
                return err
            context = {
                'title': 'Error',
                'exp': 'Error Description',
                'doc': err
            }
            template = "general/error.html"
            return render(request, template, context, status=400)
        if mode == 'ipa':
            src = os.path.join(settings.UPLD_DIR,
                               md5_hash + '/Payload/')
        elif mode == 'ios':
            src = os.path.join(settings.UPLD_DIR, md5_hash + '/')
        sfile = os.path.join(src, fil)
        dat = ''
        if typ == 'm':
            file_format = 'cpp'
            with io.open(sfile, mode='r', encoding="utf8", errors="ignore") as flip:
                dat = flip.read()
        elif typ == 'xml':
            file_format = 'xml'
            with io.open(sfile, mode='r', encoding="utf8", errors="ignore") as flip:
                dat = flip.read()
        elif typ == 'db':
            file_format = 'asciidoc'
            dat = read_sqlite(sfile)
        elif typ == 'txt' and fil == "classdump.txt":
            file_format = 'cpp'
            app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/')
            cls_dump_file = os.path.join(app_dir, "classdump.txt")
            if isFileExists(cls_dump_file):
                with io.open(cls_dump_file,
                             mode='r',
                             encoding="utf8",
                             errors="ignore"
                             ) as flip:
                    dat = flip.read()
            else:
                dat = "Class Dump result not Found"
        else:
            if api:
                return {"error": "Invalid Parameters"}
            return HttpResponseRedirect('/error/')
        context = {'title': escape(ntpath.basename(fil)),
                   'file': escape(ntpath.basename(fil)),
                   'type': file_format,
                   'dat': dat}
        template = "general/view.html"
        if api:
            return context
        return render(request, template, context)
    except Exception as exp:
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
def compare_apps(request, hash1: str, hash2: str, api=False):
    if hash1 == hash2:
        error_msg = 'Results with same hash cannot be compared'
        return print_n_send_error_response(request, error_msg, api)
    logger.info('Starting App compare for %s and %s', hash1, hash2)
    return generic_compare(request, hash1, hash2, api)
def static_analyzer_ios(request, api=False):
    """Module that performs iOS IPA/ZIP Static Analysis."""
    try:
        logger.info('iOS Static Analysis Started')
        if api:
            file_type = request.POST['scan_type']
            checksum = request.POST['hash']
            rescan = str(request.POST.get('re_scan', 0))
            filename = request.POST['file_name']
        else:
            file_type = request.GET['type']
            checksum = request.GET['checksum']
            rescan = str(request.GET.get('rescan', 0))
            filename = request.GET['name']

        md5_match = re.match('^[0-9a-f]{32}$', checksum)
        if ((md5_match) and (filename.lower().endswith('.ipa')
                             or filename.lower().endswith('.zip'))
                and (file_type in ['ipa', 'ios'])):
            app_dict = {}
            app_dict['directory'] = Path(settings.BASE_DIR)  # BASE DIR
            app_dict['file_name'] = filename  # APP ORGINAL NAME
            app_dict['md5_hash'] = checksum  # MD5
            app_dir = Path(settings.UPLD_DIR) / checksum
            tools_dir = app_dict[
                'directory'] / 'StaticAnalyzer' / 'tools' / 'ios'
            tools_dir = tools_dir.as_posix()
            if file_type == 'ipa':
                app_dict[
                    'app_file'] = app_dict['md5_hash'] + '.ipa'  # NEW FILENAME
                app_dict['app_path'] = app_dir / app_dict['app_file']
                app_dict['app_path'] = app_dict['app_path'].as_posix()
                # DB
                ipa_db = StaticAnalyzerIOS.objects.filter(
                    MD5=app_dict['md5_hash'])
                if ipa_db.exists() and rescan == '0':
                    context = get_context_from_db_entry(ipa_db)
                else:
                    logger.info('iOS Binary (IPA) Analysis Started')
                    app_dict['app_dir'] = app_dir.as_posix() + '/'
                    app_dict['size'] = str(file_size(
                        app_dict['app_path'])) + 'MB'  # FILE SIZE
                    app_dict['sha1'], app_dict['sha256'] = hash_gen(
                        app_dict['app_path'])  # SHA1 & SHA256 HASHES
                    logger.info('Extracting IPA')
                    # EXTRACT IPA
                    unzip(app_dict['app_path'], app_dict['app_dir'])
                    # Identify Payload directory
                    dirs = app_dir.glob('**/*')
                    for _dir in dirs:
                        if 'payload' in _dir.as_posix().lower():
                            app_dict['bin_dir'] = app_dict['app_dir'] / _dir
                            break
                    else:
                        msg = ('IPA is malformed! '
                               'MobSF cannot find Payload directory')
                        if api:
                            return print_n_send_error_response(
                                request, msg, True)
                        else:
                            return print_n_send_error_response(
                                request, msg, False)
                    app_dict['bin_dir'] = app_dict['bin_dir'].as_posix() + '/'
                    # Get Files
                    all_files = ios_list_files(app_dict['bin_dir'],
                                               app_dict['md5_hash'], True,
                                               'ipa')
                    infoplist_dict = plist_analysis(app_dict['bin_dir'], False)
                    app_dict['appstore'] = app_search(infoplist_dict.get('id'))
                    bin_analysis_dict = binary_analysis(
                        app_dict['bin_dir'], tools_dir, app_dict['app_dir'],
                        infoplist_dict.get('bin'))
                    # Get Icon
                    app_dict['icon_found'] = get_icon(
                        app_dict['md5_hash'], app_dict['bin_dir'],
                        infoplist_dict.get('bin'))
                    # IPA URL and Email Extract
                    recon = extract_urls_n_email(app_dict['bin_dir'],
                                                 all_files['files_long'],
                                                 bin_analysis_dict['strings'])
                    code_dict = {
                        'api': {},
                        'code_anal': {},
                        'urlnfile': recon['urlnfile'],
                        'domains': recon['domains'],
                        'emailnfile': recon['emailnfile'],
                        'firebase': firebase_analysis(recon['urls_list']),
                    }
                    # Saving to DB
                    logger.info('Connecting to DB')
                    if rescan == '1':
                        logger.info('Updating Database...')
                        save_or_update('update', app_dict, infoplist_dict,
                                       code_dict, bin_analysis_dict, all_files)
                        update_scan_timestamp(app_dict['md5_hash'])
                    elif rescan == '0':
                        logger.info('Saving to Database')
                        save_or_update('save', app_dict, infoplist_dict,
                                       code_dict, bin_analysis_dict, all_files)
                    context = get_context_from_analysis(
                        app_dict, infoplist_dict, code_dict, bin_analysis_dict,
                        all_files)
                context['virus_total'] = None
                if settings.VT_ENABLED:
                    vt = VirusTotal.VirusTotal()
                    context['virus_total'] = vt.get_result(
                        app_dict['app_path'], app_dict['md5_hash'])
                context['average_cvss'], context['security_score'] = score(
                    context['binary_analysis'])
                template = 'static_analysis/ios_binary_analysis.html'
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif file_type == 'ios':
                ios_zip_db = StaticAnalyzerIOS.objects.filter(
                    MD5=app_dict['md5_hash'])
                if ios_zip_db.exists() and rescan == '0':
                    context = get_context_from_db_entry(ios_zip_db)
                else:
                    logger.info('iOS Source Code Analysis Started')
                    app_dict['app_file'] = app_dict[
                        'md5_hash'] + '.zip'  # NEW FILENAME
                    app_dict['app_path'] = app_dir / app_dict['app_file']
                    app_dict['app_path'] = app_dict['app_path'].as_posix()
                    app_dict['app_dir'] = app_dir.as_posix() + '/'
                    # ANALYSIS BEGINS - Already Unzipped
                    app_dict['size'] = str(file_size(
                        app_dict['app_path'])) + 'MB'  # FILE SIZE
                    app_dict['sha1'], app_dict['sha256'] = hash_gen(
                        app_dict['app_path'])  # SHA1 & SHA256 HASHES
                    all_files = ios_list_files(app_dict['app_dir'],
                                               app_dict['md5_hash'], False,
                                               'ios')
                    infoplist_dict = plist_analysis(app_dict['app_dir'], True)
                    app_dict['appstore'] = app_search(infoplist_dict.get('id'))
                    code_analysis_dic = ios_source_analysis(
                        app_dict['app_dir'])
                    # Get App Icon
                    app_dict['icon_found'] = get_icon_source(
                        app_dict['md5_hash'], app_dict['app_dir'])
                    # Firebase DB Check
                    code_analysis_dic['firebase'] = firebase_analysis(
                        list(set(code_analysis_dic['urls_list'])))
                    fake_bin_dict = {
                        'checksec': {},
                        'libraries': [],
                        'bin_code_analysis': {},
                        'strings': [],
                        'bin_info': {},
                        'bin_type': code_analysis_dic['source_type'],
                    }
                    # Saving to DB
                    logger.info('Connecting to DB')
                    if rescan == '1':
                        logger.info('Updating Database...')
                        save_or_update('update', app_dict, infoplist_dict,
                                       code_analysis_dic, fake_bin_dict,
                                       all_files)
                        update_scan_timestamp(app_dict['md5_hash'])
                    elif rescan == '0':
                        logger.info('Saving to Database')
                        save_or_update('save', app_dict, infoplist_dict,
                                       code_analysis_dic, fake_bin_dict,
                                       all_files)
                    context = get_context_from_analysis(
                        app_dict, infoplist_dict, code_analysis_dic,
                        fake_bin_dict, all_files)
                context['average_cvss'], context['security_score'] = score(
                    context['code_analysis'])
                template = 'static_analysis/ios_source_analysis.html'
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                msg = 'File Type not supported!'
                if api:
                    return print_n_send_error_response(request, msg, True)
                else:
                    return print_n_send_error_response(request, msg, False)
        else:
            msg = 'Hash match failed or Invalid file extension or file type'
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)
    except Exception as exp:
        logger.exception('Error Perfroming Static Analysis')
        msg = str(exp)
        exp_doc = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
def staticanalyzer_windows(request, api=False):
    """Analyse a windows app."""
    try:
        # Input validation
        logger.info("Windows Static Analysis Started")
        app_dic = {}  # Dict to store the binary attributes
        if api:
            typ = request.POST['scan_type']
            rescan = str(request.POST.get('re_scan', 0))
            checksum = request.POST['hash']
            filename = request.POST['file_name']
        else:
            typ = request.GET['type']
            rescan = str(request.GET.get('rescan', 0))
            checksum = request.GET['checksum']
            filename = request.GET['name']
        md5_regex = re.match('^[0-9a-f]{32}$', checksum)
        if (md5_regex) and (typ in ['appx']):
            app_dic['app_name'] = filename  # APP ORGINAL NAME
            app_dic['md5'] = checksum
            app_dic['app_dir'] = os.path.join(
                settings.UPLD_DIR, app_dic['md5'] + '/')
            app_dic['tools_dir'] = os.path.join(
                settings.BASE_DIR, 'StaticAnalyzer/tools/windows/')
            if typ == 'appx':
                # DB
                db_entry = StaticAnalyzerWindows.objects.filter(  # pylint: disable-msg=E1101
                    MD5=app_dic['md5']
                )
                if db_entry.exists() and rescan == '0':
                    logger.info("Analysis is already Done. Fetching data from the DB...")
                    context = {
                        'title': db_entry[0].TITLE,
                        'name': db_entry[0].APP_NAME,
                        'pub_name': db_entry[0].PUB_NAME,
                        'size': db_entry[0].SIZE,
                        'md5': db_entry[0].MD5,
                        'sha1': db_entry[0].SHA1,
                        'sha256': db_entry[0].SHA256,
                        'bin_name': db_entry[0].BINNAME,
                        'version':  db_entry[0].VERSION,
                        'arch':  db_entry[0].ARCH,
                        'compiler_version':  db_entry[0].COMPILER_VERSION,
                        'visual_studio_version':  db_entry[0].VISUAL_STUDIO_VERSION,
                        'visual_studio_edition':  db_entry[0].VISUAL_STUDIO_EDITION,
                        'target_os':  db_entry[0].TARGET_OS,
                        'appx_dll_version':  db_entry[0].APPX_DLL_VERSION,
                        'proj_guid':  db_entry[0].PROJ_GUID,
                        'opti_tool':  db_entry[0].OPTI_TOOL,
                        'target_run':  db_entry[0].TARGET_RUN,
                        'files': python_list(db_entry[0].FILES),
                        'strings': python_list(db_entry[0].STRINGS),
                        'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS),
                        'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS)
                    }
                else:
                    logger.info("Windows Binary Analysis Started")
                    app_dic['app_path'] = os.path.join(
                        app_dic['app_dir'], app_dic['md5'] + '.appx')
                    # ANALYSIS BEGINS
                    app_dic['size'] = str(file_size(app_dic['app_path'])) + 'MB'
                    # Generate hashes
                    app_dic['sha1'], app_dic[
                        'sha256'] = hash_gen(app_dic['app_path'])
                    # EXTRACT APPX
                    logger.info("Extracting APPX")
                    app_dic['files'] = unzip(
                        app_dic['app_path'], app_dic['app_dir'])
                    xml_dic = _parse_xml(app_dic['app_dir'])
                    bin_an_dic = _binary_analysis(app_dic)
                    # Saving to db
                    logger.info("Connecting to DB")
                    if rescan == '1':
                        logger.info("Updating Database...")
                        StaticAnalyzerWindows.objects.filter(  # pylint: disable-msg=E1101
                            MD5=app_dic['md5']
                        ).update(
                            TITLE='Static Analysis',
                            APP_NAME=app_dic['app_name'],
                            PUB_NAME=xml_dic['pub_name'],
                            SIZE=app_dic['size'],
                            MD5=app_dic['md5'],
                            SHA1=app_dic['sha1'],
                            SHA256=app_dic['sha256'],
                            BINNAME=bin_an_dic['bin_name'],
                            VERSION=xml_dic['version'],
                            ARCH=xml_dic['arch'],
                            COMPILER_VERSION=xml_dic['compiler_version'],
                            VISUAL_STUDIO_VERSION=xml_dic[
                                'visual_studio_version'],
                            VISUAL_STUDIO_EDITION=xml_dic[
                                'visual_studio_edition'],
                            TARGET_OS=xml_dic['target_os'],
                            APPX_DLL_VERSION=xml_dic['appx_dll_version'],
                            PROJ_GUID=xml_dic['proj_guid'],
                            OPTI_TOOL=xml_dic['opti_tool'],
                            TARGET_RUN=xml_dic['target_run'],
                            FILES=app_dic['files'],
                            STRINGS=bin_an_dic['strings'],
                            BIN_AN_RESULTS=bin_an_dic['results'],
                            BIN_AN_WARNINGS=bin_an_dic['warnings'],
                        )
                    elif rescan == '0':
                        logger.info("Saving to Database")
                        db_item = StaticAnalyzerWindows(
                            TITLE='Static Analysis',
                            APP_NAME=app_dic['app_name'],
                            PUB_NAME=xml_dic['pub_name'],
                            SIZE=app_dic['size'],
                            MD5=app_dic['md5'],
                            SHA1=app_dic['sha1'],
                            SHA256=app_dic['sha256'],
                            BINNAME=bin_an_dic['bin_name'],
                            VERSION=xml_dic['version'],
                            ARCH=xml_dic['arch'],
                            COMPILER_VERSION=xml_dic['compiler_version'],
                            VISUAL_STUDIO_VERSION=xml_dic[
                                'visual_studio_version'],
                            VISUAL_STUDIO_EDITION=xml_dic[
                                'visual_studio_edition'],
                            TARGET_OS=xml_dic['target_os'],
                            APPX_DLL_VERSION=xml_dic['appx_dll_version'],
                            PROJ_GUID=xml_dic['proj_guid'],
                            OPTI_TOOL=xml_dic['opti_tool'],
                            TARGET_RUN=xml_dic['target_run'],
                            FILES=app_dic['files'],
                            STRINGS=bin_an_dic['strings'],
                            BIN_AN_RESULTS=bin_an_dic['results'],
                            BIN_AN_WARNINGS=bin_an_dic['warnings'],
                        )
                        db_item.save()
                    context = {
                        'title': 'Static Analysis',
                        'name': app_dic['app_name'],
                        'pub_name': xml_dic['pub_name'],
                        'size': app_dic['size'],
                        'md5': app_dic['md5'],
                        'sha1': app_dic['sha1'],
                        'sha256': app_dic['sha256'],
                        'bin_name': bin_an_dic['bin_name'],
                        'version': xml_dic['version'],
                        'arch': xml_dic['arch'],
                        'compiler_version': xml_dic['compiler_version'],
                        'visual_studio_version': xml_dic['visual_studio_version'],
                        'visual_studio_edition': xml_dic['visual_studio_edition'],
                        'target_os': xml_dic['target_os'],
                        'appx_dll_version': xml_dic['appx_dll_version'],
                        'proj_guid': xml_dic['proj_guid'],
                        'opti_tool': xml_dic['opti_tool'],
                        'target_run': xml_dic['target_run'],
                        'files': app_dic['files'],
                        'strings': bin_an_dic['strings'],
                        'bin_an_results': bin_an_dic['results'],
                        'bin_an_warnings': bin_an_dic['warnings'],
                    }
                template = "static_analysis/windows_binary_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                msg = "File type not supported"
                if api:
                    return print_n_send_error_response(request, msg, True)
                else:
                    return print_n_send_error_response(request, msg, False)
        else:
            msg = "Hash match failed or Invalid file extension"
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)
    except Exception as exception:
        msg = str(exception)
        exp_doc = exception.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
def pdf(request, api=False, jsonres=False):
    try:
        if api:
            checksum = request.POST['hash']
        else:
            checksum = request.GET['md5']
        hash_match = re.match('^[0-9a-f]{32}$', checksum)
        if not hash_match:
            if api:
                return {'error': 'Invalid scan hash'}
            else:
                return HttpResponse(json.dumps({'md5': 'Invalid scan hash'}),
                                    content_type=ctype,
                                    status=500)
        # Do Lookups
        android_static_db = StaticAnalyzerAndroid.objects.filter(MD5=checksum)
        ios_static_db = StaticAnalyzerIOS.objects.filter(MD5=checksum)
        win_static_db = StaticAnalyzerWindows.objects.filter(MD5=checksum)

        if android_static_db.exists():
            context, template = handle_pdf_android(android_static_db)
        elif ios_static_db.exists():
            context, template = handle_pdf_ios(ios_static_db)
        elif win_static_db.exists():
            context, template = handle_pdf_win(win_static_db)
        else:
            if api:
                return {'report': 'Report not Found'}
            else:
                return HttpResponse(json.dumps({'report': 'Report not Found'}),
                                    content_type=ctype,
                                    status=500)
        # Do VT Scan only on binaries
        context['virus_total'] = None
        ext = os.path.splitext(context['file_name'].lower())[1]
        if settings.VT_ENABLED and ext != '.zip':
            app_bin = os.path.join(settings.UPLD_DIR, checksum + '/',
                                   checksum + ext)
            vt = VirusTotal.VirusTotal()
            context['virus_total'] = vt.get_result(app_bin, checksum)
        # Get Local Base URL
        proto = 'file://'
        host_os = 'nix'
        if platform.system() == 'Windows':
            proto = 'file:///'
            host_os = 'windows'
        context['base_url'] = proto + settings.BASE_DIR
        context['dwd_dir'] = proto + settings.DWD_DIR
        context['host_os'] = host_os
        context['timestamp'] = RecentScansDB.objects.get(
            MD5=checksum).TIMESTAMP
        try:
            if api and jsonres:
                return {'report_dat': context}
            else:
                options = {
                    'page-size': 'Letter',
                    'quiet': '',
                    'enable-local-file-access': '',
                    'no-collate': '',
                    'margin-top': '0.50in',
                    'margin-right': '0.50in',
                    'margin-bottom': '0.50in',
                    'margin-left': '0.50in',
                    'encoding': 'UTF-8',
                    'custom-header': [
                        ('Accept-Encoding', 'gzip'),
                    ],
                    'no-outline': None,
                }
                # Added proxy support to wkhtmltopdf
                proxies, _ = upstream_proxy('https')
                if proxies['https']:
                    options['proxy'] = proxies['https']
                html = template.render(context)
                pdf_dat = pdfkit.from_string(html, False, options=options)
                if api:
                    return {'pdf_dat': pdf_dat}
                return HttpResponse(pdf_dat, content_type='application/pdf')
        except Exception as exp:
            logger.exception('Error Generating PDF Report')
            if api:
                return {
                    'error': 'Cannot Generate PDF/JSON',
                    'err_details': str(exp)
                }
            else:
                return HttpResponse(json.dumps({
                    'pdf_error': 'Cannot Generate PDF',
                    'err_details': str(exp)
                }),
                                    content_type=ctype,
                                    status=500)
    except Exception as exp:
        logger.exception('Error Generating PDF Report')
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
def activity_tester(request):
    """Activity Tester"""
    logger.info("Activity Tester")
    try:
        md5_hash = request.POST['md5']
        package = request.POST['pkg']
        if re.match('^[0-9a-f]{32}$', md5_hash):
            if re.findall(r";|\$\(|\|\||&&", package):
                return print_n_send_error_response(request, "Possible RCE Attack", True)
            if request.method == 'POST':
                base_dir = settings.BASE_DIR
                app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/')
                screen_dir = os.path.join(app_dir, 'screenshots-apk/')
                if not os.path.exists(screen_dir):
                    os.makedirs(screen_dir)
                data = {}
                adb = getADB()
                static_android_db = StaticAnalyzerAndroid.objects.filter(
                    MD5=md5_hash)
                if static_android_db.exists():
                    logger.info("Fetching Activity List from DB")
                    activities = python_list(static_android_db[0].ACTIVITIES)
                    if activities:
                        act_no = 0
                        logger.info("Starting Activity Tester...")
                        logger.info("" + str(len(activities)) +
                                    " Activities Identified")
                        for line in activities:
                            try:
                                act_no += 1
                                logger.info("Launching Activity - " +
                                            str(act_no) + ". " + line)
                                adb_command(
                                    ["am", "start", "-n", package + "/" + line], True)
                                # AVD is much slower, it should get extra time
                                if settings.ANDROID_DYNAMIC_ANALYZER == "MobSF_AVD":
                                    wait(8)
                                else:
                                    wait(4)
                                adb_command(
                                    ["screencap", "-p", "/data/local/screen.png"], True)
                                #? get appended from Air :-() if activity names are used
                                adb_command(["pull", "/data/local/screen.png",
                                             screen_dir + "act-" + str(act_no) + ".png"])
                                logger.info("Activity Screenshot Taken")
                                adb_command(
                                    ["am", "force-stop", package], True)
                                logger.info("Stopping App")
                            except:
                                PrintException("Activity Tester")
                        data = {'acttest': 'done'}
                    else:
                        logger.info("Activity Tester - No Activity Found!")
                        data = {'acttest': 'noact'}
                    return HttpResponse(json.dumps(data), content_type='application/json')
                else:
                    return print_n_send_error_response(request, "Entry does not exist in DB", True)
            else:
                return print_n_send_error_response(request, "Only POST allowed", True)
        else:
            return print_n_send_error_response(request, "Invalid Scan Hash", True)
    except:
        PrintException("Activity Tester")
        return print_n_send_error_response(request, "Error Running Activity Tester", True)
Example #25
0
def run(request, api=False):
    """View iOS Files."""
    try:
        logger.info('View iOS Source File')
        file_format = 'cpp'
        if api:
            fil = request.POST['file']
            md5_hash = request.POST['hash']
            mode = request.POST['type']
            viewsource_form = ViewSourceIOSApiForm(request.POST)
        else:
            fil = request.GET['file']
            md5_hash = request.GET['md5']
            mode = request.GET['type']
            viewsource_form = ViewSourceIOSForm(request.GET)
        typ = set_ext_api(fil)
        if not viewsource_form.is_valid():
            err = FormUtil.errors_message(viewsource_form)
            if api:
                return err
            context = {
                'title': 'Error',
                'exp': 'Error Description',
                'doc': err,
            }
            template = 'general/error.html'
            return render(request, template, context, status=400)
        if mode == 'ipa':
            src = os.path.join(settings.UPLD_DIR, md5_hash + '/Payload/')
        elif mode == 'ios':
            src = os.path.join(settings.UPLD_DIR, md5_hash + '/')
        sfile = os.path.join(src, fil)
        dat = ''
        sql_dump = {}
        if typ == 'm':
            file_format = 'cpp'
            with io.open(sfile, mode='r', encoding='utf8',
                         errors='ignore') as flip:
                dat = flip.read()
        elif typ == 'xml':
            file_format = 'xml'
            with io.open(sfile, mode='r', encoding='utf8',
                         errors='ignore') as flip:
                dat = flip.read()
        elif typ == 'plist':
            file_format = 'json'
            dat = biplist.readPlist(sfile)
            try:
                dat = json.dumps(dat, indent=4, sort_keys=True)
            except Exception:
                pass
        elif typ == 'db':
            file_format = 'asciidoc'
            sql_dump = read_sqlite(sfile)
        elif typ == 'txt' and fil == 'classdump.txt':
            file_format = 'cpp'
            app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/')
            cls_dump_file = os.path.join(app_dir, 'classdump.txt')
            if is_file_exists(cls_dump_file):
                with io.open(cls_dump_file,
                             mode='r',
                             encoding='utf8',
                             errors='ignore') as flip:
                    dat = flip.read()
            else:
                dat = 'Class Dump result not Found'
        elif typ == 'txt':
            file_format = 'text'
            with io.open(sfile, mode='r', encoding='utf8',
                         errors='ignore') as flip:
                dat = flip.read()
        else:
            if api:
                return {'error': 'Invalid Parameters'}
            return HttpResponseRedirect('/error/')
        context = {
            'title': escape(ntpath.basename(fil)),
            'file': escape(ntpath.basename(fil)),
            'type': file_format,
            'dat': dat,
            'sql': sql_dump
        }
        template = 'general/view.html'
        if api:
            return context
        return render(request, template, context)
    except Exception as exp:
        logger.exception('Error Viewing Source')
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
def run(request, api=False):
    """View the source of a file."""
    try:
        print("[INFO] View Android Source File")
        if api:
            fil = request.POST['file']
            md5 = request.POST['hash']
            typ = request.POST['type']
            viewsource_form = ViewSourceAndroidApiForm(request.POST)
        else:
            fil = request.GET['file']
            md5 = request.GET['md5']
            typ = request.GET['type']
            viewsource_form = ViewSourceAndroidForm(request.GET)
        if not viewsource_form.is_valid():
            err = FormUtil.errors_message(viewsource_form)
            if api:
                return err
            context = {
                'title': 'Error',
                'exp': 'Error Description',
                'doc': err
            }
            template = "general/error.html"
            return render(request, template, context, status=400)
        if fil.endswith('.java'):
            if typ == 'eclipse':
                src = os.path.join(settings.UPLD_DIR, md5 + '/src/')
            elif typ == 'studio':
                src = os.path.join(
                    settings.UPLD_DIR, md5 + '/app/src/main/java/')
            elif typ == 'apk':
                src = os.path.join(
                    settings.UPLD_DIR, md5 + '/java_source/')
        elif fil.endswith('.smali'):
            src = os.path.join(settings.UPLD_DIR,
                               md5 + '/smali_source/')
        sfile = os.path.join(src, fil)
        dat = ''
        with io.open(
            sfile,
            mode='r',
            encoding="utf8",
            errors="ignore"
        ) as file_pointer:
            dat = file_pointer.read()
        context = {
            'title': escape(ntpath.basename(fil)),
            'file': escape(ntpath.basename(fil)),
            'dat': dat
        }
        template = "static_analysis/view_source.html"
        if api:
            return context
        return render(request, template, context)
    except Exception as exp:
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
def staticanalyzer_windows(request, api=False):
    """Analyse a windows app."""
    try:
        # Input validation
        print "[INFO] Windows Static Analysis Started"
        app_dic = {}  # Dict to store the binary attributes
        if api:
            typ = request.POST['scan_type']
            rescan = str(request.POST.get('re_scan', 0))
            checksum = request.POST['hash']
            filename = request.POST['file_name']
        else:
            typ = request.GET['type']
            rescan = str(request.GET.get('rescan', 0))
            checksum = request.GET['checksum']
            filename = request.GET['name']
        md5_regex = re.match('^[0-9a-f]{32}$', checksum)
        if (md5_regex) and (typ in ['appx']):
            app_dic['app_name'] = filename  # APP ORGINAL NAME
            app_dic['md5'] = checksum
            app_dic['app_dir'] = os.path.join(
                settings.UPLD_DIR, app_dic['md5'] + '/')
            app_dic['tools_dir'] = os.path.join(
                settings.BASE_DIR, 'StaticAnalyzer/tools/windows/')
            if typ == 'appx':
                # DB
                db_entry = StaticAnalyzerWindows.objects.filter(  # pylint: disable-msg=E1101
                    MD5=app_dic['md5']
                )
                if db_entry.exists() and rescan == '0':
                    print "\n[INFO] Analysis is already Done. Fetching data from the DB..."
                    context = {
                        'title': db_entry[0].TITLE,
                        'name': db_entry[0].APP_NAME,
                        'pub_name': db_entry[0].PUB_NAME,
                        'size': db_entry[0].SIZE,
                        'md5': db_entry[0].MD5,
                        'sha1': db_entry[0].SHA1,
                        'sha256': db_entry[0].SHA256,
                        'bin_name': db_entry[0].BINNAME,
                        'version':  db_entry[0].VERSION,
                        'arch':  db_entry[0].ARCH,
                        'compiler_version':  db_entry[0].COMPILER_VERSION,
                        'visual_studio_version':  db_entry[0].VISUAL_STUDIO_VERSION,
                        'visual_studio_edition':  db_entry[0].VISUAL_STUDIO_EDITION,
                        'target_os':  db_entry[0].TARGET_OS,
                        'appx_dll_version':  db_entry[0].APPX_DLL_VERSION,
                        'proj_guid':  db_entry[0].PROJ_GUID,
                        'opti_tool':  db_entry[0].OPTI_TOOL,
                        'target_run':  db_entry[0].TARGET_RUN,
                        'files': python_list(db_entry[0].FILES),
                        'strings': python_list(db_entry[0].STRINGS),
                        'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS),
                        'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS)
                    }
                else:
                    print "[INFO] Windows Binary Analysis Started"
                    app_dic['app_path'] = os.path.join(
                        app_dic['app_dir'], app_dic['md5'] + '.appx')
                    # ANALYSIS BEGINS
                    app_dic['size'] = str(file_size(app_dic['app_path'])) + 'MB'
                    # Generate hashes
                    app_dic['sha1'], app_dic[
                        'sha256'] = hash_gen(app_dic['app_path'])
                    # EXTRACT APPX
                    print "[INFO] Extracting APPX"
                    app_dic['files'] = unzip(
                        app_dic['app_path'], app_dic['app_dir'])
                    xml_dic = _parse_xml(app_dic['app_dir'])
                    bin_an_dic = _binary_analysis(app_dic)
                    # Saving to db
                    print "\n[INFO] Connecting to DB"
                    if rescan == '1':
                        print "\n[INFO] Updating Database..."
                        StaticAnalyzerWindows.objects.filter(  # pylint: disable-msg=E1101
                            MD5=app_dic['md5']
                        ).update(
                            TITLE='Static Analysis',
                            APP_NAME=app_dic['app_name'],
                            PUB_NAME=xml_dic['pub_name'],
                            SIZE=app_dic['size'],
                            MD5=app_dic['md5'],
                            SHA1=app_dic['sha1'],
                            SHA256=app_dic['sha256'],
                            BINNAME=bin_an_dic['bin_name'],
                            VERSION=xml_dic['version'],
                            ARCH=xml_dic['arch'],
                            COMPILER_VERSION=xml_dic['compiler_version'],
                            VISUAL_STUDIO_VERSION=xml_dic[
                                'visual_studio_version'],
                            VISUAL_STUDIO_EDITION=xml_dic[
                                'visual_studio_edition'],
                            TARGET_OS=xml_dic['target_os'],
                            APPX_DLL_VERSION=xml_dic['appx_dll_version'],
                            PROJ_GUID=xml_dic['proj_guid'],
                            OPTI_TOOL=xml_dic['opti_tool'],
                            TARGET_RUN=xml_dic['target_run'],
                            FILES=app_dic['files'],
                            STRINGS=bin_an_dic['strings'],
                            BIN_AN_RESULTS=bin_an_dic['results'],
                            BIN_AN_WARNINGS=bin_an_dic['warnings'],
                        )
                    elif rescan == '0':
                        print "\n[INFO] Saving to Database"
                        db_item = StaticAnalyzerWindows(
                            TITLE='Static Analysis',
                            APP_NAME=app_dic['app_name'],
                            PUB_NAME=xml_dic['pub_name'],
                            SIZE=app_dic['size'],
                            MD5=app_dic['md5'],
                            SHA1=app_dic['sha1'],
                            SHA256=app_dic['sha256'],
                            BINNAME=bin_an_dic['bin_name'],
                            VERSION=xml_dic['version'],
                            ARCH=xml_dic['arch'],
                            COMPILER_VERSION=xml_dic['compiler_version'],
                            VISUAL_STUDIO_VERSION=xml_dic[
                                'visual_studio_version'],
                            VISUAL_STUDIO_EDITION=xml_dic[
                                'visual_studio_edition'],
                            TARGET_OS=xml_dic['target_os'],
                            APPX_DLL_VERSION=xml_dic['appx_dll_version'],
                            PROJ_GUID=xml_dic['proj_guid'],
                            OPTI_TOOL=xml_dic['opti_tool'],
                            TARGET_RUN=xml_dic['target_run'],
                            FILES=app_dic['files'],
                            STRINGS=bin_an_dic['strings'],
                            BIN_AN_RESULTS=bin_an_dic['results'],
                            BIN_AN_WARNINGS=bin_an_dic['warnings'],
                        )
                        db_item.save()
                    context = {
                        'title': 'Static Analysis',
                        'name': app_dic['app_name'],
                        'pub_name': xml_dic['pub_name'],
                        'size': app_dic['size'],
                        'md5': app_dic['md5'],
                        'sha1': app_dic['sha1'],
                        'sha256': app_dic['sha256'],
                        'bin_name': bin_an_dic['bin_name'],
                        'version': xml_dic['version'],
                        'arch': xml_dic['arch'],
                        'compiler_version': xml_dic['compiler_version'],
                        'visual_studio_version': xml_dic['visual_studio_version'],
                        'visual_studio_edition': xml_dic['visual_studio_edition'],
                        'target_os': xml_dic['target_os'],
                        'appx_dll_version': xml_dic['appx_dll_version'],
                        'proj_guid': xml_dic['proj_guid'],
                        'opti_tool': xml_dic['opti_tool'],
                        'target_run': xml_dic['target_run'],
                        'files': app_dic['files'],
                        'strings': bin_an_dic['strings'],
                        'bin_an_results': bin_an_dic['results'],
                        'bin_an_warnings': bin_an_dic['warnings'],
                    }
                template = "static_analysis/windows_binary_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                msg = "File type not supported"
                if api:
                    return print_n_send_error_response(request, msg, True)
                else:
                    return print_n_send_error_response(request, msg, False)
        else:
            msg = "Hash match failed or Invalid file extension"
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)
    except Exception as exception:
        msg = str(exception)
        exp_doc = exception.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
def generic_compare(request, first_hash: str, second_hash: str, api: bool = False):
    # This context consists of specific lists and analysis that is done on the classic ones
    # it will be filled during the different diff analysis
    context = {
        'title': 'Compare report',
        'first_app': dict(),
        'second_app': dict(),
        'urls': dict(),
        'api': dict(),
        'permissions': dict()
    }
    static_fields = ['md5', 'name', 'size', 'icon_found', 'icon_hidden', 'act_count', 'e_act', 'serv_count', 'e_ser',
                     'bro_count', 'e_bro', 'prov_count', 'e_cnt', 'apkid']

    # For now - support only android
    db_entry = StaticAnalyzerAndroid.objects.filter(MD5=first_hash)
    db_entry2 = StaticAnalyzerAndroid.objects.filter(MD5=second_hash)

    if not (db_entry.exists() and db_entry2.exists()):
        return print_n_send_error_response(request,
                                           "One of the Hashes wasn't found in the Android-results DB, make sure "
                                           "both of the apps finished analysis & they are both Android", False)

    # First fetch the already done analysis on each of the apps
    # We don't want to return this whole context back to the user because its a lot of data we don't use
    # it should help the performance I guess
    first_app = deepcopy(get_context_from_db_entry(db_entry))
    second_app = deepcopy(get_context_from_db_entry(db_entry2))

    # Second, fill the mutual static parts that are missing in the classic analysis
    for curr_app, db_context in [('first_app', first_app), ('second_app', second_app)]:

        # format informative title
        context[curr_app]['name_ver'] = "{0} - {1}".format(db_context['packagename'],
                                                           db_context['androvername'])

        # Fill all the static information
        for static_attr in static_fields:
            context[curr_app][static_attr] = db_context[static_attr]

        # Get only the subject of the cert
        subject_regex = re.compile(r'.*Subject:([^<]+)</br>.*', re.DOTALL)
        match = subject_regex.match(db_context['certinfo'])
        if match:
            context[curr_app]['cert_subject'] = match.group(1)
        else:
            context[curr_app]['cert_subject'] = "No subject"

        # Some preparations so we have some sort of same structures
        # (urls are lists inside the list which mess things up...)
        tmp_list = list()
        for url_obj in db_context['urls']:
            for url in url_obj['urls']:
                # urls can mess up the table because they can be really long, so let's cut them
                tmp_url = url[:70]
                while len(url) > 70:
                    url = url[70:]
                    tmp_url += '<br />'
                    tmp_url += url[:70]
                tmp_list.append(tmp_url)

        db_context['urls'] = list(set(deepcopy(tmp_list)))
        tmp_list.clear()

    # apkid check - we do it here just because its really ugly inside the template
    # I split it into these lines just it to be really clear what I'm checking

    first_error = context['first_app']['apkid'].get('error', False)
    second_error = context['second_app']['apkid'].get('error', False)
    first_has_version = context['first_app']['apkid'].get('apkid_version', False)
    second_has_version = context['second_app']['apkid'].get('apkid_version', False)
    context['apkid_error'] = (first_error or not first_has_version) or (second_error or not second_has_version)

    # Third, calculate some diffs
    for section, is_tuples in [
        ('permissions', True),
        ('api', True),
        ('urls', False)
    ]:
        if is_tuples:
            context[section]['mutual'] = [(x, y) for (x, y) in first_app[section].items() if x in
                                          second_app[section].keys()]

            # Only first
            context[section]['only_first'] = [(x, y) for (x, y) in first_app[section].items() if x not in
                                              second_app[section].keys()]

            # Only Second
            context[section]['only_second'] = [(x, y) for (x, y) in second_app[section].items() if x not in
                                               first_app[section].keys()]
        else:
            context[section]['mutual'] = [x for x in first_app[section] if x in
                                          second_app[section]]

            context[section]['only_first'] = [x for x in first_app[section] if x not in
                                              second_app[section]]

            context[section]['only_second'] = [x for x in second_app[section] if x not in
                                               first_app[section]]

    template = "static_analysis/compare.html"
    if api:
        return context
    else:
        return render(request, template, context)
def report(request):
    """Dynamic Analysis Report Generation"""
    logger.info("Dynamic Analysis Report Generation")
    try:
        if request.method == 'GET':
            md5_hash = request.GET['md5']
            package = request.GET['pkg']
            if re.findall(r";|\$\(|\|\||&&", package):
                return print_n_send_error_response(request, "Possible RCE Attack")
            if re.match('^[0-9a-f]{32}$', md5_hash):
                app_dir = os.path.join(
                    settings.UPLD_DIR, md5_hash + '/')  # APP DIRECTORY
                download_dir = settings.DWD_DIR
                droidmon_api_loc = os.path.join(app_dir, 'x_logcat.txt')
                api_analysis_result = api_analysis(package, droidmon_api_loc)
                analysis_result = run_analysis(app_dir, md5_hash, package)
                download(md5_hash, download_dir, app_dir, package)
                # Only After Download Process is Done
                imgs = []
                act_imgs = []
                act = {}
                expact_imgs = []
                exp_act = {}
                if os.path.exists(os.path.join(download_dir, md5_hash + "-screenshots-apk/")):
                    try:
                        imp_path = os.path.join(
                            download_dir, md5_hash + "-screenshots-apk/")
                        for img in os.listdir(imp_path):
                            if img.endswith(".png"):
                                if img.startswith("act"):
                                    act_imgs.append(img)
                                elif img.startswith("expact"):
                                    expact_imgs.append(img)
                                else:
                                    imgs.append(img)
                        static_android_db = StaticAnalyzerAndroid.objects.filter(
                            MD5=md5_hash)
                        if static_android_db.exists():
                            logger.info(
                                "\nFetching Exported Activity & Activity List from DB")
                            exported_act = python_list(
                                static_android_db[0].EXPORTED_ACT)
                            act_desc = python_list(
                                static_android_db[0].ACTIVITIES)
                            if act_imgs:
                                if len(act_imgs) == len(act_desc):
                                    act = dict(list(zip(act_imgs, act_desc)))
                            if expact_imgs:
                                if len(expact_imgs) == len(exported_act):
                                    exp_act = dict(
                                        list(zip(expact_imgs, exported_act)))
                        else:
                            logger.warning("Entry does not exists in the DB.")
                    except:
                        PrintException("Screenshot Sorting")
                context = {'md5': md5_hash,
                           'emails': analysis_result["emails"],
                           'urls': analysis_result["urls"],
                           'domains': analysis_result["domains"],
                           'clipboard': analysis_result["clipboard"],
                           'http': analysis_result["web_data"],
                           'xml': analysis_result["xmlfiles"],
                           'sqlite': analysis_result["sqlite_db"],
                           'others': analysis_result["other_files"],
                           'imgs': imgs,
                           'acttest': act,
                           'expacttest': exp_act,
                           'net': api_analysis_result["api_net"],
                           'base64': api_analysis_result["api_base64"],
                           'crypto': api_analysis_result["api_crypto"],
                           'fileio': api_analysis_result["api_fileio"],
                           'binder': api_analysis_result["api_binder"],
                           'divinfo': api_analysis_result["api_deviceinfo"],
                           'cntval': api_analysis_result["api_cntvl"],
                           'sms': api_analysis_result["api_sms"],
                           'sysprop': api_analysis_result["api_sysprop"],
                           'dexload': api_analysis_result["api_dexloader"],
                           'reflect': api_analysis_result["api_reflect"],
                           'sysman': api_analysis_result["api_acntmnger"],
                           'process': api_analysis_result["api_cmd"],
                           'pkg': package,
                           'title': 'Dynamic Analysis'}
                template = "dynamic_analysis/dynamic_analysis.html"
                return render(request, template, context)
            else:
                return print_n_send_error_response(request, "Invalid Scan Hash")
        else:
            return print_n_send_error_response(request, "Only GET allowed")
    except:
        PrintException("Dynamic Analysis Report Generation")
        return print_n_send_error_response(request, "Error Geneating Dynamic Analysis Report")
def static_analyzer(request, api=False):
    """Do static analysis on an request and save to db."""
    try:
        if api:
            typ = request.POST['scan_type']
            checksum = request.POST['hash']
            filename = request.POST['file_name']
            rescan = str(request.POST.get('re_scan', 0))
        else:
            typ = request.GET['type']
            checksum = request.GET['checksum']
            filename = request.GET['name']
            rescan = str(request.GET.get('rescan', 0))
        # Input validation
        app_dic = {}
        match = re.match('^[0-9a-f]{32}$', checksum)
        if ((match) and (filename.lower().endswith('.apk')
                         or filename.lower().endswith('.zip'))
                and (typ in ['zip', 'apk'])):
            app_dic['dir'] = settings.BASE_DIR  # BASE DIR
            app_dic['app_name'] = filename  # APP ORGINAL NAME
            app_dic['md5'] = checksum  # MD5
            app_dic['app_dir'] = os.path.join(
                settings.UPLD_DIR, app_dic['md5'] + '/')  # APP DIRECTORY
            app_dic['tools_dir'] = os.path.join(
                app_dic['dir'], 'StaticAnalyzer/tools/')  # TOOLS DIR
            # DWD_DIR = settings.DWD_DIR # not needed? Var is never used.
            print "[INFO] Starting Analysis on : " + app_dic['app_name']

            if typ == 'apk':
                # Check if in DB
                # pylint: disable=E1101
                db_entry = StaticAnalyzerAndroid.objects.filter(
                    MD5=app_dic['md5'])
                if db_entry.exists() and rescan == '0':
                    context = get_context_from_db_entry(db_entry)
                else:
                    app_dic[
                        'app_file'] = app_dic['md5'] + '.apk'  # NEW FILENAME
                    app_dic['app_path'] = app_dic['app_dir'] + \
                        app_dic['app_file']  # APP PATH

                    # ANALYSIS BEGINS
                    app_dic['size'] = str(file_size(
                        app_dic['app_path'])) + 'MB'  # FILE SIZE
                    app_dic['sha1'], app_dic['sha256'] = hash_gen(
                        app_dic['app_path'])

                    app_dic['files'] = unzip(app_dic['app_path'],
                                             app_dic['app_dir'])
                    app_dic['certz'] = get_hardcoded_cert_keystore(
                        app_dic['files'])

                    print "[INFO] APK Extracted"

                    # Manifest XML
                    app_dic['parsed_xml'] = get_manifest(
                        app_dic['app_dir'], app_dic['tools_dir'], '', True)

                    # Get icon
                    res_path = os.path.join(app_dic['app_dir'], 'res')
                    app_dic['icon_hidden'] = True
                    app_dic[
                        'icon_found'] = False  # Even if the icon is hidden, try to guess it by the default paths
                    app_dic['icon_path'] = ''
                    if os.path.exists(
                            res_path
                    ):  # TODO: Check for possible different names for resource folder?
                        icon_dic = get_icon(app_dic['app_path'], res_path,
                                            app_dic['tools_dir'])
                        if icon_dic:
                            app_dic['icon_hidden'] = icon_dic['hidden']
                            app_dic['icon_found'] = bool(icon_dic['path'])
                            app_dic['icon_path'] = icon_dic['path']

                    # Set Manifest link
                    app_dic['mani'] = '../ManifestView/?md5=' + \
                        app_dic['md5'] + '&type=apk&bin=1'
                    man_data_dic = manifest_data(app_dic['parsed_xml'])

                    man_an_dic = manifest_analysis(app_dic['parsed_xml'],
                                                   man_data_dic)
                    bin_an_buff = []
                    bin_an_buff += elf_analysis(app_dic['app_dir'], "apk")
                    bin_an_buff += res_analysis(app_dic['app_dir'], "apk")
                    cert_dic = cert_info(app_dic['app_dir'],
                                         app_dic['tools_dir'])
                    apkid_results = apkid_analysis(app_dic['app_dir'])
                    dex_2_jar(app_dic['app_path'], app_dic['app_dir'],
                              app_dic['tools_dir'])
                    dex_2_smali(app_dic['app_dir'], app_dic['tools_dir'])
                    jar_2_java(app_dic['app_dir'], app_dic['tools_dir'])
                    code_an_dic = code_analysis(app_dic['app_dir'],
                                                man_an_dic['permissons'],
                                                "apk")
                    print "\n[INFO] Generating Java and Smali Downloads"
                    gen_downloads(app_dic['app_dir'], app_dic['md5'],
                                  app_dic['icon_path'])

                    # Get the strings
                    app_dic['strings'] = strings(app_dic['app_file'],
                                                 app_dic['app_dir'],
                                                 app_dic['tools_dir'])
                    app_dic['zipped'] = '&type=apk'

                    print "\n[INFO] Connecting to Database"
                    try:
                        # SAVE TO DB
                        if rescan == '1':
                            print "\n[INFO] Updating Database..."
                            update_db_entry(
                                app_dic,
                                man_data_dic,
                                man_an_dic,
                                code_an_dic,
                                cert_dic,
                                bin_an_buff,
                                apkid_results,
                            )
                        elif rescan == '0':
                            print "\n[INFO] Saving to Database"
                            create_db_entry(
                                app_dic,
                                man_data_dic,
                                man_an_dic,
                                code_an_dic,
                                cert_dic,
                                bin_an_buff,
                                apkid_results,
                            )
                    except:
                        PrintException("[ERROR] Saving to Database Failed")
                    context = get_context_from_analysis(
                        app_dic,
                        man_data_dic,
                        man_an_dic,
                        code_an_dic,
                        cert_dic,
                        bin_an_buff,
                        apkid_results,
                    )
                context['dynamic_analysis_done'] = os.path.exists(
                    os.path.join(app_dic['app_dir'], 'logcat.txt'))
                template = "static_analysis/static_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif typ == 'zip':
                # Check if in DB
                # pylint: disable=E1101
                cert_dic = {}
                cert_dic['cert_info'] = ''
                cert_dic['issued'] = ''
                bin_an_buff = []
                app_dic['strings'] = ''
                app_dic['zipped'] = ''
                # Above fields are only available for APK and not ZIP
                db_entry = StaticAnalyzerAndroid.objects.filter(
                    MD5=app_dic['md5'])
                if db_entry.exists() and rescan == '0':
                    context = get_context_from_db_entry(db_entry)
                else:
                    app_dic[
                        'app_file'] = app_dic['md5'] + '.zip'  # NEW FILENAME
                    app_dic['app_path'] = app_dic['app_dir'] + \
                        app_dic['app_file']  # APP PATH
                    print "[INFO] Extracting ZIP"
                    app_dic['files'] = unzip(app_dic['app_path'],
                                             app_dic['app_dir'])
                    # Check if Valid Directory Structure and get ZIP Type
                    pro_type, valid = valid_android_zip(app_dic['app_dir'])
                    if valid and pro_type == 'ios':
                        print "[INFO] Redirecting to iOS Source Code Analyzer"
                        if api:
                            return {"type": "ios"}
                        else:
                            return HttpResponseRedirect(
                                '/StaticAnalyzer_iOS/?name=' +
                                app_dic['app_name'] + '&type=ios&checksum=' +
                                app_dic['md5'])
                    app_dic['certz'] = get_hardcoded_cert_keystore(
                        app_dic['files'])
                    app_dic['zipped'] = pro_type
                    print "[INFO] ZIP Type - " + pro_type
                    if valid and (pro_type in ['eclipse', 'studio']):
                        # ANALYSIS BEGINS
                        app_dic['size'] = str(file_size(
                            app_dic['app_path'])) + 'MB'  # FILE SIZE
                        app_dic['sha1'], app_dic['sha256'] = hash_gen(
                            app_dic['app_path'])

                        # Manifest XML
                        app_dic['persed_xml'] = get_manifest(
                            app_dic['app_dir'], app_dic['tools_dir'], pro_type,
                            False)

                        # Set manifest view link
                        app_dic['mani'] = ('../ManifestView/?md5=' +
                                           app_dic['md5'] + '&type=' +
                                           pro_type + '&bin=0')

                        man_data_dic = manifest_data(app_dic['persed_xml'])

                        man_an_dic = manifest_analysis(app_dic['persed_xml'],
                                                       man_data_dic)

                        # Get icon
                        eclipse_res_path = os.path.join(
                            app_dic['app_dir'], 'res')
                        studio_res_path = os.path.join(app_dic['app_dir'],
                                                       'app', 'src', 'main',
                                                       'res')
                        if os.path.exists(eclipse_res_path):
                            res_path = eclipse_res_path
                        elif os.path.exists(studio_res_path):
                            res_path = studio_res_path
                        else:
                            res_path = ''

                        app_dic['icon_hidden'] = man_an_dic['icon_hidden']
                        app_dic['icon_found'] = False
                        app_dic['icon_path'] = ''
                        if res_path:
                            app_dic['icon_path'] = find_icon_path_zip(
                                res_path, man_data_dic['icons'])
                            if app_dic['icon_path']:
                                app_dic['icon_found'] = True

                        if app_dic['icon_path']:
                            if os.path.exists(app_dic['icon_path']):
                                shutil.copy2(
                                    app_dic['icon_path'],
                                    os.path.join(settings.DWD_DIR,
                                                 app_dic['md5'] + '-icon.png'))

                        code_an_dic = code_analysis(app_dic['app_dir'],
                                                    man_an_dic['permissons'],
                                                    pro_type)
                        print "\n[INFO] Connecting to Database"
                        try:
                            # SAVE TO DB
                            if rescan == '1':
                                print "\n[INFO] Updating Database..."
                                update_db_entry(
                                    app_dic,
                                    man_data_dic,
                                    man_an_dic,
                                    code_an_dic,
                                    cert_dic,
                                    bin_an_buff,
                                    {},
                                )
                            elif rescan == '0':
                                print "\n[INFO] Saving to Database"
                                create_db_entry(
                                    app_dic,
                                    man_data_dic,
                                    man_an_dic,
                                    code_an_dic,
                                    cert_dic,
                                    bin_an_buff,
                                    {},
                                )
                        except:
                            PrintException("[ERROR] Saving to Database Failed")
                        context = get_context_from_analysis(
                            app_dic,
                            man_data_dic,
                            man_an_dic,
                            code_an_dic,
                            cert_dic,
                            bin_an_buff,
                            {},
                        )
                    else:
                        msg = "This ZIP Format is not supported"
                        if api:
                            return print_n_send_error_response(
                                request, msg, True)
                        else:
                            print_n_send_error_response(request, msg, False)
                            return HttpResponseRedirect('/zip_format/')
                template = "static_analysis/static_analysis_android_zip.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                print "\n[ERROR] Only APK,IPA and Zipped Android/iOS Source code supported now!"
        else:
            msg = "Hash match failed or Invalid file extension or file type"
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)

    except Exception as excep:
        msg = str(excep)
        exp = excep.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
def dynamic_analyzer(request):
    """Android Dynamic Analyzer Environment."""
    logger.info('Creating Dynamic Analysis Environment')
    try:
        bin_hash = request.GET['hash']
        package = request.GET['package']
        no_device = False
        if (is_attack_pattern(package)
                or not is_md5(bin_hash)):
            return print_n_send_error_response(request,
                                               'Invalid Parameters')
        try:
            identifier = get_device()
        except Exception:
            no_device = True
        if no_device or not identifier:
            msg = ('Is the android instance running? MobSF cannot'
                   ' find android instance identifier. '
                   'Please run an android instance and refresh'
                   ' this page. If this error persists,'
                   ' set ANALYZER_IDENTIFIER in MobSF/settings.py')
            return print_n_send_error_response(request, msg)
        env = Environment(identifier)
        if not env.connect_n_mount():
            msg = 'Cannot Connect to ' + identifier
            return print_n_send_error_response(request, msg)
        version = env.get_android_version()
        logger.info('Android Version identified as %s', version)
        xposed_first_run = False
        if not env.is_mobsfyied(version):
            msg = ('This Android instance is not MobSfyed.\n'
                   'MobSFying the android runtime environment')
            logger.warning(msg)
            if not env.mobsfy_init():
                return print_n_send_error_response(
                    request,
                    'Failed to MobSFy the instance')
            if version < 5:
                xposed_first_run = True
        if xposed_first_run:
            msg = ('Have you MobSFyed the instance before'
                   ' attempting Dynamic Analysis?'
                   ' Install Framework for Xposed.'
                   ' Restart the device and enable'
                   ' all Xposed modules. And finally'
                   ' restart the device once again.')
            return print_n_send_error_response(request, msg)
        # Clean up previous analysis
        env.dz_cleanup(bin_hash)
        # Configure Web Proxy
        env.configure_proxy(package)
        # Supported in Android 5+
        env.enable_adb_reverse_tcp(version)
        # Apply Global Proxy to device
        env.set_global_proxy(version)
        # Start Clipboard monitor
        env.start_clipmon()
        # Get Screen Resolution
        screen_width, screen_height = env.get_screen_res()
        logger.info('Installing APK')
        app_dir = os.path.join(settings.UPLD_DIR,
                               bin_hash + '/')  # APP DIRECTORY
        apk_path = app_dir + bin_hash + '.apk'  # APP PATH
        env.adb_command(['install', '-r', apk_path], False, True)
        logger.info('Testing Environment is Ready!')
        context = {'screen_witdth': screen_width,
                   'screen_height': screen_height,
                   'package': package,
                   'md5': bin_hash,
                   'android_version': version,
                   'version': settings.MOBSF_VER,
                   'title': 'Dynamic Analyzer'}
        template = 'dynamic_analysis/android/dynamic_analyzer.html'
        return render(request, template, context)
    except Exception:
        logger.exception('Dynamic Analyzer')
        return print_n_send_error_response(request,
                                           'Dynamic Analysis Failed.')
Example #32
0
def static_analyzer(request, api=False):
    """Do static analysis on an request and save to db."""
    try:
        if api:
            typ = request.POST['scan_type']
            checksum = request.POST['hash']
            filename = request.POST['file_name']
            rescan = str(request.POST.get('re_scan', 0))
        else:
            typ = request.GET['type']
            checksum = request.GET['checksum']
            filename = request.GET['name']
            rescan = str(request.GET.get('rescan', 0))
        # Input validation
        app_dic = {}
        match = re.match('^[0-9a-f]{32}$', checksum)
        if ((match) and (filename.lower().endswith('.apk')
                         or filename.lower().endswith('.zip'))
                and (typ in ['zip', 'apk'])):
            app_dic['dir'] = Path(settings.BASE_DIR)  # BASE DIR
            app_dic['app_name'] = filename  # APP ORGINAL NAME
            app_dic['md5'] = checksum  # MD5
            # APP DIRECTORY
            app_dic['app_dir'] = Path(settings.UPLD_DIR) / checksum
            app_dic['tools_dir'] = app_dic['dir'] / 'StaticAnalyzer' / 'tools'
            app_dic['tools_dir'] = app_dic['tools_dir'].as_posix()
            logger.info('Starting Analysis on : %s', app_dic['app_name'])

            if typ == 'apk':
                app_dic['app_file'] = app_dic['md5'] + '.apk'  # NEW FILENAME
                app_dic['app_path'] = (app_dic['app_dir'] /
                                       app_dic['app_file']).as_posix()
                app_dic['app_dir'] = app_dic['app_dir'].as_posix() + '/'
                # Check if in DB
                # pylint: disable=E1101
                db_entry = StaticAnalyzerAndroid.objects.filter(
                    MD5=app_dic['md5'])
                if db_entry.exists() and rescan == '0':
                    context = get_context_from_db_entry(db_entry)
                else:
                    # ANALYSIS BEGINS
                    app_dic['size'] = str(file_size(
                        app_dic['app_path'])) + 'MB'  # FILE SIZE
                    app_dic['sha1'], app_dic['sha256'] = hash_gen(
                        app_dic['app_path'])
                    app_dic['files'] = unzip(app_dic['app_path'],
                                             app_dic['app_dir'])
                    logger.info('APK Extracted')
                    if not app_dic['files']:
                        # Can't Analyze APK, bail out.
                        msg = 'APK file is invalid or corrupt'
                        if api:
                            return print_n_send_error_response(
                                request, msg, True)
                        else:
                            return print_n_send_error_response(
                                request, msg, False)
                    app_dic['certz'] = get_hardcoded_cert_keystore(
                        app_dic['files'])
                    # Manifest XML
                    mani_file, mani_xml = get_manifest(
                        app_dic['app_path'],
                        app_dic['app_dir'],
                        app_dic['tools_dir'],
                        '',
                        True,
                    )
                    app_dic['manifest_file'] = mani_file
                    app_dic['parsed_xml'] = mani_xml

                    # get app_name
                    app_dic['real_name'] = get_app_name(
                        app_dic['app_path'],
                        app_dic['app_dir'],
                        app_dic['tools_dir'],
                        True,
                    )

                    # Get icon
                    res_path = os.path.join(app_dic['app_dir'], 'res')
                    app_dic['icon_hidden'] = True
                    # Even if the icon is hidden, try to guess it by the
                    # default paths
                    app_dic['icon_found'] = False
                    app_dic['icon_path'] = ''
                    # TODO: Check for possible different names for resource
                    # folder?
                    if os.path.exists(res_path):
                        icon_dic = get_icon(app_dic['app_path'], res_path)
                        if icon_dic:
                            app_dic['icon_hidden'] = icon_dic['hidden']
                            app_dic['icon_found'] = bool(icon_dic['path'])
                            app_dic['icon_path'] = icon_dic['path']

                    # Set Manifest link
                    app_dic['mani'] = ('../ManifestView/?md5=' +
                                       app_dic['md5'] + '&type=apk&bin=1')
                    man_data_dic = manifest_data(app_dic['parsed_xml'])
                    app_dic['playstore'] = get_app_details(
                        man_data_dic['packagename'])
                    man_an_dic = manifest_analysis(
                        app_dic['parsed_xml'],
                        man_data_dic,
                        '',
                        app_dic['app_dir'],
                    )
                    elf_dict = elf_analysis(app_dic['app_dir'])
                    cert_dic = cert_info(app_dic['app_dir'],
                                         app_dic['app_file'])
                    apkid_results = apkid_analysis(app_dic['app_dir'],
                                                   app_dic['app_path'],
                                                   app_dic['app_name'])
                    tracker = Trackers.Trackers(app_dic['app_dir'],
                                                app_dic['tools_dir'])
                    tracker_res = tracker.get_trackers()

                    apk_2_java(app_dic['app_path'], app_dic['app_dir'],
                               app_dic['tools_dir'])

                    dex_2_smali(app_dic['app_dir'], app_dic['tools_dir'])

                    code_an_dic = code_analysis(app_dic['app_dir'], 'apk',
                                                app_dic['manifest_file'])

                    # Get the strings from android resource and shared objects
                    string_res = strings_from_apk(app_dic['app_file'],
                                                  app_dic['app_dir'],
                                                  elf_dict['elf_strings'])
                    if string_res:
                        app_dic['strings'] = string_res['strings']
                        app_dic['secrets'] = string_res['secrets']
                        code_an_dic['urls_list'].extend(
                            string_res['urls_list'])
                        code_an_dic['urls'].extend(string_res['url_nf'])
                        code_an_dic['emails'].extend(string_res['emails_nf'])
                    else:
                        app_dic['strings'] = []
                        app_dic['secrets'] = []
                    # Firebase DB Check
                    code_an_dic['firebase'] = firebase_analysis(
                        list(set(code_an_dic['urls_list'])))
                    # Domain Extraction and Malware Check
                    logger.info(
                        'Performing Malware Check on extracted Domains')
                    code_an_dic['domains'] = malware_check(
                        list(set(code_an_dic['urls_list'])))
                    # Copy App icon
                    copy_icon(app_dic['md5'], app_dic['icon_path'])
                    app_dic['zipped'] = 'apk'

                    logger.info('Connecting to Database')
                    try:
                        # SAVE TO DB
                        if rescan == '1':
                            logger.info('Updating Database...')
                            save_or_update(
                                'update',
                                app_dic,
                                man_data_dic,
                                man_an_dic,
                                code_an_dic,
                                cert_dic,
                                elf_dict['elf_analysis'],
                                apkid_results,
                                tracker_res,
                            )
                            update_scan_timestamp(app_dic['md5'])
                        elif rescan == '0':
                            logger.info('Saving to Database')
                            save_or_update(
                                'save',
                                app_dic,
                                man_data_dic,
                                man_an_dic,
                                code_an_dic,
                                cert_dic,
                                elf_dict['elf_analysis'],
                                apkid_results,
                                tracker_res,
                            )
                    except Exception:
                        logger.exception('Saving to Database Failed')
                    context = get_context_from_analysis(
                        app_dic,
                        man_data_dic,
                        man_an_dic,
                        code_an_dic,
                        cert_dic,
                        elf_dict['elf_analysis'],
                        apkid_results,
                        tracker_res,
                    )
                context['average_cvss'], context['security_score'] = score(
                    context['code_analysis'])
                context['dynamic_analysis_done'] = is_file_exists(
                    os.path.join(app_dic['app_dir'], 'logcat.txt'))

                context['virus_total'] = None
                if settings.VT_ENABLED:
                    vt = VirusTotal.VirusTotal()
                    context['virus_total'] = vt.get_result(
                        app_dic['app_path'], app_dic['md5'])
                template = 'static_analysis/android_binary_analysis.html'
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif typ == 'zip':
                ios_ret = HttpResponseRedirect('/StaticAnalyzer_iOS/?name=' +
                                               app_dic['app_name'] +
                                               '&type=ios&checksum=' +
                                               app_dic['md5'])
                # Check if in DB
                # pylint: disable=E1101
                cert_dic = {
                    'certificate_info': '',
                    'certificate_status': '',
                    'description': '',
                }
                app_dic['strings'] = []
                app_dic['secrets'] = []
                app_dic['zipped'] = ''
                # Above fields are only available for APK and not ZIP
                app_dic['app_file'] = app_dic['md5'] + '.zip'  # NEW FILENAME
                app_dic['app_path'] = (app_dic['app_dir'] /
                                       app_dic['app_file']).as_posix()
                app_dic['app_dir'] = app_dic['app_dir'].as_posix() + '/'
                db_entry = StaticAnalyzerAndroid.objects.filter(
                    MD5=app_dic['md5'])
                ios_db_entry = StaticAnalyzerIOS.objects.filter(
                    MD5=app_dic['md5'])
                if db_entry.exists() and rescan == '0':
                    context = get_context_from_db_entry(db_entry)
                elif ios_db_entry.exists() and rescan == '0':
                    if api:
                        return {'type': 'ios'}
                    else:
                        return ios_ret
                else:
                    logger.info('Extracting ZIP')
                    app_dic['files'] = unzip(app_dic['app_path'],
                                             app_dic['app_dir'])
                    # Check if Valid Directory Structure and get ZIP Type
                    pro_type, valid = valid_android_zip(app_dic['app_dir'])
                    if valid and pro_type == 'ios':
                        logger.info('Redirecting to iOS Source Code Analyzer')
                        if api:
                            return {'type': 'ios'}
                        else:
                            return ios_ret
                    app_dic['certz'] = get_hardcoded_cert_keystore(
                        app_dic['files'])
                    app_dic['zipped'] = pro_type
                    logger.info('ZIP Type - %s', pro_type)
                    if valid and (pro_type in ['eclipse', 'studio']):
                        # ANALYSIS BEGINS
                        app_dic['size'] = str(file_size(
                            app_dic['app_path'])) + 'MB'  # FILE SIZE
                        app_dic['sha1'], app_dic['sha256'] = hash_gen(
                            app_dic['app_path'])

                        # Manifest XML
                        mani_file, mani_xml = get_manifest(
                            '',
                            app_dic['app_dir'],
                            app_dic['tools_dir'],
                            pro_type,
                            False,
                        )
                        app_dic['manifest_file'] = mani_file
                        app_dic['parsed_xml'] = mani_xml

                        # get app_name
                        app_dic['real_name'] = get_app_name(
                            app_dic['app_path'],
                            app_dic['app_dir'],
                            app_dic['tools_dir'],
                            False,
                        )

                        # Set manifest view link
                        app_dic['mani'] = ('../ManifestView/?md5=' +
                                           app_dic['md5'] + '&type=' +
                                           pro_type + '&bin=0')

                        man_data_dic = manifest_data(app_dic['parsed_xml'])
                        app_dic['playstore'] = get_app_details(
                            man_data_dic['packagename'])
                        man_an_dic = manifest_analysis(
                            app_dic['parsed_xml'],
                            man_data_dic,
                            pro_type,
                            app_dic['app_dir'],
                        )
                        # Get icon
                        eclipse_res_path = os.path.join(
                            app_dic['app_dir'], 'res')
                        studio_res_path = os.path.join(app_dic['app_dir'],
                                                       'app', 'src', 'main',
                                                       'res')
                        if os.path.exists(eclipse_res_path):
                            res_path = eclipse_res_path
                        elif os.path.exists(studio_res_path):
                            res_path = studio_res_path
                        else:
                            res_path = ''

                        app_dic['icon_hidden'] = man_an_dic['icon_hidden']
                        app_dic['icon_found'] = False
                        app_dic['icon_path'] = ''
                        if res_path:
                            app_dic['icon_path'] = find_icon_path_zip(
                                res_path, man_data_dic['icons'])
                            if app_dic['icon_path']:
                                app_dic['icon_found'] = True

                        if app_dic['icon_path']:
                            if os.path.exists(app_dic['icon_path']):
                                shutil.copy2(
                                    app_dic['icon_path'],
                                    os.path.join(settings.DWD_DIR,
                                                 app_dic['md5'] + '-icon.png'))

                        code_an_dic = code_analysis(app_dic['app_dir'],
                                                    pro_type,
                                                    app_dic['manifest_file'])
                        # Firebase DB Check
                        code_an_dic['firebase'] = firebase_analysis(
                            list(set(code_an_dic['urls_list'])))
                        # Domain Extraction and Malware Check
                        logger.info(
                            'Performing Malware Check on extracted Domains')
                        code_an_dic['domains'] = malware_check(
                            list(set(code_an_dic['urls_list'])))
                        logger.info('Connecting to Database')
                        try:
                            # SAVE TO DB
                            if rescan == '1':
                                logger.info('Updating Database...')
                                save_or_update(
                                    'update',
                                    app_dic,
                                    man_data_dic,
                                    man_an_dic,
                                    code_an_dic,
                                    cert_dic,
                                    [],
                                    {},
                                    {},
                                )
                                update_scan_timestamp(app_dic['md5'])
                            elif rescan == '0':
                                logger.info('Saving to Database')
                                save_or_update(
                                    'save',
                                    app_dic,
                                    man_data_dic,
                                    man_an_dic,
                                    code_an_dic,
                                    cert_dic,
                                    [],
                                    {},
                                    {},
                                )
                        except Exception:
                            logger.exception('Saving to Database Failed')
                        context = get_context_from_analysis(
                            app_dic,
                            man_data_dic,
                            man_an_dic,
                            code_an_dic,
                            cert_dic,
                            [],
                            {},
                            {},
                        )
                    else:
                        msg = 'This ZIP Format is not supported'
                        if api:
                            return print_n_send_error_response(
                                request, msg, True)
                        else:
                            print_n_send_error_response(request, msg, False)
                            ctx = {
                                'title': 'Invalid ZIP archive',
                                'version': settings.MOBSF_VER,
                            }
                            template = 'general/zip.html'
                            return render(request, template, ctx)
                context['average_cvss'], context['security_score'] = score(
                    context['code_analysis'])
                template = 'static_analysis/android_source_analysis.html'
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                err = ('Only APK,IPA and Zipped '
                       'Android/iOS Source code supported now!')
                logger.error(err)
        else:
            msg = 'Hash match failed or Invalid file extension or file type'
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)

    except Exception as excep:
        logger.exception('Error Performing Static Analysis')
        msg = str(excep)
        exp = excep.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
def static_analyzer(request, api=False):
    """Do static analysis on an request and save to db."""
    try:
        if api:
            typ = request.POST['scan_type']
            checksum = request.POST['hash']
            filename = request.POST['file_name']
            rescan = str(request.POST.get('re_scan', 0))
        else:
            typ = request.GET['type']
            checksum = request.GET['checksum']
            filename = request.GET['name']
            rescan = str(request.GET.get('rescan', 0))
        # Input validation
        app_dic = {}
        match = re.match('^[0-9a-f]{32}$', checksum)
        if (
                (
                    match
                ) and (
                    filename.lower().endswith('.apk') or
                    filename.lower().endswith('.zip')
                ) and (
                    typ in ['zip', 'apk']
                )
        ):
            app_dic['dir'] = settings.BASE_DIR  # BASE DIR
            app_dic['app_name'] = filename  # APP ORGINAL NAME
            app_dic['md5'] = checksum  # MD5
            app_dic['app_dir'] = os.path.join(settings.UPLD_DIR, app_dic[
                                              'md5'] + '/')  # APP DIRECTORY
            app_dic['tools_dir'] = os.path.join(
                app_dic['dir'], 'StaticAnalyzer/tools/')  # TOOLS DIR
            # DWD_DIR = settings.DWD_DIR # not needed? Var is never used.
            logger.info("Starting Analysis on : " + app_dic['app_name'])

            if typ == 'apk':
                # Check if in DB
                # pylint: disable=E1101
                db_entry = StaticAnalyzerAndroid.objects.filter(
                    MD5=app_dic['md5'])
                if db_entry.exists() and rescan == '0':
                    context = get_context_from_db_entry(db_entry)
                else:
                    app_dic['app_file'] = app_dic[
                        'md5'] + '.apk'  # NEW FILENAME
                    app_dic['app_path'] = app_dic['app_dir'] + \
                        app_dic['app_file']  # APP PATH

                    # ANALYSIS BEGINS
                    app_dic['size'] = str(
                        file_size(app_dic['app_path'])) + 'MB'  # FILE SIZE
                    app_dic['sha1'], app_dic[
                        'sha256'] = hash_gen(app_dic['app_path'])

                    app_dic['files'] = unzip(
                        app_dic['app_path'], app_dic['app_dir'])
                    app_dic['certz'] = get_hardcoded_cert_keystore(app_dic[
                                                                   'files'])

                    logger.info("APK Extracted")

                    # Manifest XML
                    app_dic['parsed_xml'] = get_manifest(
                        app_dic['app_path'],
                        app_dic['app_dir'],
                        app_dic['tools_dir'],
                        '',
                        True
                    )

                    # Get icon
                    res_path = os.path.join(app_dic['app_dir'], 'res')
                    app_dic['icon_hidden'] = True
                    # Even if the icon is hidden, try to guess it by the
                    # default paths
                    app_dic['icon_found'] = False
                    app_dic['icon_path'] = ''
                    # TODO: Check for possible different names for resource
                    # folder?
                    if os.path.exists(res_path):
                        icon_dic = get_icon(
                            app_dic['app_path'], res_path)
                        if icon_dic:
                            app_dic['icon_hidden'] = icon_dic['hidden']
                            app_dic['icon_found'] = bool(icon_dic['path'])
                            app_dic['icon_path'] = icon_dic['path']

                    # Set Manifest link
                    app_dic['mani'] = '../ManifestView/?md5=' + \
                        app_dic['md5'] + '&type=apk&bin=1'
                    man_data_dic = manifest_data(app_dic['parsed_xml'])
                    app_dic['playstore'] = get_app_details(
                        man_data_dic['packagename'])
                    man_an_dic = manifest_analysis(
                        app_dic['parsed_xml'],
                        man_data_dic
                    )
                    bin_an_buff = []
                    bin_an_buff += elf_analysis(app_dic['app_dir'])
                    bin_an_buff += res_analysis(app_dic['app_dir'])
                    cert_dic = cert_info(
                        app_dic['app_dir'], app_dic['app_file'], app_dic['tools_dir'])
                    apkid_results = apkid_analysis(app_dic[
                        'app_dir'], app_dic['app_path'], app_dic['app_name'])
                    dex_2_jar(app_dic['app_path'], app_dic[
                              'app_dir'], app_dic['tools_dir'])
                    dex_2_smali(app_dic['app_dir'], app_dic['tools_dir'])
                    jar_2_java(app_dic['app_dir'], app_dic['tools_dir'])
                    code_an_dic = code_analysis(
                        app_dic['app_dir'],
                        man_an_dic['permissons'],
                        "apk"
                    )

                    # Get the strings
                    string_res = strings_jar(
                        app_dic['app_file'],
                        app_dic['app_dir']
                    )
                    if string_res:
                        app_dic['strings'] = string_res['strings']
                        code_an_dic["urls_list"].extend(
                            string_res['urls_list'])
                        code_an_dic["urls"].extend(string_res['url_nf'])
                        code_an_dic["emails"].extend(string_res['emails_nf'])
                    else:
                        app_dic['strings'] = []

                    # Firebase DB Check
                    code_an_dic['firebase'] = firebase_analysis(
                           list(set(code_an_dic["urls_list"])))
                    # Domain Extraction and Malware Check
                    logger.info(
                        "Performing Malware Check on extracted Domains")
                    code_an_dic["domains"] = malware_check(
                        list(set(code_an_dic["urls_list"])))

                    logger.info("Generating Java and Smali Downloads")
                    gen_downloads(app_dic['app_dir'], app_dic[
                                  'md5'], app_dic['icon_path'])
                    app_dic['zipped'] = '&type=apk'

                    logger.info("Connecting to Database")
                    try:
                        # SAVE TO DB
                        if rescan == '1':
                            logger.info("Updating Database...")
                            update_db_entry(
                                app_dic,
                                man_data_dic,
                                man_an_dic,
                                code_an_dic,
                                cert_dic,
                                bin_an_buff,
                                apkid_results,
                            )
                            update_scan_timestamp(app_dic['md5'])
                        elif rescan == '0':
                            logger.info("Saving to Database")
                            create_db_entry(
                                app_dic,
                                man_data_dic,
                                man_an_dic,
                                code_an_dic,
                                cert_dic,
                                bin_an_buff,
                                apkid_results,
                            )
                    except:
                        PrintException("Saving to Database Failed")
                    context = get_context_from_analysis(
                        app_dic,
                        man_data_dic,
                        man_an_dic,
                        code_an_dic,
                        cert_dic,
                        bin_an_buff,
                        apkid_results,
                    )
                context["average_cvss"], context[
                    "security_score"] = score(context["findings"])
                context['dynamic_analysis_done'] = os.path.exists(
                    os.path.join(app_dic['app_dir'], 'logcat.txt'))

                context['VT_RESULT'] = None
                if settings.VT_ENABLED:
                    vt = VirusTotal.VirusTotal()
                    context['VT_RESULT'] = vt.get_result(
                        os.path.join(app_dic['app_dir'],
                                     app_dic['md5']) + '.apk',
                        app_dic['md5']
                    )
                template = "static_analysis/android_binary_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif typ == 'zip':
                # Check if in DB
                # pylint: disable=E1101
                cert_dic = {}
                cert_dic['cert_info'] = ''
                cert_dic['issued'] = ''
                cert_dic['sha256Digest'] = False
                bin_an_buff = []
                app_dic['strings'] = ''
                app_dic['zipped'] = ''
                # Above fields are only available for APK and not ZIP
                db_entry = StaticAnalyzerAndroid.objects.filter(
                    MD5=app_dic['md5'])
                if db_entry.exists() and rescan == '0':
                    context = get_context_from_db_entry(db_entry)
                else:
                    app_dic['app_file'] = app_dic[
                        'md5'] + '.zip'  # NEW FILENAME
                    app_dic['app_path'] = app_dic['app_dir'] + \
                        app_dic['app_file']  # APP PATH
                    logger.info("Extracting ZIP")
                    app_dic['files'] = unzip(
                        app_dic['app_path'], app_dic['app_dir'])
                    # Check if Valid Directory Structure and get ZIP Type
                    pro_type, valid = valid_android_zip(app_dic['app_dir'])
                    if valid and pro_type == 'ios':
                        logger.info("Redirecting to iOS Source Code Analyzer")
                        if api:
                            return {"type": "ios"}
                        else:
                            return HttpResponseRedirect(
                                '/StaticAnalyzer_iOS/?name=' + app_dic['app_name'] +
                                '&type=ios&checksum=' + app_dic['md5']
                            )
                    app_dic['certz'] = get_hardcoded_cert_keystore(app_dic[
                                                                   'files'])
                    app_dic['zipped'] = pro_type
                    logger.info("ZIP Type - " + pro_type)
                    if valid and (pro_type in ['eclipse', 'studio']):
                        # ANALYSIS BEGINS
                        app_dic['size'] = str(
                            file_size(app_dic['app_path'])) + 'MB'  # FILE SIZE
                        app_dic['sha1'], app_dic[
                            'sha256'] = hash_gen(app_dic['app_path'])

                        # Manifest XML
                        app_dic['persed_xml'] = get_manifest(
                            "",
                            app_dic['app_dir'],
                            app_dic['tools_dir'],
                            pro_type,
                            False
                        )

                        # Set manifest view link
                        app_dic['mani'] = (
                            '../ManifestView/?md5=' +
                            app_dic['md5'] + '&type=' + pro_type + '&bin=0'
                        )

                        man_data_dic = manifest_data(app_dic['persed_xml'])
                        app_dic['playstore'] = get_app_details(
                            man_data_dic['packagename'])
                        man_an_dic = manifest_analysis(
                            app_dic['persed_xml'],
                            man_data_dic
                        )
                        # Get icon
                        eclipse_res_path = os.path.join(
                            app_dic['app_dir'], 'res')
                        studio_res_path = os.path.join(
                            app_dic['app_dir'], 'app', 'src', 'main', 'res')
                        if os.path.exists(eclipse_res_path):
                            res_path = eclipse_res_path
                        elif os.path.exists(studio_res_path):
                            res_path = studio_res_path
                        else:
                            res_path = ''

                        app_dic['icon_hidden'] = man_an_dic['icon_hidden']
                        app_dic['icon_found'] = False
                        app_dic['icon_path'] = ''
                        if res_path:
                            app_dic['icon_path'] = find_icon_path_zip(
                                res_path, man_data_dic['icons'])
                            if app_dic['icon_path']:
                                app_dic['icon_found'] = True

                        if app_dic['icon_path']:
                            if os.path.exists(app_dic['icon_path']):
                                shutil.copy2(app_dic['icon_path'], os.path.join(
                                    settings.DWD_DIR, app_dic['md5'] + '-icon.png'))

                        code_an_dic = code_analysis(
                            app_dic['app_dir'],
                            man_an_dic['permissons'],
                            pro_type
                        )
                        # Firebase DB Check
                        code_an_dic['firebase'] = firebase_analysis(
                            list(set(code_an_dic["urls_list"])))
                        # Domain Extraction and Malware Check
                        logger.info(
                            "Performing Malware Check on extracted Domains")
                        code_an_dic["domains"] = malware_check(
                            list(set(code_an_dic["urls_list"])))
                        logger.info("Connecting to Database")
                        try:
                            # SAVE TO DB
                            if rescan == '1':
                                logger.info("Updating Database...")
                                update_db_entry(
                                    app_dic,
                                    man_data_dic,
                                    man_an_dic,
                                    code_an_dic,
                                    cert_dic,
                                    bin_an_buff,
                                    {},
                                )
                                update_scan_timestamp(app_dic['md5'])
                            elif rescan == '0':
                                logger.info("Saving to Database")
                                create_db_entry(
                                    app_dic,
                                    man_data_dic,
                                    man_an_dic,
                                    code_an_dic,
                                    cert_dic,
                                    bin_an_buff,
                                    {},
                                )
                        except:
                            PrintException("Saving to Database Failed")
                        context = get_context_from_analysis(
                            app_dic,
                            man_data_dic,
                            man_an_dic,
                            code_an_dic,
                            cert_dic,
                            bin_an_buff,
                            {},
                        )
                    else:
                        msg = "This ZIP Format is not supported"
                        if api:
                            return print_n_send_error_response(request, msg, True)
                        else:
                            print_n_send_error_response(request, msg, False)
                            return HttpResponseRedirect('/zip_format/')
                context["average_cvss"], context[
                    "security_score"] = score(context["findings"])
                template = "static_analysis/android_source_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                logger.error(
                    "Only APK,IPA and Zipped Android/iOS Source code supported now!")
        else:
            msg = "Hash match failed or Invalid file extension or file type"
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)

    except Exception as excep:
        msg = str(excep)
        exp = excep.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
Example #34
0
def static_analyzer_ios(request, api=False):
    """Module that performs iOS IPA/ZIP Static Analysis."""
    try:
        logger.info('iOS Static Analysis Started')
        if api:
            file_type = request.POST['scan_type']
            checksum = request.POST['hash']
            rescan = str(request.POST.get('re_scan', 0))
            filename = request.POST['file_name']
        else:
            file_type = request.GET['type']
            checksum = request.GET['checksum']
            rescan = str(request.GET.get('rescan', 0))
            filename = request.GET['name']

        md5_match = re.match('^[0-9a-f]{32}$', checksum)
        if ((md5_match)
                and (filename.lower().endswith('.ipa')
            or filename.lower().endswith('.zip'))
                and (file_type in ['ipa', 'ios'])):
            app_dict = {}
            app_dict['directory'] = settings.BASE_DIR  # BASE DIR
            app_dict['file_name'] = filename  # APP ORGINAL NAME
            app_dict['md5_hash'] = checksum  # MD5
            app_dict['app_dir'] = os.path.join(
                settings.UPLD_DIR, app_dict['md5_hash'] + '/')  # APP DIRECTORY
            tools_dir = os.path.join(
                app_dict['directory'], 'StaticAnalyzer/tools/mac/')
            if file_type == 'ipa':
                # DB
                ipa_db = StaticAnalyzerIPA.objects.filter(
                    MD5=app_dict['md5_hash'])
                if ipa_db.exists() and rescan == '0':
                    context = get_context_from_db_entry_ipa(ipa_db)
                else:
                    logger.info('iOS Binary (IPA) Analysis Started')
                    app_dict['app_file'] = app_dict[
                        'md5_hash'] + '.ipa'  # NEW FILENAME
                    app_dict['app_path'] = (app_dict['app_dir']
                                            + app_dict['app_file'])
                    app_dict['bin_dir'] = os.path.join(
                        app_dict['app_dir'], 'Payload/')
                    app_dict['size'] = str(
                        file_size(app_dict['app_path'])) + 'MB'  # FILE SIZE
                    app_dict['sha1'], app_dict['sha256'] = hash_gen(
                        app_dict['app_path'])  # SHA1 & SHA256 HASHES
                    logger.info('Extracting IPA')
                    # EXTRACT IPA
                    unzip(app_dict['app_path'], app_dict['app_dir'])
                    # Get Files, normalize + to x,
                    # and convert binary plist -> xml
                    files, sfiles = ios_list_files(
                        app_dict['bin_dir'], app_dict['md5_hash'], True, 'ipa')
                    infoplist_dict = plist_analysis(app_dict['bin_dir'], False)
                    app_dict['appstore'] = app_search(infoplist_dict.get('id'))
                    bin_analysis_dict = binary_analysis(
                        app_dict['bin_dir'],
                        tools_dir,
                        app_dict['app_dir'],
                        infoplist_dict.get('bin'))
                    # Saving to DB
                    logger.info('Connecting to DB')
                    if rescan == '1':
                        logger.info('Updating Database...')
                        update_db_entry_ipa(
                            app_dict,
                            infoplist_dict,
                            bin_analysis_dict,
                            files,
                            sfiles)
                        update_scan_timestamp(app_dict['md5_hash'])
                    elif rescan == '0':
                        logger.info('Saving to Database')
                        create_db_entry_ipa(
                            app_dict,
                            infoplist_dict,
                            bin_analysis_dict,
                            files,
                            sfiles)
                    context = get_context_from_analysis_ipa(
                        app_dict,
                        infoplist_dict,
                        bin_analysis_dict,
                        files,
                        sfiles)

                context['VT_RESULT'] = None
                if settings.VT_ENABLED:
                    vt = VirusTotal.VirusTotal()
                    context['VT_RESULT'] = vt.get_result(
                        os.path.join(app_dict['app_dir'], app_dict[
                                     'md5_hash']) + '.ipa',
                        app_dict['md5_hash'])
                context['average_cvss'], context[
                    'security_score'] = score(context['bin_anal'])
                template = 'static_analysis/ios_binary_analysis.html'
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif file_type == 'ios':
                ios_zip_db = StaticAnalyzerIOSZIP.objects.filter(
                    MD5=app_dict['md5_hash'])
                if ios_zip_db.exists() and rescan == '0':
                    context = get_context_from_db_entry_ios(ios_zip_db)
                else:
                    logger.info('iOS Source Code Analysis Started')
                    app_dict['app_file'] = app_dict[
                        'md5_hash'] + '.zip'  # NEW FILENAME
                    app_dict['app_path'] = (app_dict['app_dir']
                                            + app_dict['app_file'])
                    # ANALYSIS BEGINS - Already Unzipped
                    logger.info('ZIP Already Extracted')
                    app_dict['size'] = str(
                        file_size(app_dict['app_path'])) + 'MB'  # FILE SIZE
                    app_dict['sha1'], app_dict['sha256'] = hash_gen(
                        app_dict['app_path'])  # SHA1 & SHA256 HASHES
                    files, sfiles = ios_list_files(
                        app_dict['app_dir'],
                        app_dict['md5_hash'],
                        False,
                        'ios')
                    infoplist_dict = plist_analysis(app_dict['app_dir'], True)
                    app_dict['appstore'] = app_search(infoplist_dict.get('id'))
                    code_analysis_dic = ios_source_analysis(
                        app_dict['app_dir'])
                    # Firebase DB Check
                    code_analysis_dic['firebase'] = firebase_analysis(
                        list(set(code_analysis_dic['urls_list'])))
                    # Saving to DB
                    logger.info('Connecting to DB')
                    if rescan == '1':
                        logger.info('Updating Database...')
                        update_db_entry_ios(
                            app_dict,
                            infoplist_dict,
                            code_analysis_dic,
                            files,
                            sfiles)
                        update_scan_timestamp(app_dict['md5_hash'])
                    elif rescan == '0':
                        logger.info('Saving to Database')
                        create_db_entry_ios(
                            app_dict,
                            infoplist_dict,
                            code_analysis_dic,
                            files,
                            sfiles)
                    context = get_context_from_analysis_ios(
                        app_dict,
                        infoplist_dict,
                        code_analysis_dic,
                        files,
                        sfiles)
                context['average_cvss'], context[
                    'security_score'] = score(context['insecure'])
                template = 'static_analysis/ios_source_analysis.html'
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                msg = 'File Type not supported!'
                if api:
                    return print_n_send_error_response(request, msg, True)
                else:
                    return print_n_send_error_response(request, msg, False)
        else:
            msg = 'Hash match failed or Invalid file extension or file type'
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)
    except Exception as exp:
        logger.exception('Error Perfroming Static Analysis')
        msg = str(exp)
        exp_doc = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
def pdf(request, api=False):
    try:
        if api:
            checksum = request.POST['hash']
            scan_type = request.POST['scan_type']
        else:
            checksum = request.GET['md5']
            scan_type = request.GET['type']
        hash_match = re.match('^[0-9a-f]{32}$', checksum)
        if hash_match:
            if scan_type.lower() in ['apk', 'andzip']:
                static_db = StaticAnalyzerAndroid.objects.filter(MD5=checksum)
                if static_db.exists():
                    print "\n[INFO] Fetching data from DB for PDF Report Generation (Android)"
                    context = get_context_from_db_entry(static_db)
                    if scan_type.lower() == 'apk':
                        template = get_template("pdf/static_analysis_pdf.html")
                    else:
                        template = get_template(
                            "pdf/static_analysis_zip_pdf.html")
                else:
                    if api:
                        return {"report": "Report not Found"}
                    else:
                        return HttpResponse(json.dumps({"report": "Report not Found"}),
                                            content_type="application/json; charset=utf-8", status_code=500)
            elif re.findall('ipa|ioszip', scan_type.lower()):
                if scan_type.lower() == 'ipa':
                    static_db = StaticAnalyzerIPA.objects.filter(MD5=checksum)
                    if static_db.exists():
                        print "\n[INFO] Fetching data from DB for PDF Report Generation (IOS IPA)"
                        context = get_context_from_db_entry_ipa(static_db)
                        template = get_template(
                            "pdf/ios_binary_analysis_pdf.html")
                    else:
                        if api:
                            return {"report": "Report not Found"}
                        else:
                            return HttpResponse(json.dumps({"report": "Report not Found"}),
                                                content_type="application/json; charset=utf-8", status_code=500)
                elif scan_type.lower() == 'ioszip':
                    static_db = StaticAnalyzerIOSZIP.objects.filter(MD5=checksum)
                    if static_db.exists():
                        print "\n[INFO] Fetching data from DB for PDF Report Generation (IOS ZIP)"
                        context = get_context_from_db_entry_ios(static_db)
                        template = get_template(
                            "pdf/ios_source_analysis_pdf.html")
                    else:
                        if api:
                            return {"report": "Report not Found"}
                        else:
                            return HttpResponse(json.dumps({"report": "Report not Found"}),
                                                content_type="application/json; charset=utf-8", status_code=500)
            elif re.findall('appx', scan_type.lower()):
                if scan_type.lower() == 'appx':
                    db_entry = StaticAnalyzerWindows.objects.filter(# pylint: disable-msg=E1101
                        MD5=checksum
                    )
                    if db_entry.exists():
                        print "\n[INFO] Fetching data from DB for PDF Report Generation (APPX)"

                        context = {
                            'title': db_entry[0].TITLE,
                            'name': db_entry[0].APP_NAME,
                            'pub_name': db_entry[0].PUB_NAME,
                            'size': db_entry[0].SIZE,
                            'md5': db_entry[0].MD5,
                            'sha1': db_entry[0].SHA1,
                            'sha256': db_entry[0].SHA256,
                            'bin_name': db_entry[0].BINNAME,
                            'version':  db_entry[0].VERSION,
                            'arch':  db_entry[0].ARCH,
                            'compiler_version':  db_entry[0].COMPILER_VERSION,
                            'visual_studio_version':  db_entry[0].VISUAL_STUDIO_VERSION,
                            'visual_studio_edition':  db_entry[0].VISUAL_STUDIO_EDITION,
                            'target_os':  db_entry[0].TARGET_OS,
                            'appx_dll_version':  db_entry[0].APPX_DLL_VERSION,
                            'proj_guid':  db_entry[0].PROJ_GUID,
                            'opti_tool':  db_entry[0].OPTI_TOOL,
                            'target_run':  db_entry[0].TARGET_RUN,
                            'files':  python_list(db_entry[0].FILES),
                            'strings': python_list(db_entry[0].STRINGS),
                            'bin_an_results': python_list(db_entry[0].BIN_AN_RESULTS),
                            'bin_an_warnings': python_list(db_entry[0].BIN_AN_WARNINGS)
                        }
                        template = get_template(
                            "pdf/windows_binary_analysis_pdf.html")
            else:
                if api:
                    return {"scan_type": "Type is not Allowed"}
                else:
                    return HttpResponse(json.dumps({"type": "Type is not Allowed"}),
                                        content_type="application/json; charset=utf-8", status_code=500)
            html = template.render(context)
            try:
                options = {
                    'page-size': 'A4',
                    'quiet': '',
                    'no-collate': '',
                    'margin-top': '0.50in',
                    'margin-right': '0.50in',
                    'margin-bottom': '0.50in',
                    'margin-left': '0.50in',
                    'encoding': "UTF-8",
                    'custom-header': [
                        ('Accept-Encoding', 'gzip')
                    ],
                    'no-outline': None
                }
                pdf_dat = pdfkit.from_string(html, False, options=options)
                if api:
                    return {"pdf_dat": pdf_dat}
                else:
                    return HttpResponse(pdf_dat, content_type='application/pdf')
            except Exception as exp:
                if api:
                    return {"error": "Cannot Generate PDF", "err_details": str(exp)}
                else:
                    return HttpResponse(json.dumps({"pdf_error": "Cannot Generate PDF",
                                                    "err_details": str(exp)}),
                                        content_type="application/json; charset=utf-8", status_code=500)

        else:
            if api:
                return {"error": "Invalid scan hash"}
            else:
                return HttpResponse(json.dumps({"md5": "Invalid MD5"}),
                                    content_type="application/json; charset=utf-8", status_code=500)
    except Exception as exp:
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)
Example #36
0
def report(request):
    """Dynamic Analysis Report Generation."""
    logger.info('Dynamic Analysis Report Generation')
    try:
        if request.method == 'GET':
            md5_hash = request.GET['md5']
            package = request.GET['pkg']
            if re.findall(r';|\$\(|\|\||&&', package):
                return print_n_send_error_response(request,
                                                   'Possible RCE Attack')
            if re.match('^[0-9a-f]{32}$', md5_hash):
                app_dir = os.path.join(settings.UPLD_DIR,
                                       md5_hash + '/')  # APP DIRECTORY
                download_dir = settings.DWD_DIR
                droidmon_api_loc = os.path.join(app_dir, 'x_logcat.txt')
                api_analysis_result = api_analysis(package, droidmon_api_loc)
                analysis_result = run_analysis(app_dir, md5_hash, package)
                download(md5_hash, download_dir, app_dir, package)
                # Only After Download Process is Done
                imgs = []
                act_imgs = []
                act = {}
                expact_imgs = []
                exp_act = {}
                if os.path.exists(
                        os.path.join(download_dir,
                                     md5_hash + '-screenshots-apk/')):
                    try:
                        imp_path = os.path.join(download_dir,
                                                md5_hash + '-screenshots-apk/')
                        for img in os.listdir(imp_path):
                            if img.endswith('.png'):
                                if img.startswith('act'):
                                    act_imgs.append(img)
                                elif img.startswith('expact'):
                                    expact_imgs.append(img)
                                else:
                                    imgs.append(img)
                        sadb = StaticAnalyzerAndroid.objects.filter(
                            MD5=md5_hash)
                        if sadb.exists():
                            logger.info('\nFetching Exported Activity'
                                        ' & Activity List from DB')
                            exported_act = python_list(sadb[0].EXPORTED_ACT)
                            act_desc = python_list(sadb[0].ACTIVITIES)
                            if act_imgs:
                                if len(act_imgs) == len(act_desc):
                                    act = dict(list(zip(act_imgs, act_desc)))
                            if expact_imgs:
                                if len(expact_imgs) == len(exported_act):
                                    exp_act = dict(
                                        list(zip(expact_imgs, exported_act)))
                        else:
                            logger.warning('Entry does not exists in the DB.')
                    except Exception:
                        logger.exception('Screenshot Sorting')
                context = {
                    'md5': md5_hash,
                    'emails': analysis_result['emails'],
                    'urls': analysis_result['urls'],
                    'domains': analysis_result['domains'],
                    'clipboard': analysis_result['clipboard'],
                    'http': analysis_result['web_data'],
                    'xml': analysis_result['xmlfiles'],
                    'sqlite': analysis_result['sqlite_db'],
                    'others': analysis_result['other_files'],
                    'imgs': imgs,
                    'acttest': act,
                    'expacttest': exp_act,
                    'net': api_analysis_result['api_net'],
                    'base64': api_analysis_result['api_base64'],
                    'crypto': api_analysis_result['api_crypto'],
                    'fileio': api_analysis_result['api_fileio'],
                    'binder': api_analysis_result['api_binder'],
                    'divinfo': api_analysis_result['api_deviceinfo'],
                    'cntval': api_analysis_result['api_cntvl'],
                    'sms': api_analysis_result['api_sms'],
                    'sysprop': api_analysis_result['api_sysprop'],
                    'dexload': api_analysis_result['api_dexloader'],
                    'reflect': api_analysis_result['api_reflect'],
                    'sysman': api_analysis_result['api_acntmnger'],
                    'process': api_analysis_result['api_cmd'],
                    'pkg': package,
                    'title': 'Dynamic Analysis'
                }
                template = 'dynamic_analysis/dynamic_analysis.html'
                return render(request, template, context)
            else:
                return print_n_send_error_response(request,
                                                   'Invalid Scan Hash')
        else:
            return print_n_send_error_response(request, 'Only GET allowed')
    except Exception:
        logger.exception('Dynamic Analysis Report Generation')
        err = 'Error Geneating Dynamic Analysis Report'
        return print_n_send_error_response(request, err)
def static_analyzer_ios(request, api=False):
    """Module that performs iOS IPA/ZIP Static Analysis"""
    try:
        logger.info("iOS Static Analysis Started")
        if api:
            file_type = request.POST['scan_type']
            checksum = request.POST['hash']
            rescan = str(request.POST.get('re_scan', 0))
            filename = request.POST['file_name']
        else:
            file_type = request.GET['type']
            checksum = request.GET['checksum']
            rescan = str(request.GET.get('rescan', 0))
            filename = request.GET['name']

        md5_match = re.match('^[0-9a-f]{32}$', checksum)
        if ((md5_match) and
                (filename.lower().endswith('.ipa') or
                 filename.lower().endswith('.zip')
                 ) and
                (file_type in ['ipa', 'ios'])
            ):
            app_dict = {}
            app_dict["directory"] = settings.BASE_DIR  # BASE DIR
            app_dict["file_name"] = filename  # APP ORGINAL NAME
            app_dict["md5_hash"] = checksum  # MD5
            app_dict["app_dir"] = os.path.join(
                settings.UPLD_DIR, app_dict["md5_hash"] + '/')  # APP DIRECTORY
            tools_dir = os.path.join(
                app_dict["directory"], 'StaticAnalyzer/tools/mac/')  # TOOLS DIR
            if file_type == 'ipa':
                # DB
                ipa_db = StaticAnalyzerIPA.objects.filter(
                    MD5=app_dict["md5_hash"])
                if ipa_db.exists() and rescan == '0':
                    context = get_context_from_db_entry_ipa(ipa_db)
                else:
                    logger.info("iOS Binary (IPA) Analysis Started")
                    app_dict["app_file"] = app_dict[
                        "md5_hash"] + '.ipa'  # NEW FILENAME
                    app_dict["app_path"] = app_dict["app_dir"] + \
                        app_dict["app_file"]  # APP PATH
                    app_dict["bin_dir"] = os.path.join(
                        app_dict["app_dir"], "Payload/")
                    app_dict["size"] = str(
                        file_size(app_dict["app_path"])) + 'MB'  # FILE SIZE
                    app_dict["sha1"], app_dict["sha256"] = hash_gen(
                        app_dict["app_path"])  # SHA1 & SHA256 HASHES
                    logger.info("Extracting IPA")
                    # EXTRACT IPA
                    unzip(app_dict["app_path"], app_dict["app_dir"])
                    # Get Files, normalize + to x,
                    # and convert binary plist -> xml
                    files, sfiles = ios_list_files(
                        app_dict["bin_dir"], app_dict["md5_hash"], True, 'ipa')
                    infoplist_dict = plist_analysis(app_dict["bin_dir"], False)
                    app_dict["appstore"] = app_search(infoplist_dict.get("id"))
                    bin_analysis_dict = binary_analysis(
                        app_dict["bin_dir"], tools_dir, app_dict["app_dir"], infoplist_dict.get("bin"))
                    # Saving to DB
                    logger.info("Connecting to DB")
                    if rescan == '1':
                        logger.info("Updating Database...")
                        update_db_entry_ipa(
                            app_dict, infoplist_dict, bin_analysis_dict, files, sfiles)
                        update_scan_timestamp(app_dict["md5_hash"])
                    elif rescan == '0':
                        logger.info("Saving to Database")
                        create_db_entry_ipa(
                            app_dict, infoplist_dict, bin_analysis_dict, files, sfiles)
                    context = get_context_from_analysis_ipa(
                        app_dict, infoplist_dict, bin_analysis_dict, files, sfiles)

                context['VT_RESULT'] = None
                if settings.VT_ENABLED:
                    vt = VirusTotal.VirusTotal()
                    context['VT_RESULT'] = vt.get_result(
                        os.path.join(app_dict['app_dir'], app_dict[
                                     'md5_hash']) + '.ipa',
                        app_dict['md5_hash']
                    )
                context["average_cvss"], context[
                    "security_score"] = score(context["bin_anal"])
                template = "static_analysis/ios_binary_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            elif file_type == 'ios':
                ios_zip_db = StaticAnalyzerIOSZIP.objects.filter(
                    MD5=app_dict["md5_hash"])
                if ios_zip_db.exists() and rescan == '0':
                    context = get_context_from_db_entry_ios(ios_zip_db)
                else:
                    logger.info("iOS Source Code Analysis Started")
                    app_dict["app_file"] = app_dict[
                        "md5_hash"] + '.zip'  # NEW FILENAME
                    app_dict["app_path"] = app_dict["app_dir"] + \
                        app_dict["app_file"]  # APP PATH
                    # ANALYSIS BEGINS - Already Unzipped
                    logger.info("ZIP Already Extracted")
                    app_dict["size"] = str(
                        file_size(app_dict["app_path"])) + 'MB'  # FILE SIZE
                    app_dict["sha1"], app_dict["sha256"] = hash_gen(
                        app_dict["app_path"])  # SHA1 & SHA256 HASHES
                    files, sfiles = ios_list_files(
                        app_dict["app_dir"], app_dict["md5_hash"], False, 'ios')
                    infoplist_dict = plist_analysis(app_dict["app_dir"], True)
                    app_dict["appstore"] = app_search(infoplist_dict.get("id"))
                    code_analysis_dic = ios_source_analysis(
                        app_dict["app_dir"])
                    # Firebase DB Check
                    code_analysis_dic['firebase'] = firebase_analysis(
                           list(set(code_analysis_dic["urls_list"])))
                    # Saving to DB
                    logger.info("Connecting to DB")
                    if rescan == '1':
                        logger.info("Updating Database...")
                        update_db_entry_ios(
                            app_dict, infoplist_dict, code_analysis_dic, files, sfiles)
                        update_scan_timestamp(app_dict["md5_hash"])
                    elif rescan == '0':
                        logger.info("Saving to Database")
                        create_db_entry_ios(
                            app_dict, infoplist_dict, code_analysis_dic, files, sfiles)
                    context = get_context_from_analysis_ios(
                        app_dict, infoplist_dict, code_analysis_dic, files, sfiles)
                context["average_cvss"], context[
                    "security_score"] = score(context["insecure"])
                template = "static_analysis/ios_source_analysis.html"
                if api:
                    return context
                else:
                    return render(request, template, context)
            else:
                msg = "File Type not supported!"
                if api:
                    return print_n_send_error_response(request, msg, True)
                else:
                    return print_n_send_error_response(request, msg, False)
        else:
            msg = "Hash match failed or Invalid file extension or file type"
            if api:
                return print_n_send_error_response(request, msg, True)
            else:
                return print_n_send_error_response(request, msg, False)
    except Exception as exp:
        msg = str(exp)
        exp_doc = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp_doc)
        else:
            return print_n_send_error_response(request, msg, False, exp_doc)
Example #38
0
def compare_apps(request, first_hash: str, second_hash: str):
    if first_hash == second_hash:
        error_msg = "Results with same hash cannot be compared"
        return print_n_send_error_response(request, error_msg, False)
    print("[INFO] Starting App compare for - {} and {}".format(first_hash, second_hash))
    return generic_compare(request, first_hash, second_hash)
Example #39
0
def activity_tester(request):
    """Activity Tester."""
    logger.info('Activity Tester')
    try:
        md5_hash = request.POST['md5']
        package = request.POST['pkg']
        if re.match('^[0-9a-f]{32}$', md5_hash):
            if re.findall(r';|\$\(|\|\||&&', package):
                return print_n_send_error_response(request,
                                                   'Possible RCE Attack', True)
            if request.method == 'POST':
                app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/')
                screen_dir = os.path.join(app_dir, 'screenshots-apk/')
                if not os.path.exists(screen_dir):
                    os.makedirs(screen_dir)
                data = {}
                static_android_db = StaticAnalyzerAndroid.objects.filter(
                    MD5=md5_hash)
                if static_android_db.exists():
                    logger.info('Fetching Activity List from DB')
                    activities = python_list(static_android_db[0].ACTIVITIES)
                    if activities:
                        act_no = 0
                        logger.info('Starting Activity Tester...')
                        logger.info('%s Activities Identified',
                                    str(len(activities)))
                        for line in activities:
                            try:
                                act_no += 1
                                logger.info('Launching Activity - %s. %s',
                                            str(act_no), line)
                                adb_command([
                                    'am', 'start', '-n', package + '/' + line
                                ], True)
                                wait(4)
                                adb_command([
                                    'screencap', '-p', '/data/local/screen.png'
                                ], True)
                                # ? get appended from Air :-()
                                # if activity names are used
                                outfile = ('{}act-{}.png'.format(
                                    screen_dir, act_no))
                                adb_command([
                                    'pull', '/data/local/screen.png', outfile
                                ])
                                logger.info('Activity Screenshot Taken')
                                adb_command(['am', 'force-stop', package],
                                            True)
                                logger.info('Stopping App')
                            except Exception:
                                logger.exception('Activity Tester')
                        data = {'acttest': 'done'}
                    else:
                        logger.info('Activity Tester - No Activity Found!')
                        data = {'acttest': 'noact'}
                    return HttpResponse(json.dumps(data),
                                        content_type='application/json')
                else:
                    err = 'Entry does not exist in DB'
                    return print_n_send_error_response(request, err, True)
            else:
                return print_n_send_error_response(request,
                                                   'Only POST allowed', True)
        else:
            return print_n_send_error_response(request, 'Invalid Scan Hash',
                                               True)
    except Exception:
        logger.exception('Activity Tester')
        return print_n_send_error_response(request,
                                           'Error Running Activity Tester',
                                           True)
Example #40
0
def pdf(request, api=False):
    try:
        if api:
            checksum = request.POST['hash']
            scan_type = request.POST['scan_type']
        else:
            checksum = request.GET['md5']
            scan_type = request.GET['type']
        hash_match = re.match('^[0-9a-f]{32}$', checksum)
        if hash_match:
            if scan_type.lower() in ['apk', 'andzip']:
                static_db = StaticAnalyzerAndroid.objects.filter(MD5=checksum)
                if static_db.exists():
                    print(
                        "\n[INFO] Fetching data from DB for PDF Report Generation (Android)"
                    )
                    context = get_context_from_db_entry(static_db)
                    if scan_type.lower() == 'apk':
                        template = get_template("pdf/static_analysis_pdf.html")
                    else:
                        template = get_template(
                            "pdf/static_analysis_zip_pdf.html")
                else:
                    if api:
                        return {"report": "Report not Found"}
                    else:
                        return HttpResponse(
                            json.dumps({"report": "Report not Found"}),
                            content_type="application/json; charset=utf-8",
                            status=500)
            elif re.findall('ipa|ioszip', scan_type.lower()):
                if scan_type.lower() == 'ipa':
                    static_db = StaticAnalyzerIPA.objects.filter(MD5=checksum)
                    if static_db.exists():
                        print(
                            "\n[INFO] Fetching data from DB for PDF Report Generation (IOS IPA)"
                        )
                        context = get_context_from_db_entry_ipa(static_db)
                        template = get_template(
                            "pdf/ios_binary_analysis_pdf.html")
                    else:
                        if api:
                            return {"report": "Report not Found"}
                        else:
                            return HttpResponse(
                                json.dumps({"report": "Report not Found"}),
                                content_type="application/json; charset=utf-8",
                                status=500)
                elif scan_type.lower() == 'ioszip':
                    static_db = StaticAnalyzerIOSZIP.objects.filter(
                        MD5=checksum)
                    if static_db.exists():
                        print(
                            "\n[INFO] Fetching data from DB for PDF Report Generation (IOS ZIP)"
                        )
                        context = get_context_from_db_entry_ios(static_db)
                        template = get_template(
                            "pdf/ios_source_analysis_pdf.html")
                    else:
                        if api:
                            return {"report": "Report not Found"}
                        else:
                            return HttpResponse(
                                json.dumps({"report": "Report not Found"}),
                                content_type="application/json; charset=utf-8",
                                status=500)
            elif re.findall('appx', scan_type.lower()):
                if scan_type.lower() == 'appx':
                    db_entry = StaticAnalyzerWindows.objects.filter(  # pylint: disable-msg=E1101
                        MD5=checksum)
                    if db_entry.exists():
                        print(
                            "\n[INFO] Fetching data from DB for PDF Report Generation (APPX)"
                        )

                        context = {
                            'title':
                            db_entry[0].TITLE,
                            'name':
                            db_entry[0].APP_NAME,
                            'pub_name':
                            db_entry[0].PUB_NAME,
                            'size':
                            db_entry[0].SIZE,
                            'md5':
                            db_entry[0].MD5,
                            'sha1':
                            db_entry[0].SHA1,
                            'sha256':
                            db_entry[0].SHA256,
                            'bin_name':
                            db_entry[0].BINNAME,
                            'version':
                            db_entry[0].VERSION,
                            'arch':
                            db_entry[0].ARCH,
                            'compiler_version':
                            db_entry[0].COMPILER_VERSION,
                            'visual_studio_version':
                            db_entry[0].VISUAL_STUDIO_VERSION,
                            'visual_studio_edition':
                            db_entry[0].VISUAL_STUDIO_EDITION,
                            'target_os':
                            db_entry[0].TARGET_OS,
                            'appx_dll_version':
                            db_entry[0].APPX_DLL_VERSION,
                            'proj_guid':
                            db_entry[0].PROJ_GUID,
                            'opti_tool':
                            db_entry[0].OPTI_TOOL,
                            'target_run':
                            db_entry[0].TARGET_RUN,
                            'files':
                            python_list(db_entry[0].FILES),
                            'strings':
                            python_list(db_entry[0].STRINGS),
                            'bin_an_results':
                            python_list(db_entry[0].BIN_AN_RESULTS),
                            'bin_an_warnings':
                            python_list(db_entry[0].BIN_AN_WARNINGS)
                        }
                        template = get_template(
                            "pdf/windows_binary_analysis_pdf.html")
            else:
                if api:
                    return {"scan_type": "Type is not Allowed"}
                else:
                    return HttpResponse(
                        json.dumps({"type": "Type is not Allowed"}),
                        content_type="application/json; charset=utf-8",
                        status=500)

            context['VT_RESULT'] = None
            if settings.VT_ENABLED:
                app_dir = os.path.join(settings.UPLD_DIR, checksum + '/')
                vt = VirusTotal.VirusTotal()
                if "zip" in scan_type.lower():
                    context['VT_RESULT'] = None
                else:
                    context['VT_RESULT'] = vt.get_result(
                        os.path.join(app_dir, checksum) + '.' +
                        scan_type.lower(), checksum)

            html = template.render(context)
            try:
                options = {
                    'page-size': 'A4',
                    'quiet': '',
                    'no-collate': '',
                    'margin-top': '0.50in',
                    'margin-right': '0.50in',
                    'margin-bottom': '0.50in',
                    'margin-left': '0.50in',
                    'encoding': "UTF-8",
                    'custom-header': [('Accept-Encoding', 'gzip')],
                    'no-outline': None
                }
                pdf_dat = pdfkit.from_string(html, False, options=options)
                if api:
                    return {"pdf_dat": pdf_dat, "report_dat": context}
                else:
                    return HttpResponse(pdf_dat,
                                        content_type='application/pdf')
            except Exception as exp:
                if api:
                    return {
                        "error": "Cannot Generate PDF",
                        "err_details": str(exp)
                    }
                else:
                    return HttpResponse(
                        json.dumps({
                            "pdf_error": "Cannot Generate PDF",
                            "err_details": str(exp)
                        }),
                        content_type="application/json; charset=utf-8",
                        status=500)

        else:
            if api:
                return {"error": "Invalid scan hash"}
            else:
                return HttpResponse(
                    json.dumps({"md5": "Invalid MD5"}),
                    content_type="application/json; charset=utf-8",
                    status=500)
    except Exception as exp:
        msg = str(exp)
        exp = exp.__doc__
        if api:
            return print_n_send_error_response(request, msg, True, exp)
        else:
            return print_n_send_error_response(request, msg, False, exp)