diff --git a/README.md b/README.md
index 64dffff..d9231dd 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,11 @@
# Awesome TLS
-This extension hijacks Burp's HTTP and TLS stack, allowing you to spoof any browser TLS fingerprint ([JA3](https://github.com/salesforce/ja3)).
-It boosts the power of Burp Suite while reducing the likelihood of fingerprinting by various WAFs like CloudFlare, PerimeterX, Akamai, DataDome, etc.
-It does this without resorting to hacks, reflection or forked Burp Suite Community code. All code in this repository only leverages Burp's official Extender API.
+This extension hijacks Burp's HTTP and TLS stack, allowing you to spoof any browser TLS
+fingerprint ([JA3](https://github.com/salesforce/ja3)).
+It boosts the power of Burp Suite while reducing the likelihood of fingerprinting by various WAFs like CloudFlare,
+PerimeterX, Akamai, DataDome, etc.
+
+This extension works without resorting to ugly hacks, reflection or forked Burp Suite Community code.

@@ -11,47 +14,65 @@ It does this without resorting to hacks, reflection or forked Burp Suite Communi
## Sponsors
> Maintenance of this project is made possible by all the lovely contributors and sponsors.
-If you'd like to sponsor this project and have your avatar or company logo appear in this section, click [here](https://github.com/sponsors/sleeyax). 💖
+> If you'd like to sponsor this project and have your avatar or company logo appear in this section,
+> click [here](https://github.com/sponsors/sleeyax). 💖
---
## Showcase
+
[CloudFlare bot score](https://cloudflare.manfredi.io/en/tools/connection):


-This is just one example. If you tested with another dedicated bot detection site, let me know your results!
+This is just one example. If you tested with another dedicated bot detection site, let me know your results!
## How it works
-Unfortunately Burp's Extender API is very limited for more advanced use cases like this, so I had to play around with it to make this work.
-Once a request comes in, the extension intercepts it and forwards it to a local HTTPS server that started in the background (once the extension loaded).
-This server works like a proxy; it forwards the request to the destination, while persisting the original header order and applying a customizable TLS configuration.
-Then, the local server forwards the response back to Burp. The response header order is also preserved.
+Unfortunately Burp's API is very limited for more advanced use cases like this, so I had to play around with it
+to make this work.
+
+Once a request comes in, the extension intercepts it and forwards it to a local HTTPS server that started in the
+background (once the extension loaded).
+This server works like a proxy; it forwards the request to the destination, while persisting the original header order
+and applying a customizable TLS configuration.
+Then, the local server forwards the response back to Burp.
-Configuration settings and other necessary information like the destination server address and protocol are sent to the local server per request by a magic header.
-This magic header is stripped from the request before it's forwarded to the destination server, of course.
+Configuration settings and other necessary information like the destination server address and protocol are sent to the
+local server per request by a magic header.
+This magic header is stripped from the request before it's forwarded to the destination server.

-> :information_source: Another option would've been to code an upstream proxy server and connect burp to it, but I personally needed an extension for customization and portability.
+> :information_source: Another option would've been to code an upstream proxy server and connect burp to it, but I
+> personally needed an extension for customization and portability.
## Installation
-1. Download the jar file for your operating system from [releases](https://github.com/sleeyax/burp-awesome-tls/releases). You can also download a fat jar, which works on all platforms supported by Awesome TLS. This means it's also portable and could be loaded from a USB for cross-platform access.
-2. Open burp (pro or community), go to Extender > Extensions and click on 'Add'. Then, select `Java` as the extension type and browse to the jar file you just downloaded. Click 'Next' at the bottom, and it should load the extension without any errors.
+
+1. Download the jar file for your operating system
+ from [releases](https://github.com/sleeyax/burp-awesome-tls/releases). You can also download a fat jar, which works
+ on all platforms supported by Awesome TLS. This means it's also portable and could be loaded from a USB for
+ cross-platform access.
+2. Open burp (pro or community), go to Extender > Extensions and click on 'Add'. Then, select `Java` as the extension
+ type and browse to the jar file you just downloaded. Click 'Next' at the bottom, and it should load the extension
+ without any errors.
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.
-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".
+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 from WireShark, you can copy the client hello record as hex stream and paste it
+into the field "Hex Client Hello".

Advanced usage
-
-In the 'advanced' tab, you can enable an additional proxy listener that will automatically apply the current fingerprint from the request:
+
+In the 'advanced' tab, you can enable an additional proxy listener that will automatically apply the current fingerprint
+from the request:

@@ -62,22 +83,31 @@ When enabled, the diagram changes to this:
## Manual build Instructions
-This extension was developed with JetBrains IntelliJ (and GoLand) IDE.
+
+This extension was developed with JetBrains IntelliJ (and GoLand) IDE.
The build instructions below assume you're using the same tools to build.
See [workflows](.github/workflows) for the target programming language versions.
-1. Compile the go package within `./src-go/`. Run `cd ./src-go/server && go build -o ../../src/main/resources/{OS}-{ARCH}/server.{EXT} -buildmode=c-shared ./cmd/main.go`, replacing `{OS}-{ARCH}` with your OS and CPU architecture and `{EXT}` with your platform's preferred extension for dynamic C libraries. For example: `linux-x86-64/server.so`. See the [JNA docs](https://github.com/java-native-access/jna/blob/master/www/GettingStarted.md) for more info about supported platforms.
+1. Compile the go package within `./src-go/`. Run
+ `cd ./src-go/server && go build -o ../../src/main/resources/{OS}-{ARCH}/server.{EXT} -buildmode=c-shared ./cmd/main.go`,
+ replacing `{OS}-{ARCH}` with your OS and CPU architecture and `{EXT}` with your platform's preferred extension for
+ dynamic C libraries. For example: `linux-x86-64/server.so`. See
+ the [JNA docs](https://github.com/java-native-access/jna/blob/master/www/GettingStarted.md) for more info about
+ supported platforms.
2. Compile the GUI form `SettingsTab.form` into Java code via `Build > Build project`.
3. Build the jar with Gradle: `gradle buildJar`.
You should now have one jar file (usually located at `./build/libs`) that works with Burp on your operating system.
## Credits
+
Special thanks to the maintainers of the following repositories:
+
- [refraction-networking/utls](https://github.com/refraction-networking/utls)
-- [ooni/oohttp](https://github.com/ooni/oohttp)
+- [bogdanfinn/tls-client](https://github.com/bogdanfinn/tls-client)
And the creators of the following websites:
+
- https://tlsfingerprint.io/
- https://kawayiyi.com/tls
- https://tls.peet.ws/
@@ -85,4 +115,5 @@ And the creators of the following websites:
- https://scrapfly.io/web-scraping-tools/http2-fingerprint
## License
+
[GPL V3](./LICENSE)
diff --git a/build.gradle b/build.gradle
index d12538b..0fadd0d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ repositories {
}
dependencies {
- implementation 'net.portswigger.burp.extender:burp-extender-api:2.3'
+ compileOnly "net.portswigger.burp.extensions:montoya-api:2025.2"
implementation 'net.java.dev.jna:jna:5.10.0'
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'com.intellij:forms_rt:7.0.3'
diff --git a/docs/settings.png b/docs/settings.png
index ecf80d5..f4ec5ba 100644
Binary files a/docs/settings.png and b/docs/settings.png differ
diff --git a/src-go/server/cmd/main.go b/src-go/server/cmd/main.go
index 837fa1d..64eab96 100644
--- a/src-go/server/cmd/main.go
+++ b/src-go/server/cmd/main.go
@@ -3,40 +3,17 @@ package main
import "C"
import (
- "encoding/json"
"flag"
"fmt"
"log"
-
- "server/internal"
- "server/internal/tls"
-
"server"
+ "strings"
)
func main() {
- spoofAddr := flag.String("spoof", server.DefaultSpoofProxyAddress, "Spoof proxy address to listen on ([ip:]port)")
+ spoofAddr := flag.String("spoof", "", "Spoof proxy address to listen on ([ip:]port)")
flag.Parse()
- defaultConfig, err := json.Marshal(internal.TransportConfig{
- InterceptProxyAddr: server.DefaultInterceptProxyAddress,
- BurpAddr: server.DefaultBurpProxyAddress,
- Fingerprint: tls.DefaultFingerprint,
- HexClientHello: "",
- UseInterceptedFingerprint: false,
- HttpTimeout: int(internal.DefaultHttpTimeout.Seconds()),
- HttpKeepAliveInterval: int(internal.DefaultHttpKeepAlive.Seconds()),
- IdleConnTimeout: int(internal.DefaultIdleConnTimeout.Seconds()),
- TLSHandshakeTimeout: int(internal.DefaultTLSHandshakeTimeout.Seconds()),
- })
- if err != nil {
- log.Fatalln(err)
- }
-
- if err := server.SaveSettings(string(defaultConfig)); err != nil {
- log.Fatalln(err)
- }
-
log.Fatalln(server.StartServer(*spoofAddr))
}
@@ -56,16 +33,12 @@ func StopServer() *C.char {
return C.CString("")
}
-//export SaveSettings
-func SaveSettings(configJson *C.char) *C.char {
- if err := server.SaveSettings(C.GoString(configJson)); err != nil {
- return C.CString(err.Error())
- }
-
- return C.CString("")
-}
-
//export SmokeTest
func SmokeTest() {
fmt.Println("smoke test success")
}
+
+//export GetFingerprints
+func GetFingerprints() *C.char {
+ return C.CString(strings.Join(server.GetFingerprints(), "\n"))
+}
diff --git a/src-go/server/fingerprints.go b/src-go/server/fingerprints.go
new file mode 100644
index 0000000..e99886b
--- /dev/null
+++ b/src-go/server/fingerprints.go
@@ -0,0 +1,12 @@
+package server
+
+import (
+ "github.com/bogdanfinn/tls-client/profiles"
+ "maps"
+ "slices"
+)
+
+func GetFingerprints() []string {
+ fingerprints := slices.Sorted(maps.Keys(profiles.MappedTLSClients))
+ return append([]string{"default"}, fingerprints...)
+}
diff --git a/src-go/server/go.mod b/src-go/server/go.mod
index 6d7db80..9a0a39b 100644
--- a/src-go/server/go.mod
+++ b/src-go/server/go.mod
@@ -1,23 +1,25 @@
module server
-go 1.21
+go 1.22.0
-toolchain go1.22.2
+toolchain go1.24.0
require (
- github.com/ooni/oohttp v0.7.1
- github.com/open-ch/ja3 v1.0.1
- github.com/refraction-networking/utls v1.6.4
+ github.com/bogdanfinn/fhttp v0.5.34
+ github.com/bogdanfinn/tls-client v1.8.0
+ github.com/bogdanfinn/utls v1.6.5
)
require (
- github.com/andybalholm/brotli v1.0.6 // indirect
- github.com/cloudflare/circl v1.3.7 // indirect
- github.com/klauspost/compress v1.17.4 // indirect
- golang.org/x/crypto v0.18.0 // indirect
- golang.org/x/net v0.20.0 // indirect
- golang.org/x/sys v0.16.0 // indirect
- golang.org/x/text v0.14.0 // indirect
+ github.com/andybalholm/brotli v1.1.1 // indirect
+ github.com/cloudflare/circl v1.5.0 // indirect
+ github.com/klauspost/compress v1.17.11 // indirect
+ github.com/quic-go/quic-go v0.48.1 // indirect
+ github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
+ golang.org/x/crypto v0.29.0 // indirect
+ golang.org/x/net v0.31.0 // indirect
+ golang.org/x/sys v0.27.0 // indirect
+ golang.org/x/text v0.20.0 // indirect
)
replace github.com/ooni/oohttp => github.com/sleeyax/oohttp v0.0.0-20230603105812-6ac0447b1a8e
diff --git a/src-go/server/go.sum b/src-go/server/go.sum
index 312b01c..8a6449b 100644
--- a/src-go/server/go.sum
+++ b/src-go/server/go.sum
@@ -1,20 +1,36 @@
-github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
-github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
-github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
-github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
-github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
-github.com/open-ch/ja3 v1.0.1 h1:kMqfkgS+cTasMlsQaJ627qlw7kA/qRZVTmF0BtFjOLQ=
-github.com/open-ch/ja3 v1.0.1/go.mod h1:lTWgltvZDGQjIa/TjWTzfpCVa/eGP+szng2DWz9mAvk=
-github.com/refraction-networking/utls v1.6.4 h1:aeynTroaYn7y+mFtqv8D0bQ4bw0y9nJHneGxJ7lvRDM=
-github.com/refraction-networking/utls v1.6.4/go.mod h1:2VL2xfiqgFAZtJKeUTlf+PSYFs3Eu7km0gCtXJ3m8zs=
-github.com/sleeyax/oohttp v0.0.0-20230603105812-6ac0447b1a8e h1:evx5O2TAZdPLDCqPuEI5yo4Sg3LT5cImPVbno6HKM2s=
-github.com/sleeyax/oohttp v0.0.0-20230603105812-6ac0447b1a8e/go.mod h1:/7fPgmXNkMSXBpLOdARkhyn3vsNAtmZ0C3G5C/KLd6Q=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
+github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
+github.com/bogdanfinn/fhttp v0.5.34 h1:avRD2JNYqj6I6DqjSrI9tl8mP8Nk7T4CCmUsPz7afhg=
+github.com/bogdanfinn/fhttp v0.5.34/go.mod h1:BlcawVfXJ4uhk5yyNGOOY2bwo8UmMi6ccMszP1KGLkU=
+github.com/bogdanfinn/tls-client v1.8.0 h1:IB44SqKa0XKdx3GYXpRbkqN3+tsBtg9RJYRsl36boOA=
+github.com/bogdanfinn/tls-client v1.8.0/go.mod h1:ehNITC7JBFeh6S7QNWtfD+PBKm0RsqvizAyyij2d/6g=
+github.com/bogdanfinn/utls v1.6.5 h1:rVMQvhyN3zodLxKFWMRLt19INGBCZ/OM2/vBWPNIt1w=
+github.com/bogdanfinn/utls v1.6.5/go.mod h1:czcHxHGsc1q9NjgWSeSinQZzn6MR76zUmGVIGanSXO0=
+github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
+github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
+github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
+github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc=
+github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng=
+github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
+github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
+golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/src-go/server/hexclienthello.go b/src-go/server/hexclienthello.go
new file mode 100644
index 0000000..b815869
--- /dev/null
+++ b/src-go/server/hexclienthello.go
@@ -0,0 +1,46 @@
+package server
+
+import (
+ "encoding/hex"
+ "errors"
+ utls "github.com/bogdanfinn/utls"
+)
+
+type HexClientHello string
+
+func (hexClientHello HexClientHello) ToClientHelloSpec() (utls.ClientHelloSpec, error) {
+ if hexClientHello == "" {
+ return utls.ClientHelloSpec{}, errors.New("empty client hello")
+ }
+
+ raw, err := hex.DecodeString(string(hexClientHello))
+ if err != nil {
+ return utls.ClientHelloSpec{}, err
+ }
+
+ fingerprinter := &utls.Fingerprinter{
+ AllowBluntMimicry: true,
+ }
+ spec, err := fingerprinter.RawClientHello(raw)
+ if err != nil {
+ return utls.ClientHelloSpec{}, err
+ }
+
+ for i, extension := range spec.Extensions {
+ // Replace ECH extension with a GREASE ECH extension.
+ // Real ECH is not supported yet.
+ if genericExtension, ok := extension.(*utls.GenericExtension); ok {
+ if genericExtension.Id == utls.ExtensionECH {
+ spec.Extensions[i] = utls.BoringGREASEECH()
+ }
+ }
+
+ // If the PSK extension already contains a key, it's identified as a 'fake PSK' extensions by utls.
+ // So we replace it with a real PSK extension.
+ if _, ok := extension.(*utls.FakePreSharedKeyExtension); ok {
+ spec.Extensions[i] = &utls.UtlsPreSharedKeyExtension{}
+ }
+ }
+
+ return *spec, nil
+}
diff --git a/src-go/server/intercept.go b/src-go/server/intercept.go
index 3dd43c7..0020984 100644
--- a/src-go/server/intercept.go
+++ b/src-go/server/intercept.go
@@ -9,14 +9,12 @@ import (
"io"
"log"
"net"
+ "net/http"
"net/url"
"strings"
"sync"
"syscall"
"time"
-
- http "github.com/ooni/oohttp"
- "github.com/open-ch/ja3"
)
const (
@@ -29,7 +27,7 @@ type interceptProxy struct {
burpClient *http.Client
burpAddr string
mutex sync.RWMutex
- clientHelloData map[string]string
+ clientHelloData string
listener net.Listener
ctx context.Context
cancel context.CancelFunc
@@ -56,20 +54,19 @@ func newInterceptProxy(interceptAddr, burpAddr string) (*interceptProxy, error)
burpClient: &http.Client{
Transport: tr,
},
- burpAddr: burpAddr,
- mutex: sync.RWMutex{},
- clientHelloData: map[string]string{},
- listener: l,
- ctx: ctx,
- cancel: cancel,
+ burpAddr: burpAddr,
+ mutex: sync.RWMutex{},
+ listener: l,
+ ctx: ctx,
+ cancel: cancel,
}, nil
}
-func (s *interceptProxy) getTLSFingerprint(sni string) string {
+func (s *interceptProxy) getTLSFingerprint() string {
s.mutex.RLock()
defer s.mutex.RUnlock()
- return s.clientHelloData[sni]
+ return s.clientHelloData
}
func (s *interceptProxy) Start() {
@@ -194,14 +191,8 @@ func (s *interceptProxy) readClientHello(inReader io.Reader) {
readClientHello = true
- j, err := ja3.ComputeJA3FromSegment(clientHello)
- if err != nil {
- s.writeError(err)
- return
- }
-
s.mutex.Lock()
- s.clientHelloData[j.GetSNI()] = hex.EncodeToString(clientHello)
+ s.clientHelloData = hex.EncodeToString(clientHello)
s.mutex.Unlock()
}
}
diff --git a/src-go/server/internal/tls/adapter.go b/src-go/server/internal/tls/adapter.go
deleted file mode 100644
index b30acfd..0000000
--- a/src-go/server/internal/tls/adapter.go
+++ /dev/null
@@ -1,56 +0,0 @@
-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.
-func (c *uconnAdapter) ConnectionState() tls.ConnectionState {
- uConnState := c.UConn.ConnectionState()
-
- return tls.ConnectionState{
- Version: uConnState.Version,
- HandshakeComplete: uConnState.HandshakeComplete,
- CipherSuite: uConnState.CipherSuite,
- DidResume: uConnState.DidResume,
- NegotiatedProtocol: uConnState.NegotiatedProtocol,
- NegotiatedProtocolIsMutual: uConnState.NegotiatedProtocolIsMutual,
- ServerName: uConnState.ServerName,
- PeerCertificates: uConnState.PeerCertificates,
- VerifiedChains: uConnState.VerifiedChains,
- SignedCertificateTimestamps: uConnState.SignedCertificateTimestamps,
- OCSPResponse: uConnState.OCSPResponse,
- TLSUnique: uConnState.TLSUnique,
- }
-}
-
-// 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() {
- ch <- c.UConn.Handshake()
- }()
-
- select {
- case err := <-ch:
- return err
- case <-ctx.Done():
- return ctx.Err()
- }
-}
diff --git a/src-go/server/internal/tls/clienthello.go b/src-go/server/internal/tls/clienthello.go
deleted file mode 100644
index 9591a16..0000000
--- a/src-go/server/internal/tls/clienthello.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package tls
-
-import (
- "encoding/hex"
- "errors"
- "fmt"
-
- utls "github.com/refraction-networking/utls"
-)
-
-type HexClientHello string
-
-func (hexClientHello HexClientHello) ToClientHelloSpec() (*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
-}
diff --git a/src-go/server/internal/tls/factory.go b/src-go/server/internal/tls/factory.go
deleted file mode 100644
index 4b4bd60..0000000
--- a/src-go/server/internal/tls/factory.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package tls
-
-import (
- "crypto/tls"
- "net"
-
- oohttp "github.com/ooni/oohttp"
- utls "github.com/refraction-networking/utls"
-)
-
-// DefaultClientHelloID is the default [utls.ClientHelloID].
-var DefaultClientHelloID = &utls.HelloChrome_Auto
-
-// ConnFactory is a factory for creating UTLS connections.
-type ConnFactory interface {
- // NewUTLSConn creates a new UTLS connection.
- // The conn and config arguments MUST NOT be nil.
- NewUTLSConn(conn net.Conn, config *tls.Config) oohttp.TLSConn
-}
-
-// FactoryWithClientHelloId implements ConnFactory.
-type FactoryWithClientHelloId struct {
- // The TLS client hello id (fingerprint) to use.
- // Defaults to [DefaultClientHelloID].
- GetClientHello func(sni string) (*utls.ClientHelloID, *utls.ClientHelloSpec)
-}
-
-// NewUTLSConn implements ConnFactory.
-func (f *FactoryWithClientHelloId) NewUTLSConn(conn net.Conn, config *tls.Config) oohttp.TLSConn {
- uConfig := &utls.Config{
- RootCAs: config.RootCAs,
- NextProtos: config.NextProtos,
- ServerName: config.ServerName,
- DynamicRecordSizingDisabled: config.DynamicRecordSizingDisabled,
- InsecureSkipVerify: true,
- }
-
- clientHelloID, spec := f.GetClientHello(config.ServerName)
-
- if spec != nil {
- clientHelloID = &utls.HelloCustom
- } else if clientHelloID == nil {
- clientHelloID = DefaultClientHelloID
- }
-
- return &uconnAdapter{UConn: utls.UClient(conn, uConfig, *clientHelloID), spec: spec}
-}
diff --git a/src-go/server/internal/tls/fingerprint.go b/src-go/server/internal/tls/fingerprint.go
deleted file mode 100644
index a554abd..0000000
--- a/src-go/server/internal/tls/fingerprint.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package tls
-
-import (
- "strings"
-
- utls "github.com/refraction-networking/utls"
-)
-
-const DefaultFingerprint = "Default"
-
-type Fingerprint string
-
-func (fingerprint Fingerprint) ToClientHelloId() *utls.ClientHelloID {
- if fingerprint == DefaultFingerprint {
- return DefaultClientHelloID
- }
-
- parts := strings.Split(string(fingerprint), " ")
-
- clientHelloID := &utls.ClientHelloID{}
- if len(parts) >= 1 {
- clientHelloID.Client = parts[0]
- }
- if len(parts) >= 2 {
- clientHelloID.Version = parts[1]
- }
-
- return clientHelloID
-}
diff --git a/src-go/server/internal/tls/fingerprint_test.go b/src-go/server/internal/tls/fingerprint_test.go
deleted file mode 100644
index 790b688..0000000
--- a/src-go/server/internal/tls/fingerprint_test.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package tls
-
-import "testing"
-
-func TestFingerprint_ToClientHelloId(t *testing.T) {
- clientHelloId := Fingerprint("Chrome 100").ToClientHelloId()
- if clientHelloId.Client != "Chrome" || clientHelloId.Version != "100" {
- t.FailNow()
- }
-}
diff --git a/src-go/server/internal/transport.go b/src-go/server/internal/transport.go
deleted file mode 100644
index 4f4e56f..0000000
--- a/src-go/server/internal/transport.go
+++ /dev/null
@@ -1,175 +0,0 @@
-package internal
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "net"
- "strings"
- "time"
-
- utls "github.com/refraction-networking/utls"
-
- internalTls "server/internal/tls"
-
- oohttp "github.com/ooni/oohttp"
-)
-
-const (
- DefaultHttpTimeout = time.Duration(30) * time.Second
- DefaultHttpKeepAlive = time.Duration(30) * time.Second
- DefaultIdleConnTimeout = time.Duration(90) * time.Second
- DefaultTLSHandshakeTimeout = time.Duration(10) * time.Second
-)
-
-var DefaultConfig TransportConfig
-
-type RequestConfig struct {
- Host string
- Scheme string
-}
-
-type TransportConfig struct {
- // InterceptProxyAddr to intercept client tls fingerprint
- InterceptProxyAddr string
-
- // BurpAddr
- BurpAddr string
-
- // 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
-
- // Specifies the interval between keep-alive probes for an active network connection.
- // Defaults to [DefaultHttpKeepAlive].
- HttpKeepAliveInterval int
-
- // The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
- // Defaults to [DefaultIdleConnTimeout].
- IdleConnTimeout int
-
- // The maximum amount of time to wait for a TLS handshake.
- // Defaults to [DefaultTLSHandshakeTimeout].
- TLSHandshakeTimeout int
-
- // UseInterceptedFingerprint use intercepted fingerprint
- UseInterceptedFingerprint bool
-}
-
-func ParseTransportConfig(data string) (*TransportConfig, error) {
- config := &TransportConfig{}
-
- if strings.TrimSpace(data) == "" {
- return nil, errors.New("missing transport configuration")
- }
-
- if err := json.Unmarshal([]byte(data), config); err != nil {
- return nil, err
- }
-
- return config, nil
-}
-
-func ParseRequestConfig(data string) (*RequestConfig, error) {
- config := &RequestConfig{}
-
- if strings.TrimSpace(data) == "" {
- return nil, errors.New("missing request configuration")
- }
-
- if err := json.Unmarshal([]byte(data), config); err != nil {
- return nil, err
- }
-
- return config, nil
-}
-
-// NewTransport creates a new transport using the given configuration.
-func NewTransport(getInterceptedFingerprint func(sni string) string) (*oohttp.StdlibTransport, error) {
- dialer := &net.Dialer{
- Timeout: DefaultHttpTimeout,
- KeepAlive: DefaultHttpKeepAlive,
- }
-
- config := DefaultConfig
-
- if config.HttpTimeout != 0 {
- dialer.Timeout = time.Duration(config.HttpTimeout) * time.Second
- }
- if config.HttpKeepAliveInterval != 0 {
- dialer.KeepAlive = time.Duration(config.HttpKeepAliveInterval) * time.Second
- }
-
- var clientHelloSpec *utls.ClientHelloSpec
- var clientHelloID *utls.ClientHelloID
-
- if config.HexClientHello != "" {
- spec, err := config.HexClientHello.ToClientHelloSpec()
- if err != nil {
- return nil, fmt.Errorf("create spec from client hello: %w", err)
- }
- clientHelloSpec = spec
- } else if config.Fingerprint != "" {
- clientHelloID = config.Fingerprint.ToClientHelloId()
- }
-
- getClientHello := func(sni string) (*utls.ClientHelloID, *utls.ClientHelloSpec) {
- if !config.UseInterceptedFingerprint || config.HexClientHello != "" {
- return clientHelloID, clientHelloSpec
- }
-
- interceptedFingerprint := getInterceptedFingerprint(sni)
-
- if interceptedFingerprint == "" {
- return clientHelloID, clientHelloSpec
- }
-
- interceptedSpec, err := internalTls.HexClientHello(interceptedFingerprint).ToClientHelloSpec()
- if err == nil {
- return &utls.HelloCustom, interceptedSpec
- }
-
- return clientHelloID, clientHelloSpec
- }
-
- tlsFactory := &internalTls.FactoryWithClientHelloId{GetClientHello: getClientHello}
-
- transport := &oohttp.Transport{
- Proxy: oohttp.ProxyFromEnvironment,
- DialContext: dialer.DialContext,
- ForceAttemptHTTP2: true,
- MaxIdleConns: 100,
- IdleConnTimeout: DefaultIdleConnTimeout,
- TLSHandshakeTimeout: DefaultTLSHandshakeTimeout,
- ExpectContinueTimeout: 1 * time.Second,
- TLSClientFactory: tlsFactory.NewUTLSConn,
- }
-
- // add realistic initial HTTP2 SETTINGS to Chrome browser fingerprints
- if strings.HasPrefix(string(config.Fingerprint), "Chrome") {
- transport.EnableCustomInitialSettings()
- transport.HeaderTableSize = 4096 // 65536 // TODO: 4096 seems to be the max; modify oohtpp fork (see `http2/hpack` package) to support higher value
- transport.EnablePush = 0
- transport.MaxConcurrentStreams = 1000
- transport.InitialWindowSize = 6291456
- transport.MaxFrameSize = 16384
- transport.MaxHeaderListSize = 262144
- }
-
- if config.IdleConnTimeout != 0 {
- transport.IdleConnTimeout = time.Duration(config.IdleConnTimeout) * time.Second
- }
- if config.TLSHandshakeTimeout != 0 {
- transport.TLSHandshakeTimeout = time.Duration(config.TLSHandshakeTimeout) * time.Second
- }
-
- return &oohttp.StdlibTransport{
- Transport: transport,
- }, nil
-}
diff --git a/src-go/server/server.go b/src-go/server/server.go
index e227e5a..5cda47f 100644
--- a/src-go/server/server.go
+++ b/src-go/server/server.go
@@ -2,24 +2,11 @@ package server
import (
"context"
- "crypto/tls"
"fmt"
+ fhttp "github.com/bogdanfinn/fhttp"
+ utls "github.com/bogdanfinn/utls"
"io"
"net"
- "strings"
-
- "server/internal"
-
- http "github.com/ooni/oohttp"
-)
-
-const (
- // DefaultInterceptProxyAddress is the default intercept proxy listener address.
- DefaultInterceptProxyAddress string = "127.0.0.1:8886"
- // DefaultBurpProxyAddress is the default burp proxy listener address.
- DefaultBurpProxyAddress string = "127.0.0.1:8080"
- // DefaultSpoofProxyAddress is the default spoof proxy listener address.
- DefaultSpoofProxyAddress string = "127.0.0.1:8887"
)
// ConfigurationHeaderKey is the name of the header field that contains the RoundTripper configuration.
@@ -28,37 +15,59 @@ const (
const ConfigurationHeaderKey = "Awesometlsconfig"
var (
- s *http.Server
+ s *fhttp.Server
proxy *interceptProxy
isProxyOn bool
)
func init() {
- s = &http.Server{}
+ s = &fhttp.Server{}
}
func StartServer(addr string) error {
- s = &http.Server{}
+ if addr == "" {
+ return fmt.Errorf("address must be provided")
+ }
+
+ s = &fhttp.Server{}
ca, private, err := NewCertificateAuthority()
if err != nil {
return fmt.Errorf("NewCertificateAuthority, err: %w", err)
}
- m := http.NewServeMux()
- m.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
- http.EnableHeaderOrder(w)
-
+ m := fhttp.NewServeMux()
+ m.HandleFunc("/", func(w fhttp.ResponseWriter, req *fhttp.Request) {
configHeader := req.Header.Get(ConfigurationHeaderKey)
req.Header.Del(ConfigurationHeaderKey)
- config, err := internal.ParseRequestConfig(configHeader)
+ config, err := ParseTransportConfig(configHeader)
if err != nil {
writeError(w, err)
return
}
- transport, err := internal.NewTransport(proxy.getTLSFingerprint)
+ if !isProxyOn && config.UseInterceptedFingerprint {
+ if err = StartProxy(config.InterceptProxyAddr, config.BurpAddr); err != nil {
+ writeError(w, err)
+ return
+ }
+ isProxyOn = true
+ } else if isProxyOn && !config.UseInterceptedFingerprint {
+ if err = StopProxy(); err != nil {
+ writeError(w, err)
+ return
+ }
+ isProxyOn = false
+ }
+
+ if proxy != nil {
+ if interceptedFingerprint := proxy.getTLSFingerprint(); interceptedFingerprint != "" && config.UseInterceptedFingerprint {
+ config.HexClientHello = HexClientHello(interceptedFingerprint)
+ }
+ }
+
+ client, err := NewClient(config)
if err != nil {
writeError(w, err)
return
@@ -66,14 +75,13 @@ func StartServer(addr string) error {
req.URL.Host = config.Host
req.URL.Scheme = config.Scheme
- if strings.HasPrefix(string(internal.DefaultConfig.Fingerprint), "Chrome") {
- pHeaderOrder := []string{":method", ":authority", ":scheme", ":path"}
- for _, pHeader := range pHeaderOrder {
- req.Header.Add(http.PHeaderOrderKey, pHeader)
- }
- }
+ req.RequestURI = ""
+ req.Header[fhttp.HeaderOrderKey] = config.HeaderOrder
+ // The content-length header is already set by the client (internally).
+ // Leaving it here causes strange '400 bad request' errors from the destination, so we remove it.
+ req.Header.Del("Content-Length")
- res, err := transport.RoundTrip(req)
+ res, err := client.Do(req)
if err != nil {
writeError(w, err)
return
@@ -81,31 +89,40 @@ func StartServer(addr string) error {
defer res.Body.Close()
+ body, err := io.ReadAll(res.Body)
+ if err != nil {
+ writeError(w, err)
+ return
+ }
+
// Write the response (back to burp).
for k := range res.Header {
vv := res.Header.Values(k)
for _, v := range vv {
- w.Header().Add(k, v)
+ // The response body is already automatically decompressed, so we need to update the Content-Length header accordingly.
+ // Not doing so will cause the response writer to return an error.
+ if k == "Content-Length" {
+ w.Header().Add(k, fmt.Sprintf("%d", len(body)))
+ } else {
+ w.Header().Add(k, v)
+ }
}
}
-
w.WriteHeader(res.StatusCode)
-
- body, _ := io.ReadAll(res.Body)
w.Write(body)
})
s.Addr = addr
s.Handler = m
- s.TLSConfig = &tls.Config{
- Certificates: []tls.Certificate{
+ s.TLSConfig = &utls.Config{
+ Certificates: []utls.Certificate{
{
Certificate: [][]byte{ca.Raw},
PrivateKey: private,
Leaf: ca,
},
},
- NextProtos: []string{"http/1.1", "h2"},
+ NextProtos: []string{"http/1.1"},
}
listener, err := net.Listen("tcp", s.Addr)
@@ -113,7 +130,7 @@ func StartServer(addr string) error {
return fmt.Errorf("listen, err: %w", err)
}
- tlsListener := tls.NewListener(listener, s.TLSConfig)
+ tlsListener := utls.NewListener(listener, s.TLSConfig)
if err := s.Serve(tlsListener); err != nil {
return fmt.Errorf("serve, err: %w", err)
@@ -122,29 +139,6 @@ func StartServer(addr string) error {
return nil
}
-func SaveSettings(configJson string) error {
- config, err := internal.ParseTransportConfig(configJson)
- if err != nil {
- return err
- }
-
- if !isProxyOn && config.UseInterceptedFingerprint {
- if err = StartProxy(config.InterceptProxyAddr, config.BurpAddr); err != nil {
- return err
- }
- isProxyOn = true
- } else if isProxyOn && !config.UseInterceptedFingerprint {
- if err = StopProxy(); err != nil {
- return err
- }
- isProxyOn = false
- }
-
- internal.DefaultConfig = *config
-
- return nil
-}
-
func StartProxy(interceptAddr, burpAddr string) (err error) {
p, err := newInterceptProxy(interceptAddr, burpAddr)
if err != nil {
@@ -169,7 +163,7 @@ func StopServer() error {
return s.Shutdown(context.Background())
}
-func writeError(w http.ResponseWriter, err error) {
+func writeError(w fhttp.ResponseWriter, err error) {
w.WriteHeader(500)
fmt.Fprint(w, fmt.Errorf("Awesome TLS error: %s", err))
fmt.Println(err)
diff --git a/src-go/server/server_test.go b/src-go/server/server_test.go
deleted file mode 100644
index 861cc80..0000000
--- a/src-go/server/server_test.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package server
-
-import "testing"
-
-func TestStartServer(t *testing.T) {
- if err := StartServer(DefaultSpoofProxyAddress); err != nil {
- t.Fatal(err)
- }
-
- if err := StopServer(); err != nil {
- t.Fatal(err)
- }
-}
diff --git a/src-go/server/transport.go b/src-go/server/transport.go
new file mode 100644
index 0000000..0f52139
--- /dev/null
+++ b/src-go/server/transport.go
@@ -0,0 +1,117 @@
+package server
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ tls_client "github.com/bogdanfinn/tls-client"
+ "github.com/bogdanfinn/tls-client/profiles"
+ utls "github.com/bogdanfinn/utls"
+ "strings"
+)
+
+type TransportConfig struct {
+ // Hostname.
+ Host string
+
+ // Protocol scheme (HTTP or HTTPS).
+ Scheme string
+
+ // InterceptProxyAddr to intercept client tls fingerprint
+ InterceptProxyAddr string
+
+ // BurpAddr
+ BurpAddr string
+
+ // The TLS fingerprint to use.
+ Fingerprint string
+
+ // Hexadecimal Client Hello to use
+ HexClientHello HexClientHello
+
+ // The maximum amount of time a dial will wait for a connect to complete.
+ // Defaults to [DefaultHttpTimeout].
+ HttpTimeout int
+
+ // UseInterceptedFingerprint use intercepted fingerprint
+ UseInterceptedFingerprint bool
+
+ // HeaderOrder is the order of headers to be sent in the request.
+ HeaderOrder []string
+}
+
+func ParseTransportConfig(data string) (*TransportConfig, error) {
+ config := &TransportConfig{}
+
+ if strings.TrimSpace(data) == "" {
+ return nil, errors.New("missing transport configuration")
+ }
+
+ if err := json.Unmarshal([]byte(data), config); err != nil {
+ return nil, err
+ }
+
+ return config, nil
+}
+
+func NewClient(config *TransportConfig) (tls_client.HttpClient, error) {
+ options := []tls_client.HttpClientOption{
+ tls_client.WithNotFollowRedirects(),
+ tls_client.WithInsecureSkipVerify(),
+ }
+
+ if config.HttpTimeout != 0 {
+ options = append(options, tls_client.WithTimeoutSeconds(config.HttpTimeout))
+ }
+
+ // The order of precedence is:
+ // 1. Custom client hello from intercept proxy
+ // 2. Custom client hello from hex string
+ // 3. Preconfigured fingerprint
+ if config.HexClientHello != "" {
+ customClientHelloSpec, err := config.HexClientHello.ToClientHelloSpec()
+ if err != nil {
+ return nil, err
+ }
+
+ customClientHelloID := utls.ClientHelloID{
+ Client: "CustomFromHex",
+ Version: "1",
+ SpecFactory: func() (utls.ClientHelloSpec, error) {
+ return customClientHelloSpec, nil
+ },
+ }
+
+ defaultProfile := profiles.DefaultClientProfile
+ customClientProfile := profiles.NewClientProfile(
+ customClientHelloID,
+ defaultProfile.GetSettings(),
+ defaultProfile.GetSettingsOrder(),
+ defaultProfile.GetPseudoHeaderOrder(),
+ defaultProfile.GetConnectionFlow(),
+ defaultProfile.GetPriorities(),
+ defaultProfile.GetHeaderPriority(),
+ )
+
+ options = append(options, tls_client.WithClientProfile(customClientProfile))
+ } else if config.Fingerprint != "" {
+ var clientProfile profiles.ClientProfile
+ if strings.ToLower(config.Fingerprint) == "default" {
+ clientProfile = profiles.DefaultClientProfile
+ } else {
+ var ok bool
+ if clientProfile, ok = profiles.MappedTLSClients[config.Fingerprint]; !ok {
+ return nil, fmt.Errorf("failed to create client profile for unrecognized fingerprint '%s'", config.Fingerprint)
+ }
+ }
+
+ options = append(options, tls_client.WithClientProfile(clientProfile))
+ }
+
+ client, err := tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
+ if err != nil {
+ return nil, err
+ }
+
+ return client, nil
+}
diff --git a/src/main/java/burp/BurpExtender.java b/src/main/java/burp/BurpExtender.java
deleted file mode 100644
index 64757f5..0000000
--- a/src/main/java/burp/BurpExtender.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package burp;
-
-import com.google.gson.Gson;
-import com.sun.jna.Native;
-
-import java.io.PrintWriter;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-
-public class BurpExtender implements IBurpExtender, IHttpListener, IExtensionStateListener, IProxyListener {
- private PrintWriter stdout;
- private PrintWriter stderr;
- private Gson gson;
- private Settings settings;
-
- private IExtensionHelpers helpers;
- private IBurpExtenderCallbacks callbacks;
-
- private static final String HEADER_KEY = "Awesometlsconfig";
-
- @Override
- public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
- {
- this.stdout = new PrintWriter(callbacks.getStdout(), true);
- this.stderr = new PrintWriter(callbacks.getStderr(), true);
- this.callbacks = callbacks;
- this.helpers = callbacks.getHelpers();
- this.gson = new Gson();
- this.settings = new Settings(callbacks);
-
- callbacks.setExtensionName("Awesome TLS");
- callbacks.registerHttpListener(this);
- callbacks.registerProxyListener(this);
- callbacks.registerExtensionStateListener(this);
- callbacks.addSuiteTab(new SettingsTab(this.settings, callbacks));
-
- new Thread(() -> {
- var err = ServerLibrary.INSTANCE.StartServer(this.settings.getSpoofProxyAddress());
- if (!err.equals("")) {
- var isGraceful = err.contains("Server stopped"); // server was stopped gracefully by calling StopServer()
- var out = isGraceful ? this.stdout : this.stderr;
- out.println(err);
- if (!isGraceful) callbacks.unloadExtension(); // fatal error; disable the extension
- }
-
- var transportConfig = new TransportConfig();
- 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();
- transportConfig.TlsHandshakeTimeout = this.settings.getTlsHandshakeTimeout();
- transportConfig.UseInterceptedFingerprint = this.settings.getUseInterceptedFingerprint();
- var goConfigJSON = this.gson.toJson(transportConfig);
-
- err = ServerLibrary.INSTANCE.SaveSettings(goConfigJSON);
- if (!err.equals("")) {
- this.stdout.println(err);
- }
- }).start();
- }
-
- @Override
- public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
- if (!messageIsRequest) return;
-
- var httpService = messageInfo.getHttpService();
- var req = this.helpers.analyzeRequest(messageInfo.getRequest());
-
- var transportConfig = new RequestConfig();
- transportConfig.Host = httpService.getHost();
- transportConfig.Scheme = httpService.getProtocol();
- var goConfigJSON = this.gson.toJson(transportConfig);
-
- var headers = req.getHeaders();
- headers.add(HEADER_KEY + ": " + goConfigJSON);
-
- try {
- var url = new URL("https://" + this.settings.getSpoofProxyAddress());
- messageInfo.setHttpService(helpers.buildHttpService(url.getHost(), url.getPort(), url.getProtocol()));
- messageInfo.setRequest(helpers.buildHttpMessage(headers, Arrays.copyOfRange(messageInfo.getRequest(), req.getBodyOffset(), messageInfo.getRequest().length)));
- } catch (Exception e) {
- this.stderr.println("Failed to intercept http service: " + e);
- this.callbacks.unloadExtension();
- }
- }
-
- @Override
- public void extensionUnloaded() {
- var err = ServerLibrary.INSTANCE.StopServer();
- if (!err.equals("")) {
- this.stderr.println(err);
- }
- }
-
- @Override
- public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message) {
- if (message.getMessageInfo().getHttpService().getHost().equals("awesome-tls-error")) {
- var bodyOffset = this.helpers.analyzeRequest(message.getMessageInfo().getRequest()).getBodyOffset();
- var req = message.getMessageInfo().getRequest();
- var body = Arrays.copyOfRange(req, bodyOffset, req.length);
- this.stderr.println(new String(body, StandardCharsets.UTF_8));
- }
- }
-}
diff --git a/src/main/java/burp/Extension.java b/src/main/java/burp/Extension.java
new file mode 100644
index 0000000..737da95
--- /dev/null
+++ b/src/main/java/burp/Extension.java
@@ -0,0 +1,90 @@
+package burp;
+
+import burp.api.montoya.BurpExtension;
+import burp.api.montoya.MontoyaApi;
+import burp.api.montoya.http.HttpService;
+import burp.api.montoya.proxy.http.InterceptedRequest;
+import burp.api.montoya.proxy.http.ProxyRequestHandler;
+import burp.api.montoya.proxy.http.ProxyRequestReceivedAction;
+import burp.api.montoya.proxy.http.ProxyRequestToBeSentAction;
+import com.google.gson.Gson;
+
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+public class Extension implements BurpExtension {
+ private MontoyaApi api;
+ private Gson gson;
+ private Settings settings;
+
+ private static final String HEADER_KEY = "Awesometlsconfig";
+
+ @Override
+ public void initialize(MontoyaApi api) {
+ this.api = api;
+ this.gson = new Gson();
+ this.settings = new Settings(api);
+
+ api.extension().setName("Awesome TLS");
+ api.extension().registerUnloadingHandler(() -> {
+ var err = ServerLibrary.INSTANCE.StopServer();
+ if (!err.isEmpty()) {
+ api.logging().logToError(err);
+ }
+ });
+ api.userInterface().registerSuiteTab("Awesome TLS", new SettingsTab(settings).getUI());
+ api.proxy().registerRequestHandler(new ProxyRequestHandler() {
+ @Override
+ public ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest) {
+ return processHttpRequest(interceptedRequest);
+ }
+
+ @Override
+ public ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest) {
+ return ProxyRequestReceivedAction.continueWith(interceptedRequest);
+ }
+ });
+
+ new Thread(() -> {
+ var err = ServerLibrary.INSTANCE.StartServer(settings.getSpoofProxyAddress());
+ if (!err.isEmpty()) {
+ api.logging().logToError(err);
+ var isGraceful = err.contains("Server stopped") || err.contains("address already in use");
+ if (!isGraceful) {
+ api.extension().unload(); // fatal error; disable the extension
+ }
+ }
+ }).start();
+ }
+
+ private ProxyRequestToBeSentAction processHttpRequest(InterceptedRequest request) {
+ try {
+ var requestURL = new URL(request.url());
+
+ if (requestURL.getHost().equals("awesome-tls-error")) {
+ throw new Error(new String(request.body().getBytes(), StandardCharsets.UTF_8));
+ }
+
+ var headerOrder = new String[request.headers().size()];
+ for (var i = 0; i < request.headers().size(); i++) {
+ headerOrder[i] = request.headers().get(i).name();
+ }
+
+ var transportConfig = settings.toTransportConfig();
+ transportConfig.Host = requestURL.getHost();
+ transportConfig.Scheme = requestURL.getProtocol();
+ transportConfig.HeaderOrder = headerOrder;
+
+ var goConfigJSON = gson.toJson(transportConfig);
+ var url = new URL("https://" + settings.getSpoofProxyAddress());
+ var httpService = HttpService.httpService(url.getHost(), url.getPort(), Objects.equals(url.getProtocol(), "https"));
+ var nextRequest = request.withService(httpService).withAddedHeader(HEADER_KEY, goConfigJSON);
+
+ return ProxyRequestToBeSentAction.continueWith(nextRequest);
+ } catch (Exception e) {
+ api.logging().logToError("Http request error: " + e);
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/burp/RequestConfig.java b/src/main/java/burp/RequestConfig.java
deleted file mode 100644
index d1d5b12..0000000
--- a/src/main/java/burp/RequestConfig.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package burp;
-
-public class RequestConfig {
- /*
- * Hostname.
- */
- public String Host;
-
- /**
- * Protocol scheme (HTTP or HTTPS).
- */
- public String Scheme;
-}
diff --git a/src/main/java/burp/ServerLibrary.java b/src/main/java/burp/ServerLibrary.java
index 9f91beb..7a951eb 100644
--- a/src/main/java/burp/ServerLibrary.java
+++ b/src/main/java/burp/ServerLibrary.java
@@ -5,11 +5,13 @@
import com.sun.jna.Platform;
public interface ServerLibrary extends Library {
- ServerLibrary INSTANCE = Native.load((Platform.isMac() ? "lib" : "") + "server." + (Platform.isWindows() ? "dll" : Platform.isMac() ? "dylib" : "so"), ServerLibrary.class);
+ ServerLibrary INSTANCE = Native.load((Platform.isMac() ? "lib" : "") + "server." + (Platform.isWindows() ? "dll" : Platform.isMac() ? "dylib" : "so"), ServerLibrary.class);
String StartServer(String spoofAddr);
+
String StopServer();
- String SaveSettings(String config);
+ String GetFingerprints();
+
void SmokeTest();
}
diff --git a/src/main/java/burp/Settings.java b/src/main/java/burp/Settings.java
index 1a60345..6f9a2e6 100644
--- a/src/main/java/burp/Settings.java
+++ b/src/main/java/burp/Settings.java
@@ -1,9 +1,12 @@
package burp;
-import java.io.PrintWriter;
+import burp.api.montoya.MontoyaApi;
+import burp.api.montoya.persistence.Preferences;
+
+import java.util.Objects;
public class Settings {
- private final IBurpExtenderCallbacks callbacks;
+ private final Preferences storage;
private final String spoofProxyAddress = "SpoofProxyAddress";
private final String interceptProxyAddress = "InterceptProxyAddress";
@@ -12,35 +15,30 @@ public class Settings {
private final String hexClientHello = "HexClientHello";
private final String useInterceptedFingerprint = "UseInterceptedFingerprint";
private final String httpTimeout = "HttpTimeout";
- private final String httpKeepAliveInterval = "HttpKeepAliveInterval";
- private final String idleConnTimeout = "IdleConnTimeout";
- private final String tlsHandshakeTimeout = "TlsHandshakeTimeout";
public static final String DEFAULT_SPOOF_PROXY_ADDRESS = "127.0.0.1:8887";
public static final String DEFAULT_INTERCEPT_PROXY_ADDRESS = "127.0.0.1:8886";
public static final String DEFAULT_BURP_PROXY_ADDRESS = "127.0.0.1:8080";
public static final String DEFAULT_HTTP_TIMEOUT = "30";
- public static final String DEFAULT_IDLE_CONN_TIMEOUT = "90";
- public static final String DEFAULT_TLS_HANDSHAKE_TIMEOUT = "10";
- public static final String DEFAULT_TLS_FINGERPRINT = "Default";
+ public static final String DEFAULT_TLS_FINGERPRINT = "default";
- public Settings(IBurpExtenderCallbacks callbacks) {
- this.callbacks = callbacks;
+ public Settings(MontoyaApi api) {
+ this.storage = api.persistence().preferences();
this.setDefaults();
}
private void setDefaults() {
- if (this.read(this.spoofProxyAddress) == "" || this.read(this.spoofProxyAddress) == null) {
- this.write(this.spoofProxyAddress, DEFAULT_SPOOF_PROXY_ADDRESS);
- }
+ if (Objects.equals(this.read(this.spoofProxyAddress), "") || this.read(this.spoofProxyAddress) == null) {
+ this.write(this.spoofProxyAddress, DEFAULT_SPOOF_PROXY_ADDRESS);
+ }
- if (this.read(this.interceptProxyAddress) == "" || this.read(this.interceptProxyAddress) == null) {
- this.write(this.interceptProxyAddress, DEFAULT_INTERCEPT_PROXY_ADDRESS);
- }
+ if (Objects.equals(this.read(this.interceptProxyAddress), "") || this.read(this.interceptProxyAddress) == null) {
+ this.write(this.interceptProxyAddress, DEFAULT_INTERCEPT_PROXY_ADDRESS);
+ }
- if (this.read(this.burpProxyAddress) == "" || this.read(this.burpProxyAddress) == null) {
- this.write(this.burpProxyAddress, DEFAULT_BURP_PROXY_ADDRESS);
- }
+ if (Objects.equals(this.read(this.burpProxyAddress), "") || this.read(this.burpProxyAddress) == null) {
+ this.write(this.burpProxyAddress, DEFAULT_BURP_PROXY_ADDRESS);
+ }
if (this.read(this.fingerprint) == null) {
this.write(this.fingerprint, DEFAULT_TLS_FINGERPRINT);
@@ -49,26 +47,14 @@ private void setDefaults() {
if (this.read(this.httpTimeout) == null) {
this.write(this.httpTimeout, DEFAULT_HTTP_TIMEOUT);
}
-
- if (this.read(this.httpKeepAliveInterval) == null) {
- this.write(this.httpKeepAliveInterval, DEFAULT_HTTP_TIMEOUT);
- }
-
- if (this.read(this.idleConnTimeout) == null) {
- this.write(this.idleConnTimeout, DEFAULT_IDLE_CONN_TIMEOUT);
- }
-
- if (this.read(this.tlsHandshakeTimeout) == null) {
- this.write(this.tlsHandshakeTimeout, DEFAULT_TLS_HANDSHAKE_TIMEOUT);
- }
}
public String read(String key) {
- return this.callbacks.loadExtensionSetting(key);
+ return this.storage.getString(key);
}
public void write(String key, String value) {
- this.callbacks.saveExtensionSetting(key, value);
+ this.storage.setString(key, value);
}
public String getSpoofProxyAddress() {
@@ -111,60 +97,34 @@ public void setHttpTimeout(int httpTimeout) {
this.write(this.httpTimeout, String.valueOf(httpTimeout));
}
- public int getHttpKeepAliveInterval() {
- return Integer.parseInt(this.read(this.httpKeepAliveInterval));
+ public String getFingerprint() {
+ return this.read(this.fingerprint);
}
- public void setHttpKeepAliveInterval(int httpTimeout) {
- this.write(this.httpKeepAliveInterval, String.valueOf(httpTimeout));
- }
-
- 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));
- }
-
- public void setIdleConnTimeout(int idleConnTimeout) {
- this.write(this.idleConnTimeout, String.valueOf(idleConnTimeout));
- }
-
- public int getTlsHandshakeTimeout() {
- return Integer.parseInt(this.read(this.tlsHandshakeTimeout));
+ public String getHexClientHello() {
+ return this.read(this.hexClientHello);
}
- public void setTlsHandshakeTimeout(int tlsHandshakeTimeout) {
- this.write(this.tlsHandshakeTimeout, String.valueOf(tlsHandshakeTimeout));
+ public void setHexClientHello(String hexClientHello) {
+ this.write(this.hexClientHello, hexClientHello);
}
public String[] getFingerprints() {
- return new String[]{
- "Default",
- "Chrome 102",
- "Chrome 100",
- "Chrome 96",
- "Firefox 105",
- "Firefox 102",
- "Firefox 99",
- "Edge 106",
- "Edge 85",
- "Safari 16.0",
- "QQBrowser 11.1",
- "iOS 14",
- "iOS 13",
- "iOS 12.1",
- "Android 11",
- "Randomized 0",
- "Randomized-ALPN 0",
- "Randomized-NoALPN 0",
- };
+ return ServerLibrary.INSTANCE.GetFingerprints().split("\n");
+ }
+
+ public TransportConfig toTransportConfig() {
+ var transportConfig = new TransportConfig();
+ transportConfig.Fingerprint = this.getFingerprint();
+ transportConfig.HexClientHello = this.getHexClientHello();
+ transportConfig.HttpTimeout = this.getHttpTimeout();
+ transportConfig.UseInterceptedFingerprint = this.getUseInterceptedFingerprint();
+ transportConfig.BurpAddr = this.getBurpProxyAddress();
+ transportConfig.InterceptProxyAddr = this.getInterceptProxyAddress();
+ return transportConfig;
}
}
diff --git a/src/main/java/burp/SettingsTab.form b/src/main/java/burp/SettingsTab.form
index 1f9f6a8..0a11aec 100644
--- a/src/main/java/burp/SettingsTab.form
+++ b/src/main/java/burp/SettingsTab.form
@@ -17,7 +17,7 @@
-
+
@@ -79,58 +79,10 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -159,7 +111,7 @@
-
+
diff --git a/src/main/java/burp/SettingsTab.java b/src/main/java/burp/SettingsTab.java
index cb94d6b..974b3ab 100644
--- a/src/main/java/burp/SettingsTab.java
+++ b/src/main/java/burp/SettingsTab.java
@@ -1,17 +1,13 @@
package burp;
-import com.google.gson.Gson;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Spacer;
import javax.swing.*;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.PrintWriter;
-public class SettingsTab implements ITab {
+public class SettingsTab {
private JComboBox comboBoxFingerprint;
private JPanel panelMain;
private JLabel labelFingerprint;
@@ -22,12 +18,6 @@ public class SettingsTab implements ITab {
private JButton buttonSave;
private JLabel labelTimeout;
private JSpinner spinnerHttpTimout;
- private JSpinner spinnerKeepAlive;
- private JLabel labelKeepAlive;
- private JLabel labelIdleConnTimeout;
- private JSpinner spinnerIdleConnTimeout;
- private JLabel labelTlsHandshakeTimeout;
- private JSpinner spinnerTlsHandshakeTimeout;
private JLabel labelInterceptProxyAddress;
private JLabel labelBurpProxyAddress;
private JCheckBox checkBoxButtonUseInterceptedFingerprint;
@@ -38,94 +28,34 @@ public class SettingsTab implements ITab {
private JLabel labelHexClientHello;
private JTextField textFieldHexClientHello;
- private Gson gson;
- private PrintWriter stdout;
- private PrintWriter stderr;
-
- @Override
- public String getTabCaption() {
- return "Awesome TLS";
- }
-
- @Override
- public Component getUiComponent() {
- return panelMain;
- }
-
- public SettingsTab(Settings settings, IBurpExtenderCallbacks callbacks) {
- gson = new Gson();
- this.stdout = new PrintWriter(callbacks.getStdout(), true);
- this.stderr = new PrintWriter(callbacks.getStderr(), true);
-
+ public SettingsTab(Settings settings) {
textFieldInterceptProxyAddress.setText(settings.getInterceptProxyAddress());
textFieldBurpProxyAddress.setText(settings.getBurpProxyAddress());
textFieldSpoofProxyAddress.setText(settings.getSpoofProxyAddress());
textFieldHexClientHello.setText(settings.getHexClientHello());
-
spinnerHttpTimout.setValue(settings.getHttpTimeout());
- spinnerKeepAlive.setValue(settings.getHttpKeepAliveInterval());
- spinnerIdleConnTimeout.setValue(settings.getIdleConnTimeout());
- spinnerTlsHandshakeTimeout.setValue(settings.getTlsHandshakeTimeout());
-
+ checkBoxButtonUseInterceptedFingerprint.setSelected(settings.getUseInterceptedFingerprint());
for (var item : settings.getFingerprints()) {
comboBoxFingerprint.addItem(item);
}
comboBoxFingerprint.setSelectedItem(settings.getFingerprint());
- buttonSave.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- var err = SaveSettings(settings);
- if (!err.equals("")) {
- JOptionPane.showMessageDialog(panelSettings,
- err,
- "Error",
- JOptionPane.ERROR_MESSAGE);
- }
- }
+ buttonSave.addActionListener(e -> {
+ settings.setSpoofProxyAddress(textFieldSpoofProxyAddress.getText());
+ settings.setFingerprint((String) comboBoxFingerprint.getSelectedItem());
+ settings.setHexClientHello(textFieldHexClientHello.getText());
+ settings.setHttpTimeout((int) spinnerHttpTimout.getValue());
});
- buttonSaveAdvanced.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- var err = SaveSettings(settings);
- if (!err.equals("")) {
- JOptionPane.showMessageDialog(panelAdvanced,
- err,
- "Error",
- JOptionPane.ERROR_MESSAGE);
- }
- }
+ buttonSaveAdvanced.addActionListener(e -> {
+ settings.setInterceptProxyAddress(textFieldInterceptProxyAddress.getText());
+ settings.setBurpProxyAddress(textFieldBurpProxyAddress.getText());
+ settings.setUseInterceptedFingerprint(checkBoxButtonUseInterceptedFingerprint.isSelected());
});
}
- private String SaveSettings(Settings settings) {
- settings.setSpoofProxyAddress(textFieldSpoofProxyAddress.getText());
- settings.setFingerprint((String) comboBoxFingerprint.getSelectedItem());
- settings.setHexClientHello(textFieldHexClientHello.getText());
- settings.setHttpTimeout((int) spinnerHttpTimout.getValue());
- settings.setIdleConnTimeout((int) spinnerIdleConnTimeout.getValue());
- settings.setHttpKeepAliveInterval((int) spinnerKeepAlive.getValue());
- settings.setTlsHandshakeTimeout((int) spinnerTlsHandshakeTimeout.getValue());
- settings.setInterceptProxyAddress(textFieldInterceptProxyAddress.getText());
- settings.setBurpProxyAddress(textFieldBurpProxyAddress.getText());
- settings.setUseInterceptedFingerprint(checkBoxButtonUseInterceptedFingerprint.isSelected());
-
- var transportConfig = new TransportConfig();
- transportConfig.InterceptProxyAddr = settings.getInterceptProxyAddress();
- transportConfig.BurpAddr = settings.getBurpProxyAddress();
- transportConfig.Fingerprint = settings.getFingerprint();
- transportConfig.HexClientHello = settings.getHexClientHello();
- transportConfig.HttpTimeout = settings.getHttpTimeout();
- transportConfig.HttpKeepAliveInterval = settings.getHttpKeepAliveInterval();
- transportConfig.IdleConnTimeout = settings.getIdleConnTimeout();
- transportConfig.TlsHandshakeTimeout = settings.getTlsHandshakeTimeout();
- transportConfig.UseInterceptedFingerprint = settings.getUseInterceptedFingerprint();
- var goConfigJSON = this.gson.toJson(transportConfig);
-
- this.stdout.println("Using config: " + goConfigJSON);
-
- return ServerLibrary.INSTANCE.SaveSettings(goConfigJSON);
+ public JPanel getUI() {
+ return this.panelMain;
}
{
@@ -148,7 +78,7 @@ private String SaveSettings(Settings settings) {
tabbedPaneTab = new JTabbedPane();
panelMain.add(tabbedPaneTab, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
panelSettings = new JPanel();
- panelSettings.setLayout(new GridLayoutManager(16, 1, new Insets(0, 0, 0, 0), -1, -1));
+ panelSettings.setLayout(new GridLayoutManager(10, 1, new Insets(0, 0, 0, 0), -1, -1));
tabbedPaneTab.addTab("settings", panelSettings);
labelSpoofProxyAddress = new JLabel();
labelSpoofProxyAddress.setRequestFocusEnabled(false);
@@ -173,27 +103,9 @@ private String SaveSettings(Settings settings) {
spinnerHttpTimout = new JSpinner();
spinnerHttpTimout.setToolTipText("The maximum amount of time a dial will wait for a connect to complete.");
panelSettings.add(spinnerHttpTimout, new GridConstraints(7, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- labelKeepAlive = new JLabel();
- labelKeepAlive.setText("Http keep alive interval");
- panelSettings.add(labelKeepAlive, new GridConstraints(8, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- spinnerKeepAlive = new JSpinner();
- spinnerKeepAlive.setToolTipText("Specifies the interval between keep-alive probes for an active network connection.");
- panelSettings.add(spinnerKeepAlive, new GridConstraints(9, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- labelIdleConnTimeout = new JLabel();
- labelIdleConnTimeout.setText("Idle connection timeout");
- panelSettings.add(labelIdleConnTimeout, new GridConstraints(10, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- spinnerIdleConnTimeout = new JSpinner();
- spinnerIdleConnTimeout.setToolTipText("The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.");
- panelSettings.add(spinnerIdleConnTimeout, new GridConstraints(11, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- labelTlsHandshakeTimeout = new JLabel();
- labelTlsHandshakeTimeout.setText("TLS handshake timeout");
- panelSettings.add(labelTlsHandshakeTimeout, new GridConstraints(12, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
- spinnerTlsHandshakeTimeout = new JSpinner();
- spinnerTlsHandshakeTimeout.setToolTipText("The maximum amount of time to wait for a TLS handshake.");
- panelSettings.add(spinnerTlsHandshakeTimeout, new GridConstraints(13, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
final JPanel panel1 = new JPanel();
panel1.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
- panelSettings.add(panel1, new GridConstraints(15, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ panelSettings.add(panel1, new GridConstraints(9, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
labelHexClientHello = new JLabel();
labelHexClientHello.setRequestFocusEnabled(false);
labelHexClientHello.setText("Hex Client Hello:");
@@ -205,7 +117,7 @@ private String SaveSettings(Settings settings) {
panelSettings.add(textFieldHexClientHello, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
buttonSave = new JButton();
buttonSave.setText("Save all settings");
- panelSettings.add(buttonSave, new GridConstraints(14, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ panelSettings.add(buttonSave, new GridConstraints(8, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
panelAdvanced = new JPanel();
panelAdvanced.setLayout(new GridLayoutManager(7, 1, new Insets(0, 0, 0, 0), -1, -1));
panelAdvanced.setToolTipText("");
diff --git a/src/main/java/burp/TransportConfig.java b/src/main/java/burp/TransportConfig.java
index 7fd68cb..59a78c5 100644
--- a/src/main/java/burp/TransportConfig.java
+++ b/src/main/java/burp/TransportConfig.java
@@ -2,14 +2,24 @@
/**
- * Represents the configuration for a transport.
+ * Represents the configuration for transport.
*/
public class TransportConfig {
+ /*
+ * Hostname.
+ */
+ public String Host;
+
+ /**
+ * Protocol scheme (HTTP or HTTPS).
+ */
+ public String Scheme;
/**
* Intercept ClientHello Proxy Address.
*/
public String InterceptProxyAddr;
+
/**
* Burp Proxy Address.
*/
@@ -31,27 +41,12 @@ public class TransportConfig {
public Boolean UseInterceptedFingerprint;
/**
- * The maximum amount of time a dial will wait for a connect to complete.
- * Defaults to [DefaultHttpTimeout].
+ * The maximum amount of time to wait for an HTTP response.
*/
public int HttpTimeout;
/**
- * Specifies the interval between keep-alive probes for an active network connection.
- * Defaults to [DefaultHttpKeepAlive].
+ * the order of headers to be sent in the request.
*/
- public int HttpKeepAliveInterval;
-
- /**
- * The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
- * Defaults to [DefaultIdleConnTimeout].
- */
- public int IdleConnTimeout;
-
- /**
- * The maximum amount of time to wait for a TLS handshake.
- * Defaults to [DefaultTLSHandshakeTimeout].
- */
- public int TlsHandshakeTimeout;
+ public String[] HeaderOrder;
}
-