a Static and Extensible Black-box Application Security Testing tool for iOS and Android applications
SEBASTiAn: a Static and Extensible Black-box Application Security Testing tool for iOS and Android applications is a platform agnostic and easily extensible modular tool for performing static security assessments of mobile applications.
It can analyze Android (apk
) and iOS (ipa
) applications by providing a unified JSON report containing details about the identified vulnerabilities, remediation suggestions, web resources for further insights, and the location where the vulnerable code/configuration was found.
More details about SEBASTiAn can be found in the paper SEBASTiAn: a Static and Extensible Black-box Application Security Testing tool for iOS and Android applications:
@article{PAGANO2023101448,
title = {SEBASTiAn: A static and extensible black-box application security testing tool for iOS and Android applications},
journal = {SoftwareX},
volume = {23},
pages = {101448},
year = {2023},
issn = {2352-7110},
doi = {https://doi.org/10.1016/j.softx.2023.101448},
url = {https://www.sciencedirect.com/science/article/pii/S2352711023001449},
author = {Francesco Pagano and Andrea Romdhana and Davide Caputo and Luca Verderame and Alessio Merlo}
}
The only requirement of this project is a working Python 3
installation (along with
its package manager pip
). Depending on your operating system, you might need a
different version of Python, as specified in the table below:
Python version | Ubuntu | Windows | MacOS |
---|---|---|---|
2.x | |||
3.6 or lower | ✖️ | ✖️ | ✖️ |
3.7 | ✔️ | ✔️ | |
3.8 | ✔️ | ✔️ | ✔️ |
3.9 or greater | ✔️ | ✔️ | ✔️ |
lief
package manually, since there is no stable prebuilt
wheels are currently available.
Run the following commands in the main directory of the project (SEBEASTiAn/
) to install the needed dependencies:
# Make sure to run the commands in SEBASTiAn/ directory.
# Using a virtual environment is highly recommended, e.g., virtualenv.
# If not using virtualenv (https://virtualenv.pypa.io/), skip the next two lines.
virtualenv -p python3 venv
source venv/bin/activate
# Install SEBASTiAn's requirements.
python3 -m pip install -r src/requirements.txt
After the requirements are installed, make a quick test to check that everything works correctly:
$ cd src/
$ # The following command has to be always executed from SEBASTiAn/src/ directory
$ # or by adding SEBASTiAn/src/ directory to PYTHONPATH environment variable.
$ python3 -m cli --help
usage: python3 -m cli [-h] [-l {en,it}] [-i] [--fail-fast] [-t TIMEOUT]
...
SEBASTiAn is ready to be used; see the help message for more information.
From now on, SEBASTiAn will be considered as an executable available as SEBASTiAn
, so you need to adapt the commands according to how you install the tool:
-
Docker image: a local directory containing the application to analyze has to be mounted to
/workdir
in the container (e.g., the current directory"${PWD}"
), so the command:SEBASTiAn [params...]
becomes:
docker run --rm -it -u $(id -u):$(id -g) -v "${PWD}":"/workdir" SEBASTiAn [params...]
Alternatively, you can directly run SEBASTiAn from its official docker image, available on Docker Hub:
docker run --rm -it -u $(id -u):$(id -g) -v "${PWD}":"/workdir" talossec/sebastian:latest [params...]
-
From source: every instruction has to be executed from the
SEBASTiAn/src/
directory (or by addingSEBASTiAn/src/
directory toPYTHONPATH
environment variable) and the command:SEBASTiAn [params...]
becomes:
python3 -m SEBASTiAn.cli [params...]
Let's start by looking at the help message:
$ SEBASTiAn --help
SEBASTiAn [-h] [-l {en,it}] [-i] [--fail-fast] [-t TIMEOUT] [--keep-files] <FILE>
There is only one mandatory parameter: <FILE>
, the path (relative or absolute) to
the apk file to analyze.
-
-l {en,it}, --language {en,it}
, The language used for the vulnerabilities. Allowed values are: en, it. -
-i, --ignore-libs
, Ignore known third-party libraries during the vulnerability analysis (only for Android). -
--fail-fast
, Make the entire analysis fail on the first failed vulnerability check. -
-t TIMEOUT, --timeout TIMEOUT
Make the analysis fail if it takes longer than the timeout (in seconds). By default, a timeout of 1200 seconds (20 minutes) is used. -
--keep-files
, Keep intermediate files generated during the analysis (only for iOS).
The vulnerabilities checked in SEBASTiAn are divided into two macro-categories: Android Vulnerabilities and iOS Vulnerabilities.
This Plugin checks if the app gets the device ID (IMEI) to identify the specific device. This approach has three significant drawbacks: I) it is unusable on non-phone devices, II) it persists across device data wipes, and III) it needs a special privilege to be executed.
This app contains code for Internet access but does not have the Internet permission in AndroidManifest.xml. This may be caused by an app misconfiguration or a malicious app that tries to access the network interface without proper permission.
The Plugin checks for the presence of the permission Access Mock Location in the Android Manifest. This permission only works in emulated environments, and it is deprecated.
This Plugin verifies whether the app validates the Common Name in the SSL certificate. This critical vulnerability allows attackers to implement MitM attacks with their valid certificates. It is a violation of OWASP Mobile TOP 10 Security Risks.
This Plugin checks whether the ADB Backup is enabled for the app. If this is the case, attackers with physical access to the device can copy all of the sensitive data of the app included in the backup.
This Plugin checks the presence of base64 encoded strings, notifying the developer.
A component that uses run-time permission checking via checkCallingOrSelfPermission for access control grants permission to all components if it grants permission even once to a component that is in the same app as that itself. Suppose checkCallingOrSelfPermission is used to protect a component that performs a sensitive operation. In that case, a component in a malicious app can escalate its privilege and access the component.
A component that uses checkPermission to verify access control at run-time must obtain the PID and UID of the calling component before calling checkPermission. The Binder API provides methods getCallingPID() and getCallingUID() to determine the calling component's PID and UID, respectively. However, these methods do not always return the calling PID and UID. When an application is started, the system creates a thread of execution called main. The system does not create a separate thread for each component instance. All components that run in the same process are instantiated in the main thread, and system calls to each component are dispatched from that thread. If Binder.getCallingPID() and Binder.getCallingPID() are called from the main thread, they do not return the PID and UID of the process in which the calling component is running. Instead, they return the PID and UID of the process in which the protected component is running. In such a scenario, if the process in which the protected component is running is granted permission, checkPermission will always be true. A malicious component can exploit this vulnerability to access the protected component.
This Plugin checks if the app properly configures the Network Request Whitelist options in its Cordova config.xml file. The Plugin also checks if the app configures the Network Request Whitelist options to accept plain HTTP URLs. The Network Request Whitelist options control which network requests are allowed.
This Plugin checks if the app configures the Intent Whitelist options to accept HTTPS URLs from any domain. The Intent Whitelist options control which URLs the app is allowed to ask the system to open. This Plugin also checks if the app configures the Navigation Whitelist options to accept HTTPS connections from any domain.
This Plugin checks whether the Navigation Whitelist accepts HTTPS connections from any domain. Moreover, the Plugin checks if the app does not properly configure the Navigation Whitelist options in its Cordova config.xml file.
This Plugin verifies if the initialization vector (IV) is not random. Consequently, encrypting a particular piece of information with a symmetric key will yield the same result every time encryption is applied to that information with the same symmetric key.
This Plugin checks if the app stores encryption keys in the source code. The apps that present this vulnerability are vulnerable to forgery attacks and information leaks.
This Plugin checks if the app uses a constant salt.
This Plugin checks if the app uses Block Cipher algorithms in ECB mode for encrypting sensitive information. Block Cipher algorithms in ECB mode are known to be vulnerable.
This Plugin checks if an app stores encryption keys without any protection parameter in a Keystore accessible by other apps. Apps that present this behavior are vulnerable to information exposure. Keystore provides
setEntry
API to store a key entry. Along with the alias and the key,setEntry
API takes an instance of ProtectionParameter as an argument to protect the entry contents.
The Plugin checks if the app passes an iteration count smaller than 1000. If so, the
PBEParameterSpec
and thePBEKeySpec
constructors are insecure.
The Plugin checks if the DEBUG mode is on inside the app. Debug mode is discouraged in production since malicious users can debug the app and sniff verbose error information through Logcat.
This Plugin checks if the app uses
HttpHost
, but its default scheme isHTTP
.
This Plugin checks if the app contains code that dynamically loads classes from
.jar
files.
This Plugin searches for a user-defined empty
permissionGroup
in the Android Manifest. Setting thepermissionGroup
attribute to an empty value will invalidate the permission definition, and no other application can use the permission.
A component that uses run-time permission checking via enforceCallingOrSelfPermission for access control grants permission to all components if it grants permission even once to a component that is in the same app as that itself. If enforceCallingOrSelfPermission is used to protect a component that performs a sensitive operation, then a component in a malicious app can escalate its privilege and access the component.
A component that uses enforcePermission to verify access control at run-time will need to obtain the PID and UID of the calling component before calling enforcePermission. The Binder API provides methods getCallingPID() and getCallingUID() to determine the calling component's PID and UID, respectively. However, these methods do not always return the calling PID and UID. When an application is started, the system creates a thread of execution for the application called main. The system does not create a separate thread for each component instance. All components that run in the same process are instantiated in the main thread, and system calls to each component are dispatched from that thread. If Binder.getCallingPID() and Binder.getCallingPID() are called from the main thread, they do not return the PID and UID of the process in which the calling component is running. Instead, they return the PID and UID of the process in which the protected component runs. In such a scenario, if the process in which the protected component is running is granted permission, then
enforcePermission
will not throw a SecurityException. A malicious component can exploit this vulnerability to access the protected component.
This Plugin detects exported components for receiving external applications' actions. These components can be initialized by other apps and used maliciously. The Plugin also checks exported ContentProvider, allowing any other app on the device to access it (
AndroidManifest.xml
). Found exported components lackingandroid:
prefix in an exported attribute.
This plugin checks if the app uses the external storage access API. Any app in the system can access external storage; thus, its use for security-critical files is discouraged.
Potentially unsafe uses of parameters associated with intents have been found.
This Plugin identifies implicit intents within the app that can start services. Using an implicit intent to start a service is a security hazard because of the uncertainty of what service will respond to the intent.
This Plugin checks whether the app contains URLs not under SSL. This security hazard allows the interception of all the information exchanged with those URLs.
The Plugin checks whether the app allows self-defined HOSTNAME VERIFIER to accept all Common Names. This critical vulnerability allows attackers to make MitM attacks with their valid certificates. It is a violation of OWASP Mobile TOP 10 Security Risks.
This Plugin checks the usage of
SSLCertificateSocketFactory.createSocket()
. The created socket can be vulnerable to Man-in-the-Middle (MitM) attacks.
This Plugin checks whether the app contains code that relies on instances of socket factory with all SSL security checks disabled, using an optional handshake timeout and SSL session cache. Those sockets are vulnerable to MitM attacks.
The app contains misconfiguration in the intent filter of at least one component of the
AndroidManifest.xml
. The plugin also checks if the app does not include a Content Security Policy (CSP).
This Plugin inspects whether the app validates the SSL Certificates. It allows self-signed, expired, or mismatched CN certificates for SSL connection. This critical vulnerability allows attackers to make MitM attacks.
This Plugin checks whether a password protects the Keystore. This security hazard allows malicious users with physical access to the Keystore file to access the keys and certificates.
The Plugin checks if the app is obfuscated and eventually reports its obfuscation rate.
This Plugin checks whether the protection level of the app's classes is dangerous, allowing any other app to access this permission (
AndroidManifest.xml
).
This Plugin checks whether at least one class declared in the Android Manifest of the app is protected with custom permissions, defined with a normal or default permission level. This allows malicious apps to register and receive messages for this app.
This Plugin validates if the app is using the critical function
Runtime.getRuntime().exec()
.
This Plugin verifies whether the app requests root permissions through the command
Runtime.getRuntime().exec("su")
or the commandRuntime.getRuntime().exec("sudo")
This Plugin verifies if the app can send SMS messages (
sendDataMessage
,sendMultipartTextMessage
, orsendTextMessage
) that could be a cost for the user if maliciously exploited.
The Plugin checks if the app uses the
sharedUserId
attribute. Suppose this attribute is set to the same value for two or more applications. In that case, they will all share the same ID — provided the same certificate also signs them.
This Plugin checks for the presence of SQLite's
execSQL()
method. TheexecSQL()
method could create a SQL query based on the user input content, potentially vulnerable to SQL injection attacks.
This Plugin checks if the app uses necessary permissions to manage filesystems and packages. The System Permission should be confined only to device manufacturers or Google system apps. If not, it may be a malicious app.
The Plugin checks for the presence of the
setAllowFileAccess(true)
method in a WebView.
The plugin checks if an Android app can display web pages by loading HTML/JavaScript files in a WebView.
The Plugin checks if the app loads resources (e.g., JavaScript) using a WebView, and control the resources being loaded on a webpage via the
shouldInterceptRequest
method in WebViewClient.
This Plugin checks if the app uses
setJavaScriptEnabled(true)
in WebView. Enabling JavaScript exposes to malicious code injection that would be executed with the same permissions (XSS attacks).
This Plugin verifies the presence of the
addJavascriptInterface
method within the app code.
This Plugin checks whether the Android app shows web content in a WebView, and controls navigation across webpages via the
shouldOverrideUrlLoading
method in WebViewClient.
This Plugin checks whether the app allows file access in World Readable/Writable mode. This functionality is deprecated in API Level 17 and removed since API Level 24.
The Plugin checks if the app allows the HTTP protocol.
The Plugin checks if the binary is compiled without the 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.
The Plugin checks if the binary does not have a code signature.
The Plugin checks if the binary is not encrypted.
The Plugin checks if the binary uses insecure API(s).
The Plugin checks if the app adds exceptions for possible insecure connections in
Info.plist
file.
The Plugin checks if the binary uses some insecure random API(s).
The Plugin checks if the app sets the minimum value of TLS version TLSv1.0 or TLSv1.1, which are unsafe.
The Plugin checks if the Mach-O binary uses logging function(s).
The Plugin checks if the binary may use
malloc
function.
The Plugin checks if the app turns off the server's requirement to support Perfect Forward Secrecy (PFS) through Elliptic Curve Diffie-Hellman Ephemeral (ECDHE).
The Plugin checks if the binary does not have the NX bit set. NX bit offers protection against exploitation of memory-corruption vulnerabilities by marking the memory page as non-executable. However, iOS never allows an app to execute from writeable memory.
The Plugin checks if the binary is built without the Position Independent Code flag.
The Plugin checks if the Mach-O binary file has no restricted segment that prevents dynamic loading of dylib for arbitrary code injection.
The Plugin checks if the binary has Runpath Search Path (@rpath) set. Sometimes, an attacker can abuse this feature to run the arbitrary executable for code execution and privilege escalation.
The Plugin checks if the binary does not have a stack canary value added. Stack canaries are used to detect and prevent exploits from overwriting return addresses.
The Plugin checks if the app symbols are available.
The Plugin checks if the binary uses some weak crypto API(s).
The Plugin checks if the binary uses some weak hashing API(s).
An application can load a saved HTML web page as a string using loadDataWithBaseUrl() with file scheme baseURL. Since WebView has permission to access all of the app's resources, JavaScript code executing in the context of WebView will also have the same permissions. If the saved web page sources JavaScript code from a malicious server, these permissions can be abused.
Questions, bug reports, and pull requests are welcome on GitHub at https://github.com/talos-security/SEBASTiAn.
This tool is available under a dual license: a commercial one required for closed-source or commercial projects and an AGPL license for open-source projects.
Depending on your needs, you must choose one of them and follow its policies. The policies and agreements for each license type are detailed in the LICENSE.COMMERCIAL and LICENSE files.
This software was developed for research purposes in collaboration with the Computer Security Lab (CSecLab).
- Davide Caputo - Cyber Security Engineer @ Talos
- Gabriel Claudiu Georgiu - Senior Developer @ iGenius
- Francesco Pagano - Research Assistant & Developer
- Andrea Romdhana - Cyber Security Engineer @ Talos
- Luca Verderame - Assistant Professor @ Unige & CEO @ Talos