mic.git


mic

~ proxy for JA4-TLS fingerprinting evasion
git clone https://github.com/djnnvx/mic.git
log | files | refs | archive | README | LICENSE

commit bb5fa11ac3c670907015b6560e33aba728eaf81c
parent 71efed9579fab3237ca9ea7a0ff701155d226581
Author: djnn <[email protected]>
Date: Wed, 20 Aug 2025 21:32:00 +0900

feat: adding https proxy

Diffstat:
Dca/ca.go | 0
Dcert/print.go | 0
Mcmd/root.go | 2+-
Mcmd/types.go | 12++++++------
Mgo.mod | 7++++---
Mgo.sum | 6++----
Dproxy/backend.go | 27---------------------------
Dproxy/httpconnect.go | 22----------------------
Aproxy/https.go | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mproxy/proxy.go | 11++++++++---
Dproxy/socks5.go | 25-------------------------

11 files changed, 119 insertions(+), 91 deletions(-)

diff --git a/ca/ca.go b/ca/ca.go
diff --git a/cert/print.go b/cert/print.go
diff --git a/cmd/root.go b/cmd/root.go
@@ -55,8 +55,8 @@ func getParser(opts *MicCliOptions) *cobra.Command {
 	defaults := GetDefaultOptions()
 
 	rootCmd.Flags().StringVarP(&opts.Addr, "addr", "a", defaults.Addr, "address to listen to")
-	rootCmd.Flags().StringVarP(&opts.CaPath, "ca-cert-path", "c", defaults.CaPath, "custom CA certificate path (optional)")
 	rootCmd.Flags().IntVarP(&opts.Port, "port", "p", defaults.Port, "port to listen to")
+	rootCmd.Flags().IntVarP(&opts.ForwardTo, "forward-to", "t", defaults.ForwardTo, "local port to forward to")
 	rootCmd.Flags().BoolVarP(&opts.Verbose, "verbose", "v", defaults.Verbose, "enable verbose mode")
 
 	return rootCmd
diff --git a/cmd/types.go b/cmd/types.go
@@ -8,8 +8,8 @@ type MicCliOptions struct {
 	/// Addr: address to listen to
 	Addr string
 
-	/// CaPath: custom CA certificate path (optional)
-	CaPath string
+	/// ForwardTo: local port to forward call to
+	ForwardTo int
 
 	/// Verbose: enable verbose mode
 	Verbose bool
@@ -17,10 +17,10 @@ type MicCliOptions struct {
 
 func GetDefaultOptions() MicCliOptions {
 	opts := MicCliOptions{
-		Port:    8080,
-		Addr:    "127.0.0.1",
-		CaPath:  "",
-		Verbose: false,
+		Port:      8080,
+		Addr:      "127.0.0.1",
+		ForwardTo: 1337,
+		Verbose:   false,
 	}
 
 	return opts
diff --git a/go.mod b/go.mod
@@ -2,14 +2,15 @@ module github.com/djnnvx/mic
 
 go 1.24.4
 
-require github.com/spf13/cobra v1.9.1
+require (
+	github.com/refraction-networking/utls v1.8.0
+	github.com/spf13/cobra v1.9.1
+)
 
 require (
 	github.com/andybalholm/brotli v1.0.6 // indirect
-	github.com/cloudflare/circl v1.5.0 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/klauspost/compress v1.17.4 // indirect
-	github.com/refraction-networking/utls v1.7.3 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
 	golang.org/x/crypto v0.36.0 // indirect
 	golang.org/x/sys v0.31.0 // indirect
diff --git a/go.sum b/go.sum
@@ -1,14 +1,12 @@
 github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
 github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
-github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
 github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
-github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo=
-github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
+github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE=
+github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
diff --git a/proxy/backend.go b/proxy/backend.go
@@ -1,27 +0,0 @@
-package proxy
-
-import (
-	"bufio"
-	"log"
-	"net"
-)
-
-func backendHandler(conn net.Conn, p *Proxy) {
-	defer conn.Close()
-	reader := bufio.NewReader(conn)
-
-	/* peek to determine protocol */
-	peek, err := reader.Peek(1)
-	if err != nil {
-		log.Printf("[-] peek failed: %s\n", err.Error())
-		return
-	}
-
-	if isSocks5(peek) {
-
-	}
-}
-
-func init() {
-	RegisterHandler(backendHandler)
-}
diff --git a/proxy/httpconnect.go b/proxy/httpconnect.go
@@ -1,22 +0,0 @@
-package proxy
-
-import (
-	"bufio"
-	"net"
-	"strings"
-)
-
-func httpConnectHandler(conn net.Conn, p *Proxy) {
-	reader := bufio.NewReader(conn)
-	line, err := reader.Peek(7)
-
-	if err != nil || !strings.HasPrefix(string(line), "CONNECT") {
-		return /* not HTTP, let next handler deal with it */
-	}
-
-	conn.Close()
-}
-
-func init() {
-	RegisterHandler(httpConnectHandler)
-}
diff --git a/proxy/https.go b/proxy/https.go
@@ -0,0 +1,98 @@
+package proxy
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"log"
+	"math/rand"
+	"net"
+	"net/http"
+	"strings"
+	"sync"
+
+	utls "github.com/refraction-networking/utls"
+)
+
+func httpsHandler(clientConn net.Conn, p *Proxy) {
+	defer clientConn.Close()
+
+	// List of different browser fingerprints to cycle through.
+	var helloIDs = []utls.HelloID{
+		utls.HelloChrome_116,
+		utls.HelloFirefox_105,
+		utls.HelloSafari_16_0,
+		utls.HelloEdge_106,
+	}
+
+	// Read the first line of the request to check for the CONNECT method.
+	reader := bufio.NewReader(clientConn)
+	req, err := http.ReadRequest(reader)
+	if err != nil {
+		log.Printf("Failed to read client request: %v", err)
+		return
+	}
+
+	// An HTTPS proxy relies on the HTTP CONNECT method.
+	if req.Method != http.MethodConnect {
+		log.Printf("Received non-CONNECT request: %s %s", req.Method, req.Host)
+		return
+	}
+
+	// The Host field contains the destination host and port.
+	host := req.URL.Host
+	if !strings.Contains(host, ":") {
+		// If no port is specified by the client, use the `ForwardTo` port from our Proxy struct.
+		host = fmt.Sprintf("%s:%d", host, p.ForwardTo)
+	}
+
+	log.Printf("Connecting to target: %s", host)
+
+	// Establish a TCP connection to the target server.
+	targetConn, err := net.Dial("tcp", host)
+	if err != nil {
+		log.Printf("Failed to dial target %s: %v", host, err)
+		return
+	}
+	defer targetConn.Close()
+
+	randomIndex := rand.Intn(len(helloIDs))
+	randomHelloID := helloIDs[randomIndex]
+	log.Printf("Using uTLS fingerprint: %s", randomHelloID.String())
+
+	// Create a uTLS client with the randomly selected fingerprint & perform handshake
+	utlsConfig := &utls.Config{
+		ServerName: strings.Split(host, ":")[0],
+	}
+	uclient := utls.UClient(targetConn, utlsConfig, randomHelloID)
+
+	if err := uclient.Handshake(); err != nil {
+		log.Printf("uTLS handshake failed with %s: %v", host, err)
+		return
+	}
+	defer uclient.Close()
+
+	clientConn.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	// Pipe data from the client to the target server.
+	go func() {
+		defer wg.Done()
+		_, err := io.Copy(uclient, reader)
+		if err != nil && err != io.EOF {
+			log.Printf("Error copying from client to target: %v", err)
+		}
+	}()
+
+	// Pipe data from the target server back to the client.
+	go func() {
+		defer wg.Done()
+		_, err := io.Copy(clientConn, uclient)
+		if err != nil && err != io.EOF {
+			log.Printf("Error copying from target to client: %v", err)
+		}
+	}()
+
+	wg.Wait()
+}
diff --git a/proxy/proxy.go b/proxy/proxy.go
@@ -1,13 +1,13 @@
 package proxy
 
 import (
-	"crypto/x509"
+	"log"
 	"net"
 )
 
 type Proxy struct {
 	ListenAddr string
-	CAPool *x509.CertPool
+	ForwardTo  int
 }
 
 // function is a function that processes an incoming client connection
@@ -27,16 +27,21 @@ func (p *Proxy) Run() error {
 	if err != nil {
 		return err
 	}
+	defer ln.Close()
+
+	log.Printf("[+] Starting proxy on %s, forwarding to port %d", p.ListenAddr, p.ForwardTo)
 
 	for {
 		c, err := ln.Accept()
 		if err != nil {
-			return err
+			log.Printf("Failed to accept connection: %v", err)
+			continue
 		}
 
 		conn := c
 		go func() {
 			for _, h := range handlers {
+				// Each handler processes the incoming connection.
 				h(conn, p)
 			}
 		}()
diff --git a/proxy/socks5.go b/proxy/socks5.go
@@ -1,25 +0,0 @@
-package proxy
-
-import (
-	"bufio"
-	"net"
-)
-
-func isSocks5(peek []byte) bool {
-	return peek != nil || peek[0] == 0x05
-}
-
-func socks5Handler(conn net.Conn, p *Proxy) {
-	reader := bufio.NewReader(conn)
-	peek, err := reader.Peek(1)
-
-	if err != nil || peek[0] != 0x05 {
-		return /* not SOCKS5, let next handler deal with it */
-	}
-
-	conn.Close()
-}
-
-func init() {
-	RegisterHandler(socks5Handler)
-}