mirror of https://github.com/cbeuw/Cloak
Refactor TLS handshake
This commit is contained in:
parent
3dc4c6fb3f
commit
e15536d7c7
|
|
@ -23,48 +23,6 @@ import (
|
|||
|
||||
var version string
|
||||
|
||||
// This establishes a connection with ckserver and performs a handshake
|
||||
func makeRemoteConn(sta *client.State) (net.Conn, []byte, error) {
|
||||
|
||||
// For android
|
||||
d := net.Dialer{Control: protector}
|
||||
|
||||
clientHello, sharedSecret := client.ComposeClientHello(sta)
|
||||
connectingIP := sta.RemoteHost
|
||||
if net.ParseIP(connectingIP).To4() == nil {
|
||||
// IPv6 needs square brackets
|
||||
connectingIP = "[" + connectingIP + "]"
|
||||
}
|
||||
remoteConn, err := d.Dial("tcp", connectingIP+":"+sta.RemotePort)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to connect to remote")
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = remoteConn.Write(clientHello)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to send ClientHello")
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Trace("client hello sent successfully")
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
log.Trace("waiting for ServerHello")
|
||||
_, err = util.ReadTLS(remoteConn, buf)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to read ServerHello")
|
||||
}
|
||||
serverRandom := buf[11:43]
|
||||
sessionKey := client.DecryptSessionKey(serverRandom, sharedSecret)
|
||||
_, err = util.ReadTLS(remoteConn, buf)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to read ChangeCipherSpec")
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return remoteConn, sessionKey, nil
|
||||
|
||||
}
|
||||
|
||||
func makeSession(sta *client.State) *mux.Session {
|
||||
log.Info("Attemtping to start a new session")
|
||||
if !sta.IsAdmin {
|
||||
|
|
@ -75,6 +33,7 @@ func makeSession(sta *client.State) *mux.Session {
|
|||
atomic.StoreUint32(&sta.SessionID, binary.BigEndian.Uint32(quad))
|
||||
}
|
||||
|
||||
d := net.Dialer{Control: protector}
|
||||
connsCh := make(chan net.Conn, sta.NumConn)
|
||||
var _sessionKey atomic.Value
|
||||
var wg sync.WaitGroup
|
||||
|
|
@ -82,7 +41,17 @@ func makeSession(sta *client.State) *mux.Session {
|
|||
wg.Add(1)
|
||||
go func() {
|
||||
makeconn:
|
||||
conn, sk, err := makeRemoteConn(sta)
|
||||
connectingIP := sta.RemoteHost
|
||||
if net.ParseIP(connectingIP).To4() == nil {
|
||||
// IPv6 needs square brackets
|
||||
connectingIP = "[" + connectingIP + "]"
|
||||
}
|
||||
remoteConn, err := d.Dial("tcp", connectingIP+":"+sta.RemotePort)
|
||||
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to connect to remote")
|
||||
}
|
||||
sk, err := client.PrepareConnection(sta, remoteConn)
|
||||
_sessionKey.Store(sk)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to establish new connections to remote: %v", err)
|
||||
|
|
@ -90,7 +59,7 @@ func makeSession(sta *client.State) *mux.Session {
|
|||
time.Sleep(time.Second * 3)
|
||||
goto makeconn
|
||||
}
|
||||
connsCh <- conn
|
||||
connsCh <- remoteConn
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@ var version string
|
|||
func dispatchConnection(conn net.Conn, sta *server.State) {
|
||||
remoteAddr := conn.RemoteAddr()
|
||||
var err error
|
||||
rejectLogger := log.WithFields(log.Fields{
|
||||
"remoteAddr": remoteAddr,
|
||||
"error": err,
|
||||
})
|
||||
buf := make([]byte, 1500)
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
|
|
@ -56,37 +52,15 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
|
|||
go util.Pipe(conn, webConn)
|
||||
}
|
||||
|
||||
ch, err := server.ParseClientHello(data)
|
||||
UID, sessionID, proxyMethod, encryptionMethod, finishHandshake, err := server.PrepareConnection(data, sta, conn)
|
||||
if err != nil {
|
||||
rejectLogger.Warn("+1 non Cloak non (or malformed) TLS traffic")
|
||||
goWeb()
|
||||
return
|
||||
}
|
||||
|
||||
UID, sessionID, proxyMethod, encryptionMethod, sharedSecret, err := server.TouchStone(ch, sta)
|
||||
if err != nil {
|
||||
rejectLogger.Warn("+1 non Cloak TLS traffic")
|
||||
goWeb()
|
||||
return
|
||||
}
|
||||
if _, ok := sta.ProxyBook[proxyMethod]; !ok {
|
||||
log.WithFields(log.Fields{
|
||||
"UID": UID,
|
||||
"proxyMethod": proxyMethod,
|
||||
}).Warn("+1 Cloak TLS traffic with invalid proxy method")
|
||||
goWeb()
|
||||
return
|
||||
}
|
||||
|
||||
finishHandshake := func(sessionKey []byte) error {
|
||||
reply := server.ComposeReply(ch, sharedSecret, sessionKey)
|
||||
_, err = conn.Write(reply)
|
||||
if err != nil {
|
||||
go conn.Close()
|
||||
return err
|
||||
}
|
||||
log.Trace("finished handshake")
|
||||
return nil
|
||||
"remoteAddr": remoteAddr,
|
||||
"UID": UID,
|
||||
"sessionId": sessionID,
|
||||
"proxyMethod": proxyMethod,
|
||||
"encryptionMethod": encryptionMethod,
|
||||
}).Warn(err)
|
||||
}
|
||||
|
||||
sessionKey := make([]byte, 32)
|
||||
|
|
@ -106,6 +80,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
log.Trace("finished handshake")
|
||||
sesh := mux.MakeSession(0, mux.UNLIMITED_VALVE, obfuscator, util.ReadTLS)
|
||||
sesh.AddConnection(conn)
|
||||
//TODO: Router could be nil in cnc mode
|
||||
|
|
@ -146,6 +121,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
log.Trace("finished handshake")
|
||||
sesh.AddConnection(conn)
|
||||
return
|
||||
}
|
||||
|
|
@ -155,6 +131,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
log.Trace("finished handshake")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"UID": b64(UID),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ package client
|
|||
import (
|
||||
"encoding/binary"
|
||||
"github.com/cbeuw/Cloak/internal/util"
|
||||
"net"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Browser interface {
|
||||
|
|
@ -34,8 +37,34 @@ func addExtRec(typ []byte, data []byte) []byte {
|
|||
return ret
|
||||
}
|
||||
|
||||
// ComposeClientHello composes ClientHello with record layer
|
||||
func ComposeClientHello(sta *State) ([]byte, []byte) {
|
||||
// composeClientHello composes ClientHello with record layer
|
||||
func composeClientHello(sta *State) ([]byte, []byte) {
|
||||
ch, sharedSecret := sta.Browser.composeClientHello(sta)
|
||||
return util.AddRecordLayer(ch, []byte{0x16}, []byte{0x03, 0x01}), sharedSecret
|
||||
}
|
||||
|
||||
func PrepareConnection(sta *State, conn net.Conn) (sessionKey []byte, err error) {
|
||||
|
||||
clientHello, sharedSecret := composeClientHello(sta)
|
||||
_, err = conn.Write(clientHello)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Trace("client hello sent successfully")
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
log.Trace("waiting for ServerHello")
|
||||
_, err = util.ReadTLS(conn, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
serverRandom := buf[11:43]
|
||||
sessionKey = decryptSessionKey(serverRandom, sharedSecret)
|
||||
_, err = util.ReadTLS(conn, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return sessionKey, nil
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"sync/atomic"
|
||||
)
|
||||
|
||||
func MakeHiddenData(sta *State) (random, TLSsessionID, keyShare, sharedSecret []byte) {
|
||||
func makeHiddenData(sta *State) (random, TLSsessionID, keyShare, sharedSecret []byte) {
|
||||
// random is marshalled ephemeral pub key 32 bytes
|
||||
// TLSsessionID || keyShare is [encrypted UID 16 bytes, proxy method 12 bytes, encryption method 1 byte, timestamp 8 bytes, sessionID 4 bytes] [unused data] [16 bytes authentication tag]
|
||||
ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader)
|
||||
|
|
@ -35,7 +35,7 @@ func xor(a []byte, b []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
func DecryptSessionKey(serverRandom []byte, sharedSecret []byte) []byte {
|
||||
func decryptSessionKey(serverRandom []byte, sharedSecret []byte) []byte {
|
||||
xor(serverRandom, sharedSecret)
|
||||
return serverRandom
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func (c *Chrome) composeExtensions(serverName string, keyShare []byte) []byte {
|
|||
}
|
||||
|
||||
func (c *Chrome) composeClientHello(sta *State) (ch []byte, sharedSecret []byte) {
|
||||
random, sessionID, keyShare, sharedSecret := MakeHiddenData(sta)
|
||||
random, sessionID, keyShare, sharedSecret := makeHiddenData(sta)
|
||||
var clientHello [12][]byte
|
||||
clientHello[0] = []byte{0x01} // handshake type
|
||||
clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ func (f *Firefox) composeExtensions(serverName string, keyShare []byte) []byte {
|
|||
}
|
||||
|
||||
func (f *Firefox) composeClientHello(sta *State) (ch []byte, sharedSecret []byte) {
|
||||
random, sessionID, keyShare, sharedSecret := MakeHiddenData(sta)
|
||||
random, sessionID, keyShare, sharedSecret := makeHiddenData(sta)
|
||||
|
||||
var clientHello [12][]byte
|
||||
clientHello[0] = []byte{0x01} // handshake type
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ClientHello contains every field in a ClientHello message
|
||||
|
|
@ -79,8 +82,8 @@ func parseKeyShare(input []byte) (ret []byte, err error) {
|
|||
return nil, errors.New("x25519 does not exist")
|
||||
}
|
||||
|
||||
// AddRecordLayer adds record layer to data
|
||||
func AddRecordLayer(input []byte, typ []byte, ver []byte) []byte {
|
||||
// addRecordLayer adds record layer to data
|
||||
func addRecordLayer(input []byte, typ []byte, ver []byte) []byte {
|
||||
length := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(length, uint16(len(input)))
|
||||
ret := make([]byte, 5+len(input))
|
||||
|
|
@ -91,9 +94,9 @@ func AddRecordLayer(input []byte, typ []byte, ver []byte) []byte {
|
|||
return ret
|
||||
}
|
||||
|
||||
// ParseClientHello parses everything on top of the TLS layer
|
||||
// parseClientHello parses everything on top of the TLS layer
|
||||
// (including the record layer) into ClientHello type
|
||||
func ParseClientHello(data []byte) (ret *ClientHello, err error) {
|
||||
func parseClientHello(data []byte) (ret *ClientHello, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("Malformed ClientHello")
|
||||
|
|
@ -189,13 +192,49 @@ func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte
|
|||
return ret
|
||||
}
|
||||
|
||||
// ComposeReply composes the ServerHello, ChangeCipherSpec and Finished messages
|
||||
// composeReply composes the ServerHello, ChangeCipherSpec and Finished messages
|
||||
// together with their respective record layers into one byte slice. The content
|
||||
// of these messages are random and useless for this plugin
|
||||
func ComposeReply(ch *ClientHello, sharedSecret []byte, sessionKey []byte) []byte {
|
||||
func composeReply(ch *ClientHello, sharedSecret []byte, sessionKey []byte) []byte {
|
||||
TLS12 := []byte{0x03, 0x03}
|
||||
shBytes := AddRecordLayer(composeServerHello(ch.sessionId, sharedSecret, sessionKey), []byte{0x16}, TLS12)
|
||||
ccsBytes := AddRecordLayer([]byte{0x01}, []byte{0x14}, TLS12)
|
||||
shBytes := addRecordLayer(composeServerHello(ch.sessionId, sharedSecret, sessionKey), []byte{0x16}, TLS12)
|
||||
ccsBytes := addRecordLayer([]byte{0x01}, []byte{0x14}, TLS12)
|
||||
ret := append(shBytes, ccsBytes...)
|
||||
return ret
|
||||
}
|
||||
|
||||
var ErrBadClientHello = errors.New("non (or malformed) ClientHello")
|
||||
var ErrNotCloak = errors.New("TLS but non-Cloak ClientHello")
|
||||
var ErrBadProxyMethod = errors.New("invalid proxy method")
|
||||
|
||||
func PrepareConnection(firstPacket []byte, sta *State, conn net.Conn) (UID []byte, sessionID uint32, proxyMethod string, encryptionMethod byte, finisher func([]byte) error, err error) {
|
||||
ch, err := parseClientHello(firstPacket)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
err = ErrBadClientHello
|
||||
return
|
||||
}
|
||||
|
||||
var sharedSecret []byte
|
||||
UID, sessionID, proxyMethod, encryptionMethod, sharedSecret, err = TouchStone(ch, sta)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
err = ErrNotCloak
|
||||
return
|
||||
}
|
||||
if _, ok := sta.ProxyBook[proxyMethod]; !ok {
|
||||
err = ErrBadProxyMethod
|
||||
return
|
||||
}
|
||||
|
||||
finisher = func(sessionKey []byte) error {
|
||||
reply := composeReply(ch, sharedSecret, sessionKey)
|
||||
_, err = conn.Write(reply)
|
||||
if err != nil {
|
||||
go conn.Close()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue