Skip to content
Last updated: June 24, 2025

MASTG-DEMO-0049: SSLSocket Connection to Wrong Host Server Blocked by HostnameVerifier

Download MASTG-DEMO-0049 APK Open MASTG-DEMO-0049 Folder Build MASTG-DEMO-0049 APK

Overview

The following sample code demonstrates how to connect to a server that delivers a certificate with a wrong or invalid hostname using SSLSocket which inherently doesn't perform any hostname validation checks.

However, the code implements a custom HostnameVerifier that performs hostname verification, thus blocking the connection to the server with the wrong hostname.

 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
package org.owasp.mastestapp

import android.content.Context
import java.io.BufferedReader
import java.io.InputStreamReader
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLException
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory

class MastgTest(private val context: Context) {

    fun mastgTest(): String {
        var socket: SSLSocket? = null

        return try {
            // Use the default SSLSocketFactory
            val sslSocketFactory = SSLSocketFactory.getDefault() as SSLSocketFactory

            // Connect to the server using SSLSocket
            val host = "wrong.host.badssl.com"
            val port = 443
            socket = sslSocketFactory.createSocket(host, port) as SSLSocket

            // Start the handshake
            socket.startHandshake()

            val hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier()
            val session = socket.session
            if (!hostnameVerifier.verify(host, session)) {
                throw SSLException("Hostname verification failed for host: $host")
            }

            // Send an HTTP GET request
            val request = "GET / HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n"
            val out = socket.outputStream
            out.write(request.toByteArray())
            out.flush()

            // Read the response (this will read until the server closes)
            val reader = BufferedReader(InputStreamReader(socket.inputStream))
            val response = reader.readText()

            "Connection Successful: ${response.substring(0, minOf(200, response.length))}"
        } catch (e: Exception) {
            e.printStackTrace()
            "Connection Failed: ${e::class.simpleName} - ${e.message}"
        } finally {
            // Clean up: close the socket
            socket?.let {
                try { it.close() } catch (_: Exception) {}
            }
        }
    }
}
 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
package org.owasp.mastestapp;

import android.content.Context;
import androidx.compose.runtime.ComposerKt;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import kotlin.Metadata;
import kotlin.io.TextStreamsKt;
import kotlin.jvm.internal.Intrinsics;
import kotlin.jvm.internal.Reflection;
import kotlin.text.Charsets;

/* compiled from: MastgTest.kt */
@Metadata(d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\u0006\u0010\u0005\u001a\u00020\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0007"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "(Landroid/content/Context;)V", "mastgTest", "", "app_debug"}, k = 1, mv = {1, 9, 0}, xi = 48)
/* loaded from: classes4.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() {
        String str;
        SSLSocket socket;
        HostnameVerifier hostnameVerifier;
        SSLSession session;
        SSLSocket socket2 = null;
        try {
            try {
                SocketFactory socketFactory = SSLSocketFactory.getDefault();
                Intrinsics.checkNotNull(socketFactory, "null cannot be cast to non-null type javax.net.ssl.SSLSocketFactory");
                SSLSocketFactory sslSocketFactory = (SSLSocketFactory) socketFactory;
                Socket createSocket = sslSocketFactory.createSocket("wrong.host.badssl.com", 443);
                Intrinsics.checkNotNull(createSocket, "null cannot be cast to non-null type javax.net.ssl.SSLSocket");
                socket = (SSLSocket) createSocket;
                socket.startHandshake();
                hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
                session = socket.getSession();
            } catch (Exception e) {
                e.printStackTrace();
                str = "Connection Failed: " + Reflection.getOrCreateKotlinClass(e.getClass()).getSimpleName() + " - " + e.getMessage();
                if (0 != 0) {
                    try {
                        socket2.close();
                    } catch (Exception e2) {
                    }
                }
            }
            if (!hostnameVerifier.verify("wrong.host.badssl.com", session)) {
                throw new SSLException("Hostname verification failed for host: wrong.host.badssl.com");
            }
            String request = "GET / HTTP/1.1\r\nHost: wrong.host.badssl.com\r\nConnection: close\r\n\r\n";
            OutputStream out = socket.getOutputStream();
            byte[] bytes = request.getBytes(Charsets.UTF_8);
            Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");
            out.write(bytes);
            out.flush();
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String response = TextStreamsKt.readText(reader);
            StringBuilder append = new StringBuilder().append("Connection Successful: ");
            String substring = response.substring(0, Math.min(ComposerKt.invocationKey, response.length()));
            Intrinsics.checkNotNullExpressionValue(substring, "this as java.lang.String…ing(startIndex, endIndex)");
            str = append.append(substring).toString();
            try {
                socket.close();
            } catch (Exception e3) {
            }
            return str;
        } catch (Throwable th) {
            if (0 != 0) {
                try {
                    socket2.close();
                } catch (Exception e4) {
                }
            }
            throw th;
        }
    }
}

Steps

  1. Reverse engineer the app ( Decompiling Java Code).
  2. Run a static analysis ( Static Analysis on Android) tool and look for all usages of SSLSocket and HostnameVerifier.
../../../../rules/mastg-android-ssl-socket-hostnameverifier.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
rules:
  - id: mastg-android-ssl-socket-hostnameverifier
    severity: WARNING
    languages:
      - java
    metadata:
      summary: This rules scans for the usage of SSLSocket API with a HostnameVerifier.
    message: "Detected usage of SSLSocket API with or without a HostnameVerifier"
    match:
      any:
        - pattern: SSLSocketFactory.getDefault()
        - pattern: (SSLSocketFactory) $_
        - pattern: $SF.createSocket(...)
        - pattern: HttpsURLConnection.getDefaultHostnameVerifier()
        - pattern: $HNV.verify($_, $_)
run.sh
1
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-ssl-socket-hostnameverifier.yml ./MastgTest_reversed.java > output.txt

Observation

The output contains a list of locations where SSLSocket and HostnameVerifier are used.

output.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
┌─────────────────┐
 5 Code Findings 
└─────────────────┘

    MastgTest_reversed.java
      rules.mastg-android-ssl-socket-hostnameverifier
           42 SocketFactory socketFactory = SSLSocketFactory.getDefault();
            ⋮┆----------------------------------------
           44 SSLSocketFactory sslSocketFactory = (SSLSocketFactory) socketFactory;
            ⋮┆----------------------------------------
           45 Socket createSocket = sslSocketFactory.createSocket("wrong.host.badssl.com", 443);
            ⋮┆----------------------------------------
           49 hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
            ⋮┆----------------------------------------
           61 if (!hostnameVerifier.verify("wrong.host.badssl.com", session)) {

Evaluation

The test case passes due to the use of a HostnameVerifier.

As expected, the connection aborts as you can see in the logcat output which contains the following exception:

javax.net.ssl.SSLException: Hostname verification failed for host: wrong.host.badssl.com