Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions tapdance/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,18 @@ func (m *tdTagType) Str() string {
}

// First byte of tag is for FLAGS
// bit 0 (1 << 7) determines if flow is bidirectional(0) or upload-only(1)
// bits 1-5 are unassigned
// bit 6 determines whether PROXY-protocol-formatted string will be sent
// bit 7 (1 << 0) signals to use TypeLen outer proto
//
// bit | meaning
// -------|---------
// 0 | is the flow bidirectional (0) or upload-only (1) ?
// 1 | is the connection TLS 1.3 (1) ?
// 2-5 | unassigned
// 6 | will PROXY-protocol-formatted string will be sent (1) ?
// 7 | use TypeLen outer proto (1) ?
//
var (
tdFlagUploadOnly = uint8(1 << 7)
tdFlagTLS13 = uint8(1 << 6)
tdFlagProxyHeader = uint8(1 << 1)
tdFlagUseTIL = uint8(1 << 0)
)
Expand Down Expand Up @@ -158,6 +164,11 @@ var tapDanceSupportedCiphers = []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,

// TLS 1.3
tls.TLS_AES_128_GCM_SHA256, // 4865 tls13.refraction.network
// tls.TLS_AES_256_GCM_SHA384, // 4866 www.checkintocash.com
// tls.TLS_CHACHA20_POLY1305_SHA256,
}

