Skip to content

Commit 0cf5a47

Browse files
authored
Merge pull request #2181 from BishopFox/fix/gzip-bomb
Add limit to gzip size
2 parents 8d5dbd2 + 8d67599 commit 0cf5a47

4 files changed

Lines changed: 75 additions & 9 deletions

File tree

server/c2/http.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ var (
6868
)
6969

7070
const (
71-
DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2Gb
72-
DefaultMaxUnauthBodyLength = 8 * 1024 * 1024 // 8Mb
73-
DefaultHTTPTimeout = time.Minute
74-
DefaultLongPollTimeout = time.Second
75-
DefaultLongPollJitter = time.Second
76-
minPollTimeout = time.Second
71+
DefaultMaxBodyLength = 2 * 1024 * 1024 * 1024 // 2Gb
72+
DefaultMaxUnauthBodyLength = 8 * 1024 * 1024 // 8Mb
73+
DefaultHTTPTimeout = time.Minute
74+
DefaultLongPollTimeout = time.Second
75+
DefaultLongPollJitter = time.Second
76+
minPollTimeout = time.Second
7777
)
7878

7979
var (
@@ -574,7 +574,7 @@ func (s *SliverHTTPC2) startSessionHandler(resp http.ResponseWriter, req *http.R
574574
s.defaultHandler(resp, req)
575575
return
576576
}
577-
data, err := encoder.Decode(body)
577+
data, err := decodeReqBodyWithMaxLen(encoder, body, int64(DefaultMaxUnauthBodyLength))
578578
if err != nil {
579579
httpLog.Errorf("Failed to decode body %s", err)
580580
s.defaultHandler(resp, req)
@@ -720,7 +720,7 @@ func (s *SliverHTTPC2) readReqBody(httpSession *HTTPSession, resp http.ResponseW
720720
return nil, err
721721
}
722722

723-
data, err := encoder.Decode(body)
723+
data, err := decodeReqBodyWithMaxLen(encoder, body, int64(DefaultMaxBodyLength))
724724
if err != nil {
725725
httpLog.Warnf("Failed to decode body %s", err)
726726
s.defaultHandler(resp, req)
@@ -735,6 +735,13 @@ func (s *SliverHTTPC2) readReqBody(httpSession *HTTPSession, resp http.ResponseW
735735
return plaintext, err
736736
}
737737

738+
func decodeReqBodyWithMaxLen(encoder sliverEncoders.Encoder, body []byte, maxDecodedLen int64) ([]byte, error) {
739+
if limitedDecoder, ok := encoder.(sliverEncoders.LimitedDecoder); ok {
740+
return limitedDecoder.DecodeWithMaxLen(body, maxDecodedLen)
741+
}
742+
return encoder.Decode(body)
743+
}
744+
738745
func (s *SliverHTTPC2) getServerPollTimeout() time.Duration {
739746
min := s.ServerConf.LongPollTimeout
740747
max := s.ServerConf.LongPollTimeout + s.ServerConf.LongPollJitter

util/encoders/encoders.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ type Encoder interface {
3838
Decode([]byte) ([]byte, error)
3939
}
4040

41+
// LimitedDecoder - Decoders that can enforce a max output length while decoding.
42+
type LimitedDecoder interface {
43+
DecodeWithMaxLen(data []byte, maxLen int64) ([]byte, error)
44+
}
45+
4146
// EncoderFS - Generic interface to read wasm encoders from a filesystem
4247
type EncoderFS interface {
4348
Open(name string) (fs.File, error)

util/encoders/gzip.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ package encoders
2121
import (
2222
"bytes"
2323
"compress/gzip"
24+
"fmt"
25+
"io"
2426
"sync"
2527
)
2628

2729
// Gzip - Gzip compression encoder
2830
type Gzip struct{}
2931

32+
const DefaultMaxGzipDecodeLen = 2 * 1024 * 1024 * 1024 // 2GB
33+
3034
var gzipWriterPools = &sync.Pool{}
3135

3236
func init() {
@@ -80,14 +84,33 @@ func (g Gzip) Encode(data []byte) ([]byte, error) {
8084

8185
// Decode - Uncompressed data with gzip
8286
func (g Gzip) Decode(data []byte) ([]byte, error) {
87+
return g.DecodeWithMaxLen(data, DefaultMaxGzipDecodeLen)
88+
}
89+
90+
// DecodeWithMaxLen - Uncompress data with gzip while enforcing a max output size.
91+
func (g Gzip) DecodeWithMaxLen(data []byte, maxLen int64) ([]byte, error) {
92+
if maxLen < 0 {
93+
return nil, fmt.Errorf("invalid max decode length: %d", maxLen)
94+
}
95+
8396
reader, err := gzip.NewReader(bytes.NewReader(data))
8497
if err != nil {
8598
return nil, err
8699
}
100+
defer reader.Close()
101+
102+
limitedReader := &io.LimitedReader{
103+
R: reader,
104+
N: maxLen + 1,
105+
}
106+
87107
var buf bytes.Buffer
88-
_, err = buf.ReadFrom(reader)
108+
_, err = buf.ReadFrom(limitedReader)
89109
if err != nil {
90110
return nil, err
91111
}
112+
if limitedReader.N == 0 {
113+
return nil, fmt.Errorf("gzip decoded payload exceeds %d bytes", maxLen)
114+
}
92115
return buf.Bytes(), nil
93116
}

util/encoders/gzip_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,34 @@ func TestGzipGunzip(t *testing.T) {
5858
}
5959
}
6060
}
61+
62+
func TestGzipDecodeWithMaxLen(t *testing.T) {
63+
payload := bytes.Repeat([]byte("A"), 8192)
64+
gzip := new(Gzip)
65+
encoded, err := gzip.Encode(payload)
66+
if err != nil {
67+
t.Fatalf("gzip encode failed: %v", err)
68+
}
69+
70+
decoded, err := gzip.DecodeWithMaxLen(encoded, int64(len(payload)))
71+
if err != nil {
72+
t.Fatalf("gzip decode failed: %v", err)
73+
}
74+
if !bytes.Equal(payload, decoded) {
75+
t.Fatal("decoded payload does not match original")
76+
}
77+
}
78+
79+
func TestGzipDecodeWithMaxLenRejectsOversizedOutput(t *testing.T) {
80+
payload := bytes.Repeat([]byte("A"), 8192)
81+
gzip := new(Gzip)
82+
encoded, err := gzip.Encode(payload)
83+
if err != nil {
84+
t.Fatalf("gzip encode failed: %v", err)
85+
}
86+
87+
_, err = gzip.DecodeWithMaxLen(encoded, int64(len(payload)-1))
88+
if err == nil {
89+
t.Fatal("expected decode to fail for oversized payload")
90+
}
91+
}

0 commit comments

Comments
 (0)