From bda22e47ee7c312c08ef4020d485f4a8c8b30544 Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Sat, 9 Jul 2016 19:40:23 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9listome.com/log=E6=9C=AA?= =?UTF-8?q?=E5=8C=85=E5=90=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- genKey.go | 8 +++----- gomitmproxy.go | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/genKey.go b/genKey.go index 2f961f5..0a54766 100644 --- a/genKey.go +++ b/genKey.go @@ -13,8 +13,6 @@ import ( "net" "os" "time" - - "listome.com/log" ) const ( @@ -87,7 +85,7 @@ func (key *PrivateKey) WriteToFile(filename string) (err error) { return fmt.Errorf("Unable to PEM encode private key: %s", err) } if err := keyOut.Close(); err != nil { - log.Debugf("Unable to close file: %v", err) + logger.Printf("Unable to close file: %v", err) } return } @@ -238,7 +236,7 @@ func (cert *Certificate) WriteToFile(filename string) (err error) { } defer func() { if err := certOut.Close(); err != nil { - log.Debugf("Unable to close file: %v", err) + logger.Printf("Unable to close file: %v", err) } }() return pem.Encode(certOut, cert.pemBlock()) @@ -266,7 +264,7 @@ func (cert *Certificate) WriteToDERFile(filename string) (err error) { } defer func() { if err := certOut.Close(); err != nil { - log.Debugf("Unable to close file: %v", err) + logger.Printf("Unable to close file: %v", err) } }() _, err = certOut.Write(cert.derBytes) diff --git a/gomitmproxy.go b/gomitmproxy.go index 8ba8add..c358b15 100644 --- a/gomitmproxy.go +++ b/gomitmproxy.go @@ -59,7 +59,6 @@ func gomitmproxy(conf *Cfg) { Handler: handler, ReadTimeout: 1 * time.Hour, WriteTimeout: 1 * time.Hour, - TLSConfig: tlsConfig.ServerTLSConfig, } go func() { From ddbecae506956ae3c2aa931255fce3cbb18b4de8 Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Thu, 14 Jul 2016 00:08:52 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E6=AD=A3http=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=92=8Chttp=20post=E8=AF=B7=E6=B1=82=E7=9A=84=E6=8A=93?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 14 +++ mitm.go | 346 ++++++++------------------------------------------------ 2 files changed, 63 insertions(+), 297 deletions(-) diff --git a/dump.go b/dump.go index d1a6290..e437a9c 100644 --- a/dump.go +++ b/dump.go @@ -36,7 +36,21 @@ func httpDump(req *http.Request, resp *http.Response) { for headerName, headerContext := range resp.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) } + if req.Method == "POST" { + fmt.Println(Green("URLEncoded form")) + err := req.ParseForm() + fmt.Printf("%#v\n", req.Form) + fmt.Printf("%#v\n", req.PostForm) + if err != nil { + logger.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("%s: %s\n", Blue(k), v) + } + } + + } respBody, err := ioutil.ReadAll(resp.Body) if err != nil { logger.Println("func httpDump read resp body err:", err) diff --git a/mitm.go b/mitm.go index 29656cb..c358b15 100644 --- a/mitm.go +++ b/mitm.go @@ -1,332 +1,84 @@ +// This example shows a proxy server that uses go-mitm to man-in-the-middle +// HTTPS connections opened with CONNECT requests + package main import ( - "bufio" - "crypto/tls" - "errors" - "fmt" - "io" + "flag" "log" - "net" "net/http" - "net/http/httputil" - "net/url" - "regexp" - "strings" + "os" "sync" "time" ) -type HandlerWrapper struct { - MyConfig *Cfg - tlsConfig *TlsConfig - wrapped http.Handler - pk *PrivateKey - pkPem []byte - issuingCert *Certificate - issuingCertPem []byte - serverTLSConfig *tls.Config - dynamicCerts *Cache - certMutex sync.Mutex - https bool -} - -func (hw *HandlerWrapper) GenerateCertForClient() (err error) { - if hw.tlsConfig.Organization == "" { - hw.tlsConfig.Organization = "gomitmproxy" + Version - } - if hw.tlsConfig.CommonName == "" { - hw.tlsConfig.CommonName = "gomitmproxy" - } - if hw.pk, err = LoadPKFromFile(hw.tlsConfig.PrivateKeyFile); err != nil { - hw.pk, err = GeneratePK(2048) - if err != nil { - return fmt.Errorf("Unable to generate private key: %s", err) - } - hw.pk.WriteToFile(hw.tlsConfig.PrivateKeyFile) - } - hw.pkPem = hw.pk.PEMEncoded() - hw.issuingCert, err = LoadCertificateFromFile(hw.tlsConfig.CertFile) - if err != nil || hw.issuingCert.ExpiresBefore(time.Now().AddDate(0, ONE_MONTH, 0)) { - hw.issuingCert, err = hw.pk.TLSCertificateFor( - hw.tlsConfig.Organization, - hw.tlsConfig.CommonName, - time.Now().AddDate(ONE_YEAR, 0, 0), - true, - nil) - if err != nil { - return fmt.Errorf("Unable to generate self-signed issuing certificate: %s", err) - } - hw.issuingCert.WriteToFile(hw.tlsConfig.CertFile) - } - hw.issuingCertPem = hw.issuingCert.PEMEncoded() - return -} - -func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, err error) { - kpCandidateIf, found := hw.dynamicCerts.Get(name) - if found { - return kpCandidateIf.(*tls.Certificate), nil - } - - hw.certMutex.Lock() - defer hw.certMutex.Unlock() - kpCandidateIf, found = hw.dynamicCerts.Get(name) - if found { - return kpCandidateIf.(*tls.Certificate), nil - } - - //create certificate - certTTL := TWO_WEEKS - generatedCert, err := hw.pk.TLSCertificateFor( - hw.tlsConfig.Organization, - name, - time.Now().Add(certTTL), - false, - hw.issuingCert) - if err != nil { - return nil, fmt.Errorf("Unable to issue certificate: %s", err) - } - keyPair, err := tls.X509KeyPair(generatedCert.PEMEncoded(), hw.pkPem) - if err != nil { - return nil, fmt.Errorf("Unable to parse keypair for tls: %s", err) - } - - cacheTTL := certTTL - ONE_DAY - hw.dynamicCerts.Set(name, &keyPair, cacheTTL) - return &keyPair, nil -} - -func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { - req.Header.Del("Proxy-Connection") - req.Header.Set("Connection", "Keep-Alive") - - // handle connection - connIn, _, err := resp.(http.Hijacker).Hijack() - if err != nil { - logger.Println("hijack error:", err) - } - defer connIn.Close() - - var respOut *http.Response - host := req.Host - - matched, _ := regexp.MatchString(":[0-9]+$", host) - - if !hw.https { - if !matched { - host += ":80" - } - - connOut, err := net.DialTimeout("tcp", host, time.Second*30) - if err != nil { - logger.Println("dial to", host, "error:", err) - } - respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) - if err != nil && err != io.EOF { - logger.Println("read response error:", err) - } - - } else { - if !matched { - host += ":443" - } - - connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig) - - if err != nil { - logger.Panicln("tls dial to", host, "error:", err) - } - if err = req.Write(connOut); err != nil { - logger.Println("send to server error", err) - } +const ( + Version = "1.1" +) - respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) - if err != nil && err != io.EOF { - logger.Println("read response error:", err) - } +var ( + wg sync.WaitGroup +) - } +var logFile *os.File +var logger *log.Logger - if respOut == nil { - log.Println("respOut is nil") - return - } +func main() { + var conf Cfg - respDump, err := httputil.DumpResponse(respOut, true) - if err != nil { - logger.Println("respDump error:", err) - } + conf.Port = flag.String("port", "8080", "Listen port") + conf.Raddr = flag.String("raddr", "", "Remote addr") + conf.Log = flag.String("log", "./error.log", "log file path") + conf.Monitor = flag.Bool("m", false, "monitor mode") + conf.Tls = flag.Bool("tls", false, "tls connect") + flag.Parse() - _, err = connIn.Write(respDump) + var err error + logFile, err = os.Create(*conf.Log) if err != nil { - logger.Println("connIn write error:", err) + log.Fatalln("fail to create log file!") } - if *hw.MyConfig.Monitor { - go httpDump(req, respOut) - } + logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) + wg.Add(1) + gomitmproxy(&conf) + wg.Wait() } -func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { +func gomitmproxy(conf *Cfg) { + tlsConfig := NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "") - raddr := *hw.MyConfig.Raddr - if len(raddr) != 0 { - hw.Forward(resp, req, raddr) - } else { - if req.Method == "CONNECT" { - hw.https = true - hw.InterceptHTTPs(resp, req) - } else { - hw.DumpHTTPAndHTTPs(resp, req) - } - } -} - -func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Request) { - addr := req.Host - host := strings.Split(addr, ":")[0] - - cert, err := hw.FakeCertForName(host) + handler, err := InitConfig(conf, tlsConfig) if err != nil { - msg := fmt.Sprintf("Could not get mitm cert for name: %s\nerror: %s", host, err) - respBadGateway(resp, msg) - return + logger.Fatalf("InitConfig error: %s", err) } - // handle connection - connIn, _, err := resp.(http.Hijacker).Hijack() - if err != nil { - msg := fmt.Sprintf("Unable to access underlying connection from client: %s", err) - respBadGateway(resp, msg) - return + server := &http.Server{ + Addr: ":" + *conf.Port, + Handler: handler, + ReadTimeout: 1 * time.Hour, + WriteTimeout: 1 * time.Hour, } - tlsConfig := copyTlsConfig(hw.tlsConfig.ServerTLSConfig) - tlsConfig.Certificates = []tls.Certificate{*cert} - tlsConnIn := tls.Server(connIn, tlsConfig) - listener := &mitmListener{tlsConnIn} - handler := http.HandlerFunc(func(resp2 http.ResponseWriter, req2 *http.Request) { - req2.URL.Scheme = "https" - req2.URL.Host = req2.Host - hw.DumpHTTPAndHTTPs(resp2, req2) - - }) go func() { - err = http.Serve(listener, handler) - if err != nil && err != io.EOF { - logger.Printf("Error serving mitm'ed connection: %s", err) - } - }() + log.Printf("proxy listening port:%s", *conf.Port) - connIn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) -} - -func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, raddr string) { - connIn, _, err := resp.(http.Hijacker).Hijack() - connOut, err := net.Dial("tcp", raddr) - if err != nil { - logger.Println("dial tcp error", err) - } - - err = connectProxyServer(connOut, raddr) - if err != nil { - logger.Println("connectProxyServer error:", err) - } - - if req.Method == "CONNECT" { - b := []byte("HTTP/1.1 200 Connection Established\r\n" + - "Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n") - _, err := connIn.Write(b) - if err != nil { - logger.Println("Write Connect err:", err) - return + if *conf.Tls { + log.Println("ListenAndServeTLS") + err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem") + } else { + log.Println("ListenAndServe") + err = server.ListenAndServe() } - } else { - req.Header.Del("Proxy-Connection") - req.Header.Set("Connection", "Keep-Alive") - if err = req.Write(connOut); err != nil { - logger.Println("send to server err", err) - return + if err != nil { + logger.Fatalf("Unable to start HTTP proxy: %s", err) } - } - err = Transport(connIn, connOut) - if err != nil { - log.Println("trans error ", err) - } -} -func InitConfig(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { - hw := &HandlerWrapper{ - MyConfig: conf, - tlsConfig: tlsConfig, - dynamicCerts: NewCache(), - } - err := hw.GenerateCertForClient() - if err != nil { - return nil, err - } - return hw, nil -} - -func copyTlsConfig(template *tls.Config) *tls.Config { - tlsConfig := &tls.Config{} - if template != nil { - *tlsConfig = *template - } - return tlsConfig -} + wg.Done() -func respBadGateway(resp http.ResponseWriter, msg string) { - log.Println(msg) - resp.WriteHeader(502) - resp.Write([]byte(msg)) -} - -//两个io口的连接 -func Transport(conn1, conn2 net.Conn) (err error) { - rChan := make(chan error, 1) - wChan := make(chan error, 1) - - go MyCopy(conn1, conn2, wChan) - go MyCopy(conn2, conn1, rChan) - - select { - case err = <-wChan: - case err = <-rChan: - } + log.Printf("gomitmproxy stop!!!!") + }() return } - -func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { - _, err := io.Copy(dst, src) - ch <- err -} - -func connectProxyServer(conn net.Conn, addr string) error { - - req := &http.Request{ - Method: "CONNECT", - URL: &url.URL{Host: addr}, - Host: addr, - ProtoMajor: 1, - ProtoMinor: 1, - Header: make(http.Header), - } - req.Header.Set("Proxy-Connection", "keep-alive") - - if err := req.Write(conn); err != nil { - return err - } - - resp, err := http.ReadResponse(bufio.NewReader(conn), req) - if err != nil { - return err - } - if resp.StatusCode != http.StatusOK { - return errors.New(resp.Status) - } - return nil -} From 6a999726d2e44f89a09baf8235a8874d1785cffd Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Thu, 14 Jul 2016 00:10:29 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E4=BF=AE=E6=AD=A3http=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=92=8Chttp=20post=E8=AF=B7=E6=B1=82=E7=9A=84=E6=8A=93?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mitm.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/mitm.go b/mitm.go index c358b15..6a1a8f2 100644 --- a/mitm.go +++ b/mitm.go @@ -1,6 +1,3 @@ -// This example shows a proxy server that uses go-mitm to man-in-the-middle -// HTTPS connections opened with CONNECT requests - package main import ( From 3d4555e1dfac8c5f306a3a4ef22c6797bc40aa34 Mon Sep 17 00:00:00 2001 From: listomebao <1421967301@qq.com> Date: Fri, 15 Jul 2016 00:03:15 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84mitm=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 9 +- mitm.go | 353 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 312 insertions(+), 50 deletions(-) diff --git a/dump.go b/dump.go index e437a9c..ca261a1 100644 --- a/dump.go +++ b/dump.go @@ -32,10 +32,6 @@ func httpDump(req *http.Request, resp *http.Response) { for headerName, headerContext := range req.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) } - fmt.Println(Green("Response:")) - for headerName, headerContext := range resp.Header { - fmt.Printf("%s: %s\n", Blue(headerName), headerContext) - } if req.Method == "POST" { fmt.Println(Green("URLEncoded form")) err := req.ParseForm() @@ -51,6 +47,11 @@ func httpDump(req *http.Request, resp *http.Response) { } } + fmt.Println(Green("Response:")) + for headerName, headerContext := range resp.Header { + fmt.Printf("%s: %s\n", Blue(headerName), headerContext) + } + respBody, err := ioutil.ReadAll(resp.Body) if err != nil { logger.Println("func httpDump read resp body err:", err) diff --git a/mitm.go b/mitm.go index 6a1a8f2..9f8a54e 100644 --- a/mitm.go +++ b/mitm.go @@ -1,81 +1,342 @@ package main import ( - "flag" + "bufio" + "crypto/tls" + "errors" + "fmt" + "io" "log" + "net" "net/http" - "os" + "net/http/httputil" + "net/url" + "regexp" + "strings" "sync" "time" ) -const ( - Version = "1.1" -) +type HandlerWrapper struct { + MyConfig *Cfg + tlsConfig *TlsConfig + wrapped http.Handler + pk *PrivateKey + pkPem []byte + issuingCert *Certificate + issuingCertPem []byte + serverTLSConfig *tls.Config + dynamicCerts *Cache + certMutex sync.Mutex + https bool +} -var ( - wg sync.WaitGroup -) +func (hw *HandlerWrapper) GenerateCertForClient() (err error) { + if hw.tlsConfig.Organization == "" { + hw.tlsConfig.Organization = "gomitmproxy" + Version + } + if hw.tlsConfig.CommonName == "" { + hw.tlsConfig.CommonName = "gomitmproxy" + } + if hw.pk, err = LoadPKFromFile(hw.tlsConfig.PrivateKeyFile); err != nil { + hw.pk, err = GeneratePK(2048) + if err != nil { + return fmt.Errorf("Unable to generate private key: %s", err) + } + hw.pk.WriteToFile(hw.tlsConfig.PrivateKeyFile) + } + hw.pkPem = hw.pk.PEMEncoded() + hw.issuingCert, err = LoadCertificateFromFile(hw.tlsConfig.CertFile) + if err != nil || hw.issuingCert.ExpiresBefore(time.Now().AddDate(0, ONE_MONTH, 0)) { + hw.issuingCert, err = hw.pk.TLSCertificateFor( + hw.tlsConfig.Organization, + hw.tlsConfig.CommonName, + time.Now().AddDate(ONE_YEAR, 0, 0), + true, + nil) + if err != nil { + return fmt.Errorf("Unable to generate self-signed issuing certificate: %s", err) + } + hw.issuingCert.WriteToFile(hw.tlsConfig.CertFile) + } + hw.issuingCertPem = hw.issuingCert.PEMEncoded() + return +} -var logFile *os.File -var logger *log.Logger +func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, err error) { + kpCandidateIf, found := hw.dynamicCerts.Get(name) + if found { + return kpCandidateIf.(*tls.Certificate), nil + } -func main() { - var conf Cfg + hw.certMutex.Lock() + defer hw.certMutex.Unlock() + kpCandidateIf, found = hw.dynamicCerts.Get(name) + if found { + return kpCandidateIf.(*tls.Certificate), nil + } - conf.Port = flag.String("port", "8080", "Listen port") - conf.Raddr = flag.String("raddr", "", "Remote addr") - conf.Log = flag.String("log", "./error.log", "log file path") - conf.Monitor = flag.Bool("m", false, "monitor mode") - conf.Tls = flag.Bool("tls", false, "tls connect") - flag.Parse() + //create certificate + certTTL := TWO_WEEKS + generatedCert, err := hw.pk.TLSCertificateFor( + hw.tlsConfig.Organization, + name, + time.Now().Add(certTTL), + false, + hw.issuingCert) + if err != nil { + return nil, fmt.Errorf("Unable to issue certificate: %s", err) + } + keyPair, err := tls.X509KeyPair(generatedCert.PEMEncoded(), hw.pkPem) + if err != nil { + return nil, fmt.Errorf("Unable to parse keypair for tls: %s", err) + } - var err error - logFile, err = os.Create(*conf.Log) + cacheTTL := certTTL - ONE_DAY + hw.dynamicCerts.Set(name, &keyPair, cacheTTL) + return &keyPair, nil +} + +func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.Request) { + req.Header.Del("Proxy-Connection") + req.Header.Set("Connection", "Keep-Alive") + + // handle connection + connIn, _, err := resp.(http.Hijacker).Hijack() if err != nil { - log.Fatalln("fail to create log file!") + logger.Println("hijack error:", err) } + defer connIn.Close() - logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) + var respOut *http.Response + host := req.Host - wg.Add(1) - gomitmproxy(&conf) - wg.Wait() -} + matched, _ := regexp.MatchString(":[0-9]+$", host) + + if !hw.https { + if !matched { + host += ":80" + } + + connOut, err := net.DialTimeout("tcp", host, time.Second*30) + if err != nil { + logger.Println("dial to", host, "error:", err) + return + } + + if err = req.Write(connOut); err != nil { + logger.Println("send to server error", err) + return + } + + respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) + if err != nil && err != io.EOF { + logger.Println("read response error:", err) + } + + } else { + if !matched { + host += ":443" + } + + connOut, err := tls.Dial("tcp", host, hw.tlsConfig.ServerTLSConfig) + + if err != nil { + logger.Panicln("tls dial to", host, "error:", err) + return + } + if err = req.Write(connOut); err != nil { + logger.Println("send to server error", err) + return + } -func gomitmproxy(conf *Cfg) { - tlsConfig := NewTlsConfig("gomitmproxy-ca-pk.pem", "gomitmproxy-ca-cert.pem", "", "") + respOut, err = http.ReadResponse(bufio.NewReader(connOut), req) + if err != nil && err != io.EOF { + logger.Println("read response error:", err) + } + + } - handler, err := InitConfig(conf, tlsConfig) + if respOut == nil { + log.Println("respOut is nil") + return + } + + respDump, err := httputil.DumpResponse(respOut, true) if err != nil { - logger.Fatalf("InitConfig error: %s", err) + logger.Println("respDump error:", err) } - server := &http.Server{ - Addr: ":" + *conf.Port, - Handler: handler, - ReadTimeout: 1 * time.Hour, - WriteTimeout: 1 * time.Hour, + _, err = connIn.Write(respDump) + if err != nil { + logger.Println("connIn write error:", err) } - go func() { - log.Printf("proxy listening port:%s", *conf.Port) + if *hw.MyConfig.Monitor { + go httpDump(req, respOut) + } + +} - if *conf.Tls { - log.Println("ListenAndServeTLS") - err = server.ListenAndServeTLS("gomitmproxy-ca-cert.pem", "gomitmproxy-ca-pk.pem") +func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + + raddr := *hw.MyConfig.Raddr + if len(raddr) != 0 { + hw.Forward(resp, req, raddr) + } else { + if req.Method == "CONNECT" { + hw.https = true + hw.InterceptHTTPs(resp, req) } else { - log.Println("ListenAndServe") - err = server.ListenAndServe() + hw.https = false + hw.DumpHTTPAndHTTPs(resp, req) } + } +} + +func (hw *HandlerWrapper) InterceptHTTPs(resp http.ResponseWriter, req *http.Request) { + addr := req.Host + host := strings.Split(addr, ":")[0] + + cert, err := hw.FakeCertForName(host) + if err != nil { + msg := fmt.Sprintf("Could not get mitm cert for name: %s\nerror: %s", host, err) + respBadGateway(resp, msg) + return + } + + // handle connection + connIn, _, err := resp.(http.Hijacker).Hijack() + if err != nil { + msg := fmt.Sprintf("Unable to access underlying connection from client: %s", err) + respBadGateway(resp, msg) + return + } + tlsConfig := copyTlsConfig(hw.tlsConfig.ServerTLSConfig) + tlsConfig.Certificates = []tls.Certificate{*cert} + tlsConnIn := tls.Server(connIn, tlsConfig) + listener := &mitmListener{tlsConnIn} + handler := http.HandlerFunc(func(resp2 http.ResponseWriter, req2 *http.Request) { + req2.URL.Scheme = "https" + req2.URL.Host = req2.Host + hw.DumpHTTPAndHTTPs(resp2, req2) + + }) + + go func() { + err = http.Serve(listener, handler) + if err != nil && err != io.EOF { + logger.Printf("Error serving mitm'ed connection: %s", err) + } + }() + + connIn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) +} + +func (hw *HandlerWrapper) Forward(resp http.ResponseWriter, req *http.Request, raddr string) { + connIn, _, err := resp.(http.Hijacker).Hijack() + connOut, err := net.Dial("tcp", raddr) + if err != nil { + logger.Println("dial tcp error", err) + } + + err = connectProxyServer(connOut, raddr) + if err != nil { + logger.Println("connectProxyServer error:", err) + } + + if req.Method == "CONNECT" { + b := []byte("HTTP/1.1 200 Connection Established\r\n" + + "Proxy-Agent: gomitmproxy/" + Version + "\r\n\r\n") + _, err := connIn.Write(b) if err != nil { - logger.Fatalf("Unable to start HTTP proxy: %s", err) + logger.Println("Write Connect err:", err) + return + } + } else { + req.Header.Del("Proxy-Connection") + req.Header.Set("Connection", "Keep-Alive") + if err = req.Write(connOut); err != nil { + logger.Println("send to server err", err) + return } + } + err = Transport(connIn, connOut) + if err != nil { + log.Println("trans error ", err) + } +} - wg.Done() +func InitConfig(conf *Cfg, tlsConfig *TlsConfig) (*HandlerWrapper, error) { + hw := &HandlerWrapper{ + MyConfig: conf, + tlsConfig: tlsConfig, + dynamicCerts: NewCache(), + } + err := hw.GenerateCertForClient() + if err != nil { + return nil, err + } + return hw, nil +} - log.Printf("gomitmproxy stop!!!!") - }() +func copyTlsConfig(template *tls.Config) *tls.Config { + tlsConfig := &tls.Config{} + if template != nil { + *tlsConfig = *template + } + return tlsConfig +} + +func respBadGateway(resp http.ResponseWriter, msg string) { + log.Println(msg) + resp.WriteHeader(502) + resp.Write([]byte(msg)) +} + +//两个io口的连接 +func Transport(conn1, conn2 net.Conn) (err error) { + rChan := make(chan error, 1) + wChan := make(chan error, 1) + + go MyCopy(conn1, conn2, wChan) + go MyCopy(conn2, conn1, rChan) + + select { + case err = <-wChan: + case err = <-rChan: + } return } + +func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { + _, err := io.Copy(dst, src) + ch <- err +} + +func connectProxyServer(conn net.Conn, addr string) error { + + req := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Host: addr}, + Host: addr, + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + req.Header.Set("Proxy-Connection", "keep-alive") + + if err := req.Write(conn); err != nil { + return err + } + + resp, err := http.ReadResponse(bufio.NewReader(conn), req) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return errors.New(resp.Status) + } + return nil +} From 1c14528edc3f5e64f630fa3ea4e3baaca8bc39ba Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Wed, 27 Jul 2016 18:06:05 +0800 Subject: [PATCH 5/8] =?UTF-8?q?=E5=B0=9D=E8=AF=95post=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E7=9A=84=E6=8A=93=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 11 ++--------- mitm.go | 4 ++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dump.go b/dump.go index ca261a1..5629329 100644 --- a/dump.go +++ b/dump.go @@ -34,16 +34,9 @@ func httpDump(req *http.Request, resp *http.Response) { } if req.Method == "POST" { fmt.Println(Green("URLEncoded form")) - err := req.ParseForm() - fmt.Printf("%#v\n", req.Form) - fmt.Printf("%#v\n", req.PostForm) - if err != nil { - logger.Println("parseForm error:", err) - } else { - for k, v := range req.Form { - fmt.Printf("%s: %s\n", Blue(k), v) - } + for k, v := range req.Form { + fmt.Printf("%s: %s\n", Blue(k), v) } } diff --git a/mitm.go b/mitm.go index 9f8a54e..edcb998 100644 --- a/mitm.go +++ b/mitm.go @@ -179,7 +179,11 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + err := req.ParseForm() + if err != nil { + logger.Println("parseForm error:", err) + } raddr := *hw.MyConfig.Raddr if len(raddr) != 0 { hw.Forward(resp, req, raddr) From c5e10c274be457e148f3e7e0c346fd1815a2a355 Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Mon, 8 Aug 2016 10:21:27 +0800 Subject: [PATCH 6/8] develog --- dump.go | 33 ++++++++++++++++++++++++++------- mitm.go | 29 ++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/dump.go b/dump.go index 5629329..4a458f0 100644 --- a/dump.go +++ b/dump.go @@ -12,7 +12,7 @@ import ( "strconv" ) -func httpDump(req *http.Request, resp *http.Response) { +func httpDump(reqDump []byte, resp *http.Response) { defer resp.Body.Close() var respStatusStr string respStatus := resp.StatusCode @@ -27,19 +27,33 @@ func httpDump(req *http.Request, resp *http.Response) { case 5: respStatusStr = Red("<--" + strconv.Itoa(respStatus)) } - fmt.Println(Green("Request:")) - fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + fmt.Println(Green("Request:"), respStatusStr) + ParseReq(reqDump) + /*req, _ := ParseReq(reqDump) + for k, v := range req { + fmt.Println(k, ":::::", v) + }*/ + /*fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) for headerName, headerContext := range req.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) - } - if req.Method == "POST" { + }*/ + /*if req.Method == "POST" { fmt.Println(Green("URLEncoded form")) - for k, v := range req.Form { + err := req.ParseForm() + if err != nil { + logger.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("%s: %s\n", Blue(k), v) + } + } + values, _ := ParsePostValues(reqDump) + for k, v := range values { fmt.Printf("%s: %s\n", Blue(k), v) } - } + }*/ fmt.Println(Green("Response:")) for headerName, headerContext := range resp.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) @@ -77,3 +91,8 @@ func httpDump(req *http.Request, resp *http.Response) { fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) } + +func ParseReq(b []byte) error { + str := string(b) + +} diff --git a/mitm.go b/mitm.go index edcb998..4817d3a 100644 --- a/mitm.go +++ b/mitm.go @@ -102,6 +102,10 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R req.Header.Set("Connection", "Keep-Alive") // handle connection + reqDump, err := httputil.DumpRequest(req, true) + if err != nil { + logger.Println("DumpRequest error ", err) + } connIn, _, err := resp.(http.Hijacker).Hijack() if err != nil { logger.Println("hijack error:", err) @@ -173,17 +177,13 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } if *hw.MyConfig.Monitor { - go httpDump(req, respOut) + go httpDump(reqDump, respOut) } } func (hw *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { - err := req.ParseForm() - if err != nil { - logger.Println("parseForm error:", err) - } raddr := *hw.MyConfig.Raddr if len(raddr) != 0 { hw.Forward(resp, req, raddr) @@ -344,3 +344,22 @@ func connectProxyServer(conn net.Conn, addr string) error { } return nil } + +/*func ReadNotDrain(r *http.Request) (content []byte, err error) { + content, err = ioutil.ReadAll(r.Body) + r.Body = io.ReadCloser(bytes.NewBuffer(content)) + return +} + +func ParsePostValues(req *http.Request) (url.Values, error) { + c, err := ReadNotDrain(req) + if err != nil { + return nil, err + } + values, err := url.ParseQuery(string(c)) + if err != nil { + return nil, err + } + return values, nil +} +*/ From a990253ee4b464fe7d72208f1823db61bf2bd944 Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Tue, 23 Aug 2016 18:30:13 +0800 Subject: [PATCH 7/8] try catch post --- dump.go | 59 ++++++++++++++++++++++++++++++-------------------- gomitmproxy.go | 4 ++++ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/dump.go b/dump.go index 4a458f0..b7f5cee 100644 --- a/dump.go +++ b/dump.go @@ -6,6 +6,7 @@ import ( "compress/flate" "compress/gzip" "fmt" + "io" "io/ioutil" "math" "net/http" @@ -28,32 +29,38 @@ func httpDump(reqDump []byte, resp *http.Response) { respStatusStr = Red("<--" + strconv.Itoa(respStatus)) } fmt.Println(Green("Request:"), respStatusStr) - ParseReq(reqDump) - /*req, _ := ParseReq(reqDump) - for k, v := range req { - fmt.Println(k, ":::::", v) - }*/ - /*fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + req, _ := ParseReq(reqDump) + // for k, v := range req { + // fmt.Println(k, ":::::", v) + // } + fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) for headerName, headerContext := range req.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) - }*/ - /*if req.Method == "POST" { - fmt.Println(Green("URLEncoded form")) + } - err := req.ParseForm() + fmt.Printf("req:---->%#v\n", req) + if req.Method == "POST" { + fmt.Println(Green("URLEncoded form")) + fmt.Println(reqDump) + body, err := ioutil.ReadAll(req.Body) if err != nil { - logger.Println("parseForm error:", err) - } else { - for k, v := range req.Form { - fmt.Printf("%s: %s\n", Blue(k), v) - } - } - values, _ := ParsePostValues(reqDump) - for k, v := range values { - fmt.Printf("%s: %s\n", Blue(k), v) + fmt.Println("readall ", err) } + fmt.Println(string(body)) + // err := req.ParseForm() + // if err != nil { + // logger.Println("parseForm error:", err) + // } else { + // for k, v := range req.Form { + // fmt.Printf("%s: %s\n", Blue(k), v) + // } + // } + // values, _ := ParsePostValues(reqDump) + // for k, v := range values { + // fmt.Printf("%s: %s\n", Blue(k), v) + // } - }*/ + } fmt.Println(Green("Response:")) for headerName, headerContext := range resp.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) @@ -92,7 +99,13 @@ func httpDump(reqDump []byte, resp *http.Response) { fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) } -func ParseReq(b []byte) error { - str := string(b) - +func ParseReq(b []byte) (*http.Request, error) { + // func ReadRequest(b *bufio.Reader) (req *Request, err error) { return readRequest(b, deleteHostHeader) } + fmt.Println(string(b)) + fmt.Println("-----------------------") + var buf io.ReadWriter + buf = new(bytes.Buffer) + buf.Write(b) + bufr := bufio.NewReader(buf) + return http.ReadRequest(bufr) } diff --git a/gomitmproxy.go b/gomitmproxy.go index c358b15..7641918 100644 --- a/gomitmproxy.go +++ b/gomitmproxy.go @@ -31,8 +31,12 @@ func main() { conf.Log = flag.String("log", "./error.log", "log file path") conf.Monitor = flag.Bool("m", false, "monitor mode") conf.Tls = flag.Bool("tls", false, "tls connect") + help := flag.Bool("h", false, "help") flag.Parse() + if *help { + flag.PrintDefaults() + } var err error logFile, err = os.Create(*conf.Log) if err != nil { From baf79e0b3fb09353dea7c07c4c4f2ea0fc3dabc4 Mon Sep 17 00:00:00 2001 From: zhengbaoyang Date: Tue, 23 Aug 2016 19:37:11 +0800 Subject: [PATCH 8/8] =?UTF-8?q?http=20post=E8=AF=B7=E6=B1=82=E7=9A=84?= =?UTF-8?q?=E6=8A=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dump.go | 31 ++++++++----------------------- mitm.go | 11 ++++++++++- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/dump.go b/dump.go index b7f5cee..da5c07f 100644 --- a/dump.go +++ b/dump.go @@ -30,36 +30,21 @@ func httpDump(reqDump []byte, resp *http.Response) { } fmt.Println(Green("Request:"), respStatusStr) req, _ := ParseReq(reqDump) - // for k, v := range req { - // fmt.Println(k, ":::::", v) - // } - fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + fmt.Printf("%s %s %s\n", Blue(req.Method), req.Host+req.RequestURI, respStatusStr) for headerName, headerContext := range req.Header { fmt.Printf("%s: %s\n", Blue(headerName), headerContext) } - fmt.Printf("req:---->%#v\n", req) if req.Method == "POST" { - fmt.Println(Green("URLEncoded form")) - fmt.Println(reqDump) - body, err := ioutil.ReadAll(req.Body) + fmt.Println(Green("POST Param:")) + err := req.ParseForm() if err != nil { - fmt.Println("readall ", err) + logger.Println("parseForm error:", err) + } else { + for k, v := range req.Form { + fmt.Printf("\t%s: %s\n", Blue(k), v) + } } - fmt.Println(string(body)) - // err := req.ParseForm() - // if err != nil { - // logger.Println("parseForm error:", err) - // } else { - // for k, v := range req.Form { - // fmt.Printf("%s: %s\n", Blue(k), v) - // } - // } - // values, _ := ParsePostValues(reqDump) - // for k, v := range values { - // fmt.Printf("%s: %s\n", Blue(k), v) - // } - } fmt.Println(Green("Response:")) for headerName, headerContext := range resp.Header { diff --git a/mitm.go b/mitm.go index 4817d3a..bbd8337 100644 --- a/mitm.go +++ b/mitm.go @@ -101,8 +101,14 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R req.Header.Del("Proxy-Connection") req.Header.Set("Connection", "Keep-Alive") + var reqDump []byte + var err error + ch := make(chan bool) // handle connection - reqDump, err := httputil.DumpRequest(req, true) + go func() { + reqDump, err = httputil.DumpRequestOut(req, true) + ch <- true + }() if err != nil { logger.Println("DumpRequest error ", err) } @@ -177,7 +183,10 @@ func (hw *HandlerWrapper) DumpHTTPAndHTTPs(resp http.ResponseWriter, req *http.R } if *hw.MyConfig.Monitor { + <-ch go httpDump(reqDump, respOut) + } else { + <-ch } }