Android unsecure HTTPS request

69 views Asked by At

We have an android app which is tested by security team called AppSec. This AppSec team requires a two kind of apps ie. Pinned (With SSL code) and UnPinned (Without SSL code). They use burp suit to detect traffic. But since few days they are unable capture api traffic of UnPinned build. Unpinned build is always unsecure one. I unable to understand what is wrong since I am new in this project.

Our application is build for older version and due to some reasons we cannot upgrade code to newer versions. here is snippets from app level build.gradle file

defaultConfig {
    applicationId "com.axis.fxconnect"
    minSdkVersion 26
    targetSdkVersion 21
    compileSdkVersion 24
    versionCode 32
    versionName "2.32"
}

dependencies {
    implementation project(':listViewOrdering')
    implementation project(':andMenuDrawerLibrary')
    implementation 'com.android.support:support-v4:19.0.0'
    implementation files('libs/commons-codec-1.10.jar')
    implementation files('libs/sqlcipher.jar')
    implementation 'com.google.android.gms:play-services-auth:11.0.4'
    implementation 'com.scottyab:rootbeer-lib:0.0.7'
}

And the file which is responsible for api calls with SSL Pinning and without Pinning.

public class PostData {
    // Lock for synchronization
    private static final Object LOCK = new Object();
    List<NameValuePair> nameValuePairs;
    String url;
    ContentValues cv;
    String response = "";
    HttpPost httppost;
    HttpResponse execute;
    InputStream content;
    public static String JSESSIONID = "";
    private static org.apache.http.client.CookieStore cookieStore;

    public PostData(ContentValues cv, String url) {
        this.url = url;
        this.cv = cv;
        nameValuePairs = new ArrayList<NameValuePair>(cv.size());
        Set<Entry<String, Object>> s = cv.valueSet();
        Iterator itr = s.iterator();

        while (itr.hasNext()) {
            Map.Entry me = (Map.Entry) itr.next();
            String key = me.getKey().toString();
            Object value = me.getValue();

//          if(key.equalsIgnoreCase("pass") || key.equalsIgnoreCase("MPIN") || key.equalsIgnoreCase("NEWPASS") || key.equalsIgnoreCase("NEWMPIN"))
//              nameValuePairs.add(new BasicNameValuePair(key, Encryption.encrypt(value.toString())));
//          else
            nameValuePairs.add(new BasicNameValuePair(key, value.toString()));
        }
        nameValuePairs.add(new BasicNameValuePair("version", "" + Constants.VERSION));
    }

    static final String COOKIES_HEADER = "Set-Cookie";

    public String post(Context context) {
        
        //Comment from here till else for ssl unpinning (post1 method is for unpinning)
        if (Constants.BASE_URL.equals("https://production_url.com/services/..."))
            return postNormal1(context, "production_url.com");
        else if (Constants.BASE_URL.equals("https://uat_url.com/services/"))
            return postNormal1(context, "uat_url.com");
        else
            return post1(context);
    }

    public String postNormal1(Context con, final String domain) {
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream caInput = null;
            if (domain.equals("production_url.com"))
                caInput = con.getResources().openRawResource(R.raw.crt_connect_live);
            else if (domain.equals("uat_url.com"))
                caInput = con.getResources().openRawResource(R.raw.crt_connect_uat);

            Certificate ca;

            try {
                ca = cf.generateCertificate(caInput);
            } finally {
                caInput.close();
            }

            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);

            //Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);

            TrustManager[] trustManagers = tmf.getTrustManagers();
            final X509TrustManager origTrustmanager = (X509TrustManager) trustManagers[0];

