How can I use a uTLS connection when sending a request? - http

I'm trying to send a request using a Hello Chrome fingerprint by using the uTLS library, but after looking through the docs I'm struggling to figure out how to utilise the uTLS connection when sending a request.
I've got both parts of the code below, but not sure how to put them together, or if I'm even doing it the correct way.
package main
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"net/http/cookiejar"
tls "github.com/refraction-networking/utls"
)
func main() {
tcpConn, err := net.Dial("tcp", "151.101.65.69:443")
if err != nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config := tls.Config{ServerName: "www.stackoverflow.com"}
tlsConn := tls.UClient(tcpConn, &config, tls.HelloChrome_Auto)
defer tlsConn.Close()
err = tlsConn.Handshake()
if err != nil {
fmt.Printf("uTlsConn.Handshake() error: %+v", err)
} else {
fmt.Println("Handshake met")
}
cookieJar, _ := cookiejar.New(nil)
client := &http.Client{
Jar: cookieJar,
Transport: &http.Transport{},
}
req, err := http.NewRequest("GET", "https://ja3er.com/json", nil)
req.Header.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36")
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
}

Simply move your code into Transport.DialTLSContext:
DialTLSContext specifies an optional dial function for creating
TLS connections for non-proxied HTTPS requests.
[...]
The returned net.Conn is assumed to already be
past the TLS handshake.
client := &http.Client{
Jar: cookieJar,
Transport: &http.Transport{
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
// Note that hardcoding the address is not necessary here. Only
// do that if you want to ignore the DNS lookup that already
// happened behind the scenes.
tcpConn, err := (&net.Dialer{}).DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
config := tls.Config{ServerName: "www.stackoverflow.com"}
tlsConn := tls.UClient(tcpConn, &config, tls.HelloChrome_Auto)
err = tlsConn.Handshake()
if err != nil {
return nil, fmt.Errorf("uTlsConn.Handshake() error: %w", err)
}
return tlsConn, nil
},
},
}

Related

How to reqeust using Http/2.0 in http library with DialTLS?

i want to request with http library in Golang using http2, i tried almost everything and made a lot of research without any success so i am asking here if any one can help me and give me the right answer.
package main
import (
"fmt"
"net"
"net/http"
"strings"
tls "github.com/refraction-networking/utls"
)
func main() {
req, _ := http.NewRequest("GET", "https://http2.pro/api/v1", nil)
tlsspec, _ := ParseJA3("771,49195-49196-52393-49199-49200-52392-49161-49162-49171-49172-156-157-47-53,65281-0-23-35-13-5-16-11-10,29-23-24,0")
transport := &http.Transport{
DialTLS: func(network, addr string) (net.Conn, error) {
conn, err := net.Dial(network, addr)
if err != nil {
return nil, err
}
host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
config := &tls.Config{ServerName: host}
uconn := tls.UClient(conn, config, tls.HelloCustom)
//tlsspec is just custom tls
if uconn.ApplyPreset(tlsspec); err != nil {
return nil, err
}
if uconn.Handshake(); err != nil {
return nil, err
}
return uconn, nil
},
}
client := http.Client{
Transport: transport,
}
resp, _ := client.Do(req)
fmt.Println(resp)
}

Golang Listener and Dialer

I am having difficulty connecting my program to my local host. After doing some debugging I found that my problem is my listener and dialer. They aren't "talking". Could someone assist me in figuring out the problem.
client
func NewServer(httpAddr string , remoteAddr string , webdirect string, cacheInterval time.Duration) (*Server, error) {
conn, err := grpc.DialContext( context.Background(),"localhost:8080", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
//return nil, err
log.Fatalf("Unable to connect to server: %v", err)
}
defer conn.Close()
// make a grpc pb client
client := pb.NewClicksClient(conn)
return &APIServer{
grpcConn: conn,
clicks: client,
webdirect: webdirect,
httpAddr: httpAddr,
cache: Cache{
loop: sync2.NewCycle(cacheInterval),
},
}, nil
}
server
func Start(ctx context.Context, remoteAddr string) error {
lis, err := net.Listen("tcp", "localhost: 8080")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
clicks := &ClicksDatabase{}
s := grpc.NewServer()
pb.RegisterClicksServer(s, clicks)
log.Println("Serving gRPC on 0.0.0.0:8080")
if err := s.Serve(lis); err != nil {
log.Fatal(err)
}
return s.Serve(lis)
}
Replace localhost: 8080 with just :8080 in your net.Listen

Connect via Proxy while using UTLS and HTTP 1.1 Request

