Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 30 additions & 27 deletions golang/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
// JA3 "github.com/CUCyber/ja3transport"
)

type myTLSRequest struct {
type MyTlsRequest struct {
RequestID string `json:"requestId"`
Options struct {
URL string `json:"url"`
Expand All @@ -26,15 +26,15 @@ type myTLSRequest struct {
} `json:"options"`
}

type response struct {
type RequestResponse struct {
Status int
Body string
Headers map[string]string
}

type myTLSResponse struct {
type MyTlsResponse struct {
RequestID string
Response response
Response RequestResponse
}

func getWebsocketAddr() string {
Expand Down Expand Up @@ -65,17 +65,18 @@ func main() {
return
}

// TODO: move all definitions out of infinite loop, memory leak
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Print(err)
log.Println("error reading ws message:", err)
continue
}

mytlsrequest := new(myTLSRequest)
e := json.Unmarshal(message, &mytlsrequest)
tlsRequest := new(MyTlsRequest)
e := json.Unmarshal(message, &tlsRequest)
if e != nil {
log.Print(err)
log.Println("error unmarshalling request json:", err)
continue
}

Expand All @@ -85,83 +86,85 @@ func main() {

var transport http.RoundTripper

rawProxy := mytlsrequest.Options.Proxy
rawProxy := tlsRequest.Options.Proxy
if rawProxy != "" {
proxyURL, _ := url.Parse(rawProxy)
proxy, err := FromURL(proxyURL, Direct)
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error parsing proxy url: %s\n", tlsRequest.RequestID, err)
continue
}

tr, err := NewTransportWithDialer(string(mytlsrequest.Options.Ja3), config, proxy)
tr, err := NewTransportWithDialer(tlsRequest.Options.Ja3, config, proxy)
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error creating transport: %s\n", tlsRequest.RequestID, err)
continue
}
transport = tr

} else {
tr, err := NewTransportWithConfig(string(mytlsrequest.Options.Ja3), config)
tr, err := NewTransportWithConfig(tlsRequest.Options.Ja3, config)
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error creating transport: %s\n", tlsRequest.RequestID, err)
continue
}
transport = tr
}

client := &http.Client{Transport: transport}

req, err := http.NewRequest(strings.ToUpper(mytlsrequest.Options.Method), mytlsrequest.Options.URL, strings.NewReader(mytlsrequest.Options.Body))
req, err := http.NewRequest(strings.ToUpper(tlsRequest.Options.Method), tlsRequest.Options.URL, strings.NewReader(tlsRequest.Options.Body))
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error creating request: %s\n", tlsRequest.RequestID, err)
continue
}

for k, v := range mytlsrequest.Options.Headers {
for k, v := range tlsRequest.Options.Headers {
// TODO: reconsider this check for 2 reasons,
// 1st we should trust that the correct host header is provided if it is provided at all
// and 2nd it doesn't even work if they name the header with any capital letters
if k != "host" {
req.Header.Set(k, v)
}
}

resp, err := client.Do(req)
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error performing request: %s\n", tlsRequest.RequestID, err)
continue
}

defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error reading response body: %s\n", tlsRequest.RequestID, err)
continue
}

headers := make(map[string]string)

// TODO: better header handling. there are blatant issues with this method of handling headers.
for name, values := range resp.Header {
if name == "Set-Cookie" {
headers[name] = strings.Join(values, "/,/")
} else {
for _, value := range values {
headers[name] = value
}
headers[name] = values[len(values)-1]
}
}

Response := response{resp.StatusCode, string(bodyBytes), headers}
Response := RequestResponse{resp.StatusCode, string(bodyBytes), headers}

reply := myTLSResponse{mytlsrequest.RequestID, Response}
reply := MyTlsResponse{tlsRequest.RequestID, Response}

data, err := json.Marshal(reply)
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error marshalling reply json: %s\n", tlsRequest.RequestID, err)
continue
}

err = c.WriteMessage(websocket.TextMessage, data)
if err != nil {
log.Print(mytlsrequest.RequestID + "Request_Id_On_The_Left" + err.Error())
log.Printf("[%s] error writing message to ws: %s\n", tlsRequest.RequestID, err)
continue
}
}
Expand Down
8 changes: 0 additions & 8 deletions golang/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,6 @@ func FromURL(u *url.URL, forward proxy.Dialer) (proxy.Dialer, error) {
return proxy.FromURL(u, forward)
}

func FromURLnil(u *url.URL) (proxy.Dialer, error) {
return proxy.FromURL(u, proxy.Direct)
}

func FromEnvironment() proxy.Dialer {
return proxy.FromEnvironment()
}

