def strings_on_ipa(bin_path):
    """Extract Strings from IPA"""
    try:
        print "[INFO] Running strings against the Binary"
        unique_str = []
        list_of_strings = list(strings(bin_path))
        unique_str = list(set(list_of_strings))  # Make unique
        '''unique_str = [ipa_str if isinstance(ipa_str, unicode) else unicode(
            ipa_str, encoding="utf-8", errors="replace") for ipa_str in unique_str]'''
        unique_str = [str(escape(ip_str)) for ip_str in unique_str]  # Escape evil strings
        return unique_str
    except:
        PrintException("[ERROR] - Running strings against the Binary")
def strings_on_ipa(bin_path):
    """Extract Strings from IPA"""
    try:
        print "[INFO] Running strings against the Binary"
        unique_str = []
        list_of_strings = list(strings(bin_path))
        unique_str = list(set(list_of_strings))  # Make unique
        unique_str = [ipa_str if isinstance(ipa_str, unicode) else unicode(
            ipa_str, encoding="utf-8", errors="replace") for ipa_str in unique_str]
        unique_str = [escape(ip_str) for ip_str in unique_str]  # Escape evil strings
        return unique_str
    except:
        PrintException("[ERROR] - Running strings against the Binary")
def _binary_analysis(app_dic):
    """Start binary analsis."""
    print "[INFO] Starting Binary Analysis"
    bin_an_dic = {}

    # Init optional sections to prevent None-Pointer-Errors
    bin_an_dic['results'] = []
    bin_an_dic['warnings'] = []

    # Search for exe
    for file_name in app_dic['files']:
        if file_name.endswith(".exe"):
            bin_an_dic['bin'] = file_name
            bin_an_dic['bin_name'] = file_name.replace(".exe", "")
            break
    if not bin_an_dic['bin_name']:
        PrintException("[ERROR] No executeable in appx.")

    bin_path = os.path.join(app_dic['app_dir'], bin_an_dic['bin'])

    # Execute strings command
    bin_an_dic['strings'] = ""
    str_list = list(strings(bin_path))
    str_list = set(str_list)  # Make unique # pylint: disable-msg=R0204

    str_list = [
        s if isinstance(s, unicode) else unicode(
            s, encoding="utf-8", errors="replace") for s in str_list
    ]
    str_list = [escape(s) for s in str_list]
    bin_an_dic['strings'] = str_list

    # Search for unsave function
    pattern = re.compile(
        "(alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy)"
    )
    for elem in str_list:
        if pattern.match(elem):
            result = {
                "rule_id":
                'Possible Insecure Function',
                "status":
                'Insecure',
                "desc":
                "Possible Insecure Function detected: {}".format(elem[5:-5])
            }
            bin_an_dic['results'].append(result)

    # Execute binskim analysis if vm is available
    if settings.CURRENT_PLATFROM != 'Windows':
        if settings.WINDOWS_VM_IP:
            print "[INFO] Windows VM configured."
            global proxy
            proxy = xmlrpclib.ServerProxy(  # pylint: disable-msg=C0103
                "http://{}:{}".format(settings.WINDOWS_VM_IP,
                                      settings.WINDOWS_VM_PORT))
            name = _upload_sample(bin_path)
            bin_an_dic = __binskim(name, bin_an_dic)
            bin_an_dic = __binscope(name, bin_an_dic)
        else:
            print "[INFO] Windows VM not configured in settings.py. Skipping Binskim and Binscope."
            warning = {
                "rule_id":
                "VM",
                "status":
                "Info",
                "desc":
                "VM is not configured. Please read the readme.md in MobSF/install/windows."
            }
            bin_an_dic['results'].append(warning)
    else:
        print "[INFO] Running lokal analysis."

        global config
        config = configparser.ConfigParser()
        # Switch to settings definded path if available
        config.read(expanduser("~") + "\\MobSF\\Config\\config.txt")

        # Run analysis functions
        bin_an_dic = __binskim(bin_path,
                               bin_an_dic,
                               run_local=True,
                               app_dir=app_dic['app_dir'])
        bin_an_dic = __binscope(bin_path,
                                bin_an_dic,
                                run_local=True,
                                app_dir=app_dic['app_dir'])

    return bin_an_dic