I am trying to connect to a Host using Random TLS Fingerprinting. I am using https://github.com/refraction-networking/utls (see my issue i created on https://github.com/refraction-networking/utls/issues/42)
My issue is now, how can i utilize a HTTP or SOCKS5 Proxy while opening that connection?
The Code im using right now is:
package main
import (
"bufio"
"fmt"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"time"
"github.com/refraction-networking/utls"
)
var (
dialTimeout = time.Duration(15) * time.Second
)
var requestHostname = "google.com"
var requestAddr = "172.217.22.110:443"
// this example generates a randomized fingeprint, then re-uses it in a follow-up connection
func HttpGetConsistentRandomized(hostname string, addr , uri string) (*http.Response, error) {
config := tls.Config{ServerName: hostname}
tcpConn, err := net.DialTimeout("tcp", addr, dialTimeout)
if err != nil {
return nil, fmt.Errorf("net.DialTimeout error: %+v", err)
}
uTlsConn := tls.UClient(tcpConn, &config, tls.HelloRandomized)
defer uTlsConn.Close()
err = uTlsConn.Handshake()
if err != nil {
return nil, fmt.Errorf("uTlsConn.Handshake() error: %+v", err)
}
uTlsConn.Close()
// At this point uTlsConn.ClientHelloID holds a seed that was used to generate
// randomized fingerprint. Now we can establish second connection with same fp
tcpConn2, err := net.DialTimeout("tcp", addr, dialTimeout)
if err != nil {
return nil, fmt.Errorf("net.DialTimeout error: %+v", err)
}
uTlsConn2 := tls.UClient(tcpConn2, &config, uTlsConn.ClientHelloID)
defer uTlsConn2.Close()
err = uTlsConn2.Handshake()
if err != nil {
return nil, fmt.Errorf("uTlsConn.Handshake() error: %+v", err)
}
return httpGetOverConn(uTlsConn2, uTlsConn2.HandshakeState.ServerHello.AlpnProtocol, uri)
}
func main() {
var response *http.Response
var err error
response, err = HttpGetConsistentRandomized(requestHostname, requestAddr, "/2.0/ssocookie")
if err != nil {
fmt.Printf("#> HttpGetConsistentRandomized() failed: %+v\n", err)
} else {
//fmt.Printf("#> HttpGetConsistentRandomized() response: %+s\n", httputil.DumpResponse(response,true))
dump, err := httputil.DumpResponse(response, true)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+s\n", dump)
}
return
}
func httpGetOverConn(conn net.Conn, alpn string, uri string) (*http.Response, error) {
req := &http.Request{
Method: "GET",
URL: &url.URL{Host: "www." + requestHostname + uri},
Header: make(http.Header),
Host: "www." + requestHostname,
}
req.Proto = "HTTP/1.1"
req.ProtoMajor = 1
req.ProtoMinor = 1
err := req.Write(conn)
if err != nil {
return nil, err
}
return http.ReadResponse(bufio.NewReader(conn), req)
}
As Steffen said, you have to create a proxy dialer first, dial the proxy to create a net.Conn, then use that net.Conn when creating the uTLS Client, before handshaking. For brevity's sake, your custom dialTLS function would look something like:
import (
"crypto/tls"
"net"
"net/url"
"github.com/magisterquis/connectproxy"
"golang.org/x/net/proxy"
utls "github.com/refraction-networking/utls"
)
var proxyString = "http://127.0.0.1:8080"
dialTLS := func(network, addr string, _ *tls.Config) (net.Conn, error) {
proxyURI, _ := url.Parse(proxyString)
switch proxyURI.Scheme {
case "socks5":
proxyDialer, err = proxy.SOCKS5("tcp", proxyString, nil, proxy.Direct)
case "http":
proxyDialer, err = connectproxy.New(proxyURI, proxy.Direct)
}
conn, err := proxyDialer.Dial("tcp", addr)
uconn := utls.UClient(conn, cfg, &utls.HelloRandomizedALPN)
...
}
Two suggestions:
Use the "connectproxy" module referenced above if you intend to tunnel through a HTTP CONNECT proxy.
Make life easier for yourself and take a look at the Meek pluggable transport source for Tor. There's a 'utls.go' module which takes care of everything for you, including setting up either a http or http2 transport depending on the negotiated ALPN protocol. It only supports SOCKS but you could easily adapt it to handle HTTP proxies.
A HTTP proxy and SOCKS proxy work be having some initial proxy specific handshake after the TCP connect. After this handshake is done they provide a normal TCP socket which then can be used for doing the TLS handshake etc. Thus, all you need is to replace your
tcpConn, err := net.DialTimeout("tcp", addr, dialTimeout)
with a proxy specific method to setup the TCP connection. This can be done by using SOCKS5 in x/net/proxy to create the appropriate Dialer or similar using the HTTP CONNECT method is done in connectproxy.

How can I upgrade a client http connection to websockets in golang after sending the connection upgrade

