def RunAnalysis(APKDIR, MD5, PACKAGE): print "\n[INFO] Dynamic File Analysis" Web = os.path.join(APKDIR, 'WebTraffic.txt') Logcat = os.path.join(APKDIR, 'logcat.txt') xLogcat = os.path.join(APKDIR, 'x_logcat.txt') traffic = '' wb = '' xlg = '' DOMAINS = {} try: with io.open(Web, mode='r', encoding="utf8", errors="ignore") as f: wb = f.read() except: pass with io.open(Logcat, mode='r', encoding="utf8", errors="ignore") as f: traffic = f.read() with io.open(xLogcat, mode='r', encoding="utf8", errors="ignore") as f: xlg = f.read() traffic = wb + traffic + xlg URLS = [] #URLs My Custom regex p = re.compile( ur'((?:https?://|s?ftps?://|file://|javascript:|data:|www\d{0,3}[.])[\w().=/;,#:@?&~*+!$%\'{}-]+)', re.UNICODE) urllist = re.findall(p, traffic.lower()) #Domain Extraction and Malware Check print "[INFO] Performing Malware Check on extracted Domains" DOMAINS = MalwareCheck(urllist) for url in urllist: if url not in URLS: URLS.append(url) #Email Etraction Regex EMAILS = [] regex = re.compile(("[\w.-]+@[\w-]+\.[\w.]+")) for email in regex.findall(traffic.lower()): if ((email not in EMAILS) and (not email.startswith('//'))): if email == "*****@*****.**": pass else: EMAILS.append(email) #Extract Device Data try: TARLOC = os.path.join(APKDIR, PACKAGE + '.tar') UNTAR_DIR = os.path.join(APKDIR, 'DYNAMIC_DeviceData/') if not os.path.exists(UNTAR_DIR): os.makedirs(UNTAR_DIR) with tarfile.open(TARLOC) as tar: try: tar.extractall(UNTAR_DIR) except: pass except: PrintException("[ERROR] TAR EXTRACTION FAILED") #Do Static Analysis on Data from Device xmlfiles = '' SQLiteDB = '' OtherFiles = '' typ = '' UNTAR_DIR = os.path.join(APKDIR, 'DYNAMIC_DeviceData/') if not os.path.exists(UNTAR_DIR): os.makedirs(UNTAR_DIR) try: for dirName, subDir, files in os.walk(UNTAR_DIR): for jfile in files: file_path = os.path.join(UNTAR_DIR, dirName, jfile) if "+" in file_path: shutil.move(file_path, file_path.replace("+", "x")) file_path = file_path.replace("+", "x") fileparam = file_path.replace(UNTAR_DIR, '') if jfile == 'lib': pass else: if jfile.endswith('.xml'): typ = 'xml' xmlfiles += "<tr><td><a href='../View/?file=" + escape( fileparam ) + "&md5=" + MD5 + "&type=" + typ + "'>" + escape( fileparam) + "</a></td><tr>" else: with io.open(file_path, mode='r', encoding="utf8", errors="ignore") as f: b = f.read(6) if b == "SQLite": typ = 'db' SQLiteDB += "<tr><td><a href='../View/?file=" + escape( fileparam ) + "&md5=" + MD5 + "&type=" + typ + "'>" + escape( fileparam) + "</a></td><tr>" elif not jfile.endswith('.DS_Store'): typ = 'others' OtherFiles += "<tr><td><a href='../View/?file=" + escape( fileparam ) + "&md5=" + MD5 + "&type=" + typ + "'>" + escape( fileparam) + "</a></td><tr>" except: PrintException("[ERROR] Dynamic File Analysis") pass return URLS, DOMAINS, EMAILS, wb, xmlfiles, SQLiteDB, OtherFiles
def code_analysis(app_dir, md5, perms, typ): """Perform the code analysis.""" try: print "[INFO] Static Android Code Analysis Started" code = { key: [] for key in ('inf_act', 'inf_ser', 'inf_bro', 'log', 'fileio', 'rand', 'd_hcode', 'd_app_tamper', 'dex_cert', 'dex_tamper', 'd_rootcheck', 'd_root', 'd_ssl_pin', 'dex_root', 'dex_debug_key', 'dex_debug', 'dex_debug_con', 'dex_emulator', 'd_prevent_screenshot', 'd_webviewdisablessl', 'd_webviewdebug', 'd_sensitive', 'd_ssl', 'd_sqlite', 'd_con_world_readable', 'd_con_world_writable', 'd_con_private', 'd_extstorage', 'd_tmpfile', 'd_jsenabled', 'gps', 'crypto', 'exec', 'server_socket', 'socket', 'datagramp', 'datagrams', 'ipc', 'msg', 'webview_addjs', 'webview', 'webviewget', 'webviewpost', 'httpcon', 'urlcon', 'jurl', 'httpsurl', 'nurl', 'httpclient', 'notify', 'cellinfo', 'cellloc', 'subid', 'devid', 'softver', 'simserial', 'simop', 'opname', 'contentq', 'refmethod', 'obf', 'gs', 'bencode', 'bdecode', 'dex', 'mdigest', 'sqlc_password', 'd_sql_cipher', 'd_con_world_rw', 'ecb', 'rsa_no_pad', 'weak_iv') } crypto = False obfus = False reflect = False dynamic = False native = False email_n_file = '' url_n_file = '' url_list = list() domains = dict() if typ == "apk": java_src = os.path.join(app_dir, 'java_source/') elif typ == "studio": java_src = os.path.join(app_dir, 'app/src/main/java/') elif typ == "eclipse": java_src = os.path.join(app_dir, 'src/') print "[INFO] Code Analysis Started on - " + java_src # pylint: disable=unused-variable # Needed by os.walk for dir_name, sub_dir, files in os.walk(java_src): for jfile in files: jfile_path = os.path.join(java_src, dir_name, jfile) if "+" in jfile: p_2 = os.path.join(java_src, dir_name, jfile.replace("+", "x")) shutil.move(jfile_path, p_2) jfile_path = p_2 repath = dir_name.replace(java_src, '') if (jfile.endswith('.java') and any(cls in repath for cls in settings.SKIP_CLASSES) is False): dat = '' with io.open(jfile_path, mode='r', encoding="utf8", errors="ignore") as file_pointer: dat = file_pointer.read() # Initialize urls = [] emails = [] # Code Analysis # print "[INFO] Doing Code Analysis on - " + jfile_path #==========================Android Security Code Review === if (re.findall( r'MODE_WORLD_READABLE|Context\.MODE_WORLD_READABLE', dat) or re.findall( r'openFileOutput\(\s*".+"\s*,\s*1\s*\)', dat)): code['d_con_world_readable'].append( jfile_path.replace(java_src, '')) if (re.findall( r'MODE_WORLD_WRITABLE|Context\.MODE_WORLD_WRITABLE', dat) or re.findall( r'openFileOutput\(\s*".+"\s*,\s*2\s*\)', dat)): code['d_con_world_writable'].append( jfile_path.replace(java_src, '')) if re.findall(r'openFileOutput\(\s*".+"\s*,\s*3\s*\)', dat): code['d_con_world_rw'].append( jfile_path.replace(java_src, '')) if re.findall(r'MODE_PRIVATE|Context\.MODE_PRIVATE', dat): code['d_con_private'].append( jfile_path.replace(java_src, '')) if (any("WRITE_EXTERNAL_STORAGE" in perm for perm in perms) and ('.getExternalStorage' in dat or '.getExternalFilesDir(' in dat)): code['d_extstorage'].append( jfile_path.replace(java_src, '')) if (any("WRITE_EXTERNAL_STORAGE" in perm for perm in perms) and '.createTempFile(' in dat): code['d_tmpfile'].append( jfile_path.replace(java_src, '')) if ('setJavaScriptEnabled(true)' in dat and '.addJavascriptInterface(' in dat): code['d_jsenabled'].append( jfile_path.replace(java_src, '')) if ('.setWebContentsDebuggingEnabled(true)' in dat and 'WebView' in dat): code['d_webviewdebug'].append( jfile_path.replace(java_src, '')) if 'onReceivedSslError(WebView' in dat and '.proceed();' in dat: code['d_webviewdisablessl'].append( jfile_path.replace(java_src, '')) if (('rawQuery(' in dat or 'execSQL(' in dat) and 'android.database.sqlite' in dat): code['d_sqlite'].append( jfile_path.replace(java_src, '')) if ((('javax.net.ssl') in dat) and (('TrustAllSSLSocket-Factory') in dat or ('AllTrustSSLSocketFactory') in dat or ('NonValidatingSSLSocketFactory') in dat or ('ALLOW_ALL_HOSTNAME_VERIFIER') in dat or ('.setDefaultHostnameVerifier(') in dat or ('NullHostnameVerifier(') in dat)): code['d_ssl'].append(jfile_path.replace(java_src, '')) if ('password = "******"' in dat.lower() or 'username = "******"' in dat.lower()): code['d_sensitive'].append( jfile_path.replace(java_src, '')) if ('import dexguard.util' in dat and 'DebugDetector.isDebuggable' in dat): code['dex_debug'].append( jfile_path.replace(java_src, '')) if ('import dexguard.util' in dat and 'DebugDetector.isDebuggerConnected' in dat): code['dex_debug_con'].append( jfile_path.replace(java_src, '')) if (('import dexguard.util') in dat and ('EmulatorDetector.isRunningInEmulator') in dat): code['dex_emulator'].append( jfile_path.replace(java_src, '')) if (('import dexguard.util') in dat and ('DebugDetector.isSignedWithDebugKey') in dat): code['dex_debug_key'].append( jfile_path.replace(java_src, '')) if 'import dexguard.util' in dat and 'RootDetector.isDeviceRooted' in dat: code['dex_root'].append( jfile_path.replace(java_src, '')) if 'import dexguard.util' in dat and 'TamperDetector.checkApk' in dat: code['dex_tamper'].append( jfile_path.replace(java_src, '')) if ('import dexguard.util' in dat and 'CertificateChecker.checkCertificate' in dat): code['dex_cert'].append( jfile_path.replace(java_src, '')) if ('org.thoughtcrime.ssl.pinning' in dat and ('PinningHelper.getPinnedHttpsURLConnection' in dat or 'PinningHelper.getPinnedHttpClient' in dat or 'PinningSSLSocketFactory(' in dat)): code['d_ssl_pin'].append( jfile_path.replace(java_src, '')) if ('PackageManager.GET_SIGNATURES' in dat) and ('getPackageName(' in dat): code['d_app_tamper'].append( jfile_path.replace(java_src, '')) if ('com.noshufou.android.su' in dat or 'com.thirdparty.superuser' in dat or 'eu.chainfire.supersu' in dat or 'com.koushikdutta.superuser' in dat or 'eu.chainfire.' in dat): code['d_root'].append(jfile_path.replace(java_src, '')) if (('.contains("test-keys")') in dat or ('/system/app/Superuser.apk') in dat or ('isDeviceRooted()') in dat or ('/system/bin/failsafe/su') in dat or ('/system/sd/xbin/su') in dat or ('"/system/xbin/which", "su"') in dat or ('RootTools.isAccessGiven()') in dat): code['d_rootcheck'].append( jfile_path.replace(java_src, '')) if re.findall(r'java\.util\.Random', dat): code['rand'].append(jfile_path.replace(java_src, '')) if re.findall(r'Log\.(v|d|i|w|e|f|s)|System\.out\.print', dat): code['log'].append(jfile_path.replace(java_src, '')) if ".hashCode()" in dat: code['d_hcode'].append(jfile_path.replace( java_src, '')) if "getWindow().setFlags(" in dat and ".FLAG_SECURE" in dat: code['d_prevent_screenshot'].append( jfile_path.replace(java_src, '')) if "SQLiteOpenHelper.getWritableDatabase(" in dat: code['sqlc_password'].append( jfile_path.replace(java_src, '')) if "SQLiteDatabase.loadLibs(" in dat and "net.sqlcipher." in dat: code['d_sql_cipher'].append( jfile_path.replace(java_src, '')) if re.findall(r'Cipher\.getInstance\(\s*"\s*AES\/ECB', dat): code['ecb'].append(jfile_path.replace(java_src, '')) if re.findall(r'cipher\.getinstance\(\s*"rsa/.+/nopadding', dat.lower()): code['rsa_no_pad'].append( jfile_path.replace(java_src, '')) if ("0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00" in dat or "0x01,0x02,0x03,0x04,0x05,0x06,0x07" in dat): code['weak_iv'].append(jfile_path.replace( java_src, '')) # Inorder to Add rule to Code Analysis, add identifier to c, add rule here and # define identifier description and severity the bottom of this function. #=========================Android API Analysis ============ # API Check if re.findall(r"System.loadLibrary\(|System.load\(", dat): native = True if (re.findall( (r'dalvik.system.DexClassLoader|java.security.ClassLoader|' r'java.net.URLClassLoader|java.security.SecureClassLoader' ), dat)): dynamic = True if (re.findall( 'java.lang.reflect.Method|java.lang.reflect.Field|Class.forName', dat)): reflect = True if re.findall( 'javax.crypto|kalium.crypto|bouncycastle.crypto', dat): crypto = True code['crypto'].append(jfile_path.replace(java_src, '')) if 'utils.AESObfuscator' in dat and 'getObfuscator' in dat: code['obf'].append(jfile_path.replace(java_src, '')) obfus = True if 'getRuntime().exec(' in dat and 'getRuntime(' in dat: code['exec'].append(jfile_path.replace(java_src, '')) if 'ServerSocket' in dat and 'net.ServerSocket' in dat: code['server_socket'].append( jfile_path.replace(java_src, '')) if 'Socket' in dat and 'net.Socket' in dat: code['socket'].append(jfile_path.replace(java_src, '')) if 'DatagramPacket' in dat and 'net.DatagramPacket' in dat: code['datagramp'].append( jfile_path.replace(java_src, '')) if 'DatagramSocket' in dat and 'net.DatagramSocket' in dat: code['datagrams'].append( jfile_path.replace(java_src, '')) if re.findall( 'IRemoteService|IRemoteService.Stub|IBinder|Intent', dat): code['ipc'].append(jfile_path.replace(java_src, '')) if (('sendMultipartTextMessage' in dat or 'sendTextMessage' in dat or 'vnd.android-dir/mms-sms' in dat) and ('telephony.SmsManager' in dat)): code['msg'].append(jfile_path.replace(java_src, '')) if ('addJavascriptInterface' in dat and 'WebView' in dat and 'android.webkit' in dat): code['webview_addjs'].append( jfile_path.replace(java_src, '')) if 'WebView' in dat and 'loadData' in dat and 'android.webkit' in dat: code['webviewget'].append( jfile_path.replace(java_src, '')) if 'WebView' in dat and 'postUrl' in dat and 'android.webkit' in dat: code['webviewpost'].append( jfile_path.replace(java_src, '')) if (('HttpURLConnection' in dat or 'org.apache.http' in dat) and ('openConnection' in dat or 'connect' in dat or 'HttpRequest' in dat)): code['httpcon'].append(jfile_path.replace( java_src, '')) if (('net.URLConnection' in dat) and ('connect' in dat or 'openConnection' in dat or 'openStream' in dat)): code['urlcon'].append(jfile_path.replace(java_src, '')) if (('net.JarURLConnection' in dat) and ('JarURLConnection' in dat or 'jar:' in dat)): code['jurl'].append(jfile_path.replace(java_src, '')) if (('javax.net.ssl.HttpsURLConnection' in dat) and ('HttpsURLConnection' in dat or 'connect' in dat)): code['httpsurl'].append( jfile_path.replace(java_src, '')) if (('net.URL') and ('openConnection' or 'openStream')) in dat: code['nurl'].append(jfile_path.replace(java_src, '')) if (re.findall( ('http.client.HttpClient|net.http.AndroidHttpClient|' 'http.impl.client.AbstractHttpClient'), dat)): code['httpclient'].append( jfile_path.replace(java_src, '')) if 'app.NotificationManager' in dat and 'notify' in dat: code['notify'].append(jfile_path.replace(java_src, '')) if 'telephony.TelephonyManager' in dat and 'getAllCellInfo' in dat: code['cellinfo'].append( jfile_path.replace(java_src, '')) if 'telephony.TelephonyManager' in dat and 'getCellLocation' in dat: code['cellloc'].append(jfile_path.replace( java_src, '')) if 'telephony.TelephonyManager' in dat and 'getSubscriberId' in dat: code['subid'].append(jfile_path.replace(java_src, '')) if 'telephony.TelephonyManager' in dat and 'getDeviceId' in dat: code['devid'].append(jfile_path.replace(java_src, '')) if 'telephony.TelephonyManager' in dat and 'getDeviceSoftwareVersion' in dat: code['softver'].append(jfile_path.replace( java_src, '')) if 'telephony.TelephonyManager' in dat and 'getSimSerialNumber' in dat: code['simserial'].append( jfile_path.replace(java_src, '')) if 'telephony.TelephonyManager' in dat and 'getSimOperator' in dat: code['simop'].append(jfile_path.replace(java_src, '')) if 'telephony.TelephonyManager' in dat and 'getSimOperatorName' in dat: code['opname'].append(jfile_path.replace(java_src, '')) if 'content.ContentResolver' in dat and 'query' in dat: code['contentq'].append( jfile_path.replace(java_src, '')) if 'java.lang.reflect.Method' in dat and 'invoke' in dat: code['refmethod'].append( jfile_path.replace(java_src, '')) if 'getSystemService' in dat: code['gs'].append(jfile_path.replace(java_src, '')) if (('android.util.Base64' in dat) and ('.encodeToString' in dat or '.encode' in dat)): code['bencode'].append(jfile_path.replace( java_src, '')) if 'android.util.Base64' in dat and '.decode' in dat: code['bdecode'].append(jfile_path.replace( java_src, '')) if (('dalvik.system.PathClassLoader' in dat or 'dalvik.system.DexFile' in dat or 'dalvik.system.DexPathList' in dat or 'dalvik.system.DexClassLoader' in dat) and ('loadDex' in dat or 'loadClass' in dat or 'DexClassLoader' in dat or 'loadDexFile' in dat)): code['dex'].append(jfile_path.replace(java_src, '')) if (('java.security.MessageDigest' in dat) and ('MessageDigestSpi' in dat or 'MessageDigest' in dat)): code['mdigest'].append(jfile_path.replace( java_src, '')) if (('android.location' in dat) and (('getLastKnownLocation(') in dat or 'requestLocationUpdates(' in dat or ('getLatitude(') in dat or 'getLongitude(' in dat)): code['gps'].append(jfile_path.replace(java_src, '')) if re.findall( ('OpenFileOutput|getSharedPreferences|SharedPreferences.Editor|' 'getCacheDir|getExternalStorageState|openOrCreateDatabase' ), dat): code['fileio'].append(jfile_path.replace(java_src, '')) if re.findall(r'startActivity\(|startActivityForResult\(', dat): code['inf_act'].append(jfile_path.replace( java_src, '')) if re.findall(r'startService\(|bindService\(', dat): code['inf_ser'].append(jfile_path.replace( java_src, '')) if re.findall( r'sendBroadcast\(|sendOrderedBroadcast\(|sendStickyBroadcast\(', dat): code['inf_bro'].append(jfile_path.replace( java_src, '')) j_file = jfile_path.replace(java_src, '') base_fl = ntpath.basename(j_file) # URLs My Custom regex pattern = re.compile(( ur'((?:https?://|s?ftps?://|file://|javascript:|data:|www\d{0,3}[.])' ur'[\w().=/;,#:@?&~*+!$%\'{}-]+)'), re.UNICODE) urllist = re.findall(pattern, dat.lower()) url_list.extend(urllist) uflag = 0 for url in urllist: if url not in urls: urls.append(url) uflag = 1 if uflag == 1: url_n_file += ( "<tr><td>" + "<br>".join(urls) + "</td><td><a href='../ViewSource/?file=" + escape(j_file) + "&md5=" + md5 + "&type=" + typ + "'>" + escape(base_fl) + "</a></td></tr>") # Email Etraction Regex regex = re.compile(r'[\w.-]+@[\w-]+\.[\w.]+') eflag = 0 for email in regex.findall(dat.lower()): if (email not in emails) and ( not email.startswith('//')): emails.append(email) eflag = 1 if eflag == 1: email_n_file += ( "<tr><td>" + "<br>".join(emails) + "</td><td><a href='../ViewSource/?file=" + escape(j_file) + "&md5=" + md5 + "&type=" + typ + "'>" + escape(base_fl) + "</a></td></tr>") # Domain Extraction and Malware Check print "[INFO] Performing Malware Check on extracted Domains" domains = MalwareCheck(url_list) print "[INFO] Finished Code Analysis, Email and URL Extraction" # API Description api_desc = { 'gps': 'GPS Location', 'crypto': 'Crypto ', 'exec': 'Execute System Command ', 'server_socket': 'TCP Server Socket ', 'socket': 'TCP Socket ', 'datagramp': 'UDP Datagram Packet ', 'datagrams': 'UDP Datagram Socket ', 'ipc': 'Inter Process Communication ', 'msg': 'Send SMS ', 'webview_addjs': 'WebView JavaScript Interface ', 'webview': 'WebView Load HTML/JavaScript ', 'webviewget': 'WebView GET Request ', 'webviewpost': 'WebView POST Request ', 'httpcon': 'HTTP Connection ', 'urlcon': 'URL Connection to file/http/https/ftp/jar ', 'jurl': 'JAR URL Connection ', 'httpsurl': 'HTTPS Connection ', 'nurl': 'URL Connection supports file,http,https,ftp and jar ', 'httpclient': 'HTTP Requests, Connections and Sessions ', 'notify': 'Android Notifications ', 'cellinfo': 'Get Cell Information ', 'cellloc': 'Get Cell Location ', 'subid': 'Get Subscriber ID ', 'devid': 'Get Device ID, IMEI,MEID/ESN etc. ', 'softver': 'Get Software Version, IMEI/SV etc. ', 'simserial': 'Get SIM Serial Number ', 'simop': 'Get SIM Provider Details ', 'opname': 'Get SIM Operator Name ', 'contentq': 'Query Database of SMS, Contacts etc. ', 'refmethod': 'Java Reflection Method Invocation ', 'obf': 'Obfuscation ', 'gs': 'Get System Service ', 'bencode': 'Base64 Encode ', 'bdecode': 'Base64 Decode ', 'dex': 'Load and Manipulate Dex Files ', 'mdigest': 'Message Digest ', 'fileio': 'Local File I/O Operations', 'inf_act': 'Starting Activity', 'inf_ser': 'Starting Service', 'inf_bro': 'Sending Broadcast' } html = '' for api_key in api_desc: if code[api_key]: link = '' # TODO(No idea what hd means here..) h_d = "<tr><td>" + api_desc[api_key] + "</td><td>" for elem in code[api_key]: link += ("<a href='../ViewSource/?file=" + escape(elem) + "&md5=" + md5 + "&type=" + typ + "'>" + escape(ntpath.basename(elem)) + "</a> ") html += h_d + link + "</td></tr>" # Security Code Review Description desc = { 'd_sensitive': ('Files may contain hardcoded sensitive informations like ' 'usernames, passwords, keys etc.'), 'd_ssl': ('Insecure Implementation of SSL. Trusting all the certificates or accepting ' 'self signed certificates is a critical Security Hole. This application is ' 'vulnerable to MITM attacks'), 'd_sqlite': ('App uses SQLite Database and execute raw SQL query. Untrusted user input in ' 'raw SQL queries can cause SQL Injection. Also sensitive information should be ' 'encrypted and written to the database.'), 'd_con_world_readable': ('The file is World Readable. Any App can read from the file'), 'd_con_world_writable': ('The file is World Writable. Any App can write to the file'), 'd_con_world_rw': ('The file is World Readable and Writable. Any App can read/write to the file' ), 'd_con_private': ('App can write to App Directory. Sensitive Information should be encrypted.' ), 'd_extstorage': ('App can read/write to External Storage. Any App can read data written to ' 'External Storage.'), 'd_tmpfile': ('App creates temp file. Sensitive information should never be written into a ' 'temp file.'), 'd_jsenabled': ('Insecure WebView Implementation. Execution of user controlled code in WebView ' 'is a critical Security Hole.'), 'd_webviewdisablessl': ('Insecure WebView Implementation. WebView ignores SSL Certificate errors and ' 'accept any SSL Certificate. This application is vulnerable to MITM attacks' ), 'd_webviewdebug': ('Remote WebView debugging is enabled.'), 'dex_debug': ('DexGuard Debug Detection code to detect wheather an App is debuggable or not ' 'is identified.'), 'dex_debug_con': ('DexGuard Debugger Detection code is identified.'), 'dex_debug_key': ('DecGuard code to detect wheather the App is signed with a debug key or not ' 'is identified.'), 'dex_emulator': ('DexGuard Emulator Detection code is identified.'), 'dex_root': ('DexGuard Root Detection code is identified.'), 'dex_tamper': ('DexGuard App Tamper Detection code is identified.'), 'dex_cert': ('DexGuard Signer Certificate Tamper Detection code is identified.' ), 'd_ssl_pin': (' This App uses an SSL Pinning Library (org.thoughtcrime.ssl.pinning) to ' 'prevent MITM attacks in secure communication channel.'), 'd_root': ('This App may request root (Super User) privileges.'), 'd_rootcheck': ('This App may have root detection capabilities.'), 'd_hcode': ('This App uses Java Hash Code. It\'s a weak hash function and should never be ' 'used in Secure Crypto Implementation.'), 'rand': ('The App uses an insecure Random Number Generator.'), 'log': ('The App logs information. Sensitive information should never be logged.' ), 'd_app_tamper': ('The App may use package signature for tamper detection.'), 'd_prevent_screenshot': ('This App has capabilities to prevent against Screenshots from Recent Task ' 'History/ Now On Tap etc.'), 'd_sql_cipher': ('This App uses SQL Cipher. SQLCipher provides 256-bit AES encryption to sqlite ' 'database files.'), 'sqlc_password': ('This App uses SQL Cipher. But the secret may be hardcoded.'), 'ecb': ('The App uses ECB mode in Cryptographic encryption algorithm. ECB mode is ' 'known to be weak as it results in the same ciphertext for identical blocks ' 'of plaintext.'), 'rsa_no_pad': ('This App uses RSA Crypto without OAEP padding. The purpose of the padding ' 'scheme is to prevent a number of attacks on RSA that only work when the ' 'encryption is performed without padding.'), 'weak_iv': ('The App may use weak IVs like "0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00" or ' '"0x01,0x02,0x03,0x04,0x05,0x06,0x07". Not using a random IV makes the ' 'resulting ciphertext much more predictable and susceptible to a dictionary ' 'attack.'), } dang = '' spn_dang = '<span class="label label-danger">high</span>' spn_info = '<span class="label label-info">info</span>' spn_sec = '<span class="label label-success">secure</span>' spn_warn = '<span class="label label-warning">warning</span>' for k in desc: if code[k]: link = '' if re.findall('d_con_private|log', k): h_d = '<tr><td>' + desc[k] + \ '</td><td>' + spn_info + '</td><td>' elif re.findall( ('d_sql_cipher|d_prevent_screenshot|d_app_tamper|d_rootcheck|dex_cert|' 'dex_tamper|dex_debug|dex_debug_con|dex_debug_key|dex_emulator|dex_root' '|d_ssl_pin'), k): h_d = '<tr><td>' + desc[k] + \ '</td><td>' + spn_sec + '</td><td>' elif re.findall('d_jsenabled', k): h_d = '<tr><td>' + desc[k] + \ '</td><td>' + spn_warn + '</td><td>' else: h_d = '<tr><td>' + desc[k] + \ '</td><td>' + spn_dang + '</td><td>' for elem in code[k]: link += ("<a href='../ViewSource/?file=" + escape(elem) + "&md5=" + md5 + "&type=" + typ + "'>" + escape(ntpath.basename(elem)) + "</a> ") dang += h_d + link + "</td></tr>" code_an_dic = { 'api': html, 'dang': dang, 'urls': url_n_file, 'domains': domains, 'emails': email_n_file, 'crypto': crypto, 'obfus': obfus, 'reflect': reflect, 'dynamic': dynamic, 'native': native } return code_an_dic except: PrintException("[ERROR] Performing Code Analysis")
def iOS_Source_Analysis(SRC, MD5): try: print "[INFO] Starting iOS Source Code and PLIST Analysis" ALLURLSLST = list() DOMAINS = dict() APP = '' InfoP = '' BIN_NAME = '' BIN = '' ID = '' VER = '' SDK = '' PLTFM = '' MIN = '' XML = '' for f in os.listdir(SRC): if f.endswith(".xcodeproj"): APP = f.replace(".xcodeproj", "") PlistFile = APP + "-Info.plist" for dirName, subDir, files in os.walk(SRC): for jfile in files: if PlistFile in jfile: InfoP = os.path.join(SRC, dirName, jfile) break if isFileExists(InfoP): with io.open(InfoP, mode='r', encoding="utf8", errors="ignore") as f: XML = f.read() if XML: p = plistlib.readPlistFromString(XML) BIN_NAME = p["CFBundleDisplayName"] BIN = p["CFBundleExecutable"] ID = p["CFBundleIdentifier"] VER = p["CFBundleVersion"] SDK = '' # p["DTSDKName"] PLTFM = '' # p["DTPlatformVersion"] MIN = '' # p["MinimumOSVersion"] # Code Analysis EmailnFile = '' URLnFile = '' c = { key: [] for key in ('i_buf', 'webv', 'i_log', 'net', 'i_sqlite', 'fileio', 'ssl_bypass', 'ssl_uiwebview', 'path_traversal') } for dirName, subDir, files in os.walk(SRC): for jfile in files: if jfile.endswith(".m"): jfile_path = os.path.join(SRC, dirName, jfile) if "+" in jfile: p2 = os.path.join(SRC, dirName, jfile.replace("+", "x")) shutil.move(jfile_path, p2) jfile_path = p2 repath = dirName.replace(SRC, '') dat = '' with io.open(jfile_path, mode='r', encoding="utf8", errors="ignore") as f: dat = f.read() URLS = [] EMAILS = [] # API if (re.findall("NSURL|CFStream|NSStream", dat)): c['net'].append(jfile_path.replace(SRC, '')) if (re.findall( "Keychain|kSecAttrAccessibleWhenUnlocked|kSecAttrAccessibleAfterFirstUnlock|SecItemAdd|SecItemUpdate|NSDataWritingFileProtectionComplete", dat)): c['fileio'].append(jfile_path.replace(SRC, '')) if (re.findall("WebView|UIWebView", dat)): c['webv'].append(jfile_path.replace(SRC, '')) # SECURITY ANALYSIS if (re.findall( "strcpy|memcpy|strcat|strncat|strncpy|sprintf|vsprintf|gets", dat)): c['i_buf'].append(jfile_path.replace(SRC, '')) if (re.findall("NSLog", dat)): c['i_log'].append(jfile_path.replace(SRC, '')) if (re.findall("sqlite3_exec", dat)): c['i_sqlite'].append(jfile_path.replace(SRC, '')) if re.findall( 'canAuthenticateAgainstProtectionSpace|continueWithoutCredentialForAuthenticationChallenge|kCFStreamSSLAllowsExpiredCertificates|kCFStreamSSLAllowsAnyRoot|kCFStreamSSLAllowsExpiredRoots|allowInvalidCertificates\s*=\s*(YES|yes)', dat): c['ssl_bypass'].append(jfile_path.replace(SRC, '')) if re.findall( 'setAllowsAnyHTTPSCertificate:\s*YES|allowsAnyHTTPSCertificateForHost|loadingUnvalidatedHTTPSPage\s*=\s*(YES|yes)', dat): c['ssl_uiwebview'].append(jfile_path.replace(SRC, '')) if "NSTemporaryDirectory()," in dat: c['path_traversal'].append(jfile_path.replace(SRC, '')) fl = jfile_path.replace(SRC, '') base_fl = ntpath.basename(fl) # URLs My Custom regex p = re.compile( ur'((?:https?://|s?ftps?://|file://|javascript:|data:|www\d{0,3}[.])[\w().=/;,#:@?&~*+!$%\'{}-]+)', re.UNICODE) urllist = re.findall(p, dat.lower()) ALLURLSLST.extend(urllist) uflag = 0 for url in urllist: if url not in URLS: URLS.append(url) uflag = 1 if uflag == 1: URLnFile += "<tr><td>" + "<br>".join( URLS ) + "</td><td><a href='../ViewFile/?file=" + escape( fl ) + "&type=m&mode=ios&md5=" + MD5 + "'>" + escape( base_fl) + "</a></td></tr>" # Email Etraction Regex regex = re.compile("[\w.-]+@[\w-]+\.[\w.]+") eflag = 0 for email in regex.findall(dat.lower()): if ((email not in EMAILS) and (not email.startswith('//'))): EMAILS.append(email) eflag = 1 if eflag == 1: EmailnFile += "<tr><td>" + "<br>".join( EMAILS ) + "</td><td><a href='../ViewFile/?file=" + escape( fl ) + "&type=m&mode=ios&md5=" + MD5 + "'>" + escape( base_fl) + "</a></td></tr>" # Domain Extraction and Malware Check print "[INFO] Performing Malware Check on extracted Domains" DOMAINS = MalwareCheck(ALLURLSLST) print "[INFO] Finished Code Analysis, Email and URL Extraction" dc = { 'webv': 'WebView Component', 'net': 'Network Calls', 'fileio': 'Local File I/O Operations.', } html = '' for ky in dc: if c[ky]: link = '' hd = "<tr><td>" + dc[ky] + "</td><td>" for l in c[ky]: link += "<a href='../ViewFile/?file=" + \ escape(l) + "&type=m&mode=ios&md5=" + MD5 + \ "'>" + escape(ntpath.basename(l)) + "</a> " html += hd + link + "</td></tr>" dg = { 'i_buf': 'The App may contain banned API(s). These API(s) are insecure and must not be used.', 'i_log': 'The App logs information. Sensitive information should never be logged.', 'i_sqlite': 'App uses SQLite Database. Sensitive Information should be encrypted.', 'ssl_bypass': '******', 'ssl_uiwebview': 'UIWebView in App ignore SSL errors and accept any SSL Certificate. App is vulnerable to MITM attacks.', 'path_traversal': 'Untrusted user input to "NSTemporaryDirectory()"" will result in path traversal vulnerability.', } dang = '' spn_dang = '<span class="label label-danger">high</span>' spn_info = '<span class="label label-info">info</span>' spn_sec = '<span class="label label-success">secure</span>' spn_warn = '<span class="label label-warning">warning</span>' for k in dg: if c[k]: link = '' if (re.findall('i_sqlite', k)): hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_info + '</td><td>' elif (re.findall('path_traversal', k)): hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_warn + '</td><td>' else: hd = '<tr><td>' + dg[k] + \ '</td><td>' + spn_dang + '</td><td>' for ll in c[k]: link += "<a href='../ViewFile/?file=" + \ escape(ll) + "&type=m&mode=ios&md5=" + MD5 + \ "'>" + escape(ntpath.basename(ll)) + "</a> " dang += hd + link + "</td></tr>" return html, dang, URLnFile, DOMAINS, EmailnFile, XML, BIN_NAME, ID, VER, SDK, PLTFM, MIN except: PrintException("[ERROR] iOS Source Code Analysis")
def ios_source_analysis(src, md5_hash): """iOS Objective-C Code Analysis""" try: code_analysis_dict = {} print "[INFO] Starting iOS Source Code and PLIST Analysis" all_urls_list = [] urls = [] emails = [] code_analysis_dict["html"] = '' code_analysis_dict["code_anal"] = '' code_analysis_dict["urlnfile"] = '' code_analysis_dict["emailnfile"] = '' code_analysis_dict["domains"] = {} findings = {key: [] for key in ('i_buf', 'webv', 'i_log', 'net', 'i_sqlite', 'fileio', 'ssl_bypass', 'ssl_uiwebview', 'path_traversal')} for dirname, _, files in os.walk(src): for jfile in files: if jfile.endswith(".m"): jfile_path = os.path.join(src, dirname, jfile) if "+" in jfile: new_path = os.path.join( src, dirname, jfile.replace("+", "x")) shutil.move(jfile_path, new_path) jfile_path = new_path dat = '' with io.open(jfile_path, mode='r', encoding="utf8", errors="ignore") as flip: dat = flip.read() # API if re.findall(r"NSURL|CFStream|NSStream", dat): findings['net'].append(jfile_path.replace(src, '')) if (re.findall(r"Keychain|kSecAttrAccessibleWhenUnlocked|" + r"kSecAttrAccessibleAfterFirstUnlock|SecItemAdd|" + r"SecItemUpdate|NSDataWritingFileProtectionComplete", dat) ): findings['fileio'].append(jfile_path.replace(src, '')) if re.findall(r"WebView|UIWebView", dat): findings['webv'].append(jfile_path.replace(src, '')) # SECURITY ANALYSIS if (re.findall(r"strcpy|memcpy|strcat|strncat|strncpy|sprintf|" + r"vsprintf|gets", dat) ): findings['i_buf'].append(jfile_path.replace(src, '')) if re.findall(r"NSLog", dat): findings['i_log'].append(jfile_path.replace(src, '')) if re.findall(r"sqlite3_exec", dat): findings['i_sqlite'].append( jfile_path.replace(src, '')) if re.findall(r'canAuthenticateAgainstProtectionSpace|' + r'continueWithoutCredentialForAuthenticationChallenge|' + r'kCFStreamSSLAllowsExpiredCertificates|' + r'kCFStreamSSLAllowsAnyRoot|' + r'kCFStreamSSLAllowsExpiredRoots|' + r'allowInvalidCertificates\s*=\s*(YES|yes)', dat): findings['ssl_bypass'].append( jfile_path.replace(src, '')) if re.findall(r'setAllowsAnyHTTPSCertificate:\s*YES|'+ r'allowsAnyHTTPSCertificateForHost|'+ r'loadingUnvalidatedHTTPSPage\s*=\s*(YES|yes)', dat): findings['ssl_uiwebview'].append( jfile_path.replace(src, '')) if "NSTemporaryDirectory()," in dat: findings['path_traversal'].append( jfile_path.replace(src, '')) relative_fpath = jfile_path.replace(src, '') base_fl = ntpath.basename(relative_fpath) # URLs My Custom regex url_regex = re.compile(ur'((?:https?://|s?ftps?://|file://|javascript:|'+ ur'data:|www\d{0,3}[.])[\w().=/;,#:'+ ur'@?&~*+!$%\'{}-]+)', re.UNICODE) urllist = re.findall(url_regex, dat.lower()) all_urls_list.extend(urllist) uflag = 0 for url in urllist: if url not in urls: urls.append(url) uflag = 1 if uflag == 1: code_analysis_dict["urlnfile"] += "<tr><td>" + "<br>".join(urls) + \ "</td><td><a href='../ViewFile/?file=" + escape(relative_fpath) + \ "&type=m&mode=ios&md5=" + md5_hash + "'>" + escape(base_fl) + \ "</a></td></tr>" # Email Etraction Regex regex = re.compile(r"[\w.-]+@[\w-]+\.[\w.]+") eflag = 0 for email in regex.findall(dat.lower()): if (email not in emails) and (not email.startswith('//')): emails.append(email) eflag = 1 if eflag == 1: code_analysis_dict["emailnfile"] += "<tr><td>" + "<br>".join(emails) + \ "</td><td><a href='../ViewFile/?file=" +\ escape(relative_fpath) + "&type=m&mode=ios&md5=" + md5_hash +\ "'>" + escape(base_fl) + "</a></td></tr>" # Domain Extraction and Malware Check print "[INFO] Performing Malware Check on extracted Domains" code_analysis_dict["domains"] = MalwareCheck(all_urls_list) print "[INFO] Finished Code Analysis, Email and URL Extraction" api_mappings = {'webv': 'WebView Component', 'net': 'Network Calls', 'fileio': 'Local File I/O Operations.', } html = '' for key_tag in api_mappings: if findings[key_tag]: link = '' item = "<tr><td>" + api_mappings[key_tag] + "</td><td>" for ktag in findings[key_tag]: link += "<a href='../ViewFile/?file=" + \ escape(ktag) + "&type=m&mode=ios&md5=" + md5_hash + \ "'>" + escape(ntpath.basename(ktag)) + "</a> " html += item + link + "</td></tr>" sec_desc_mappings = {'i_buf': 'The App may contain banned API(s). These API(s)' + ' are insecure and must not be used.', 'i_log': 'The App logs information. Sensitive information ' + 'should never be logged.', 'i_sqlite': 'App uses SQLite Database. ' + 'Sensitive Information should be encrypted.', 'ssl_bypass': '******' + 'SSL certificates. App is vulnerable to MITM attacks.', 'ssl_uiwebview': 'UIWebView in App ignore SSL errors and accept' + ' any SSL Certificate. App is vulnerable '+ 'to MITM attacks.', 'path_traversal': 'Untrusted user input to "NSTemporaryDirectory()"' + ' will result in path traversal vulnerability.', } spn_dang = '<span class="label label-danger">high</span>' spn_info = '<span class="label label-info">info</span>' spn_sec = '<span class="label label-success">secure</span>' spn_warn = '<span class="label label-warning">warning</span>' for k in sec_desc_mappings: if findings[k]: link = '' if re.findall('i_sqlite', k): item = '<tr><td>' + sec_desc_mappings[k] + \ '</td><td>' + spn_info + '</td><td>' elif re.findall('path_traversal', k): item = '<tr><td>' + sec_desc_mappings[k] + \ '</td><td>' + spn_warn + '</td><td>' else: item = '<tr><td>' + sec_desc_mappings[k] + \ '</td><td>' + spn_dang + '</td><td>' for filname in findings[k]: link += "<a href='../ViewFile/?file=" + \ escape(filname) + "&type=m&mode=ios&md5=" + md5_hash + \ "'>" + escape(ntpath.basename(filname)) + "</a> " code_analysis_dict["code_anal"] += item + link + "</td></tr>" code_analysis_dict["html"] = html return code_analysis_dict except: PrintException("[ERROR] iOS Source Code Analysis")