fedosgad / mirror_proxy

TLS-level man-in-the-middle proxy
42 stars 7 forks source link

Proxy CONNECT Headers not passed upstream #5

Closed chribro88 closed 1 year ago

chribro88 commented 1 year ago

I'm looking to add a proxy header to a request such that it will get passed to the upstream proxy. Basically, need to pass along some information to the upstream proxy (HAProxy) which will influence which actual external proxy to use.

e.g.

curl https://httpbin.org/headers --proxy-header "upstream-proxy-header: hello" -x "http://mirror-proxy:8080"

The above example doesn't actually show the CONNECT headers on the response, but on the upstream haproxy logs it doesn't exist, which may very well be by design...

Do you know if this is possible with the current module in mirror_proxy - which looks like your own, at least for http/s, at github.com/fedosgad/go-http-dialer?

chribro88 commented 1 year ago

I've just done some further testing with node-mitm-proxy using a passthrough proxy as debugging HAProxy for me is a bit of pain.

curl https://httpbin.org/headers --proxy-header "Proxy-Custom: hello" -x "http://mirror-proxy:8080" -k -v

In mirror_proxy > tls_hijacker.go#L25 indeed shows the CONNECT header.

ctx.Req.Header = {
  "User-Agent": "curl/7.74.0",
  "Proxy-Connection": "Keep-Alive",
  "Proxy-Custom": "hello"
}

I assume this is the 'Client to Mirror Proxy' connection...

After running #L25:

hj.GetConns(req.URL, connL, ctx)

node-mitm-proxy, the proxy.onConnect() event shows the request headers as

req.headers = {
  "host": "httpbin.org:443",
  "user-agent":"Go-http-client/1.1"
}

So it definitely appears as the proxy header is not passed upstream. Also though, the user agent is changed - not sure if this should be mirrored as well?

fedosgad commented 1 year ago

Hello and sorry for late answer (again)! github.com/fedosgad/go-http-dialer is actually my fork of https://github.com/mwitkow/go-http-dialer. As far as I remember it does not support adding headers to upstream CONNECT request (relevant code here). Initial mirror_proxy design didn't cover the upstream proxy connection mimicry because my usual use-case for upstream proxy is only to change the IP from which TLS-encrypted traffic originates. It is a tool to mimic initial connection to server, not to proxy. I think it will be easier for you to write your own Dialer which will send all the headers you need to upstream proxy. It is set by function getDialer and is called here. All the relevant headers are inside ctx (you have already found them). Actually, you can reuse mimic code itself (packages cert_generator, hijackers, utils) in your own tool - you only need to rewrite https://github.com/fedosgad/mirror_proxy/blob/master/mirror_proxy.go to provide your own dialer.

chribro88 commented 1 year ago

Not a problem at all!

So I did some digging thru go-http-dialer and you would need to add a way to add/set headers after L134 (will update links shortly).

This would also require a way to pass the headers as well, likely needing to modify the HTTP Tunnel properties (i.e. t.auth, t.headers) and then set them.

However, for my use case, I'm thinking I'll modify mirror proxy to parse specific proxy headers (e.g. x-upstream-port, x-upstream-address etc) which will change the upstream proxy.

This will achieve the same result for me. HAProxy doesn't terminate SSL ( or it can be it is OpenSSL and will mess up the Ciphers). So instead of HAProxy using a proxy header value to do some specific function, I can just use the source port of the upstream proxy instead.

I am curious though, and you might have some knowledge on this.. the User-agent on the Connect layer, is this something which an end server would receive? So in this case of using mirror_proxy with an upstream proxy and curl client, miror_proxy changes the UA to "Go-http-client/1.1".. I'm just not aware of any tools to test this?

Obviously mirroring the client UA would be of no effect if the upstream proxy doesn't do the same, but perhaps some elite/anonymous proxies do mirror the UA?

fedosgad commented 1 year ago

UA is a HTTP-level feature. It is seen by proxy in CONNECT request, which is a plaintext HTTP. However, after successful CONNECT all further traffic going through proxy is TLS-encrypted, and non-TLS-terminating upstream proxy can't access the plaintext data (this is what SSL/TLS has been made for), including actual application UA. Thus there's no info it can send to server, because that would require decrypting TLS. However, if your upstream proxy terminates SSL/TLS (usual case for reverse proxies), it has full access to all HTTP data, including headers, so new headers like X-Forwarded-For can be added to track request at actual server. Such header can contain proxy CONNECT request UA, so server could compare it to actual UA. This is more about mimicing connection to proxy, not to server.