Closed sleeyax closed 1 year ago
Full customization of the frames might be overkill, perhaps we should just set the recommended SETTINGS frame based on the selected browser fingerprint (especially chrome).
Hi @sleeyax. Thanks for your great work with this project. Would be happy to help with this feature. Any tips you could provide to help me get started?
Hey @ChrisMcMStone sure thing, thanks for your interest and willingness to contribute to the project! It's been a while but I can point you in a general direction.
The inital settings are created here: https://github.com/sleeyax/burp-awesome-tls/blob/main/src-go/server/internal/net/http2/transport.go#L674 You could patch it like this (this is from a different project of mine, so adjust accordingly):
From 2c399035ddb74e8c40e7f60fba3299b463fd44fd Mon Sep 17 00:00:00 2001
From: Sleeyax <yourd3veloper@gmail.com>
Date: Thu, 3 Jun 2021 21:46:23 +0200
Subject: [PATCH] Customizable initial SETTINGS frame
---
packages/http2/transport.go | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/packages/http2/transport.go b/packages/http2/transport.go
index ff71e3e..7ab542d 100644
--- a/packages/http2/transport.go
+++ b/packages/http2/transport.go
@@ -89,6 +89,21 @@ type Transport struct {
// plain-text "http" scheme. Note that this does not enable h2c support.
AllowHTTP bool
+ // HeaderTableSize is the http2 SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame.
+ HeaderTableSize uint32
+
+ // EnablePush is the http2 SETTINGS_ENABLE_PUSH to send in the initial settings frame.
+ EnablePush uint32
+
+ // MaxConcurrentStreams is the http2 SETTINGS_MAX_CONCURRENT_STREAMS to send in the initial settings frame.
+ MaxConcurrentStreams uint32
+
+ // InitialWindowSize is the http2 SETTINGS_INITIAL_WINDOW_SIZE to send in the initial settings frame.
+ InitialWindowSize uint32
+
+ // MaxFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the initial settings frame.
+ MaxFrameSize uint32
+
// MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
// send in the initial settings frame. It is how many bytes
// of response headers are allowed. Unlike the http2 spec, zero here
@@ -675,8 +690,11 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
}
initialSettings := []Setting{
- {ID: SettingEnablePush, Val: 0},
- {ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow},
+ {ID: SettingHeaderTableSize, Val: t.HeaderTableSize},
+ {ID: SettingEnablePush, Val: t.EnablePush},
+ {ID: SettingMaxConcurrentStreams, Val: t.MaxConcurrentStreams},
+ {ID: SettingInitialWindowSize, Val: t.InitialWindowSize},
+ {ID: SettingMaxFrameSize, Val: t.MaxFrameSize},
}
if max := t.maxHeaderListSize(); max != 0 {
initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max})
Next, make sure any hardcoded defaults are overriden by these new settings:
diff --git a/packages/http2/transport.go b/packages/http2/transport.go
index 7ab542d..82a691f 100644
--- a/packages/http2/transport.go
+++ b/packages/http2/transport.go
@@ -648,10 +648,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
tconn: c,
readerDone: make(chan struct{}),
nextStreamID: 1,
- maxFrameSize: 16 << 10, // spec default
- initialWindowSize: 65535, // spec default
- maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
- peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
+ maxFrameSize: t.MaxFrameSize, // spec default
+ initialWindowSize: t.InitialWindowSize, // spec default
+ maxConcurrentStreams: t.MaxConcurrentStreams, // "infinite", per spec. 1000 seems good enough.
+ peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
streams: make(map[uint32]*clientStream),
singleUse: singleUse,
wantSettingsAck: true,
@@ -666,14 +666,14 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
}
cc.cond = sync.NewCond(&cc.mu)
- cc.flow.add(int32(initialWindowSize))
+ cc.flow.add(int32(t.InitialWindowSize))
// TODO: adjust this writer size to account for frame size +
// MTU + crypto/tls record padding.
cc.bw = bufio.NewWriter(stickyErrWriter{c, &cc.werr})
cc.br = bufio.NewReader(c)
cc.fr = NewFramer(cc.bw, cc.br)
- cc.fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
+ cc.fr.ReadMetaHeaders = hpack.NewDecoder(t.InitialWindowSize, nil)
cc.fr.MaxHeaderListSize = t.maxHeaderListSize()
// TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on
@@ -703,7 +703,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
cc.bw.Write(clientPreface)
cc.fr.WriteSettings(initialSettings...)
cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow)
- cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
+ cc.inflow.add(transportDefaultConnFlow + int32(t.InitialWindowSize))
cc.bw.Flush()
if cc.werr != nil {
cc.Close()
Finally, you can set set the correct initial settings frame based on the specified r.TlsFingerprint
here: https://github.com/sleeyax/burp-awesome-tls/blob/main/src-go/server/roundtripper.go#L115
You can use Charles proxy to see which settings are the defaults for several browsers. Please try to add support for the latest Chrome version at minimum.
I hope that's enough information to get you started. Good luck and have fun!
Hi @ChrisMcMStone any news? Let me know if you need any additional help!
rfc7540