Direct proxy works

This commit is contained in:
9seconds
2019-09-04 10:19:01 +03:00
parent 07985cf418
commit 2492a47d0a
87 changed files with 1883 additions and 1447 deletions
+121
View File
@@ -0,0 +1,121 @@
package obfuscated2
import (
"bytes"
"crypto/rand"
"encoding/binary"
"io"
"github.com/juju/errors"
"github.com/9seconds/mtg/mtproto"
)
// [frameOffsetFirst:frameOffsetKey:frameOffsetIV:frameOffsetMagic:frameOffsetDC:frameOffsetEnd]
const (
frameLenKey = 32
frameLenIV = 16
frameLenMagic = 4
frameLenDC = 2
frameOffsetFirst = 8
frameOffsetKey = frameOffsetFirst + frameLenKey
frameOffsetIV = frameOffsetKey + frameLenIV
frameOffsetMagic = frameOffsetIV + frameLenMagic
frameOffsetDC = frameOffsetMagic + frameLenDC
FrameLen = 64
)
// Frame represents handshake frame. Telegram sends 64 bytes of obfuscated2
// initialization data first.
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
type Frame []byte
// Key returns AES encryption key.
func (f Frame) Key() []byte {
return f[frameOffsetFirst:frameOffsetKey]
}
// IV returns AES encryption initialization vector
func (f Frame) IV() []byte {
return f[frameOffsetKey:frameOffsetIV]
}
// Magic returns magic bytes from last 8 bytes of frame. Telegram checks
// for values there. If after decryption magic is not as expected,
// connection considered as failed.
func (f Frame) Magic() []byte {
return f[frameOffsetIV:frameOffsetMagic]
}
// DC returns number of datacenter IP client wants to use.
func (f Frame) DC() (n int16) {
buf := bytes.NewReader(f[frameOffsetMagic:frameOffsetDC])
if err := binary.Read(buf, binary.LittleEndian, &n); err != nil {
n = 1
}
return
}
// ConnectionType identifies connection type of the handshake frame.
func (f Frame) ConnectionType() (mtproto.ConnectionType, error) {
return mtproto.ConnectionTagFromHandshake(f.Magic())
}
// Invert inverts frame for extracting encryption keys. Pkease check that link:
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
func (f Frame) Invert() Frame {
reversed := make(Frame, FrameLen)
copy(reversed, f)
for i := 0; i < frameLenKey+frameLenIV; i++ {
reversed[frameOffsetFirst+i] = f[frameOffsetIV-1-i]
}
return reversed
}
// ExtractFrame extracts exact obfuscated2 handshake frame from given reader.
func ExtractFrame(conn io.Reader) (Frame, error) {
frame := make(Frame, FrameLen)
buf := bytes.NewBuffer(frame)
buf.Reset()
if _, err := io.CopyN(buf, conn, FrameLen); err != nil {
return nil, errors.Annotate(err, "Cannot extract obfuscated header")
}
copy(frame, buf.Bytes())
return frame, nil
}
func generateFrame(connectionType mtproto.ConnectionType) Frame {
frame := make(Frame, FrameLen)
for {
if _, err := rand.Read(frame); err != nil {
continue
}
if frame[0] == 0xef {
continue
}
val := (uint32(frame[3]) << 24) | (uint32(frame[2]) << 16) | (uint32(frame[1]) << 8) | uint32(frame[0])
if val == 0x44414548 || val == 0x54534f50 || val == 0x20544547 || val == 0x4954504f || val == 0xeeeeeeee {
continue
}
val = (uint32(frame[7]) << 24) | (uint32(frame[6]) << 16) | (uint32(frame[5]) << 8) | uint32(frame[4])
if val == 0x00000000 {
continue
}
// error has to be checked before calling this function
tag, _ := connectionType.Tag() // nolint: errcheck, gosec
copy(frame.Magic(), tag)
return frame
}
}
+106
View File
@@ -0,0 +1,106 @@
package obfuscated2
import (
"bytes"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/9seconds/mtg/mtproto"
)
func TestFrameKey(t *testing.T) {
toCompare := make([]byte, 32)
for i := 0; i < 32; i++ {
toCompare[i] = byte(1)
}
assert.Equal(t, toCompare, makeFrame().Key())
}
func TestFrameIV(t *testing.T) {
toCompare := make([]byte, 16)
for i := 0; i < 16; i++ {
toCompare[i] = byte(2)
}
assert.Equal(t, toCompare, makeFrame().IV())
}
func TestFrameMagic(t *testing.T) {
toCompare := make([]byte, 4)
for i := 0; i < 4; i++ {
toCompare[i] = 0xee
}
assert.Equal(t, toCompare, makeFrame().Magic())
}
func TestFrameDC(t *testing.T) {
assert.Equal(t, int16(771), makeFrame().DC())
}
func TestFrameValid(t *testing.T) {
frame := makeFrame()
connType, err := frame.ConnectionType()
assert.Nil(t, err)
assert.Equal(t, connType, mtproto.ConnectionTypeIntermediate)
frame[8+32+16+2] = byte(3)
_, err = frame.ConnectionType()
assert.NotNil(t, err)
}
func TestFrameDoubleInvert(t *testing.T) {
frame := makeFrame()
assert.True(t, bytes.Equal(frame, frame.Invert().Invert()))
}
func TestFrameInvert(t *testing.T) {
frame := makeFrame()
reversed := frame.Invert()
assert.Exactly(t, frame[:8], reversed[:8])
assert.Exactly(t, frame[56:], reversed[56:])
toCompare := make([]byte, 48)
for i := 0; i < 48; i++ {
toCompare[i] = frame[55-i]
}
assert.Equal(t, []byte(reversed[8:56]), toCompare)
}
func TestFrameGenerateValid(t *testing.T) {
validTests := []mtproto.ConnectionType{
mtproto.ConnectionTypeIntermediate,
mtproto.ConnectionTypeAbridged,
}
for _, test := range validTests {
t.Run(strconv.Itoa(int(test)), func(tt *testing.T) {
frame := generateFrame(test) // nolint: scopelint
conType, err := frame.ConnectionType()
assert.Nil(tt, err)
assert.Equal(tt, conType, test) // nolint: scopelint
})
}
}
func makeFrame() Frame {
f := make(Frame, FrameLen)
for i := 8; i < (8 + 32); i++ {
f[i] = byte(1)
}
for i := (8 + 32); i < (8 + 32 + 16); i++ {
f[i] = byte(2)
}
for i := (8 + 32 + 16); i < (8 + 32 + 16 + 4); i++ {
f[i] = 0xee
}
for i := (8 + 32 + 16 + 4); i < (8 + 32 + 16 + 4 + 2); i++ {
f[i] = byte(3)
}
return f
}
+81
View File
@@ -0,0 +1,81 @@
package obfuscated2
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"github.com/juju/errors"
"github.com/9seconds/mtg/mtproto"
)
// Obfuscated2 contains AES CTR encryption and decryption streams
// for telegram connection.
type Obfuscated2 struct {
Decryptor cipher.Stream
Encryptor cipher.Stream
}
// ParseObfuscated2ClientFrame parses client frame. Please check this link for
// details: http://telegra.ph/telegram-blocks-wtf-05-26
//
// Beware, link above is in russian.
func ParseObfuscated2ClientFrame(secret []byte, frame Frame) (*Obfuscated2, *mtproto.ConnectionOpts, error) {
decHasher := sha256.New()
decHasher.Write(frame.Key()) // nolint: errcheck, gosec
decHasher.Write(secret) // nolint: errcheck, gosec
decryptor := makeStreamCipher(decHasher.Sum(nil), frame.IV())
invertedFrame := frame.Invert()
encHasher := sha256.New()
encHasher.Write(invertedFrame.Key()) // nolint: errcheck, gosec
encHasher.Write(secret) // nolint: errcheck, gosec
encryptor := makeStreamCipher(encHasher.Sum(nil), invertedFrame.IV())
decryptedFrame := make(Frame, FrameLen)
decryptor.XORKeyStream(decryptedFrame, frame)
connType, err := decryptedFrame.ConnectionType()
if err != nil {
return nil, nil, errors.Annotate(err, "Unknown protocol")
}
obfs := &Obfuscated2{
Decryptor: decryptor,
Encryptor: encryptor,
}
connOpts := &mtproto.ConnectionOpts{
DC: decryptedFrame.DC(),
ConnectionType: connType,
}
return obfs, connOpts, nil
}
// MakeTelegramObfuscated2Frame creates new handshake frame to send to
// Telegram.
// https://blog.susanka.eu/how-telegram-obfuscates-its-mtproto-traffic/
func MakeTelegramObfuscated2Frame(opts *mtproto.ConnectionOpts) (*Obfuscated2, Frame) {
frame := generateFrame(opts.ConnectionType)
encryptor := makeStreamCipher(frame.Key(), frame.IV())
decryptorFrame := frame.Invert()
decryptor := makeStreamCipher(decryptorFrame.Key(), decryptorFrame.IV())
copyFrame := make(Frame, FrameLen)
copy(copyFrame[:frameOffsetIV], frame[:frameOffsetIV])
encryptor.XORKeyStream(frame, frame)
copy(frame[:frameOffsetIV], copyFrame[:frameOffsetIV])
obfs := &Obfuscated2{
Decryptor: decryptor,
Encryptor: encryptor,
}
return obfs, frame
}
func makeStreamCipher(key, iv []byte) cipher.Stream {
block, _ := aes.NewCipher(key) // nolint: gosec
return cipher.NewCTR(block, iv)
}
+97
View File
@@ -0,0 +1,97 @@
package obfuscated2
import (
"crypto/sha256"
"testing"
"github.com/stretchr/testify/assert"
"github.com/9seconds/mtg/mtproto"
)
func TestObfs2TelegramFrameDecrypt(t *testing.T) {
connOpts := &mtproto.ConnectionOpts{
DC: 1,
ConnectionType: mtproto.ConnectionTypeIntermediate,
}
_, frame := MakeTelegramObfuscated2Frame(connOpts)
decryptor := makeStreamCipher(frame.Key(), frame.IV())
decrypted := make(Frame, FrameLen)
decryptor.XORKeyStream(decrypted, frame)
_, err := decrypted.ConnectionType()
assert.Nil(t, err)
}
func TestObfs2TelegramDecryptEncryptDecrypt(t *testing.T) {
connOpts := &mtproto.ConnectionOpts{
DC: 1,
ConnectionType: mtproto.ConnectionTypeIntermediate,
}
obfs2, frame := MakeTelegramObfuscated2Frame(connOpts)
inverted := frame.Invert()
encryptor := makeStreamCipher(inverted.Key(), inverted.IV())
data := []byte{1, 2, 3}
encrypted := make([]byte, 3)
encryptor.XORKeyStream(encrypted, data)
decrypted := make([]byte, 3)
obfs2.Decryptor.XORKeyStream(decrypted, encrypted)
assert.Equal(t, data, decrypted)
}
func TestObfs2Full(t *testing.T) {
secret := []byte{1, 2, 3, 4, 5}
clientFrame := generateFrame(mtproto.ConnectionTypeIntermediate)
clientHasher := sha256.New()
clientHasher.Write(clientFrame.Key()) // nolint: errcheck, gosec
clientHasher.Write(secret) // nolint: errcheck, gosec
clientKey := clientHasher.Sum(nil)
encryptor := makeStreamCipher(clientKey, clientFrame.IV())
encrypted := make(Frame, FrameLen)
encryptor.XORKeyStream(encrypted, clientFrame)
copy(encrypted[:56], clientFrame[:56])
invertedClientFrame := clientFrame.Invert()
clientHasher = sha256.New()
clientHasher.Write(invertedClientFrame.Key()) // nolint: errcheck, gosec
clientHasher.Write(secret) // nolint: errcheck, gosec
invertedClientKey := clientHasher.Sum(nil)
clientDecryptor := makeStreamCipher(invertedClientKey, invertedClientFrame.IV())
clientObfs, _, err := ParseObfuscated2ClientFrame(secret, encrypted)
assert.Nil(t, err)
connOpts := &mtproto.ConnectionOpts{
DC: 1,
ConnectionType: mtproto.ConnectionTypeIntermediate,
}
tgObfs, tgFrame := MakeTelegramObfuscated2Frame(connOpts)
tgDecryptor := makeStreamCipher(tgFrame.Key(), tgFrame.IV())
decrypted := make(Frame, FrameLen)
tgDecryptor.XORKeyStream(decrypted, tgFrame)
_, err = decrypted.ConnectionType()
assert.Nil(t, err)
tgInvertedFrame := tgFrame.Invert()
tgEncryptor := makeStreamCipher(tgInvertedFrame.Key(), tgInvertedFrame.IV())
message := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
tgEncryptedMessage := make([]byte, len(message))
tgEncryptor.XORKeyStream(tgEncryptedMessage, message)
tgEncDecryptedMessage := make([]byte, len(tgEncryptedMessage))
tgObfs.Decryptor.XORKeyStream(tgEncDecryptedMessage, tgEncryptedMessage)
assert.Equal(t, message, tgEncDecryptedMessage)
clientEncryptedMessage := make([]byte, len(tgEncDecryptedMessage))
clientObfs.Encryptor.XORKeyStream(clientEncryptedMessage, tgEncDecryptedMessage)
finalMessage := make([]byte, len(clientEncryptedMessage))
clientDecryptor.XORKeyStream(finalMessage, clientEncryptedMessage)
assert.Equal(t, finalMessage, message)
}