Skip to content

Commit

Permalink
Merge pull request #29 from VovkoO/custom-client-hello
Browse files Browse the repository at this point in the history
  • Loading branch information
sleeyax authored Jun 21, 2023
2 parents 5517e8c + d96ae3d commit db2b178
Show file tree
Hide file tree
Showing 17 changed files with 158 additions and 54 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ This magic header is stripped from the request before it's forwarded to the dest
3. Check your new 'Awesome TLS' tab in Burp for configuration settings and start hacking!

## Configuration
This extension is 'plug and play' and should speak for itself. You can hover with your mouse over each field in the 'Awesome TLS' tab for more information about each field.
This extension is 'plug and play' and should speak for itself. You can hover with your mouse over each field in the 'Awesome TLS' tab for more information about each field.

To load your custom Client Hello, you can capture it in Wireshark, copy client hello record as hex stream and paste it into the field "Hex Client Hello".
![screenshot](./docs/wireshark_capture_client_hello.png)

## Manual build Instructions
This extension was developed with JetBrains IntelliJ (and GoLand) IDE.
Expand Down
Binary file modified docs/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/wireshark_capture_client_hello.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions src-go/server/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import (

// Based on: https://github.com/ulixee/hero/blob/main/mitm-socket/go/generate_cert.go

var caFile string = "ca.der"
var caKeyFile string = "caKey.der"
var (
caFile string = "ca.der"
caKeyFile string = "caKey.der"
)

// While generating a new certificate, in order to get a unique serial
// number every time we increment this value.
Expand Down Expand Up @@ -116,7 +118,7 @@ func NewCertificateAuthority() (*x509.Certificate, *rsa.PrivateKey, error) {
return nil, nil, err
}

err = os.WriteFile(caFile, raw, 0600)
err = os.WriteFile(caFile, raw, 0o600)
if err != nil {
return nil, nil, err
}
Expand All @@ -126,7 +128,7 @@ func NewCertificateAuthority() (*x509.Certificate, *rsa.PrivateKey, error) {
return nil, nil, err
}

err = os.WriteFile(caKeyFile, privBytes, 0600)
err = os.WriteFile(caKeyFile, privBytes, 0o600)
if err != nil {
return nil, nil, err
}
Expand Down
1 change: 1 addition & 0 deletions src-go/server/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"log"

"server"
)

Expand Down
8 changes: 8 additions & 0 deletions src-go/server/internal/tls/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package tls
import (
"context"
"crypto/tls"

utls "github.com/refraction-networking/utls"
)

// uconnAdapter is an adapter from utls.UConn to oohttp.TLSConn.
type uconnAdapter struct {
*utls.UConn
spec *utls.ClientHelloSpec
}

