Skip to content

Commit 9132f94

Browse files
committed
Fix DoS attack vulnerability in VMess Option Processing
1 parent 6dce0d9 commit 9132f94

File tree

4 files changed

+72
-46
lines changed

4 files changed

+72
-46
lines changed

proxy/vmess/encoding/client.go

+28-20
Original file line numberDiff line numberDiff line change
@@ -137,31 +137,35 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
137137
return nil
138138
}
139139

140-
func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
140+
func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
141141
var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
142142
if request.Option.Has(protocol.RequestOptionChunkMasking) {
143143
sizeParser = NewShakeSizeParser(c.requestBodyIV[:])
144144
}
145145
var padding crypto.PaddingLengthGenerator
146146
if request.Option.Has(protocol.RequestOptionGlobalPadding) {
147-
padding = sizeParser.(crypto.PaddingLengthGenerator)
147+
var ok bool
148+
padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
149+
if !ok {
150+
return nil, newError("invalid option: RequestOptionGlobalPadding")
151+
}
148152
}
149153

150154
switch request.Security {
151155
case protocol.SecurityType_NONE:
152156
if request.Option.Has(protocol.RequestOptionChunkStream) {
153157
if request.Command.TransferType() == protocol.TransferTypeStream {
154-
return crypto.NewChunkStreamWriter(sizeParser, writer)
158+
return crypto.NewChunkStreamWriter(sizeParser, writer), nil
155159
}
156160
auth := &crypto.AEADAuthenticator{
157161
AEAD: new(NoOpAuthenticator),
158162
NonceGenerator: crypto.GenerateEmptyBytes(),
159163
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
160164
}
161-
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
165+
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil
162166
}
163167

164-
return buf.NewWriter(writer)
168+
return buf.NewWriter(writer), nil
165169
case protocol.SecurityType_LEGACY:
166170
aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey[:], c.requestBodyIV[:])
167171
cryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
@@ -171,10 +175,10 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
171175
NonceGenerator: crypto.GenerateEmptyBytes(),
172176
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
173177
}
174-
return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding)
178+
return crypto.NewAuthenticationWriter(auth, sizeParser, cryptionWriter, request.Command.TransferType(), padding), nil
175179
}
176180

177-
return &buf.SequentialWriter{Writer: cryptionWriter}
181+
return &buf.SequentialWriter{Writer: cryptionWriter}, nil
178182
case protocol.SecurityType_AES128_GCM:
179183
aead := crypto.NewAesGcm(c.requestBodyKey[:])
180184
auth := &crypto.AEADAuthenticator{
@@ -193,7 +197,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
193197
}
194198
sizeParser = NewAEADSizeParser(lengthAuth)
195199
}
196-
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
200+
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
197201
case protocol.SecurityType_CHACHA20_POLY1305:
198202
aead, err := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey[:]))
199203
common.Must(err)
@@ -215,9 +219,9 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write
215219
}
216220
sizeParser = NewAEADSizeParser(lengthAuth)
217221
}
218-
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
222+
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
219223
default:
220-
panic("Unknown security type.")
224+
return nil, newError("invalid option: Security")
221225
}
222226
}
223227

@@ -306,21 +310,25 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
306310
return header, nil
307311
}
308312

309-
func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
313+
func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) {
310314
var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{}
311315
if request.Option.Has(protocol.RequestOptionChunkMasking) {
312316
sizeParser = NewShakeSizeParser(c.responseBodyIV[:])
313317
}
314318
var padding crypto.PaddingLengthGenerator
315319
if request.Option.Has(protocol.RequestOptionGlobalPadding) {
316-
padding = sizeParser.(crypto.PaddingLengthGenerator)
320+
var ok bool
321+
padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
322+
if !ok {
323+
return nil, newError("invalid option: RequestOptionGlobalPadding")
324+
}
317325
}
318326

319327
switch request.Security {
320328
case protocol.SecurityType_NONE:
321329
if request.Option.Has(protocol.RequestOptionChunkStream) {
322330
if request.Command.TransferType() == protocol.TransferTypeStream {
323-
return crypto.NewChunkStreamReader(sizeParser, reader)
331+
return crypto.NewChunkStreamReader(sizeParser, reader), nil
324332
}
325333

326334
auth := &crypto.AEADAuthenticator{
@@ -329,21 +337,21 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
329337
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
330338
}
331339

332-
return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
340+
return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil
333341
}
334342

335-
return buf.NewReader(reader)
343+
return buf.NewReader(reader), nil
336344
case protocol.SecurityType_LEGACY:
337345
if request.Option.Has(protocol.RequestOptionChunkStream) {
338346
auth := &crypto.AEADAuthenticator{
339347
AEAD: new(FnvAuthenticator),
340348
NonceGenerator: crypto.GenerateEmptyBytes(),
341349
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
342350
}
343-
return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding)
351+
return crypto.NewAuthenticationReader(auth, sizeParser, c.responseReader, request.Command.TransferType(), padding), nil
344352
}
345353

346-
return buf.NewReader(c.responseReader)
354+
return buf.NewReader(c.responseReader), nil
347355
case protocol.SecurityType_AES128_GCM:
348356
aead := crypto.NewAesGcm(c.responseBodyKey[:])
349357

@@ -363,7 +371,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
363371
}
364372
sizeParser = NewAEADSizeParser(lengthAuth)
365373
}
366-
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
374+
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
367375
case protocol.SecurityType_CHACHA20_POLY1305:
368376
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey[:]))
369377

