Browse Source

Drain Connection with invalid checksum before closing. Emergency fix for weakness described in https://github.com/v2ray/v2ray-core/issues/2523

Shelikhoo 5 years ago
parent
commit
e0aa18b6f3
2 changed files with 22 additions and 1 deletions
  1. 8 0
      common/dice/dice.go
  2. 14 1
      proxy/vmess/encoding/server.go

+ 8 - 0
common/dice/dice.go

@@ -15,6 +15,14 @@ func Roll(n int) int {
 	return rand.Intn(n)
 }
 
+// Roll returns a non-negative number between 0 (inclusive) and n (exclusive).
+func RollDeterministic(n int, seed int64) int {
+	if n == 1 {
+		return 0
+	}
+	return rand.New(rand.NewSource(seed)).Intn(n)
+}
+
 // RollUint16 returns a random uint16 value.
 func RollUint16() uint16 {
 	return uint16(rand.Intn(65536))

+ 14 - 1
proxy/vmess/encoding/server.go

@@ -5,8 +5,10 @@ import (
 	"encoding/binary"
 	"hash/fnv"
 	"io"
+	"io/ioutil"
 	"sync"
 	"time"
+	"v2ray.com/core/common/dice"
 
 	"golang.org/x/crypto/chacha20poly1305"
 
@@ -194,7 +196,13 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
 	expectedHash := binary.BigEndian.Uint32(buffer.BytesFrom(-4))
 
 	if actualHash != expectedHash {
-		return nil, newError("invalid auth")
+		//It is possible that we are under attack described in https://github.com/v2ray/v2ray-core/issues/2523
+		//We read a deterministic generated length of data before closing the connection to offset padding read pattern
+		drainSum := dice.RollDeterministic(48, int64(actualHash))
+		if err := s.DrainConnN(reader, drainSum); err != nil {
+			return nil, newError("invalid auth, failed to drain connection").Base(err)
+		}
+		return nil, newError("invalid auth, connection drained")
 	}
 
 	if request.Address == nil {
@@ -347,3 +355,8 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ
 		panic("Unknown security type.")
 	}
 }
+
+func (s *ServerSession) DrainConnN(reader io.Reader, n int) error {
+	_, err := io.CopyN(ioutil.Discard, reader, int64(n))
+	return err
+}