Example #4
0
def BinaryAnalysis(SRC, TOOLS_DIR, APP_DIR):
    try:
        print "[INFO] Starting Binary Analysis"
        dirs = os.listdir(SRC)
        for d in dirs:
            if d.endswith(".app"):
                break

        BIN_DIR = os.path.join(SRC, d)  # Full Dir/Payload/x.app
        XML_FILE = os.path.join(BIN_DIR, "Info.plist")
        BIN = d.replace(".app", "")
        BIN_NAME = BIN
        ID = ""
        VER = ""
        SDK = ""
        PLTFM = ""
        MIN = ""
        XML = ""

        try:
            print "[INFO] Reading Info.plist"
            XML = readBinXML(XML_FILE)
            if isinstance(XML, unicode):
                XML = XML.encode("utf-8", "replace")
            p = plistlib.readPlistFromString(XML)
            BIN_NAME = BIN = ID = VER = SDK = PLTFM = MIN = ""
            if "CFBundleDisplayName" in p:
                BIN_NAME = p["CFBundleDisplayName"]
            if "CFBundleExecutable" in p:
                BIN = p["CFBundleExecutable"]
            if "CFBundleIdentifier" in p:
                ID = p["CFBundleIdentifier"]
            if "CFBundleVersion" in p:
                VER = p["CFBundleVersion"]
            if "DTSDKName" in p:
                SDK = p["DTSDKName"]
            if "DTPlatformVersion" in p:
                PLTFM = p["DTPlatformVersion"]
            if "MinimumOSVersion" in p:
                MIN = p["MinimumOSVersion"]

        except:
            PrintException("[ERROR] - Reading from Info.plist")
        BIN_PATH = os.path.join(BIN_DIR, BIN)  # Full Dir/Payload/x.app/x
        print "[INFO] iOS Binary : " + BIN
        print "[INFO] Running otool against the Binary"
        # Libs Used
        LIBS = ''
        if len(settings.OTOOL_BINARY) > 0 and isFileExists(
                settings.OTOOL_BINARY):
            OTOOL = settings.OTOOL_BINARY
        else:
            OTOOL = "otool"
        args = [OTOOL, '-L', BIN_PATH]
        dat = subprocess.check_output(args)
        dat = escape(dat.replace(BIN_DIR + "/", ""))
        LIBS = dat.replace("\n", "</br>")
        # PIE
        args = [OTOOL, '-hv', BIN_PATH]
        dat = subprocess.check_output(args)
        if "PIE" in dat:
            PIE = "<tr><td><strong>fPIE -pie</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Position Independent Executable (PIE) flag. This enables Address Space Layout Randomization (ASLR), a memory protection mechanism for exploit mitigation.</td></tr>"
        else:
            PIE = "<tr><td><strong>fPIE -pie</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Position Independent Executable (PIE) flag. So Address Space Layout Randomization (ASLR) is missing. ASLR is a memory protection mechanism for exploit mitigation.</td></tr>"
        # Stack Smashing Protection & ARC
        args = [OTOOL, '-Iv', BIN_PATH]
        dat = subprocess.check_output(args)
        if "stack_chk_guard" in dat:
            SSMASH = "<tr><td><strong>fstack-protector-all</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Stack Smashing Protector (SSP) flag and is having protection against Stack Overflows/Stack Smashing Attacks.</td></tr>"
        else:
            SSMASH = "<tr><td><strong>fstack-protector-all</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Stack Smashing Protector (SSP) flag. It is vulnerable to Stack Overflows/Stack Smashing Attacks.</td></tr>"
        # ARC
        if "_objc_release" in dat:
            ARC = "<tr><td><strong>fobjc-arc</strong> flag is Found</td><td><span class='label label-success'>Secure</span></td><td>App is compiled with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that provides automatic memory management of Objective-C objects and is an exploit mitigation mechanism against memory corruption vulnerabilities.</td></tr>"
        else:
            ARC = "<tr><td><strong>fobjc-arc</strong> flag is not Found</td><td><span class='label label-danger'>Insecure</span></td><td>App is not compiled with Automatic Reference Counting (ARC) flag. ARC is a compiler feature that provides automatic memory management of Objective-C objects and protects from memory corruption vulnerabilities.</td></tr>"
        ##########
        BANNED_API = ''
        x = re.findall(
            "alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy",
            dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            BANNED_API = "<tr><td>Binary make use of banned API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may contain the following banned API(s) </br><strong>" + str(
                x) + "</strong>.</td></tr>"
        WEAK_CRYPTO = ''
        x = re.findall(
            "kCCAlgorithmDES|kCCAlgorithm3DES||kCCAlgorithmRC2|kCCAlgorithmRC4|kCCOptionECBMode|kCCOptionCBCMode",
            dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            WEAK_CRYPTO = "<tr><td>Binary make use of some Weak Crypto API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following weak crypto API(s)</br><strong>" + str(
                x) + "</strong>.</td></tr>"
        CRYPTO = ''
        x = re.findall(
            "CCKeyDerivationPBKDF|CCCryptorCreate|CCCryptorCreateFromData|CCCryptorRelease|CCCryptorUpdate|CCCryptorFinal|CCCryptorGetOutputLength|CCCryptorReset|CCCryptorRef|kCCEncrypt|kCCDecrypt|kCCAlgorithmAES128|kCCKeySizeAES128|kCCKeySizeAES192|kCCKeySizeAES256|kCCAlgorithmCAST|SecCertificateGetTypeID|SecIdentityGetTypeID|SecKeyGetTypeID|SecPolicyGetTypeID|SecTrustGetTypeID|SecCertificateCreateWithData|SecCertificateCreateFromData|SecCertificateCopyData|SecCertificateAddToKeychain|SecCertificateGetData|SecCertificateCopySubjectSummary|SecIdentityCopyCertificate|SecIdentityCopyPrivateKey|SecPKCS12Import|SecKeyGeneratePair|SecKeyEncrypt|SecKeyDecrypt|SecKeyRawSign|SecKeyRawVerify|SecKeyGetBlockSize|SecPolicyCopyProperties|SecPolicyCreateBasicX509|SecPolicyCreateSSL|SecTrustCopyCustomAnchorCertificates|SecTrustCopyExceptions|SecTrustCopyProperties|SecTrustCopyPolicies|SecTrustCopyPublicKey|SecTrustCreateWithCertificates|SecTrustEvaluate|SecTrustEvaluateAsync|SecTrustGetCertificateCount|SecTrustGetCertificateAtIndex|SecTrustGetTrustResult|SecTrustGetVerifyTime|SecTrustSetAnchorCertificates|SecTrustSetAnchorCertificatesOnly|SecTrustSetExceptions|SecTrustSetPolicies|SecTrustSetVerifyDate|SecCertificateRef|SecIdentityRef|SecKeyRef|SecPolicyRef|SecTrustRef",
            dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            CRYPTO = "<tr><td>Binary make use of the following Crypto API(s)</td><td><span class='label label-info'>Info</span></td><td>The binary may use the following crypto API(s)</br><strong>" + str(
                x) + "</strong>.</td></tr>"
        WEAK_HASH = ''
        x = re.findall(
            "CC_MD2_Init|CC_MD2_Update|CC_MD2_Final|CC_MD2|MD2_Init|MD2_Update|MD2_Final|CC_MD4_Init|CC_MD4_Update|CC_MD4_Final|CC_MD4|MD4_Init|MD4_Update|MD4_Final|CC_MD5_Init|CC_MD5_Update|CC_MD5_Final|CC_MD5|MD5_Init|MD5_Update|MD5_Final|MD5Init|MD5Update|MD5Final|CC_SHA1_Init|CC_SHA1_Update|CC_SHA1_Final|CC_SHA1|SHA1_Init|SHA1_Update|SHA1_Final",
            dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            WEAK_HASH = "<tr><td>Binary make use of the following Weak HASH API(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following weak hash API(s)</br><strong>" + str(
                x) + "</strong>.</td></tr>"
        HASH = ''
        x = re.findall(
            "CC_SHA224_Init|CC_SHA224_Update|CC_SHA224_Final|CC_SHA224|SHA224_Init|SHA224_Update|SHA224_Final|CC_SHA256_Init|CC_SHA256_Update|CC_SHA256_Final|CC_SHA256|SHA256_Init|SHA256_Update|SHA256_Final|CC_SHA384_Init|CC_SHA384_Update|CC_SHA384_Final|CC_SHA384|SHA384_Init|SHA384_Update|SHA384_Final|CC_SHA512_Init|CC_SHA512_Update|CC_SHA512_Final|CC_SHA512|SHA512_Init|SHA512_Update|SHA512_Final",
            dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            HASH = "<tr><td>Binary make use of the following HASH API(s)</td><td><span class='label label-info'>Info</span></td><td>The binary may use the following hash API(s)</br><strong>" + str(
                x) + "</strong>.</td></tr>"
        RAND = ''
        x = re.findall("srand|random", dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            RAND = "<tr><td>Binary make use of the insecure Random Function(s)</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use the following insecure Random Function(s)</br><strong>" + str(
                x) + "</strong>.</td></tr>"
        LOG = ''
        x = re.findall("NSLog", dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            LOG = "<tr><td>Binary make use of Logging Function</td><td><span class='label label-info'>Info</span></td><td>The binary may use <strong>NSLog</strong> function for logging.</td></tr>"
        MALL = ''
        x = re.findall("malloc", dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            MALL = "<tr><td>Binary make use of <strong>malloc</strong> Function</td><td><span class='label label-danger'>Insecure</span></td><td>The binary may use <strong>malloc</strong> function instead of <strong>calloc</strong>.</td></tr>"
        DBG = ''
        x = re.findall("ptrace", dat)
        x = list(set(x))
        x = ', '.join(x)
        if len(x) > 1:
            DBG = "<tr><td>Binary calls <strong>ptrace</strong> Function for anti-debugging.</td><td><span class='label label-warning'>warning</span></td><td>The binary may use <strong>ptrace</strong> function. It can be used to detect and prevent debuggers. Ptrace is not a public API and Apps that use non-public APIs will be rejected from AppStore. </td></tr>"
        CDUMP = ''
        WVIEW = ''
        try:
            print "[INFO] Running class-dump-z against the Binary"
            if len(settings.CLASSDUMPZ_BINARY) > 0 and isFileExists(
                    settings.CLASSDUMPZ_BINARY):
                CLASSDUMPZ_BIN = settings.CLASSDUMPZ_BINARY
            else:
                CLASSDUMPZ_BIN = os.path.join(TOOLS_DIR, 'class-dump-z')
            subprocess.call(["chmod", "777", CLASSDUMPZ_BIN])
            dat = subprocess.check_output([CLASSDUMPZ_BIN, BIN_PATH])
            CDUMP = dat
            FILE = os.path.join(APP_DIR, "classdump.txt")
            with open(FILE, "w") as f:
                f.write(CDUMP)
            if "UIWebView" in CDUMP:
                WVIEW = "<tr><td>Binary uses WebView Component.</td><td><span class='label label-info'>Info</span></td><td>The binary may use WebView Component.</td></tr>"

        except:
            PrintException("[ERROR] - Cannot perform class dump")
        BIN_RES = PIE + SSMASH + ARC + BANNED_API + WEAK_CRYPTO + \
            CRYPTO + WEAK_HASH + HASH + RAND + LOG + MALL + DBG + WVIEW
        # classdump

        # strings
        print "[INFO] Running strings against the Binary"
        STRINGS = ""
        sl = list(strings(BIN_PATH))
        sl = set(sl)  # Make unique
        sl = [
            s if isinstance(s, unicode) else unicode(
                s, encoding="utf-8", errors="replace") for s in sl
        ]
        sl = [escape(s) for s in sl]  # Escape evil strings
        STRINGS = "</br>".join(sl)

        return XML, BIN_NAME, ID, VER, SDK, PLTFM, MIN, LIBS, BIN_RES, STRINGS
    except:
        PrintException("[ERROR] iOS Binary Analysis")
def _binary_analysis(app_dic):
    """Start binary analsis."""
    print "[INFO] Starting Binary Analysis"
    bin_an_dic = {}

    # Init optional sections to prevent None-Pointer-Errors
    bin_an_dic['results'] = []
    bin_an_dic['warnings'] = []

    # Search for exe
    for file_name in app_dic['files']:
        if file_name.endswith(".exe"):
            bin_an_dic['bin'] = file_name
            bin_an_dic['bin_name'] = file_name.replace(".exe", "")
            break
    if not bin_an_dic['bin_name']:
        PrintException("[ERROR] No executeable in appx.")

    bin_path = os.path.join(app_dic['app_dir'], bin_an_dic['bin'])

    # Execute strings command
    bin_an_dic['strings'] = ""
    str_list = list(strings(bin_path))
    str_list = set(str_list)  # Make unique # pylint: disable-msg=R0204

    str_list = [s if isinstance(s, unicode) else unicode(
        s, encoding="utf-8", errors="replace") for s in str_list]
    str_list = [escape(s) for s in str_list]
    bin_an_dic['strings'] = str_list

    # Search for unsave function
    pattern = re.compile("(alloca|gets|memcpy|printf|scanf|sprintf|sscanf|strcat|StrCat|strcpy|StrCpy|strlen|StrLen|strncat|StrNCat|strncpy|StrNCpy|strtok|swprintf|vsnprintf|vsprintf|vswprintf|wcscat|wcscpy|wcslen|wcsncat|wcsncpy|wcstok|wmemcpy)")
    for elem in str_list:
        if pattern.match(elem[5:-5]):
            result = {
                "rule_id": 'Possible Insecure Function',
                "status": 'Insecure',
                "desc": "Possible Insecure Function detected: {}".format(elem[5:-5])
            }
            bin_an_dic['results'].append(result)

    # Execute binskim analysis if vm is available
    if platform.system() != 'Windows':
        if settings.WINDOWS_VM_IP:
            print "[INFO] Windows VM configured."
            global proxy
            proxy = xmlrpclib.ServerProxy(  # pylint: disable-msg=C0103
                "http://{}:{}".format(
                    settings.WINDOWS_VM_IP,
                    settings.WINDOWS_VM_PORT
                )
            )
            name = _upload_sample(bin_path)
            bin_an_dic = __binskim(name, bin_an_dic)
            bin_an_dic = __binscope(name, bin_an_dic)
        else:
            print "[INFO] Windows VM not configured in settings.py. Skipping Binskim and Binscope."
            warning = {
                "rule_id": "VM",
                "status": "Info",
                "desc": "VM is not configured. Please read the readme.md in MobSF/install/windows."
            }
            bin_an_dic['results'].append(warning)
    else:
        print "[INFO] Running lokal analysis."

        global config
        config = configparser.ConfigParser()
        # Switch to settings definded path if available
        config.read(expanduser("~") + "\\MobSF\\Config\\config.txt")

        # Run analysis functions
        bin_an_dic = __binskim(bin_path, bin_an_dic,
                               run_local=True, app_dir=app_dic['app_dir'])
        bin_an_dic = __binscope(bin_path, bin_an_dic,
                                run_local=True, app_dir=app_dic['app_dir'])

    return bin_an_dic