            TrustManager[] wrappedTrustManagers = new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return origTrustmanager.getAcceptedIssuers();
                        }

                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            try {
                                origTrustmanager.checkClientTrusted(certs, authType);
                            } catch (CertificateException e) {
                                //e.printStackTrace();
                            }
                        }

                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            try {
                                origTrustmanager.checkServerTrusted(certs, authType);
                            } catch (CertificateException e) {
                            }
                        }
                    }
            };

            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);

            SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(context.getSocketFactory());
            //HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);

            /*SSLSocketFactory ss = HttpsURLConnection.getDefaultSSLSocketFactory();
            SSLSocket sslSocket = (SSLSocket) ss.createSocket("mfx-connectuat.axisbank.co.in",443);

            sslSocket.startHandshake();
            Certificate[] peerCertificates = sslSocket.getSession().getPeerCertificates();

            Log.e("Certificate", peerCertificates[0].toString());*/

            HostnameVerifier hostnameVerifier = new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {

                    HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
                    return hv.verify(domain, session);
                }
            };

            Constants.printLog("url", this.url);
            // Tell the URLConnection to use a SocketFactory from our SSLContext
            URL url1 = new URL(this.url);
            Constants.urlConnection = (HttpsURLConnection) url1.openConnection();

            if (JSESSIONID.length() > 0)
                Constants.urlConnection.setRequestProperty("Cookie", "JSESSIONID=" + JSESSIONID);

            Constants.urlConnection.setHostnameVerifier(hostnameVerifier);
            Constants.urlConnection.setSSLSocketFactory(NoSSLv3Factory);

            int timeoutConnection = 240000;
            int timeoutSocket = 240000;

            Constants.urlConnection.setConnectTimeout(timeoutConnection);
            Constants.urlConnection.setReadTimeout(timeoutSocket);
            Constants.urlConnection.setRequestMethod("POST");
            Constants.urlConnection.setDoInput(true);
            Constants.urlConnection.setDoOutput(true);

            List<NameValuePair> params = new ArrayList<NameValuePair>();

            Set<Map.Entry<String, Object>> s = this.cv.valueSet();
            Iterator itr = s.iterator();

            while (itr.hasNext()) {
                Map.Entry me = (Map.Entry) itr.next();
                String key = me.getKey().toString();
                Object value = me.getValue();
                String decrypt = Encryption.decrypt(value.toString());
                //Log.e(key,"--"+decrypt);
                params.add(new BasicNameValuePair(key, value.toString()));
            }

            //String cookies = Constants.urlConnection.getHeaderField("set-cookie");

            OutputStream os = Constants.urlConnection.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
            writer.write(getQuery(params));
            writer.flush();
            writer.close();
            os.close();

            if (JSESSIONID.length() == 0) {
                java.net.CookieManager msCookieManager = new java.net.CookieManager();

                Map<String, List<String>> headerFields = Constants.urlConnection.getHeaderFields();
                List<String> cookiesHeader = headerFields.get(COOKIES_HEADER);

                if (cookiesHeader != null) {
                    for (String cookie : cookiesHeader) {
                        if (cookie.contains("JSESSIONID")) {
                            msCookieManager.getCookieStore().add(null, HttpCookie.parse(cookie).get(0));
                        }
                    }
                }

                if (msCookieManager.getCookieStore().getCookies().size() != 0) {
                    String jSessionVal = ("" + msCookieManager.getCookieStore().getCookies().get(0));
                    JSESSIONID = jSessionVal.substring(jSessionVal.indexOf("JSESSIONID=") + "JSESSIONID=".length(), jSessionVal.length());

                    Constants.printLog("JSESSIONID", JSESSIONID);
                    Constants.printLog("jSessionVal", jSessionVal);
                }
            }

            // POST PARAMETERS
            InputStream inputStream = Constants.urlConnection.getInputStream();

            BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
            String s1 = "";
            while ((s1 = buffer.readLine()) != null) {
                response += s1;
            }
            Constants.printLog("data", response);
        } catch (Exception e) {
            response = "Error " + e;
        } finally {

        }

        return response;
    }

    private String getQuery(List<NameValuePair> params) throws UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;

        for (NameValuePair pair : params) {
            if (first)
                first = false;
            else
                result.append("&");

            result.append(URLEncoder.encode(pair.getName(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
        }

        return result.toString();
    }

    public String post1(Context context) {
        Constants.printLog("inside", "post1");
        synchronized (LOCK) {
            try {

                if (Constants.httpClient == null) {
                    HttpParams httpParameters = new BasicHttpParams();
                    int timeoutConnection = 240000;
                    HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);

                    // Set the default socket timeout (SO_TIMEOUT)
                    // in milliseconds which is the timeout for waiting for data.
                    int timeoutSocket = 240000;
                    HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

                    Constants.httpClient = new DefaultHttpClient(); // new DefaultHttpClient(httpParameters);
                    // cookieStore = new BasicCookieStore();
                    // localContext = new BasicHttpContext();
                    cookieStore = Constants.httpClient.getCookieStore();
                }

                httppost = new HttpPost(url);
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

                if (execute != null)
                    execute = null;

                // leo -------------------------------
                if (JSESSIONID.length() > 0) {
                    // Constants.httpClient.setCookieStore(Constants.cookieStore);
                    setDefaultHeaders(httppost);
                    // setDefaultHeaders(httppost,Constants.httpClient);
                    // setDefaultHeadersSharedPreference(httppost);
                }
                // leo -------------------------------

                execute = Constants.httpClient.execute(httppost);

                if (content != null)
                    content = null;

                content = execute.getEntity().getContent();

                // leo -------------------------------
                if (JSESSIONID.length() == 0) {
                    // Constants.cookieStore = Constants.httpClient.getCookieStore();
                    parseSessionID(execute);
                    // parseSessionIDSharedPreference(execute);
                }
                // leo -------------------------------

                BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
                String s = "";

                while ((s = buffer.readLine()) != null) {
                    response += s;
                }
                return response;

            } catch (SocketTimeoutException e) {
                response = "Error: " + e;
            } catch (Exception e) {
                response = "Error: " + e;
            }
            return response;
        }
    }

    private void parseSessionID(HttpResponse response) {
        try {
            Header header = response.getFirstHeader("Set-Cookie");
            String value = header.getValue();
            if (value.contains("JSESSIONID")) {
                int index = value.indexOf("JSESSIONID=");
                int endIndex = value.indexOf(";", index);
                String sessionID = value.substring(index + "JSESSIONID=".length(), endIndex);

                if (sessionID != null) {
                    JSESSIONID = sessionID;
                    Constants.printLog("parseSessionID JSESSIONID", JSESSIONID);
                }
            }
        } catch (Exception e) {
        }
    }

    private void setDefaultHeaders(HttpUriRequest httpRequest) {
        if (JSESSIONID != null && JSESSIONID.length() > 0) {
            httpRequest.setHeader("Cookie", "JSESSIONID=" + JSESSIONID);
        }
    }
}

Valid SSL certificates are kept in raw folder with .crt extensions.

When we wanted to give unpinned build we use to comment section mention in code as //Comment from here till else for ssl unpinning (post1 method is for unpinning)

Now I don't know what is wrong with it. If any more info needed I will update question.

Note : AppSec team has rooted devices which they use to capture traffic of app. Portswigger certificate are installed at there system level.

0

There are 0 answers