// How much time to sleep on trying to connect to decoys to prevent overwhelming them
Expand Down
69 changes: 59 additions & 10 deletions tapdance/conn_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ func (tdRaw *tdRawConn) tryDialOnce(ctx context.Context, expectedTransition pb.S
" with connection ID: " + hex.EncodeToString(tdRaw.remoteConnId[:]) + ", method: " +
tdRaw.tagType.Str())
rttToStationStartTs := time.Now()
fmt.Println(string(tdRequest))
_, err = tdRaw.tlsConn.Write([]byte(tdRequest))
if err != nil {
Logger().Errorf(tdRaw.idStr() +
Expand Down Expand Up @@ -292,7 +293,7 @@ func (tdRaw *tdRawConn) establishTLStoDecoy(ctx context.Context) error {
config.ServerName)
}
// parrot Chrome 62 ClientHello
tdRaw.tlsConn = tls.UClient(dialConn, &config, tls.HelloChrome_62)
tdRaw.tlsConn = tls.UClient(dialConn, &config, tls.HelloChrome_72)
err = tdRaw.tlsConn.BuildHandshakeState()
if err != nil {
dialConn.Close()
Expand All @@ -304,6 +305,7 @@ func (tdRaw *tdRawConn) establishTLStoDecoy(ctx context.Context) error {
return err
}
tdRaw.tlsConn.SetDeadline(deadline)
Logger().Infof("Session %d starting Handshake", tdRaw.sessionId)
err = tdRaw.tlsConn.Handshake()
if err != nil {
dialConn.Close()
Expand Down Expand Up @@ -337,31 +339,67 @@ func (tdRaw *tdRawConn) closeWrite() error {
return tdRaw.tcpConn.CloseWrite()
}

func (tdRaw *tdRawConn) isTLS13() bool {
return tdRaw.tlsConn.ConnectionState().Version == tls.VersionTLS13
}

// Generate tag for the initial TapDance request
// Documentation for tag:
// https://github.com/refraction-networking/gotapdance/wiki/Tagging-and-Signalling#payload
func (tdRaw *tdRawConn) prepareTDRequest(handshakeType tdTagType) (string, error) {
// Generate tag for the initial TapDance request
buf := new(bytes.Buffer) // What we have to encrypt with the shared secret using AES

masterKey := tdRaw.tlsConn.HandshakeState.MasterSecret

// write flags
// Byte 0: Flags
flags := default_flags
if tdRaw.tagType == tagHttpPostIncomplete {
flags |= tdFlagUploadOnly
}
if tdRaw.isTLS13() {
flags |= tdFlagTLS13
}
if err := binary.Write(buf, binary.BigEndian, flags); err != nil {
return "", err
}
buf.Write([]byte{0}) // Unassigned byte

// Byte 1: Unassigned
buf.Write([]byte{0})

// Bytes 2-3: Cipher Suite
negotiatedCipher := tdRaw.tlsConn.HandshakeState.State12.Suite.Id
if tdRaw.tlsConn.HandshakeState.ServerHello.Vers == tls.VersionTLS13 {
if negotiatedCipher == 0 || tdRaw.isTLS13() {
negotiatedCipher = tdRaw.tlsConn.HandshakeState.State13.Suite.Id
}
buf.Write([]byte{byte(negotiatedCipher >> 8),
byte(negotiatedCipher & 0xff)})

// Bytes 4-51: handshake master secret
masterKey := tdRaw.tlsConn.HandshakeState.MasterSecret
buf.Write(masterKey[:])
buf.Write(tdRaw.tlsConn.HandshakeState.ServerHello.Random)
buf.Write(tdRaw.tlsConn.HandshakeState.Hello.Random)
buf.Write(tdRaw.remoteConnId[:]) // connection id for persistence

// Make up for shorter master keys in TLS 1.3
buf.Write(make([]byte, 48 - len(masterKey)))

// Bytes 52-115: protocol secrets
if (tdRaw.isTLS13()) {

// For TLS 1.3 we use the traffic secret
client_sec, server_sec := tdRaw.tlsConn.TrafficSecrets()
buf.Write(server_sec)
buf.Write(client_sec)
fmt.Printf("client %x\n", client_sec)
fmt.Printf("server %x\n", server_sec)
} else {

// For <= TLS 1.2 we use the handshake randoms
buf.Write(tdRaw.tlsConn.HandshakeState.ServerHello.Random)
buf.Write(tdRaw.tlsConn.HandshakeState.Hello.Random)
}

// Bytes 116-131: remote connection ID
buf.Write(tdRaw.remoteConnId[:])

// buf.Len() should == 132
// fmt.Println(buf.Len())

err := WriteTlsLog(tdRaw.tlsConn.HandshakeState.Hello.Random,
tdRaw.tlsConn.HandshakeState.MasterSecret)
Expand Down Expand Up @@ -447,6 +485,17 @@ Content-Disposition: form-data; name=\"td.zip\"
if tdRaw.tagType == tagHttpGetComplete {
httpTag += "\r\n\r\n"
}

// Add an extra padding byte for non TLS 1.3 connections.
//
// 1.3 has a ciphertext feature where it encrypts the actual header type
// byte at the end of the sent plaintext. This shifts the offset we need to
// read by 1. Rather than try extracting at both offsets, we adjust non-1.3
// connections to have the same offset.
if ! tdRaw.isTLS13() {
httpTag += " "
}

Logger().Debugf("Generated HTTP TAG:\n%s\n", httpTag)
return httpTag, nil
}
Expand Down
31 changes: 31 additions & 0 deletions tapdance/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,34 @@ func errIsTimeout(err error) bool {
}
return false
}

func extractTelexTag(packet []byte) []byte {

// Strip TLS payload
payload := packet[5:]
out := make([]byte, 180)
in_offset := len(payload) - 257
out_offset := 0

// While there are packets left to read and bytes left to fill
for in_offset < len(payload) - 3 && out_offset < 180 {
in_range := payload[in_offset:(in_offset)+4]
out_range := out[out_offset:(out_offset)+3]

scaler := 64*64*64
store := 0
for i := 0; i < 4; i++ {
store += (int(in_range[i] & 0x3F) * scaler)
scaler /= 64
}

for i := 0; i < 3; i++ {
out_range[i] = byte((store >> (8 * (2-i))) & 0xFF)
}

in_offset += 4
out_offset += 3
}

return out
}