ä½ã£ã¦å¦ã¶ ãHttps Man in The Middle Proxyã in Go
á( á )á ããã«ã¡ããããããã§ãã
webã®httpsåãæ¨é²ãããæ¨ä»ã§ãã?
httpséä¿¡ã¯çµè·¯ä¸ã§ã®éä¿¡å
容ãçè´ã»æ¹ç«ãããã®ãé²ããã¨ãã§ãã¾ãããéçºç¨éã§httpséä¿¡ã®å
容ã確èªãããå ´åãç¨ã«ããã¾ãã
ãã®ãããªå ´å㯠mitmproxy ãªã©ãå°å
¥ããã°ããã®ã§ããããã£ãããªã®ã§å®éã«ãã®ãããªProxyãGoã§å®è£
ãã¦ã¿ã¦ã ä¸éè
æ»æ(Man-in-The-Middle Attack)ãã©ã®ãããªææ³ã§httpséä¿¡ãçè´ã»æ¹ç«ããã®ã確ããã¦ã¿ã¾ããã
å®éã«æ¸ããProxyã®ã³ã¼ãã¯ãã¡ãã§ã
https proxy 㨠HTTP CONNECT tunneling
ã¾ããé常ã®https Proxyã®åä½ã確èªãã¦ã¿ã¾ãããã
httpsã§ã¯ãProxyã¯ã¯ã©ã¤ã¢ã³ãããã®CONNECTã¡ã½ãããåä¿¡ããã¨ãã¯ã©ã¤ã¢ã³ãã«ä»£ãã£ã¦å¯¾è±¡ãã¹ãã¨ã®TCPã³ãã¯ã·ã§ã³ã確ç«ãã以éã¯ã¯ã©ã¤ã¢ã³ãã¨å¯¾è±¡ãã¹ãã®TCPéä¿¡ã転éãã¾ããã¯ã©ã¤ã¢ã³ã-ãã¹ãéã®TLSæ¥ç¶ã®handshakeãproxyãçµç±ãã¦è¡ãã¾ãã
ãã®æ¹å¼ã«ãããProxyãçµç±ãã¤ã¤ã¯ã©ã¤ã¢ã³ã-ãã¹ãéã§TLSã»ãã·ã§ã³ã確ç«ãããProxyãçµç±ãã¤ã¤ãçµè·¯ä¸ã§ã¯æå·åãããéä¿¡ãå¯è½ã¨ãªãã¾ãã
å³ã«ããã¨ãããªæãã§ã
å®è£
ã§ã¯å
·ä½çãªå®è£
ãè¦ã¦ã¾ãããã
ã¾ãã¯ããªãã¿ ServeHTTP
ã§ããã¯ã©ã¤ã¢ã³ããã CONNECT
ã¡ã½ãããéä¿¡ãããããéä¿¡ã転éãã relayHTTPSRequest
ãå¼ã³åºãã¾ã
https://github.com/yuroyoro/mitm_proxy_sample/blob/master/main.go#L34
func (proxy *MiTMProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { // CONNECT ã¡ã½ãããæ¥ãã if r.Method == http.MethodConnect { if proxy.mitm { proxy.mitmRequest(w, r) // Man in The Middleãã } else { proxy.relayHTTPSRequest(w, r) // Tunnelingã§éä¿¡ã転éãã } return } proxy.transportHTTPRequest(w, r) }
å®éã«éä¿¡ã転éãã¦ããã³ã¼ãã¯ä»¥ä¸ã®ã¨ããã§ããå¦çã®æµãã¯ã³ã¡ã³ããèªãã§ãããã°ãããã¨æãã¾ããããã£ã¦ãããã¨ã¯åç´ã§ãã
CONNECT
ã¡ã½ããã§æå®ããããã¹ãã«TCPæ¥ç¶ãå¼µã£ã¦ãã¯ã©ã¤ã¢ã³ãããã®éä¿¡ããã®ã¾ã¾æµãè¾¼ãã ãã§ã
https://github.com/yuroyoro/mitm_proxy_sample/blob/master/https.go#L12
func (proxy *MiTMProxy) relayHTTPSRequest(w http.ResponseWriter, r *http.Request) { proxy.info("relayHTTPSRequest : %s %s", r.Method, r.URL.String()) // CONNECTå ã®Hostã¸TCPã³ãã¯ã·ã§ã³ãå¼µã dest, err := net.Dial("tcp", r.Host) if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) return } // http.Hicjacker ãå©ç¨ãã¦ã¯ã©ã¤ã¢ã³ãã¨ã®çã®TCPã³ãã¯ã·ã§ã³ãåãåºã conn := hijackConnect(w) // ã¯ã©ã¤ã¢ã³ãã«ã¯200 OKãè¿ããããã§ã¯ã©ã¤ã¢ã³ãã¯ãã®TCPæ¥ç¶ã«HTTPãªã¯ã¨ã¹ããéã£ã¦ãã conn.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) proxy.info("relayHTTPSRequest : start relaying tcp packets %s %s", r.Method, r.URL.String()) // ã¯ã©ã¤ã¢ã³ã-対象Hostéã®TCPéä¿¡ããã®ã¾ã¾ä¸ç¶ãã go transfer(dest, conn) go transfer(conn, dest) } func transfer(dest io.WriteCloser, source io.ReadCloser) { defer dest.Close() defer source.Close() io.Copy(dest, source) }
Goã«ã¯ http.Hijacker ã¨ãã便å©ãªã¤ã³ã¿ã¼ãã§ã¼ã¹ãããã http.ResponseWriter
ããçã®ã¯ã©ã¤ã¢ã³ãã¨ã®TCPæ¥ç¶ãåãåºããã¨ãã§ãã¾ãã
ãããå©ç¨ãã¦ãTCPéä¿¡ã®è»¢éãè¡ã£ã¦ãã¾ã
func hijackConnect(w http.ResponseWriter) net.Conn { hj, ok := w.(http.Hijacker) if !ok { panic("httpserver does not support hijacking") } conn, _, err := hj.Hijack() if err != nil { panic("Cannot hijack connection " + err.Error()) } return conn }
å®éã¯timeoutãªã©ãèæ ®ããå®è£ ããã¹ããªã®ã§ãããããã ãã§ãåãã¾ãã
Man in The Middel Proxyã®ä»çµã¿
ã§ã¯ãæ¬é¡ã®ä¸éè æ»æãè¡ãProxyã«ã¤ãã¦ã§ãã
é常ã®https proxyã§ã¯ãã¯ã©ã¤ã¢ã³ãããã® CONNECT
ã¡ã½ããã奿©ã«ã対象Hostã¨ã®TCPéä¿¡ãä¸ç¶ãã¦ãã¾ããã
Proxyãæµããéä¿¡å
容ã¯TLSã«ãã£ã¦æå·åããã¦ãããå
容ãçè´ã»æ¹ç«ãããã¨ã¯ã§ãã¾ããã
ãããã対象Hostã¨ã¯ã©ã¤ã¢ã³ãéã®TLS handshakeãProxyãçµç±ããã®ã§ããã®æ®µéã§ã¯ã©ã¤ã¢ã³ãããã®TLS handshakeãã対象ãã¹ãã«ãªããã¾ãã¦Proxyãè¡ãã¨ã©ããªãã§ããããï¼
ã¤ã¾ããProxyã¯å¯¾è±¡ãã¹ãã®ãµã¼ãã¼è¨¼ææ¸ããã®å ´ã§çæãã¦ç½²åããã¯ã©ã¤ã¢ã³ãã«æç¤ºãã¾ãã
ãã¡ãããProxyãç½²åãããµã¼ãã¼è¨¼ææ¸ã¯ä¿¡é ¼ã§ããªãCAã®ãã®ã¨ãã¦ãã©ã¦ã¶ã«ã¯è¦åãåºã¾ããããã®ã¾ã¾ã¦ã¼ã¶ã¼ãç¶è¡ãããã¨ã§TLS handshakeãæåãã¾ãã
ã¯ã©ã¤ã¢ã³ãã¯ç¢ºç«ããTLSæ¥ç¶ã対象ãã¹ãã¨ã®ãã®ã ã¨æãããã§ãProxyãã¯ã©ã¤ã¢ã³ãã«éãè¾¼ãã ãã»ã®ãµã¼ãã¼è¨¼ææ¸ã®å
¬ééµã§éä¿¡ãæå·åããã®ã§ãProxyã¯ãã®å
容ã復å·ãããã¨ãã§ãã¾ãã
ãã¨ã¯ã復å·ãããªã¯ã¨ã¹ãããã®ã¾ã¾å¯¾è±¡ã®ãã¹ãã«è»¢éããã°ãhttpsã«ãé¢ãããProxyã¯éä¿¡å
å®¹ãææ¡ãã¤ã¤ã対象ãã¹ãã¨ã®éä¿¡ãåãæã¤ãã¨ãã§ãã¦ãã¾ãã¾ãã
ããã§ä¸éè
æ»æãæç«ãã¾ãÊã ï¾ç¿ï¾ Ê ã
å³ã«ããã¨ä»¥ä¸ã®æµãã¨ãªãã¾ã
é常ããã®ãããªæ»æã¯ãã©ã¦ã¶ãè¦åãåºãããã«æç«ãã¾ããã
ã¾ããã¦ã¼ã¶ã¼ãæç¤ºçã«ãã©ã¦ã¶ã«Proxyãæå®ããå¿
è¦ãããã¾ãã(port forwardãå©ç¨ããééProxyã¯ãã®éãã§ã¯ãªã)ãProxyãç½²åã«ä½¿ç¨ããã«ã¼ãè¨¼ææ¸(ã¾ãã¯ä¸éè¨¼ææ¸)ãTrust Chainã«ãªãããã§ãã
éã«è¨ãã°ãä¿¡é ¼ã§ããªãã«ã¼ãè¨¼ææ¸ãã·ã¹ãã ã«ã¤ã³ã¹ãã¼ã«ãã¦ãã¾ãã¨ããã®ãããªæ»æãæç«ããä½å°ãçã¾ãã¦ãã¾ãã¾ãã
å®éã«ãä¸é¨ã®ã»ãã¥ãªãã£ã¢ãã©ã¤ã¢ã³ã¹ãã¢ã³ãã¦ã£ã«ã¹ã½ããã¦ã§ã¢ã¯ããã®ãããªææ³ã§httpséä¿¡ã®å
容ããã§ãã¯ãã¦ãã¾ãã
Avastãå
¥ããç¶æ
ã§ãã©ã¦ã¶ã§è¨¼ææ¸ãã§ã¼ã³ã確èªããã¨ã ãAvast trusted CAãã¨ããè¬ã®èªè¨¼å±ãåºç¾ããã®ã¯ãã®ããã§ã( ;ï¾ç¿ï¾)ï¾ï½¼Î£ ï¾ï½¨ï¾ï½·ï¾ï½¨ï½¨ï½°ï½°ï½¯!!!
以åãLenovoã®PCã«ããªã¤ã³ã¹ãã¼ã«ãããã¢ãã¦ã§ã¢ãSuperfishããã«ã¼ãè¨¼ææ¸ãã·ã¹ãã ã«ã¤ã³ã¹ãã¼ã«ããä¸ã«ãå
¨PCã§å
±éã®CAç§å¯éµã使ã£ã¦ãããã¨ã§å¤§åé¡ã«ãªãã¾ãããã
Dellã§ãä¼¼ããããªãã¨ããã£ãã¿ããã§ã( êªâêª)
Lenovoã®PCå ¨æ©ç¨®ã«ãã¬ãã¼ãããã¦ããã¢ãã¦ã§ã¢ãå®ã¯æããããã«ã¦ã§ã¢ã ã£ãï¼ | TechCrunch Japan Dellã®PCã«ä¸å¯©ãªã«ã¼ãè¨¼ææ¸ãLenovoã®Superfishã¨åãåé¡ã - ITmedia ã¨ã³ã¿ã¼ãã©ã¤ãº
å®è£
ããã§ã¯å ·ä½çãªå®è£ ã®è§£èª¬ãè¡ãã¾ããå¦çã®æµãã¯ä»¥ä¸ã®ã¨ããã§ãã
- CONNECTã¡ã½ããã®ãªã¯ã¨ã¹ããããhttp.Hijackerã使ã£ã¦çã®TCPã³ãã¯ã·ã§ã³ãåãåºã
- ã¯ã©ã¤ã¢ã³ãã«ã¯200 okãè¿ã
- æ¥ç¶å ãã¹ãã®è¨¼ææ¸ããäºãç¨æãã¦ããrootè¨¼ææ¸ã§ãµã¤ã³ãã¦çæãã
- çæããè¨¼ææ¸ã§ã¯ã©ã¤ã¢ã³ãã¨tlsæ¥ç¶ã確ç«ãã (rootè¨¼ææ¸ãç»é²ããã¦ããªãã¨ãã©ã¦ã¶ã§è¦åãåºã)
- goroutineèµ·ããã¦ãã¯ã©ã¤ã¢ã³ãã¨ã®tlsæ¥ç¶ããhttp requestãèªã¿è¾¼ã
- åããhttp requestããã®ã¾ã¾æ¥ç¶å hostã«éä¿¡ãã
- æ¥ç¶å hostããã®http responseããã¯ã©ã¤ã¢ã³ãtlsæ¥ç¶ã«æ¸ãè¾¼ã
- EOFãæ¥ãã¾ã§ 5-7ç¹°ãè¿ã
https://github.com/yuroyoro/mitm_proxy_sample/blob/master/https.go#L57
func (proxy *MiTMProxy) mitmRequest(w http.ResponseWriter, r *http.Request) { // http.Hicjacker ãå©ç¨ãã¦ã¯ã©ã¤ã¢ã³ãã¨ã®çã®TCPã³ãã¯ã·ã§ã³ãåãåºã conn := hijackConnect(w) // ã¯ã©ã¤ã¢ã³ãã«200 OKãè¿ãã¦ãã conn.Write([]byte("HTTP/1.0 200 OK\r\n\r\n")) // 以éã®å¦çã¯goroutineä¸ã§è¡ã go proxy.transportHTTPSRequest(w, r, conn) } func (proxy *MiTMProxy) transportHTTPSRequest(w http.ResponseWriter, r *http.Request, conn net.Conn) { proxy.info("transportHTTPSRequest : %s %s", r.Method, r.URL.String()) // 対象ãã¹ãã®ãã»ã®ãµã¼ãã¼è¨¼ææ¸ãçæãã¦ç½²åãã host := r.Host tlsConfig, err := proxy.generateTLSConfig(host) if err != nil { if _, err := conn.Write([]byte("HTTP/1.0 500 Internal Server Error\r\n\r\n")); err != nil { proxy.error("Failed to write response : %v", err) } conn.Close() } // ã¯ã©ã¤ã¢ã³ãã¨ã®TCPæ¥ç¶ä¸ã§ããã»ã®ãµã¼ãã¼è¨¼ææ¸ãå©ç¨ãã¦TLSæ¥ç¶ãå¾ ã¡åãã tlsConn := tls.Server(conn, tlsConfig) if err := tlsConn.Handshake(); err != nil { proxy.error("Cannot handshake client %v %v", r.Host, err) return } defer tlsConn.Close() proxy.info("transportHTTPSRequest : established tls connection") // ãã»ã®è¨¼ææ¸ã§ç¢ºç«ããTLSæ¥ç¶ä¸ã§ã¯ã©ã¤ã¢ã³ãããã®ãªã¯ã¨ã¹ããèªã¿è¾¼ã tlsIn := bufio.NewReader(tlsConn) for !isEOF(tlsIn) { req, err := http.ReadRequest(tlsIn) // http.Requestãªãã¸ã§ã¯ãã¨ãã¦éä¿¡ãèªã¿è¾¼ã if err != nil { if err == io.EOF { proxy.error("EOF detected when read request from client: %v %v", r.Host, err) } else { proxy.error("Cannot read request from client: %v %v", r.Host, err) } return } proxy.info("transportHTTPSRequest : read request : %s %s", req.Method, req.URL.String()) // 転éç¨ã«URLããããã¼ãªã©ãè¨å® req.URL.Scheme = "https" req.URL.Host = r.Host req.RequestURI = req.URL.String() req.RemoteAddr = r.RemoteAddr dumpRequest(req) removeProxyHeaders(req) // http.RoundTripper ã§åä¿¡ãããªã¯ã¨ã¹ãã対象ãã¹ãã«è»¢éããã¬ã¹ãã³ã¹ãåãåã resp, err := proxy.transport.RoundTrip(req) if err != nil { proxy.error("error read response %v %v", r.URL.Host, err.Error()) if resp == nil { http.Error(w, err.Error(), 500) return } } proxy.info("transportHTTPSRequest : transport request: %s", resp.Status) dumpResponse(resp) // ã¬ã¹ãã³ã¹ãã¯ã©ã¤ã³ãã¸ã®TLSæ¥ç¶ã«æ¸ãè¾¼ã resp.Write(tlsConn) } proxy.info("transportHTTPSRequest : finished ") }
ãã¤ã³ãã¯ã ãªã¯ã¨ã¹ããåããã¨ãã»ã®ãµã¼ãã¼è¨¼ææ¸ããã®å ´ã§çæãã¦ããã®è¨¼ææ¸ã¨ http.Hijacker
ã§åãåºããã¯ã©ã¤ã³ãã®TCPã§ tls.Server
ãç¨ãã¦TLSæ¥ç¶ããªããã¾ããã¨ã§ã(çæããè¨¼ææ¸ã¯ãã£ãã·ã¥ãã¾ã)ã
è¨¼ææ¸ã®çæã¯é·ããªãã®ã§ããã«ã¯è¼ãã¾ãããã ãã¡ã ãè¦ã¦ãããã°ã¨æãã¾ãã
ã¯ã©ã¤ã³ãã¨ã®TLSæ¥ç¶ãä¹ã£åãã°ããã¨ã¯ãã®æ¥ç¶ä¸ã§Httpãªã¯ã¨ã¹ããèªã¿è¾¼ã¿ã対象ãã¹ãã«è»¢éããã°OKã§ããGoã§ã¯ã http.RoundTripper
ãå©ç¨ããã° http.Request
ããã®ã¾ã¾è»¢éã§ããã®ã§ä¾¿å©ã§ãããã®éã«ããªã¯ã¨ã¹ãã»ã¬ã¹ãã³ã¹ã®å
容ãdumpãã¦ãã¾ãã
æªæãããã°ããã®æ®µéã§æ¹ç«ãå¯è½ã§ãããã
ã¾ã¨ã
以ä¸ããMan-in-The-Middle Attackãè¡ãç°¡åãªProxyã®å®è£ ã§ãããã®æ»æãæåããæ¡ä»¶ã¨ãã¦ã¯ã
- çµè·¯ä¸ã«ãã®ãããªProxyãåå¨ãã (httpséä¿¡ãport forwardããã¦ããå ´åããã)
- Proxyãç½²åã«ä½¿ãã«ã¼ãè¨¼ææ¸ãTrust Chainã«åå¨ãã
ã®2ç¹ã§ããç¹ã«2ç¹ç®ã¯TLSã®æ ¹å¹¹ããªãé¨åã§ãããããã«ã«ã¼ãè¨¼ææ¸ã®ç®¡çã¯å³æ ¼ã«è¡ãå¿ è¦ããããGoogleã¯Symantecã®è¨¼ææ¸ãç¡å¹ã«ããLenoveãDellã¯è²¬ããããã¹ããªã®ã§ããAvastãã¡ãã£ã¨ã©ããã¨æãã¾ãã
å®éã«æãåããã¦å®è£ ãã¦ã¿ãã¨ãProxyã®å®è£ ã§æ³¨æã¹ã¹ãç¹ããTLSã¨èªè¨¼å±ã®ä»çµã¿ã¨ãè²ã ã¨å¦ã³ããããããã£ãã¨ãããã¾ãã( êªâêª)