@@ -384,9 +392,9 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read
384392
}
385393
sizeParser = NewAEADSizeParser(lengthAuth)
386394
}
387-
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
395+
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
388396
default:
389-
panic("Unknown security type.")
397+
return nil, newError("invalid option: Security")
390398
}
391399
}
392400

proxy/vmess/encoding/server.go

+28-20
Original file line numberDiff line numberDiff line change
@@ -305,31 +305,35 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
305305
}
306306

307307
// DecodeRequestBody returns Reader from which caller can fetch decrypted body.
308-
func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) buf.Reader {
308+
func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) (buf.Reader, error) {
309309
var sizeParser crypto.ChunkSizeDecoder = crypto.PlainChunkSizeParser{}
310310
if request.Option.Has(protocol.RequestOptionChunkMasking) {
311311
sizeParser = NewShakeSizeParser(s.requestBodyIV[:])
312312
}
313313
var padding crypto.PaddingLengthGenerator
314314
if request.Option.Has(protocol.RequestOptionGlobalPadding) {
315-
padding = sizeParser.(crypto.PaddingLengthGenerator)
315+
var ok bool
316+
padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
317+
if !ok {
318+
return nil, newError("invalid option: RequestOptionGlobalPadding")
319+
}
316320
}
317321

318322
switch request.Security {
319323
case protocol.SecurityType_NONE:
320324
if request.Option.Has(protocol.RequestOptionChunkStream) {
321325
if request.Command.TransferType() == protocol.TransferTypeStream {
322-
return crypto.NewChunkStreamReader(sizeParser, reader)
326+
return crypto.NewChunkStreamReader(sizeParser, reader), nil
323327
}
324328

325329
auth := &crypto.AEADAuthenticator{
326330
AEAD: new(NoOpAuthenticator),
327331
NonceGenerator: crypto.GenerateEmptyBytes(),
328332
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
329333
}
330-
return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding)
334+
return crypto.NewAuthenticationReader(auth, sizeParser, reader, protocol.TransferTypePacket, padding), nil
331335
}
332-
return buf.NewReader(reader)
336+
return buf.NewReader(reader), nil
333337

334338
case protocol.SecurityType_LEGACY:
335339
aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey[:], s.requestBodyIV[:])
@@ -340,9 +344,9 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
340344
NonceGenerator: crypto.GenerateEmptyBytes(),
341345
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
342346
}
343-
return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding)
347+
return crypto.NewAuthenticationReader(auth, sizeParser, cryptionReader, request.Command.TransferType(), padding), nil
344348
}
345-
return buf.NewReader(cryptionReader)
349+
return buf.NewReader(cryptionReader), nil
346350

347351
case protocol.SecurityType_AES128_GCM:
348352
aead := crypto.NewAesGcm(s.requestBodyKey[:])
@@ -362,7 +366,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
362366
}
363367
sizeParser = NewAEADSizeParser(lengthAuth)
364368
}
365-
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
369+
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
366370

367371
case protocol.SecurityType_CHACHA20_POLY1305:
368372
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey[:]))
@@ -384,10 +388,10 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade
384388
}
385389
sizeParser = NewAEADSizeParser(lengthAuth)
386390
}
387-
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding)
391+
return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType(), padding), nil
388392

389393
default:
390-
panic("Unknown security type.")
394+
return nil, newError("invalid option: Security")
391395
}
392396
}
393397

@@ -448,31 +452,35 @@ func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
448452
}
449453

