Add base faketls processing

This commit is contained in:
9seconds
2021-03-25 17:33:22 +03:00
parent a444a10bd0
commit c3e8e8b1fe
7 changed files with 150 additions and 15 deletions
+12 -8
View File
@@ -11,9 +11,10 @@ import (
)
type ClientHello struct {
Time time.Time
Digest [RandomLen]byte
SessionID []byte
Time time.Time
Random [RandomLen]byte
SessionID []byte
CipherSuite uint16
}
func ParseClientHello(secret, handshake []byte) (ClientHello, error) {
@@ -27,7 +28,7 @@ func ParseClientHello(secret, handshake []byte) (ClientHello, error) {
return hello, fmt.Errorf("unknown handshake type %#x", handshake[0])
}
copy(hello.Digest[:], handshake[ClientHelloRandomOffset:])
copy(hello.Random[:], handshake[ClientHelloRandomOffset:])
for i := ClientHelloRandomOffset; i < ClientHelloRandomOffset+RandomLen; i++ {
handshake[i] = 0
@@ -45,23 +46,26 @@ func ParseClientHello(secret, handshake []byte) (ClientHello, error) {
mac := hmac.New(sha256.New, secret)
rec.Dump(mac)
computedDigest := mac.Sum(nil)
computedRandom := mac.Sum(nil)
for i := 0; i < RandomLen; i++ {
computedDigest[i] ^= hello.Digest[i]
computedRandom[i] ^= hello.Random[i]
}
for i := 0; i < RandomLen-4; i++ {
if computedDigest[i] != 0 {
if computedRandom[i] != 0 {
return hello, ErrBadDigest
}
}
timestamp := int64(binary.LittleEndian.Uint32(computedDigest[RandomLen-4:]))
timestamp := int64(binary.LittleEndian.Uint32(computedRandom[RandomLen-4:]))
hello.Time = time.Unix(timestamp, 0)
hello.SessionID = make([]byte, handshake[ClientHelloSessionIDOffset])
copy(hello.SessionID, handshake[ClientHelloSessionIDOffset+1:])
cipherSuiteOffset := ClientHelloSessionIDOffset + 1 + len(hello.SessionID) + 2
hello.CipherSuite = binary.BigEndian.Uint16(handshake[cipherSuiteOffset : cipherSuiteOffset+2])
return hello, nil
}
+16 -1
View File
@@ -7,12 +7,27 @@ const (
ClientHelloRandomOffset = 6
ClientHelloSessionIDOffset = ClientHelloRandomOffset + RandomLen
ClientHelloMinLen = ClientHelloSessionIDOffset + 1
ClientHelloMinLen = ClientHelloSessionIDOffset + 1
WelcomePacketRandomOffset = 11
HandshakeTypeClient = 0x01
HandshakeTypeServer = 0x02
)
var (
ErrBadDigest = errors.New("bad digest")
ErrAntiReplayAttack = errors.New("antireplay attack was detected")
serverHelloSuffix = []byte{
0x00, // no compression
0x00, 0x2e, // 46 bytes of data
0x00, 0x2b, // Extension - Supported Versions
0x00, 0x02, // 2 bytes are following
0x03, 0x04, // TLS 1.3
0x00, 0x33, // Extension - Key Share
0x00, 0x24, // 36 bytes
0x00, 0x1d, // x25519 curve
0x00, 0x20, // 32 bytes of key
}
)
+21
View File
@@ -0,0 +1,21 @@
package faketls
import (
"bytes"
"sync"
)
var bytesBufferPool = sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
func acquireBytesBuffer() *bytes.Buffer {
return bytesBufferPool.Get().(*bytes.Buffer)
}
func releaseBytesBuffer(b *bytes.Buffer) {
b.Reset()
bytesBufferPool.Put(b)
}
+88
View File
@@ -0,0 +1,88 @@
package faketls
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"io"
mrand "math/rand"
"github.com/9seconds/mtg/v2/mtglib/internal/faketls/record"
"golang.org/x/crypto/curve25519"
)
func SendWelcomePacket(writer io.Writer, secret []byte, clientHello ClientHello) error {
buf := acquireBytesBuffer()
defer releaseBytesBuffer(buf)
rec := record.AcquireRecord()
defer record.ReleaseRecord(rec)
rec.Type = record.TypeHandshake
rec.Version = record.Version12
generateServerHello(&rec.Payload, clientHello)
rec.Dump(buf)
rec.Reset()
rec.Type = record.TypeChangeCipherSpec
rec.Version = record.Version12
rec.Payload.WriteByte(0x01)
rec.Dump(buf)
rec.Reset()
rec.Type = record.TypeApplicationData
rec.Version = record.Version12
if _, err := io.CopyN(&rec.Payload, rand.Reader, int64(1024+mrand.Intn(3092))); err != nil {
panic(err)
}
rec.Dump(buf)
packet := buf.Bytes()
mac := hmac.New(sha256.New, secret)
mac.Write(clientHello.Random[:])
mac.Write(packet)
copy(packet[WelcomePacketRandomOffset:], mac.Sum(nil))
if _, err := writer.Write(packet); err != nil {
return err // nolint: wrapcheck
}
return nil
}
func generateServerHello(writer io.Writer, clientHello ClientHello) {
bodyBuf := acquireBytesBuffer()
defer releaseBytesBuffer(bodyBuf)
sliceBuf := [2]byte{}
digest := [RandomLen]byte{}
binary.BigEndian.PutUint16(sliceBuf[:], uint16(record.Version12))
bodyBuf.Write(sliceBuf[:])
bodyBuf.Write(digest[:])
bodyBuf.WriteByte(byte(len(clientHello.SessionID)))
bodyBuf.Write(clientHello.SessionID)
binary.BigEndian.PutUint16(sliceBuf[:], clientHello.CipherSuite)
bodyBuf.Write(sliceBuf[:])
bodyBuf.Write(serverHelloSuffix)
scalar := [32]byte{}
rand.Read(scalar[:])
curve, _ := curve25519.X25519(scalar[:], curve25519.Basepoint)
bodyBuf.Write(curve)
header := [4]byte{0, 0, 0, 0}
binary.BigEndian.PutUint32(header[:], uint32(bodyBuf.Len()))
header[0] = HandshakeTypeServer
writer.Write(header[:])
bodyBuf.WriteTo(writer)
}
+10 -6
View File
@@ -122,15 +122,14 @@ func (p *Proxy) Shutdown() {
}
func (p *Proxy) doFakeTLSHandshake(ctx *streamContext, conn net.Conn) error {
clientHelloRecord := record.AcquireRecord()
defer record.ReleaseRecord(clientHelloRecord)
rec := record.AcquireRecord()
defer record.ReleaseRecord(rec)
if err := clientHelloRecord.Read(conn); err != nil {
if err := rec.Read(conn); err != nil {
return fmt.Errorf("cannot read client hello: %w", err)
}
hello, err := faketls.ParseClientHello(p.secret.Key[:],
clientHelloRecord.Payload.Bytes())
hello, err := faketls.ParseClientHello(p.secret.Key[:], rec.Payload.Bytes())
if err != nil {
return fmt.Errorf("cannot parse client hello: %w", err)
}
@@ -138,13 +137,18 @@ func (p *Proxy) doFakeTLSHandshake(ctx *streamContext, conn net.Conn) error {
if err := p.timeAttackDetector.Valid(hello.Time); err != nil {
return fmt.Errorf("invalid time: %w", err)
}
if p.antiReplayCache.SeenBefore(hello.SessionID) {
p.logger.Warning("anti replay attack was detected")
return fmt.Errorf("anti replay attack from %s", ctx.ClientIP().String())
}
return fmt.Errorf("SUCCESS")
if err := faketls.SendWelcomePacket(conn, p.secret.Key[:], hello); err != nil {
return fmt.Errorf("cannot send a welcome packet: %w", err)
}
return nil
}
func (p *Proxy) doObfuscated2Handshake(ctx *streamContext) error {