android
demo
MSTG-TEST-0282
MASTG-DEMO-0054: Use of a TrustManager that Does Not Validate Certificate Chains
Download MASTG-DEMO-0054 APK
Open MASTG-DEMO-0054 Folder
Build MASTG-DEMO-0054 APK
Sample
This sample connects to https://tlsexpired.no , which has an expired certificate, to demonstrate the insecure use of a custom TrustManager
that ignores certificate chain validity. It does this by overriding the checkServerTrusted(...)
method and leaving it empty, which effectively disables certificate validation.
MastgTest.kt MastgTest_reversed.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 package org.owasp.mastestapp
import android.content.Context
import android.util.Log
import java.net.URL
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
class MastgTest ( private val context : Context ) {
fun mastgTest (): String {
val content = StringBuilder ( "Response:\n\n" )
val thread = Thread {
content . append ( fetchUrl ( "https://tlsexpired.no" )) // Expired cert
}
thread . start ()
thread . join ()
return content . toString ()
}
private fun fetchUrl ( urlString : String ): String {
return try {
val url = URL ( urlString )
val connection = url . openConnection () as HttpsURLConnection
trustAllCertificates ( connection ) // ❌ Ignores certificate chain validity
connection . setRequestProperty ( "User-Agent" , "OWASP MAS APP 9000" )
connection . connect ()
val response = connection . inputStream . bufferedReader (). use { it . readText () }
"\n[ $ urlString ] Response OK\n $ response \n"
} catch ( e : Exception ) {
"\n[ $ urlString ] Error: ${ e . message } \n"
}
}
private fun trustAllCertificates ( connection : HttpsURLConnection ) {
try {
val trustAllCerts = arrayOf < TrustManager > (
object : X509TrustManager {
override fun checkClientTrusted ( chain : Array < out X509Certificate >? , authType : String? ) {}
override fun checkServerTrusted ( chain : Array < out X509Certificate >? , authType : String? ) {}
override fun getAcceptedIssuers (): Array < X509Certificate > = arrayOf ()
}
)
val sslContext = SSLContext . getInstance ( "TLS" )
sslContext . init ( null , trustAllCerts , SecureRandom ())
connection . sslSocketFactory = sslContext . socketFactory
} catch ( e : Exception ) {
Log . e ( "TRUST_MANAGER" , "Failed to setup trust manager: ${ e . message } " )
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 package org.owasp.mastestapp ;
import android.content.Context ;
import android.util.Log ;
import java.io.BufferedReader ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.InputStreamReader ;
import java.io.Reader ;
import java.net.URL ;
import java.net.URLConnection ;
import java.security.KeyManagementException ;
import java.security.NoSuchAlgorithmException ;
import java.security.SecureRandom ;
import java.security.cert.X509Certificate ;
import javax.net.ssl.HttpsURLConnection ;
import javax.net.ssl.SSLContext ;
import javax.net.ssl.TrustManager ;
import javax.net.ssl.X509TrustManager ;
import kotlin.Metadata ;
import kotlin.io.CloseableKt ;
import kotlin.io.TextStreamsKt ;
import kotlin.jvm.internal.Intrinsics ;
import kotlin.text.Charsets ;
/* compiled from: MastgTest.kt */
@Metadata ( d1 = { "\u0000&\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0002\b\u0003\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0006\u0010\u0006\u001a\u00020\u0007J\u0010\u0010\b\u001a\u00020\u00072\u0006\u0010\t\u001a\u00020\u0007H\u0002J\u0010\u0010\n\u001a\u00020\u000b2\u0006\u0010\f\u001a\u00020\rH\u0002R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u000e" }, d2 = { "Lorg/owasp/mastestapp/MastgTest;" , "" , "context" , "Landroid/content/Context;" , "<init>" , "(Landroid/content/Context;)V" , "mastgTest" , "" , "fetchUrl" , "urlString" , "trustAllCertificates" , "" , "connection" , "Ljavax/net/ssl/HttpsURLConnection;" , "app_debug" }, k = 1 , mv = { 2 , 0 , 0 }, xi = 48 )
/* loaded from: classes3.dex */
public final class MastgTest {
public static final int $stable = 8 ;
private final Context context ;
public MastgTest ( Context context ) {
Intrinsics . checkNotNullParameter ( context , "context" );
this . context = context ;
}
public final String mastgTest () throws InterruptedException {
final StringBuilder content = new StringBuilder ( "Response:\n\n" );
Thread thread = new Thread ( new Runnable () { // from class: org.owasp.mastestapp.MastgTest$$ExternalSyntheticLambda0
@Override // java.lang.Runnable
public final void run () {
MastgTest . mastgTest$lambda$0 ( content , this );
}
});
thread . start ();
thread . join ();
String string = content . toString ();
Intrinsics . checkNotNullExpressionValue ( string , "toString(...)" );
return string ;
}
/* JADX INFO: Access modifiers changed from: private */
public static final void mastgTest$lambda$0 ( StringBuilder content , MastgTest this $0 ) {
Intrinsics . checkNotNullParameter ( content , "$content" );
Intrinsics . checkNotNullParameter ( this $0 , "this$0" );
content . append ( this $0 . fetchUrl ( "https://tlsexpired.no" ));
}
private final String fetchUrl ( String urlString ) throws IOException {
try {
URL url = new URL ( urlString );
URLConnection uRLConnectionOpenConnection = url . openConnection ();
Intrinsics . checkNotNull ( uRLConnectionOpenConnection , "null cannot be cast to non-null type javax.net.ssl.HttpsURLConnection" );
HttpsURLConnection connection = ( HttpsURLConnection ) uRLConnectionOpenConnection ;
trustAllCertificates ( connection );
connection . setRequestProperty ( "User-Agent" , "OWASP MAS APP 9000" );
connection . connect ();
InputStream inputStream = connection . getInputStream ();
Intrinsics . checkNotNullExpressionValue ( inputStream , "getInputStream(...)" );
Reader inputStreamReader = new InputStreamReader ( inputStream , Charsets . UTF_8 );
BufferedReader bufferedReader = inputStreamReader instanceof BufferedReader ? ( BufferedReader ) inputStreamReader : new BufferedReader ( inputStreamReader , 8192 );
try {
BufferedReader it = bufferedReader ;
String response = TextStreamsKt . readText ( it );
CloseableKt . closeFinally ( bufferedReader , null );
return "\n[" + urlString + "] Response OK\n" + response + "\n" ;
} finally {
}
} catch ( Exception e ) {
return "\n[" + urlString + "] Error: " + e . getMessage () + "\n" ;
}
}
private final void trustAllCertificates ( HttpsURLConnection connection ) throws NoSuchAlgorithmException , KeyManagementException {
try {
TrustManager [] trustAllCerts = { new X509TrustManager () { // from class: org.owasp.mastestapp.MastgTest$trustAllCertificates$trustAllCerts$1
@Override // javax.net.ssl.X509TrustManager
public void checkClientTrusted ( X509Certificate [] chain , String authType ) {
}
@Override // javax.net.ssl.X509TrustManager
public void checkServerTrusted ( X509Certificate [] chain , String authType ) {
}
@Override // javax.net.ssl.X509TrustManager
public X509Certificate [] getAcceptedIssuers () {
return new X509Certificate [ 0 ] ;
}
}};
SSLContext sslContext = SSLContext . getInstance ( "TLS" );
sslContext . init ( null , trustAllCerts , new SecureRandom ());
connection . setSSLSocketFactory ( sslContext . getSocketFactory ());
} catch ( Exception e ) {
Log . e ( "TRUST_MANAGER" , "Failed to setup trust manager: " + e . getMessage ());
}
}
}
If the app wouldn't use the insecure TrustManager
, you would see this message:
[https://tlsexpired.no] Error: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Steps
Let's run our semgrep rule against the sample code.
../../../../rules/mastg-android-network-checkservertrusted.yml rules :
- id : mastg - android - network - checkservertrusted
severity : WARNING
languages :
- java
metadata :
summary : This rule looks for the use of checkServerTrusted and ensures it throws an exception instead of silently muting invalid server certificates
message : Improper Server Certificate verification detected .
match :
any :
- public void checkServerTrusted ( ... ) { ... }
run.sh NO_COLOR = true semgrep - c ../../../../ rules / mastg - android - network - checkservertrusted . yml ./ MastgTest_reversed . java -- text > output . txt
Observation
The rule identified one instance in the code where checkServerTrusted(...)
is used without exception handling.
output.txt ┌────────────────┐
│ 1 Code Finding │
└────────────────┘
MastgTest_reversed . java
❯❱ rules . mastg - android - network - checkservertrusted
Improper Server Certificate verification detected .
92 ┆ @Override // javax . net . ssl . X509TrustManager
93 ┆ public void checkServerTrusted ( X509Certificate [] chain , String authType ) {
94 ┆ }
Evaluation
The test fails because of the presence of the checkServerTrusted(...)
method on in the TrustManager
implementation, as well as the absence of exceptions being thrown.