I need a golang client that can upgrade from an http get response to a websocket connection.
I have a JS client that works and I've seen direct ws client connections but I have to upgrade from http. I have tried looking for other 3GL solutions (Java, C#, Python) but I need to be able to implement the upgrade in Go. I have seen Dart detaching the socket and creating a websocket from it.
WebSocket.fromUpgradedSocket
I noticed Client does not support Hijack but the discussion didn't get me anywhere.
I am using github.com/gorilla/websocket but can change that if it helps.
Server:
func main() {
srv := Srv{}
count = 0
http.HandleFunc("/", srv.handleRoot)
http.HandleFunc("/ws", srv.handleWs)
log.Fatal(http.ListenAndServe(":5002", nil))
}
func (tool *Srv) handleRoot(w http.ResponseWriter, r *http.Request) {
webSocketKey := hdr.Get("Sec-WebSocket-Key")
log.Printf("Socket key = '%v'", webSocketKey)
secWsAccept := computeAcceptKey(webSocketKey)
log.Printf("Accept = '%v'", secWsAccept)
w.Header().Add("sec-websocket-accept", secWsAccept)
w.Header().Add("upgrade", "websockt")
w.Header().Add("connection", "upgrade")
w.WriteHeader(101)
}
func (tool *Srv) handleWs(w http.ResponseWriter, r *http.Request) {
var upgrader = websocket.Upgrader{}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatalf("Websocket fatal error. %v", err)
}
tool.conn = conn
go tool.serviceWsRequests()
}
func (tool *Srv) serviceWsRequests() {
for {
log.Printf("starting ws")
req := request{}
err := tool.conn.ReadJSON(&req)
if err != nil {
log.Printf("Failed to decode ws message. %v", err)
break
}
fmt.Printf("Got request. %v\n", req)
if req.Method == "ping" {
fmt.Printf("Param=%v\n", req.Parameters)
}
}
}
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
func computeAcceptKey(challengeKey string) string {
h := sha1.New()
h.Write([]byte(challengeKey))
h.Write(keyGUID)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
Client:
func main() {
tr := &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
}
client := &http.Client{
Transport: tr,
// Do NOT follow redirects
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
wsKey, err := generateKey()
if err != nil {
log.Printf("Cannot generate challenge key %v", err)
}
// Get request for ws upgrade.
req, err := http.NewRequest("GET", "http://localhost:5002", nil)
req.Header.Add("Connection", "Upgrade")
req.Header.Add("Upgrade", "websocket")
req.Header.Add("Sec-WebSocket-Version", "13")
req.Header.Add("Sec-WebSocket-Key", wsKey)
log.Printf("ws key '%v'", wsKey)
resp, err := client.Do(req)
if err != nil {
log.Printf("Get error %v", err)
}
defer func() {
if resp != nil {
err = resp.Body.Close()
}
}()
log.Printf("Status='%v', proto='%v'", resp.Status, resp.Proto)
body, err := ioutil.ReadAll(resp.Body)
hdr := resp.Header
for k, v := range hdr{
log.Printf("%v : %v", k, v)
}
log.Printf("Body = %v", string(body))
resp, err = http.Get("ws://localhost:5002/ws")
if err != nil {
log.Printf("Error '%v'", err)
}
}
func generateKey() (string, error) {
p := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, p); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(p), nil
}
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
func computeAcceptKey(challengeKey string) string {
h := sha1.New()
h.Write([]byte(challengeKey))
h.Write(keyGUID)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
I get an error
Error 'Get ws://localhost:5002/ws: unsupported protocol scheme "ws"'
Which doesn't surprise me because I haven't upgraded the connection.
So how do I go an upgrade in Go?
Use the Gorilla client to dial websocket connections:
func main() {
c, _ , err := websocket.DefaultDialer.Dial("ws://localhost:5002/ws", nil)
if err != nil {
// handle error
}
defer c.Close()
// do something with c, a *websocket.Conn
}
The Dial method issues a GET to the server requesting an upgrade to the WebSocket protocol. On successful completion of the upgrade, Dial returns a *websocket.Conn.

Connection reset by peer GO

I'm trying to do a request in GO but I always receive "Connection reset by peer" error. The following code shows how I'm doing the request:
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
client := = &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
fmt.Println(resp.Body)
...and I receive:
Get https://example.com: read tcp 1.2.3.4:1234->5.6.7.8:5678: read: connection reset by peer
When I do curl https://example.com I receive response form the server.
Why can't I do the request in GO?
Your code works if I use it against a URL like https://example.com. Are you sure you are passing it the correct URL?
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
url := "https://example.com"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println(err)
return
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("OK")
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
str := string(bytes[:])
fmt.Printf("%s", str)
}

Resources