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
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