Can correctly accept faketls messages

This commit is contained in:
9seconds
2021-03-25 16:22:00 +03:00
parent a3c64c1d1e
commit 4a2d1df384
7 changed files with 305 additions and 22 deletions
+1
View File
@@ -13,6 +13,7 @@ var (
ErrSecretInvalid = errors.New("secret is invalid") ErrSecretInvalid = errors.New("secret is invalid")
ErrNetworkIsNotDefined = errors.New("network is not defined") ErrNetworkIsNotDefined = errors.New("network is not defined")
ErrAntiReplayCacheIsNotDefined = errors.New("anti-replay cache is not defined") ErrAntiReplayCacheIsNotDefined = errors.New("anti-replay cache is not defined")
ErrTimeAttackDetectorIsNotDefined = errors.New("time attack detector is not defined")
ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined") ErrIPBlocklistIsNotDefined = errors.New("ip blocklist is not defined")
ErrEventStreamIsNotDefined = errors.New("event stream is not defined") ErrEventStreamIsNotDefined = errors.New("event stream is not defined")
ErrLoggerIsNotDefined = errors.New("logger is not defined") ErrLoggerIsNotDefined = errors.New("logger is not defined")
@@ -0,0 +1,67 @@
package clienthello
import (
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"fmt"
"time"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
)
type ClientHello struct {
Time time.Time
Digest [RandomLen]byte
SessionID []byte
}
func ParseHandshake(secret, handshake []byte) (ClientHello, error) {
hello := ClientHello{}
if len(handshake) < MinLen {
return hello, fmt.Errorf("lengh of handshake is too small: %d", len(handshake))
}
if handshake[0] != HandshakeTypeClient {
return hello, fmt.Errorf("unknown handshake type %#x", handshake[0])
}
copy(hello.Digest[:], handshake[RandomOffset:])
for i := RandomOffset; i < RandomOffset+RandomLen; i++ {
handshake[i] = 0
}
rec := record.AcquireRecord()
defer record.ReleaseRecord(rec)
rec.Type = record.TypeHandshake
rec.Version = record.Version10
rec.Payload.Write(handshake)
// mac is calculated for the whole record, not only
// for the payload part
mac := hmac.New(sha256.New, secret)
rec.Dump(mac)
computedDigest := mac.Sum(nil)
for i := 0; i < RandomLen; i++ {
computedDigest[i] ^= hello.Digest[i]
}
for i := 0; i < RandomLen-4; i++ {
if computedDigest[i] != 0 {
return hello, ErrBadDigest
}
}
timestamp := int64(binary.LittleEndian.Uint32(computedDigest[RandomLen-4:]))
hello.Time = time.Unix(timestamp, 0)
hello.SessionID = make([]byte, handshake[SessionIDOffset])
copy(hello.SessionID, handshake[SessionIDOffset+1:])
return hello, nil
}
@@ -0,0 +1,17 @@
package clienthello
import "errors"
const (
RandomLen = 32
RandomOffset = 6
SessionIDOffset = RandomOffset + RandomLen
MinLen = SessionIDOffset + 1
HandshakeTypeClient = 0x01
)
var (
ErrBadDigest = errors.New("bad digest")
ErrAntiReplayAttack = errors.New("antireplay attack was detected")
)
+66
View File
@@ -0,0 +1,66 @@
package record
import "fmt"
type Type uint8
const (
TypeChangeCipherSpec Type = 0x14
TypeHandshake Type = 0x16
TypeApplicationData Type = 0x17
)
func (t Type) String() string {
switch t {
case TypeChangeCipherSpec:
return "changeCipher(0x14)"
case TypeHandshake:
return "handshake(0x16)"
case TypeApplicationData:
return "applicationData(0x17)"
}
return fmt.Sprintf("unknown(%#x)", byte(t))
}
func (t Type) Valid() error {
switch t {
case TypeChangeCipherSpec, TypeHandshake, TypeApplicationData:
return nil
}
return fmt.Errorf("unknown type %#x", byte(t))
}
type Version uint16
const (
Version10 Version = 769 // 0x03 0x01
Version11 Version = 770 // 0x03 0x02
Version12 Version = 771 // 0x03 0x03
Version13 Version = 772 // 0x03 0x04
)
func (v Version) String() string {
switch v {
case Version10:
return "tls1.0"
case Version11:
return "tls1.1"
case Version12:
return "tls1.2"
case Version13:
return "tls1.3"
}
return fmt.Sprintf("tls(%d)", uint16(v))
}
func (v Version) Valid() error {
switch v {
case Version10, Version11, Version12, Version13:
return nil
}
return fmt.Errorf("unknown version %d", uint16(v))
}
+18
View File
@@ -0,0 +1,18 @@
package record
import "sync"
var recordPool = sync.Pool{
New: func() interface{} {
return &Record{}
},
}
func AcquireRecord() *Record {
return recordPool.Get().(*Record)
}
func ReleaseRecord(r *Record) {
r.Reset()
recordPool.Put(r)
}
+87
View File
@@ -0,0 +1,87 @@
package record
import (
"bytes"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
)
type Record struct {
Type Type
Version Version
Payload bytes.Buffer
}
func (r *Record) String() string {
return fmt.Sprintf("<tlsRecord(type=%v, version=%v, payload=%s)>",
r.Type,
r.Version,
base64.StdEncoding.EncodeToString(r.Payload.Bytes()))
}
func (r *Record) Reset() {
r.Payload.Reset()
}
func (r *Record) Read(reader io.Reader) error {
r.Reset()
buf := [2]byte{}
if _, err := io.ReadFull(reader, buf[:1]); err != nil {
return fmt.Errorf("cannot read type: %w", err)
}
r.Type = Type(buf[0])
if err := r.Type.Valid(); err != nil {
return fmt.Errorf("invalid type: %w", err)
}
if _, err := io.ReadFull(reader, buf[:]); err != nil {
return fmt.Errorf("cannot read version: %w", err)
}
r.Version = Version(binary.BigEndian.Uint16(buf[:]))
if err := r.Version.Valid(); err != nil {
return fmt.Errorf("invalid version: %w", err)
}
if _, err := io.ReadFull(reader, buf[:]); err != nil {
return fmt.Errorf("cannot read payload length: %w", err)
}
length := int64(binary.BigEndian.Uint16(buf[:]))
if _, err := io.CopyN(&r.Payload, reader, length); err != nil {
return fmt.Errorf("cannot read payload: %w", err)
}
return nil
}
func (r *Record) Dump(writer io.Writer) error {
buf := [2]byte{byte(r.Type), 0}
if _, err := writer.Write(buf[:1]); err != nil {
return fmt.Errorf("cannot dump type: %w", err)
}
binary.BigEndian.PutUint16(buf[:], uint16(r.Version))
if _, err := writer.Write(buf[:]); err != nil {
return fmt.Errorf("cannot dump version: %w", err)
}
binary.BigEndian.PutUint16(buf[:], uint16(r.Payload.Len()))
if _, err := writer.Write(buf[:]); err != nil {
return fmt.Errorf("cannot dump payload length: %w", err)
}
if _, err := writer.Write(r.Payload.Bytes()); err != nil {
return fmt.Errorf("cannot dump payload: %w", err)
}
return nil
}
+27
View File
@@ -8,6 +8,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/clienthello"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
"github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2" "github.com/9seconds/mtg/v2/mtglib/internal/obfuscated2"
"github.com/9seconds/mtg/v2/mtglib/internal/relay" "github.com/9seconds/mtg/v2/mtglib/internal/relay"
"github.com/9seconds/mtg/v2/mtglib/internal/telegram" "github.com/9seconds/mtg/v2/mtglib/internal/telegram"
@@ -26,6 +28,7 @@ type Proxy struct {
secret Secret secret Secret
antiReplayCache AntiReplayCache antiReplayCache AntiReplayCache
timeAttackDetector TimeAttackDetector
ipBlocklist IPBlocklist ipBlocklist IPBlocklist
eventStream EventStream eventStream EventStream
logger Logger logger Logger
@@ -55,6 +58,12 @@ func (p *Proxy) ServeConn(conn net.Conn) {
ctx.logger.Info("Stream has been finished") ctx.logger.Info("Stream has been finished")
}() }()
if err := p.doFakeTLSHandshake(ctx); err != nil {
p.logger.InfoError("faketls handshake is failed", err)
return
}
if err := p.doObfuscated2Handshake(ctx); err != nil { if err := p.doObfuscated2Handshake(ctx); err != nil {
p.logger.InfoError("obfuscated2 handshake is failed", err) p.logger.InfoError("obfuscated2 handshake is failed", err)
@@ -112,6 +121,21 @@ func (p *Proxy) Shutdown() {
p.workerPool.Release() p.workerPool.Release()
} }
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext) error {
clientHelloRecord := record.AcquireRecord()
defer record.ReleaseRecord(clientHelloRecord)
if err := clientHelloRecord.Read(ctx.clientConn); err != nil {
return fmt.Errorf("cannot read client hello: %w", err)
}
hello, _ := clienthello.ParseHandshake(p.secret.Key[:],
clientHelloRecord.Payload.Bytes())
fmt.Println(hello)
return fmt.Errorf("SUCCESS")
}
func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error { func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {
dc, encryptor, decryptor, err := obfuscated2.ClientHandshake(p.secret.Key[:], ctx.clientConn) dc, encryptor, decryptor, err := obfuscated2.ClientHandshake(p.secret.Key[:], ctx.clientConn)
if err != nil { if err != nil {
@@ -173,6 +197,8 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) { // nolint: cyclop
return nil, ErrIPBlocklistIsNotDefined return nil, ErrIPBlocklistIsNotDefined
case opts.EventStream == nil: case opts.EventStream == nil:
return nil, ErrEventStreamIsNotDefined return nil, ErrEventStreamIsNotDefined
case opts.TimeAttackDetector == nil:
return nil, ErrTimeAttackDetectorIsNotDefined
case opts.Logger == nil: case opts.Logger == nil:
return nil, ErrLoggerIsNotDefined return nil, ErrLoggerIsNotDefined
case !opts.Secret.Valid(): case !opts.Secret.Valid():
@@ -205,6 +231,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) { // nolint: cyclop
ctxCancel: cancel, ctxCancel: cancel,
secret: opts.Secret, secret: opts.Secret,
antiReplayCache: opts.AntiReplayCache, antiReplayCache: opts.AntiReplayCache,
timeAttackDetector: opts.TimeAttackDetector,
ipBlocklist: opts.IPBlocklist, ipBlocklist: opts.IPBlocklist,
eventStream: opts.EventStream, eventStream: opts.EventStream,
logger: opts.Logger.Named("proxy"), logger: opts.Logger.Named("proxy"),