450454
// EncodeResponseBody returns a Writer that auto-encrypt content written by caller.
451-
func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) buf.Writer {
455+
func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
452456
var sizeParser crypto.ChunkSizeEncoder = crypto.PlainChunkSizeParser{}
453457
if request.Option.Has(protocol.RequestOptionChunkMasking) {
454458
sizeParser = NewShakeSizeParser(s.responseBodyIV[:])
455459
}
456460
var padding crypto.PaddingLengthGenerator
457461
if request.Option.Has(protocol.RequestOptionGlobalPadding) {
458-
padding = sizeParser.(crypto.PaddingLengthGenerator)
462+
var ok bool
463+
padding, ok = sizeParser.(crypto.PaddingLengthGenerator)
464+
if !ok {
465+
return nil, newError("invalid option: RequestOptionGlobalPadding")
466+
}
459467
}
460468

461469
switch request.Security {
462470
case protocol.SecurityType_NONE:
463471
if request.Option.Has(protocol.RequestOptionChunkStream) {
464472
if request.Command.TransferType() == protocol.TransferTypeStream {
465-
return crypto.NewChunkStreamWriter(sizeParser, writer)
473+
return crypto.NewChunkStreamWriter(sizeParser, writer), nil
466474
}
467475

468476
auth := &crypto.AEADAuthenticator{
469477
AEAD: new(NoOpAuthenticator),
470478
NonceGenerator: crypto.GenerateEmptyBytes(),
471479
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
472480
}
473-
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding)
481+
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, protocol.TransferTypePacket, padding), nil
474482
}
475-
return buf.NewWriter(writer)
483+
return buf.NewWriter(writer), nil
476484

477485
case protocol.SecurityType_LEGACY:
478486
if request.Option.Has(protocol.RequestOptionChunkStream) {
@@ -481,9 +489,9 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
481489
NonceGenerator: crypto.GenerateEmptyBytes(),
482490
AdditionalDataGenerator: crypto.GenerateEmptyBytes(),
483491
}
484-
return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding)
492+
return crypto.NewAuthenticationWriter(auth, sizeParser, s.responseWriter, request.Command.TransferType(), padding), nil
485493
}
486-
return &buf.SequentialWriter{Writer: s.responseWriter}
494+
return &buf.SequentialWriter{Writer: s.responseWriter}, nil
487495

488496
case protocol.SecurityType_AES128_GCM:
489497
aead := crypto.NewAesGcm(s.responseBodyKey[:])
@@ -503,7 +511,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
503511
}
504512
sizeParser = NewAEADSizeParser(lengthAuth)
505513
}
506-
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
514+
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
507515

508516
case protocol.SecurityType_CHACHA20_POLY1305:
509517
aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey[:]))
@@ -525,9 +533,9 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
525533
}
526534
sizeParser = NewAEADSizeParser(lengthAuth)
527535
}
528-
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding)
536+
return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType(), padding), nil
529537

530538
default:
531-
panic("Unknown security type.")
539+
return nil, newError("invalid option: Security")
532540
}
533541
}

proxy/vmess/inbound/inbound.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,10 @@ func (h *Handler) RemoveUser(ctx context.Context, email string) error {
182182
func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSession, request *protocol.RequestHeader, response *protocol.ResponseHeader, input buf.Reader, output *buf.BufferedWriter) error {
183183
session.EncodeResponseHeader(response, output)
184184

185-
bodyWriter := session.EncodeResponseBody(request, output)
186-
185+
bodyWriter, err := session.EncodeResponseBody(request, output)
186+
if err != nil {
187+
return newError("failed to start decoding response").Base(err)
188+
}
187189
{
188190
// Optimize for small response packet
189191
data, err := input.ReadMultiBuffer()
@@ -290,7 +292,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i
290292
requestDone := func() error {
291293
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
292294

293-
bodyReader := svrSession.DecodeRequestBody(request, reader)
295+
bodyReader, err := svrSession.DecodeRequestBody(request, reader)
296+
if err != nil {
297+
return newError("failed to start decoding").Base(err)
298+
}
294299
if err := buf.Copy(bodyReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
295300
return newError("failed to transfer request").Base(err)
296301
}

proxy/vmess/outbound/outbound.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
151151
return newError("failed to encode request").Base(err).AtWarning()
152152
}
153153

154-
bodyWriter := session.EncodeRequestBody(request, writer)
154+
bodyWriter, err := session.EncodeRequestBody(request, writer)
155+
if err != nil {
156+
return newError("failed to start encoding").Base(err)
157+
}
155158
if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
156159
return newError("failed to write first payload").Base(err)
157160
}
@@ -183,8 +186,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
183186
}
184187
h.handleCommand(rec.Destination(), header.Command)
185188

186-
bodyReader := session.DecodeResponseBody(request, reader)
187-
189+
bodyReader, err := session.DecodeResponseBody(request, reader)
190+
if err != nil {
191+
return newError("failed to start encoding response").Base(err)
192+
}
188193
return buf.Copy(bodyReader, output, buf.UpdateActivity(timer))
189194
}
190195

0 commit comments

Comments
 (0)