Introduction
The number of Android applications has been growing rapidly in recent years. In 2022, there were over 3.55 million Android apps available in the Google Play Store, and this number is expected to continue to grow in the years to come. The expansion of the Android app market is being driven by a number of factors, including the increasing popularity of smartphones, the growing demand for mobile apps, and the ease of developing and publishing Android apps. At the same time, the number of Android appdownloads is also growing rapidly. In 2022, there were over 255 billion Android app downloads worldwide.
For this reason, introducing automatic security controls during Mobile Application Penetration Testing (MAPT) activity and the CI phase is necessary to ensure the security of Android apps by scanning for vulnerabilities before merging into the main repository.
Decompiling Android Packages
The compilation of Android applications is a multi-step process that involves different bytecodes, compilers, and execution engines. Generally speaking, a common compilation flow is divided into three phases:
- Precompilation: The Java source code(".java") is converted into Java bytecode(".class").
- Postcompilation: The Java bytecode is converted into Dalvik bytecode(".dex").
- Release: The ".dex" and resource files are packed, signed and compressed into the Android App Package (APK)
Finally, the Dalvik bytecode is executed by the Android runtime (ART) environment.
Generally, the target of a Mobile Application Penetration Testing (MAPT) activity is in the form of an APK file. The decompilation of the both aforementioned bytecodes is possible and can be performed through the use of tools such as Jadx.
jadx -d ./out-dir target.apk
OWASP MAS
- OWASP MASVS: This resource outlines requirements for software architects and developers who aim to create secure mobile applications. It establishes an industry standard that can be used as a benchmark in mobile app security assessments. Additionally, it clarifies the role of software protection mechanisms in mobile security and offers requirements to verify their effectiveness.
- OWASP MASTG: This comprehensive manual covers the processes, techniques, and tools used during mobile application security analysis. It also includes an exhaustive set of test cases for verifying the requirements outlined in the OWASP Mobile Application Security Verification Standard (MASVS). This serves as a foundational basis for conducting thorough and consistent security tests.
- OWASP MAS Checklist: This checklist aligns with the tests described in the MASTG and provides an output template for mobile security testing.
https://mas.owasp.org/ |
Semgrep
Semgrep is a Static Application Security Testing (SAST) tool that performs intra-file analysis, allowing you to define code patterns for detecting misconfigurations or security issues by analyzing one file at a time in isolation. Some advantages of using Semgrep include:
- It does not require that the source code is uploaded to an external cloud.
- It does not require that the target source code is buildable and have all dependencies. It can work only with a single source file.
- It is exceptionally fast.
- It allows you to write your custom patterns very easily.
Key Insights on Semgrep
python3 -m pip install semgrep
- Rules collection: A collection is composed by ".yaml" files, alternatively referred to as "rules". A rule includes a series of patterns designed to identify or exclude specific elements within the target source code.
- Target source code: This denotes the source code subject to analysis. It may also encompass partial code or code with certain dependencies omitted.
- taint: It enables the data-flow analysis feature allowing to specify sources and sinks.
- join: It allows to use multiple rules on more than one file and to join the results.
- extract: It allows work with source file that contains different programming languages.
semgrep -c ./myrules ./mytarget
The Project:
Semgrep Rules for Android Application Security
The proposal
- Stefano Di Paola (Twitter: @WisecWisec)
- Riccardo Cardelli (Github: @gand3lf)
- Andrea Agnello (Github: @AndreNoli)
- Christian Cotignola (Twitter: @b4dsheep)
- Federico Dotta (Twitter: @apps3c)
- Giacomo Zorzin (Mastodon: @gellge)
- Giovanni Fazi (Github: @giovifazi)
- Martino Lessio (Twitter: @Martinolessio)
- Maurizio Siddu (Github: @akabe1)
- Michele Di Bonaventura (Twitter: @cyberaz0r)
- Michele Tumolo (Twitter: @0s0urce)
- Riccardo Granata (Github @riccardogranata)
# Download the target APK and the rules of the current project
$ git clone https://github.com/mindedsecurity/semgrep-rules-android-security
# Extract and decompile the source code from the target APK file
$ jadx -d target_src target.apk
# To use the .semgrepignore file launch the scan from the project folder
$ cd semgrep-rules-android-security/
# Run Semgrep with the new security rules
$ semgrep -c ./rules/ ../target_src/
Some Implemented Rules
MSTG-STORAGE-3
- The methods and classes that are delegated to perform logging operations.
- A regex that can be used to identify potentially sensitive data and attributes names.
- Log.v | Log.i | Log.w | Log.e | Log.wtf
- System.out.print | System.err.print | System.out.println | System.err.println
- Logger.log | Logger.info | Logger.logp | Logger.logrb | Logger.severe | Logger.warning
message: The application writes sensitive data in application logs.
patterns:
- pattern-either:
- pattern: Log.v(...);
- pattern: Log.i(...);
- pattern: Log.w(...);
- pattern: Log.e(...);
- pattern: Log.wtf(...);
- pattern: System.$X.print(...);
- pattern: System.$X.println(...);
- pattern: (BufferedWriter $X).write(...);
- pattern: (Logger $X).log(...);
- pattern: (Logger $X).info(...);
- pattern: (Logger $X).logp(...);
- pattern: (Logger $X).logrb(...);
- pattern: (Logger $X).severe(...);
- pattern: (Logger $X).warning(...);
- pattern-regex: .*(?i)(key|secret|password|pwd|...|bearer|otp|crypt|auth(?-i)|IV).*
patterns:
- pattern-either:
- pattern: A
- pattern: B
- pattern: C
MSTG-ARCH-9
- Check for updates ("getAppUpdateInfo(...)").
- Request the update ("startUpdateFlowForResult(...)").
- Check if the update is completed successfully.
<!-- AndroidManifest.xml -->
<activity android:name="com.myexample.test.SplashScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
rules:
-id: join-rule-id
mode: join
join:
rules:
- id: rule1
languages: # . . .
pattern: # . . .
- id: rule2
languages: # . . .
pattern: # . . .
on:
- 'rule1.$A == rule2.$B' # 1
- 'rule1.$X > rule2.$Y' # 2
- "# 1": the metavariable $A is equal to the metavariable $B
- "# 2": the metavariable $X contains the metavariable $Y
mode: join
join:
rules:
- id: activity-without-update
languages:
- java
patterns: # 1
- pattern: |
public class $CLASSNAME extends $ACTIVITY{
public void onCreate(...){...}
}
- pattern-not: |
public class $CLASSNAME extends $ACTIVITY{
$X(...){
...
(AppUpdateManager $Y).startUpdateFlowForResult(...);
...
}
}
- focus-metavariable:
- $CLASSNAME
- id: main-activity
languages:
- xml
patterns: # 2
- pattern: |
<activity ... android:name="$ACT" ...> ...
<intent-filter> ...
<action android:name="android.intent.action.MAIN"/> ...
<category android:name="android.intent.category.LAUNCHER"/> ...
</intent-filter> ...
</activity>
- focus-metavariable:
- $ACT
on:
- 'main-activity.$ACT > activity-without-update.$CLASSNAME'
MSTG-STORAGE-9
public void setFlags (int flags, int mask)
public void addFlags (int flags)
- patterns:
- pattern-either:
- pattern: getWindow().setFlags($P1, $P2)
- pattern: (...).getWindow().setFlags($P1, $P2)
- pattern: (Window $W).setFlags($P1, $P2)
- metavariable-comparison:
comparison: $P1 & 8192 == 0
- patterns:
- pattern: public class $CLASS extends $ACT{ ... }
- pattern-not: |
public class $CLASS{...
$M(...){...
(...).addFlags(...);
...}
...}
- pattern-not: |
public class $CLASS{...
$M(...){...
(...).setFlags(...);
...}
...}
- metavariable-regex:
metavariable: $ACT
regex: .*Activity.*
- patterns:
- pattern-either:
- pattern: getWindow().addFlags($P1, $P2)
- pattern: (...).getWindow().addFlags($P1, $P2)
- pattern: (Window $W).addFlags($P1, $P2)
- metavariable-comparison:
comparison: $P1 & 8192 == 0
public class MainActivity extends AppCompatActivity {
private void test(){
getWindow().addFlags(222); // This triggers the rule, generating a false positive
getWindow().addFlags(8192); // FLAG_SECURE
}
}
MSTG-NETWORK-4
- mstg-network-4.1.yaml ⇒ Rule to analyze misconfiguration inside the "network_security_config.xml" file.
- mstg-network-4.1.xml ⇒ Example of "network_security_config.xml" vulnerable file.
- mstg-network-4.2.yaml ⇒ Eule to detect security issues inside the Java source code.
- mstg-network-4.2.java ⇒ Example of insecure use of pinning inside the Java source code.
- It verifies the presence of the pin expiration date.
- It verifies that the pin expiration date is not expired.
- It verifies the presence of a backup pin.
- It verifies that the trust-anchors does not use user-level certificates.
pattern-either:
# Pin expiration not present
- patterns:
- pattern: <pin-set ... />
- pattern-not: <pin-set expiration="..." />
# Pin expired
- patterns:
- pattern: <pin-set expiration="$X" />
- metavariable-comparison:
comparison: strptime($X) < today()
# Backup pin not present
- patterns:
- pattern: <pin-set ... />
- pattern-not: <pin-set><pin/><pin/></pin-set>
# Trust anchors contains user certificates
- patterns:
- pattern: <trust-anchors>...<certificates src="user" />...</trust-anchors>
- It verifies that the "SSLContext" is correctly initialized.
- It verifies that the "TrustManagerFactory" is correctly initialized.
- It verifies that the certificate pin does not use the SHA1 deprecated hashing function.
- It verifies that an "HttpsURLConnection" object calls the "connect" method only if the "SSLSocketFactory" has been configured.
pattern-either:
- pattern: (SSLContext $X).init($P1, null, $P3);
- pattern: (TrustManagerFactory $X).init(null);
- patterns:
- pattern: new CertificatePinner.Builder().add("$D", "$P")
- metavariable-regex:
metavariable: $P
regex: .*(?i)(sha1/).*
- patterns:
- pattern: (HttpsURLConnection $X).connect();
- pattern-not-inside: |
(HttpsURLConnection $X).setSSLSocketFactory(...);
...
(HttpsURLConnection $X).connect();
Tests and Results
- Download an Android application.
- Extract and decompile the code with JADX.
- Upload the source code on a cloud storage.
- Analyze the application source code, one application at the time.
- The presence of rules associated to an high number of results, potentially false positives.
- The average number of findings per application. This value has to be maintained in a reasonable range in order to allow a security expert to read and verify each result.
- The speed of each rule in order to detect eventual bottleneck.
- Total number of findings: 107173.
- Average number of findings per application: 382.
- Most impacted categories: storage, code, platform.
- The rule about memory leaks potentially produces an high number of findings ("MSTG-CODE-8.X").
No comments:
Post a Comment