// ConnectionState implements TLSConn's ConnectionState.
Expand All @@ -33,6 +35,12 @@ func (c *uconnAdapter) ConnectionState() tls.ConnectionState {

// HandshakeContext implements TLSConn's HandshakeContext.
func (c *uconnAdapter) HandshakeContext(ctx context.Context) error {
if c.spec != nil {
if err := c.UConn.ApplyPreset(c.spec); err != nil {
return err
}
}

ch := make(chan error, 1)

go func() {
Expand Down
30 changes: 30 additions & 0 deletions src-go/server/internal/tls/clienthello.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tls

import (
"encoding/hex"
"errors"
"fmt"

utls "github.com/refraction-networking/utls"
)

type HexClientHello string

func (hexClientHello HexClientHello) ToClientHelloId() (*utls.ClientHelloSpec, error) {
if hexClientHello == "" {
return nil, errors.New("empty client hello")
}

raw, err := hex.DecodeString(string(hexClientHello))
if err != nil {
return nil, fmt.Errorf("decode hexClientHello: %w", err)
}

fingerprinter := &utls.Fingerprinter{}
spec, err := fingerprinter.RawClientHello(raw)
if err != nil {
return nil, fmt.Errorf("FingerprintClientHello: %w", err)
}

return spec, nil
}
5 changes: 3 additions & 2 deletions src-go/server/internal/tls/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import (
"context"
"crypto/tls"
"fmt"
utls "github.com/refraction-networking/utls"
"log"
"net"
"time"

utls "github.com/refraction-networking/utls"
)

// DefaultNetDialer is the default [net.Dialer].
Expand Down Expand Up @@ -51,7 +52,7 @@ func (d *Dialer) DialTLSContext(ctx context.Context, network string, addr string
d.beforeHandshakeFunc() // useful for testing
}

adapter := (&FactoryWithClientHelloId{d.ClientHelloID}).NewUTLSConn(conn, config)
adapter := (&FactoryWithClientHelloId{d.ClientHelloID, nil}).NewUTLSConn(conn, config)
if err = adapter.HandshakeContext(ctx); err != nil {
conn.Close()
return nil, err
Expand Down
12 changes: 8 additions & 4 deletions src-go/server/internal/tls/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package tls

import (
"crypto/tls"
"net"

oohttp "github.com/ooni/oohttp"
utls "github.com/refraction-networking/utls"
"net"
)

// DefaultClientHelloID is the default [utls.ClientHelloID].
Expand All @@ -21,14 +22,17 @@ type ConnFactory interface {
type FactoryWithClientHelloId struct {
// The TLS client hello id (fingerprint) to use.
// Defaults to [DefaultClientHelloID].
ClientHelloID *utls.ClientHelloID
ClientHelloID *utls.ClientHelloID
ClientHelloSpec *utls.ClientHelloSpec
}

// NewUTLSConn implements ConnFactory.
func (f *FactoryWithClientHelloId) NewUTLSConn(conn net.Conn, config *tls.Config) oohttp.TLSConn {
clientHelloID := f.ClientHelloID

if clientHelloID == nil {
if f.ClientHelloSpec != nil {
clientHelloID = &utls.HelloCustom
} else if clientHelloID == nil {
clientHelloID = DefaultClientHelloID
}

Expand All @@ -40,5 +44,5 @@ func (f *FactoryWithClientHelloId) NewUTLSConn(conn net.Conn, config *tls.Config
InsecureSkipVerify: true,
}

return &uconnAdapter{utls.UClient(conn, uConfig, *clientHelloID)}
return &uconnAdapter{UConn: utls.UClient(conn, uConfig, *clientHelloID), spec: f.ClientHelloSpec}
}
3 changes: 2 additions & 1 deletion src-go/server/internal/tls/fingerprint.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package tls

import (
utls "github.com/refraction-networking/utls"
"strings"

utls "github.com/refraction-networking/utls"
)

type Fingerprint string
Expand Down
22 changes: 17 additions & 5 deletions src-go/server/internal/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package internal
import (
"encoding/json"
"errors"
oohttp "github.com/ooni/oohttp"
"fmt"
"net"
internalTls "server/internal/tls"
"strings"
"time"

internalTls "server/internal/tls"

oohttp "github.com/ooni/oohttp"
)

const (
Expand All @@ -27,6 +30,9 @@ type TransportConfig struct {
// The TLS fingerprint to use.
Fingerprint internalTls.Fingerprint

// Hexadecimal Client Hello to use
HexClientHello internalTls.HexClientHello

// The maximum amount of time a dial will wait for a connect to complete.
// Defaults to [DefaultHttpTimeout].
HttpTimeout int
Expand Down Expand Up @@ -59,7 +65,7 @@ func ParseTransportConfig(data string) (*TransportConfig, error) {
}

// NewTransport creates a new transport using the given configuration.
func NewTransport(config *TransportConfig) *oohttp.StdlibTransport {
func NewTransport(config *TransportConfig) (*oohttp.StdlibTransport, error) {
dialer := &net.Dialer{
Timeout: DefaultHttpTimeout,
KeepAlive: DefaultHttpKeepAlive,
Expand All @@ -74,7 +80,13 @@ func NewTransport(config *TransportConfig) *oohttp.StdlibTransport {

tlsFactory := &internalTls.FactoryWithClientHelloId{}

if config.Fingerprint != "" {
if config.HexClientHello != "" {
spec, err := config.HexClientHello.ToClientHelloId()
if err != nil {
return nil, fmt.Errorf("create spec from client hello: %w", err)
}
tlsFactory.ClientHelloSpec = spec
} else if config.Fingerprint != "" {
tlsFactory.ClientHelloID = config.Fingerprint.ToClientHelloId()
}

Expand Down Expand Up @@ -109,5 +121,5 @@ func NewTransport(config *TransportConfig) *oohttp.StdlibTransport {

return &oohttp.StdlibTransport{
Transport: transport,
}
}, nil
}
12 changes: 9 additions & 3 deletions src-go/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"context"
"crypto/tls"
"fmt"
http "github.com/ooni/oohttp"
"io"
"net"
"server/internal"
"strings"

"server/internal"

http "github.com/ooni/oohttp"
)

// DefaultAddress is the default listener address.
Expand Down Expand Up @@ -44,7 +46,11 @@ func StartServer(addr string) error {
return
}

transport := internal.NewTransport(config)
transport, err := internal.NewTransport(config)
if err != nil {
writeError(w, err)
return
}

req.URL.Host = config.Host
req.URL.Scheme = config.Scheme
Expand Down
1 change: 1 addition & 0 deletions src/main/java/burp/BurpExtender.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequ
transportConfig.Host = httpService.getHost();
transportConfig.Scheme = httpService.getProtocol();
transportConfig.Fingerprint = this.settings.getFingerprint();
transportConfig.HexClientHello = this.settings.getHexClientHello();
transportConfig.HttpTimeout = this.settings.getHttpTimeout();
transportConfig.HttpKeepAliveInterval = this.settings.getHttpKeepAliveInterval();
transportConfig.IdleConnTimeout = this.settings.getIdleConnTimeout();
Expand Down
9 changes: 6 additions & 3 deletions src/main/java/burp/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public class Settings {

private final String address = "Address";
private final String fingerprint = "Fingerprint";
private final String hexClientHello = "HexClientHello";
private final String httpTimeout = "HttpTimeout";
private final String httpKeepAliveInterval = "HttpKeepAliveInterval";
private final String idleConnTimeout = "IdleConnTimeout";
Expand Down Expand Up @@ -79,14 +80,16 @@ public void setHttpKeepAliveInterval(int httpTimeout) {
this.write(this.httpKeepAliveInterval, String.valueOf(httpTimeout));
}

public String getFingerprint() {
return this.read(this.fingerprint);
}
public String getFingerprint() { return this.read(this.fingerprint); }

public void setFingerprint(String fingerprint) {
this.write(this.fingerprint, fingerprint);
}

public String getHexClientHello() { return this.read(this.hexClientHello); }

public void setHexClientHello(String hexClientHello) { this.write(this.hexClientHello, hexClientHello); }

public int getIdleConnTimeout() {
return Integer.parseInt(this.read(this.idleConnTimeout));
}
Expand Down
Loading

0 comments on commit db2b178

Please sign in to comment.