<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/tips/3296

                    0x00 起因


                    1、近期icloud.com、yahoo.com、apple.com遭遇到大規模劫持

                    WooYun: Yahoo雅虎在國內訪問遭遇SSL中間人攻擊(被替換為自簽名證書)

                    2、烏云平臺、CVE都收到大量有關Android APP信任所有證書的漏洞

                    WooYun: 國內絕大部分Android APP存在信任所有證書漏洞

                    3、老外寫有關大表哥的文章中提到MITM時360瀏覽器不提示證書錯誤

                    http://www.computerworld.com/article/2836084/chinese-big-brother-launches-nationwide-attack-on-icloud.html

                    之前信任證書問題一直都有被提到,但是普遍不受大家重視,因為這個漏洞是利用是需要場景的:MITM(中間人攻擊 Man-in-the-middle attack)。一般情況下MITM相對其他攻擊是比較少見的,如果有良好的上網習慣如不接入不受信任的網絡,那就更少可能受此類攻擊了。但是近期發生的MITM據傳是在核心骨干網BGP上做了改動所以劫持范圍非常之廣,真是防不勝防呀,你被劫持了么?

                    0x01 科普


                    https&&ssl

                    為了提高網站的安全性,一般會在比較敏感的部分頁面采用https傳輸,比如注冊、登錄、控制臺等。像Gmail、網銀、icloud等則全部采用https傳輸。https/ssl主要起到兩個作用:網站認證、內容加密傳輸和數據一致性。經CA簽發的證書才起到認證可信的作用,所有有效證書均可以起到加密傳輸的作用。

                    數字證書

                    主要在互聯網上的用于身份驗證的用途。 安全站點在獲得CA(Certificate Authority數字證書認證機構)認證后,獲得一個數字證書,以此來標識其合法身份的真實性。數字證書主要分為服務器證書和客戶端證書。服務器證書(SSL證書)用來進行身份驗證和通信的加密,客戶端證書主要用于身份驗證和電子簽名。找CA申請證書是要收費的。

                    自簽名證書

                    非CA頒發的證書,通過自簽名的方式得到的證書。通常Web瀏覽器會顯示一個對話框,詢問您是否希望信任一個自簽名證書。這個是不用花錢的。

                    中間人攻擊

                    是指攻擊者與通訊的兩端分別創建獨立的聯系,并交換其所收到的數據,使通訊的兩端認為他們正在通過一個私密的連接與對方直接對話,但事實上整個會話都被攻擊者完全控制。在中間人攻擊中,攻擊者可以攔截通訊雙方的通話并插入新的內容。在許多情況下這是很簡單的。

                    0x02 分析


                    如果自己簡單的實現android webview加載網頁,如果直接訪問可信證書的站點是可以正常顯示,但是如果訪問自簽名的證書的站點就會顯示notfound的頁面。(寫本文時apple.com以及apple.com.cn處于劫持狀態)

                    logcat會輸出網頁顯示不安全的內容

                    Web Console:The page displayed insecure content!
                    

                    功能健全的手機瀏覽器訪問自簽名證書的站點會如下提醒

                    在PC端如果訪問自簽名證書的站點則會出現如下圖左側的提醒

                    為解決javax.net.ssl.SSLPeerUnverifiedException: No peer certificate的異常,開發者往往會采用以下的錯誤解決方案。如此是瀏覽器應用采用此類解決方案,那么風險就更大了。

                    覆蓋google默認的證書檢查機制

                    #!java
                    class bv
                      implements X509TrustManager
                    {
                      bv(bu parambu) {}
                    
                      public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {// Do nothing -> accept any certificates}
                    
                      public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {// Do nothing -> accept any certificates}
                    
                      public X509Certificate[] getAcceptedIssuers()
                      {
                        return null;
                      }
                    }
                    

                    信任所有主機名

                    #!java
                    public static HttpClient getNewHttpClient() {  
                        try {  
                            //獲得密匙庫
                            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());  
                            trustStore.load(null, null);  
                    
                            SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore); 
                            //信任所有主機名 
                            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);  
                    
                            HttpParams params = new BasicHttpParams();  
                            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);  
                            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);  
                    
                            SchemeRegistry registry = new SchemeRegistry();  
                            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));  
                            registry.register(new Scheme("https", sf, 443));  
                    
                            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);  
                    
                            return new DefaultHttpClient(ccm, params);  
                        } catch (Exception e) {  
                            return new DefaultHttpClient();  
                        }  
                    }  
                    

                    empty HostnameVerifier

                    #!java
                    HostnameVerifier hv = new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                    // Always return true -> Accespt any host names
                    return true;
                    }
                    };
                    

                    忽略WebView證書錯誤繼續加載

                    #!java
                        myWebView.setWebViewClient(new WebViewClient(){
                    
                            @Override
                            public void onReceivedError(WebView view, int errorCode,
                                    String description, String failingUrl) {
                                // TODO Auto-generated method stub
                                super.onReceivedError(view, errorCode, description, failingUrl);
                            }
                    
                            @Override
                            public void onReceivedSslError(WebView view,
                                    SslErrorHandler handler, SslError error) {
                                // TODO Auto-generated method stub
                                handler.proceed();
                            }});
                    

                    其實早在14年2月竊聽風暴: Android平臺https嗅探劫持漏洞文中就有提到android平臺的app因為覆蓋google默認的證書檢查機制(X509TrustManager)之后沒有對證書進行應有的安全性檢查,直接接受了所有異常的https證書,不提醒用戶存在安全風險,也不終止這次危險的連接。文中對證書域名檢查(HostnameVerifier)部分沒有細說。

                    上文有提到PC版的360瀏覽器訪問被劫持網站居然沒有證書錯誤提示,讓人很不敢相信。加上最近android app 證書問題頻發,猜想是否有可能一些手機瀏覽器也會有此類漏洞了。測試過程中發現360手機瀏覽器、和搜狗瀏覽器存在此風險。

                    百度和遨游輕松檢測出證書異常

                    而360和搜狗直接加載進入了被劫持的網站。

                    反編譯查看遨游瀏覽器的代碼,針對證書異常做了處理

                    而搜狗瀏覽器則是做了證書信任所有主機名不當處理

                    關鍵字:checkServerTrusted、setHostnameVerifier、ALLOW_ALL_HOSTNAME_VERIFIER、X509TrustManager、onReceivedSslError

                    0x03 對比


                    對主流手機瀏覽器進行了橫向對比,測試對象包括:firefox、chrome、UC瀏覽器、搜狗瀏覽器、百度瀏覽器、360安全瀏覽器、歐鵬瀏覽器、遨游云瀏覽器、獵豹瀏覽器。

                    測試方法:手機訪問https://example.com/,觀察是否有安全提醒。(update:此方法已經無效.)

                    未做提醒直接加載網頁:360安全瀏覽器、獵豹瀏覽器、搜狗瀏覽器

                    正常做出安全提醒:firefox、chrome、UC瀏覽器、百度瀏覽器、歐鵬瀏覽器、遨游云瀏覽器

                    0x04 建議


                    開發者:

                    1、非瀏覽器app,有錢申請ca證書沒錢在客戶端中添加證書,切勿信任所有證書。

                    2、瀏覽器app,嚴格按照客戶端校驗服務器證書流程處理:

                    3、建議使用setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER)

                    一個SSLSocketFactory的example

                    #!java
                    java public class SecureSocketFactory extends SSLSocketFactory {
                    
                        private static final String LOG_TAG = "SecureSocketFactory";
                    
                        private final SSLContext sslCtx;
                        private final X509Certificate[] acceptedIssuers;
                    
                        /**
                         * Instantiate a new secured factory pertaining to the passed store. Be sure to initialize the
                         * store with the password using [email protected] java.security.KeyStore#load(java.io.InputStream,
                         * char[])} method.
                         *
                         * @param store The key store holding the certificate details
                         * @param alias The alias of the certificate to use
                         */
                        public SecureSocketFactory(KeyStore store, String alias)
                                throws
                                CertificateException,
                                NoSuchAlgorithmException,
                                KeyManagementException,
                                KeyStoreException,
                                UnrecoverableKeyException {
                    
                            super(store);
                    
                            // Loading the CA certificate from store.
                            final Certificate rootca = store.getCertificate(alias);
                    
                            // Turn it to X509 format.
                            InputStream is = new ByteArrayInputStream(rootca.getEncoded());
                            X509Certificate x509ca = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
                            AsyncHttpClient.silentCloseInputStream(is);
                    
                            if (null == x509ca) {
                                throw new CertificateException("Embedded SSL certificate has expired.");
                            }
                    
                            // Check the CA's validity.
                            x509ca.checkValidity();
                    
                            // Accepted CA is only the one installed in the store.
                            acceptedIssuers = new X509Certificate[]{x509ca};
                    
                            sslCtx = SSLContext.getInstance("TLS");
                            sslCtx.init(
                                    null,
                                    new TrustManager[]{
                                            new X509TrustManager() {
                                                @Override
                                                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                                                }
                    
                                                @Override
                                                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                                                    Exception error = null;
                    
                                                    if (null == chain || 0 == chain.length) {
                                                        error = new CertificateException("Certificate chain is invalid.");
                                                    } else if (null == authType || 0 == authType.length()) {
                                                        error = new CertificateException("Authentication type is invalid.");
                                                    } else {
                                                        Log.i(LOG_TAG, "Chain includes " + chain.length + " certificates.");
                                                        try {
                                                            for (X509Certificate cert : chain) {
                                                                Log.i(LOG_TAG, "Server Certificate Details:");
                                                                Log.i(LOG_TAG, "---------------------------");
                                                                Log.i(LOG_TAG, "IssuerDN: " + cert.getIssuerDN().toString());
                                                                Log.i(LOG_TAG, "SubjectDN: " + cert.getSubjectDN().toString());
                                                                Log.i(LOG_TAG, "Serial Number: " + cert.getSerialNumber());
                                                                Log.i(LOG_TAG, "Version: " + cert.getVersion());
                                                                Log.i(LOG_TAG, "Not before: " + cert.getNotBefore().toString());
                                                                Log.i(LOG_TAG, "Not after: " + cert.getNotAfter().toString());
                                                                Log.i(LOG_TAG, "---------------------------");
                    
                                                                // Make sure that it hasn't expired.
                                                                cert.checkValidity();
                    
                                                                // Verify the certificate's public key chain.
                                                                cert.verify(rootca.getPublicKey());
                                                            }
                                                        } catch (InvalidKeyException e) {
                                                            error = e;
                                                        } catch (NoSuchAlgorithmException e) {
                                                            error = e;
                                                        } catch (NoSuchProviderException e) {
                                                            error = e;
                                                        } catch (SignatureException e) {
                                                            error = e;
                                                        }
                                                    }
                                                    if (null != error) {
                                                        Log.e(LOG_TAG, "Certificate error", error);
                                                        throw new CertificateException(error);
                                                    }
                                                }
                    
                                                @Override
                                                public X509Certificate[] getAcceptedIssuers() {
                                                    return acceptedIssuers;
                                                }
                                            }
                                    },
                                    null
                            );
                    
                            setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
                        }
                    
                        @Override
                        public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
                                throws IOException {
                    
                            injectHostname(socket, host);
                            Socket sslSocket = sslCtx.getSocketFactory().createSocket(socket, host, port, autoClose);
                    
                            // throw an exception if the hostname does not match the certificate
                            getHostnameVerifier().verify(host, (SSLSocket) sslSocket);
                    
                            return sslSocket;
                        }
                    
                        @Override
                        public Socket createSocket() throws IOException {
                            return sslCtx.getSocketFactory().createSocket();
                        }
                    
                        /**
                         * Pre-ICS Android had a bug resolving HTTPS addresses. This workaround fixes that bug.
                         *
                         * @param socket The socket to alter
                         * @param host   Hostname to connect to
                         * @see <a >https://code.google.com/p/android/issues/detail?id=13117#c14</a>
                         */
                        private void injectHostname(Socket socket, String host) {
                            try {
                                if (Integer.valueOf(Build.VERSION.SDK) >= 4) {
                                    Field field = InetAddress.class.getDeclaredField("hostName");
                                    field.setAccessible(true);
                                    field.set(socket.getInetAddress(), host);
                                }
                            } catch (Exception ignored) {
                            }
                        }
                    
                    
                    } 
                    

                    用戶:使用安全性較好的app

                    0x05 參考


                    http://drops.wooyun.org/tips/2775

                    http://drops.wooyun.org/papers/959

                    http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html

                    http://developer.android.com/reference/javax/net/ssl/X509TrustManager.html

                    http://developer.android.com/training/articles/security-ssl.html

                    http://developer.android.com/reference/org/apache/http/conn/ssl/SSLSocketFactory.html

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频