func init() {
proxy.RegisterDialerType("http", newHTTPProxy)
proxy.RegisterDialerType("https", newHTTPProxy)
Expand Down
47 changes: 16 additions & 31 deletions golang/transport.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package main

import (
"context"
"crypto/sha256"
"fmt"
tls "github.com/refraction-networking/utls"
"golang.org/x/net/proxy"
"net"
"net/http"
"net/url"
"strconv"
"strings"
tls "github.com/refraction-networking/utls"
)

// greasePlaceholder is a random value (well, kindof '0x?a?a) specified in a
Expand All @@ -23,10 +24,6 @@ func (e ErrExtensionNotExist) Error() string {
return fmt.Sprintf("Extension does not exist: %s\n", e)
}

type Dialer interface {
Dial(network, addr string) (net.Conn, error)
}

// extMap maps extension values to the TLSExtension object associated with the
// number. Some values are not put in here because they must be applied in a
// special way. For example, "10" is the SupportedCurves extension which is also
Expand Down Expand Up @@ -60,36 +57,31 @@ var extMap = map[string]tls.TLSExtension{
"27": &tls.FakeCertCompressionAlgsExtension{},
"28": &tls.FakeRecordSizeLimitExtension{},
"35": &tls.SessionTicketExtension{},
"43": &tls.SupportedVersionsExtension{[]uint16{
"43": &tls.SupportedVersionsExtension{Versions: []uint16{
tls.GREASE_PLACEHOLDER,
tls.VersionTLS13,
tls.VersionTLS12,
tls.VersionTLS11,
tls.VersionTLS10}},
"44": &tls.CookieExtension{},
"45": &tls.PSKKeyExchangeModesExtension{[]uint8{
"45": &tls.PSKKeyExchangeModesExtension{Modes: []uint8{
tls.PskModeDHE,
}},
"51": &tls.KeyShareExtension{[]tls.KeyShare{}},
"51": &tls.KeyShareExtension{KeyShares: []tls.KeyShare{}},
"13172": &tls.NPNExtension{},
"65281": &tls.RenegotiationInfoExtension{
Renegotiation: tls.RenegotiateOnceAsClient,
},
}

// NewTransport creates an http.Transport which mocks the given JA3 signature when HTTPS is used
func NewTransport(ja3 string) (*http.Transport, error) {
return NewTransportWithConfig(ja3, &tls.Config{})
}

// NewTransportWithConfig creates an http.Transport object given a utls.Config
func NewTransportWithConfig(ja3 string, config *tls.Config) (*http.Transport, error) {
spec, err := stringToSpec(ja3)
if err != nil {
return nil, err
}

dialtls := func(network, addr string) (net.Conn, error) {
dialTls := func(ctx context.Context, network, addr string) (net.Conn, error) {
dialConn, err := net.Dial(network, addr)
if err != nil {
return nil, err
Expand All @@ -107,7 +99,7 @@ func NewTransportWithConfig(ja3 string, config *tls.Config) (*http.Transport, er
return uTlsConn, nil
}

return &http.Transport{DialTLS: dialtls}, nil
return &http.Transport{DialTLSContext: dialTls}, nil
}

// stringToSpec creates a ClientHelloSpec based on a JA3 string
Expand Down Expand Up @@ -135,7 +127,7 @@ func stringToSpec(ja3 string) (*tls.ClientHelloSpec, error) {
}
targetCurves = append(targetCurves, tls.CurveID(cid))
}
extMap["10"] = &tls.SupportedCurvesExtension{targetCurves}
extMap["10"] = &tls.SupportedCurvesExtension{Curves: targetCurves}

// parse point formats
var targetPointFormats []byte
Expand Down Expand Up @@ -184,21 +176,14 @@ func stringToSpec(ja3 string) (*tls.ClientHelloSpec, error) {
}, nil
}

func urlToHost(target *url.URL) *url.URL {
if !strings.Contains(target.Host, ":") {
if target.Scheme == "http" {
target.Host = target.Host + ":80"
} else if target.Scheme == "https" {
target.Host = target.Host + ":443"
}
}
return target
}

// NewTransportWithConfig - creates an http.Transport object given a utls.Config
func NewTransportWithDialer(ja3 string, config *tls.Config, dialer Dialer) (*http.Transport, error) {
func NewTransportWithDialer(ja3 string, config *tls.Config, dialer proxy.Dialer) (*http.Transport, error) {

dial := func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialer.Dial(network, addr)
}

dialtls := func(network, addr string) (net.Conn, error) {
dialTls := func(ctx context.Context, network, addr string) (net.Conn, error) {
dialConn, err := dialer.Dial(network, addr)
if err != nil {
return nil, err
Expand All @@ -220,5 +205,5 @@ func NewTransportWithDialer(ja3 string, config *tls.Config, dialer Dialer) (*htt
return uTlsConn, nil
}

return &http.Transport{DialTLS: dialtls, Dial: dialer.Dial}, nil
return &http.Transport{DialTLSContext: dialTls, DialContext: dial}, nil
}