mic~ proxy for JA4-TLS fingerprinting evasion |
| git clone https://github.com/djnnvx/mic.git |
| log | files | refs | archive | README | LICENSE |
README.md (6411B)
1 # mic 2 3 mic (mina-is-cute) is a modular Go proxy for controlling outbound TLS fingerprints. 4 Pick a browser profile and the proxy connects upstream with the matching 5 [`bogdanfinn/utls`](https://github.com/bogdanfinn/utls) preset. 6 7 Two modes: 8 9 - **client-front**: HTTP CONNECT proxy with optional MitM TLS interception. The proxy 10 generates a local CA, issues per-host leaf certs on the fly, terminates TLS from 11 the client, and re-dials the target with the configured fingerprint. Standard 12 tools (curl, browsers) work after importing the CA once. 13 - **server-front**: the proxy terminates incoming TLS with your own cert/key, then 14 re-dials the backend with the configured fingerprint. Use it when the client cannot 15 be pointed at a CONNECT proxy. 16 17 ## How it works 18 19 ```mermaid 20 sequenceDiagram 21 participant C as Client 22 participant P as mic proxy 23 participant T as Target 24 25 rect rgb(30, 30, 60) 26 note over C,T: client-front mode (MitM TLS) 27 C->>P: HTTP CONNECT target:443 28 P->>T: TCP + uTLS handshake (configured fingerprint) 29 note right of P: JA4 = configured profile 30 P-->>C: 200 Connection Established 31 C->>P: TLS handshake (mic-issued cert for target) 32 P-->>C: TLS established 33 note left of P: JA4S = stdlib crypto/tls (measured only) 34 C->>P: HTTP request (decrypted by proxy) 35 P->>T: request bytes (through uTLS tunnel) 36 T-->>P: HTTP response (through uTLS) 37 P-->>C: HTTP response (re-encrypted for client) 38 end 39 40 rect rgb(30, 60, 30) 41 note over C,T: server-front mode 42 C->>P: TLS handshake (proxy cert) 43 P-->>C: TLS established 44 note left of P: JA4S = stdlib crypto/tls (measured only) 45 P->>T: TCP + uTLS handshake (configured fingerprint) 46 note right of P: JA4 = configured profile 47 C->>P: HTTP request (decrypted by proxy) 48 P->>T: request bytes (through uTLS tunnel) 49 T-->>P: HTTP response (through uTLS) 50 P-->>C: HTTP response (re-encrypted for client) 51 end 52 ``` 53 54 The ServerHello mic returns to the client (JA4S) still comes from stdlib 55 `crypto/tls`. It is measured, not spoofed. See the JA4S section. 56 57 ## Build 58 59 ```bash 60 go build -o mic . 61 ``` 62 63 ## Running 64 65 ### client-front 66 67 ```bash 68 mic client --listen :8080 --fingerprint chrome-120 \ 69 --intercept-cert ca.pem --intercept-key ca-key.pem 70 ``` 71 72 `--intercept-cert` and `--intercept-key` enable MitM interception. mic generates the 73 CA files on first run and reuses them. Omit both to run as a plain CONNECT proxy with 74 a raw tunnel and no MitM. 75 76 **Trust the CA** (pick whichever applies): 77 78 ```bash 79 # curl: pass on every call, or set CURL_CA_BUNDLE 80 curl --cacert ca.pem -x http://localhost:8080 https://tlsinfo.me/json 81 82 # Debian / Ubuntu / Kali 83 sudo cp ca.pem /usr/local/share/ca-certificates/mic-ca.crt && sudo update-ca-certificates 84 85 # macOS 86 sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.pem 87 ``` 88 89 Once trusted, no extra flags are needed: 90 91 ```bash 92 curl -x http://localhost:8080 https://tlsinfo.me/json 93 # check the "ja4" field. It should match the fingerprint you configured. 94 ``` 95 96 ### server-front 97 98 Generate a cert for the proxy to present to clients: 99 100 ```bash 101 go run $(go env GOROOT)/src/crypto/tls/generate_cert.go --host="localhost,127.0.0.1" 102 # produces cert.pem and key.pem 103 ``` 104 105 ```bash 106 mic server --listen :8080 --backend 10.0.0.1:443 \ 107 --cert cert.pem --key key.pem \ 108 --fingerprint chrome-120 109 ``` 110 111 `--backend`, `--cert`, and `--key` are required. `--fingerprint` is optional. Without 112 it the proxy falls back to a randomised uTLS preset. 113 114 ### Available fingerprint profiles 115 116 Hashes are measured by `cmd/probe` against tlsinfo.me and reflect what each utls 117 preset actually emits. Re-run the probe after upgrading the utls dependency. 118 119 | Profile | JA4 hash | 120 |---|---| 121 | `chrome-120` | `t13d1516h2_8daaf6152771_02713d6af862` | 122 | `chrome-120-pq` | same as `chrome-120`¹ | 123 | `chrome-131` | same as `chrome-120`¹ | 124 | `chrome-133` | `t13d1516h2_8daaf6152771_d8a2da3f94cd` | 125 | `firefox-120` | `t13d1715h2_5b57614c22b0_5c2c66f702b0` | 126 | `safari-16` | `t13d2014h2_a09f3c656075_14788d8d241b` | 127 | `ios-16` | same as `safari-16`² | 128 | `edge-85` | `t13d1515h2_8daaf6152771_de4a06bb82e3` | 129 | `edge-106` | `t13d1516h2_8daaf6152771_e5627efa2ab1` | 130 | `opera-91` | same as `edge-106`³ | 131 | `android-11-okhttp` | `t12d120700_d34a8e72043a_036209cd1ead` (TLS 1.2) | 132 | `ipad-15` | same as `safari-16`² | 133 | `qq-11` | same as `edge-106`³ | 134 | `360-7` | `t12d2010s2_0bf03fa604e3_736b2a1ed4d3` (TLS 1.2, spdy ALPN) | 135 | `360-11` | `t13d1616h2_46e7e9700bed_4551aecd7b38` | 136 137 > ¹ JA4 does not distinguish key share entries. Use `chrome-120-pq` when you need the 138 > X25519MLKEM768 post-quantum key exchange, `chrome-131` for the newest non-PQ Chrome 139 > preset under this hash. 140 > 141 > ² `ios-16`, `ipad-15` and `safari-16` ship the same TLS stack. 142 > 143 > ³ `opera-91`, `qq-11` and `edge-106` are all Chromium derivatives with the same 144 > ClientHello shape. 145 146 ## Testing 147 148 ```bash 149 # unit tests 150 go test ./... 151 152 # integration tests: spin up in-process TLS servers, no network required 153 go test -tags integration -v ./proxy/... ./fingerprint/... 154 ``` 155 156 `TestClientFront_JA4` and `TestServerFront_JA4` capture the raw ClientHello the uTLS 157 engine sends for each profile and assert the computed JA4 hash. This is the offline 158 equivalent of running `cmd/probe` against tlsinfo.me. 159 160 CI runs both suites on every push via `.github/workflows/ci.yml`. 161 162 ## JA4S (server-side fingerprint, measurement only) 163 164 mic computes JA4S, the fingerprint of the ServerHello it emits to clients. There is no 165 spoofing engine. `TestServerFront_JA4S_Baseline` and `TestClientFront_JA4S_Baseline` 166 capture the bytes mic writes during the handshake and assert the JA4S against a 167 recorded baseline. 168 169 The baseline is what stdlib `crypto/tls.Server` emits today: `t130200_1301_a56c5b993250` 170 (TLS 1.3, no ALPN, `TLS_AES_128_GCM_SHA256`, default extension set). It applies to 171 both modes because both terminate TLS with the same stdlib server. 172 173 Why no spoofing yet: 174 175 - `bogdanfinn/utls` does not expose server-side fingerprint control. Its `Server()` 176 delegates to stdlib's TLS state machine. 177 - TLS 1.3 ServerHello carries only 2-3 extensions, so the JA4S surface is much 178 smaller than JA4. 179 180 Spoofing would need either a utls fork adding a `ServerHelloSpec`, or a custom TLS server.