obfuscator

package
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2024 License: GPL-3.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	HISTORY_SEED_TTL              = 24 * time.Hour
	HISTORY_SEED_MAX_ENTRIES      = 1000000
	HISTORY_CLIENT_IP_TTL         = 2 * time.Minute
	HISTORY_CLIENT_IP_MAX_ENTRIES = 10000
)
View Source
const (
	SSH_MAX_SERVER_LINE_LENGTH = 1024
	SSH_PACKET_PREFIX_LENGTH   = 5          // uint32 + byte
	SSH_MAX_PACKET_LENGTH      = 256 * 1024 // OpenSSH max packet length
	SSH_MSG_NEWKEYS            = 21
	SSH_MAX_PADDING_LENGTH     = 255 // RFC 4253 sec. 6
	SSH_PADDING_MULTIPLE       = 16  // Default cipher block size
)
View Source
const (
	OBFUSCATION_CONN_MODE_CLIENT = iota
	OBFUSCATION_CONN_MODE_SERVER
)
View Source
const (
	OBFUSCATION_READ_STATE_CLIENT_READ_PREFIX = iota
	OBFUSCATION_READ_STATE_IDENTIFICATION_LINES
	OBFUSCATION_READ_STATE_KEX_PACKETS
	OBFUSCATION_READ_STATE_FLUSH
	OBFUSCATION_READ_STATE_FINISHED
)
View Source
const (
	OBFUSCATION_WRITE_STATE_CLIENT_SEND_PREAMBLE = iota
	OBFUSCATION_WRITE_STATE_SERVER_SEND_PREFIX_AND_IDENTIFICATION_LINE_PADDING
	OBFUSCATION_WRITE_STATE_IDENTIFICATION_LINE
	OBFUSCATION_WRITE_STATE_KEX_PACKETS
	OBFUSCATION_WRITE_STATE_FINISHED
)
View Source
const (
	OBFUSCATE_SEED_LENGTH         = 16
	OBFUSCATE_KEY_LENGTH          = 16
	OBFUSCATE_HASH_ITERATIONS     = 6000
	OBFUSCATE_MAX_PADDING         = 8192
	OBFUSCATE_MAGIC_VALUE         = 0x0BF5CA7E
	OBFUSCATE_CLIENT_TO_SERVER_IV = "client_to_server"
	OBFUSCATE_SERVER_TO_CLIENT_IV = "server_to_client"

	// Preamble header is the first 24 bytes of the connection. If no prefix is applied,
	// the first 24 bytes are the Obfuscated SSH seed, magic value and padding length.
	PREAMBLE_HEADER_LENGTH = OBFUSCATE_SEED_LENGTH + 8 // 4 bytes each for magic value and padding length

	PREFIX_TERMINATOR_LENGTH    = 16
	PREFIX_TERM_SEARCH_BUF_SIZE = 8192
	PREFIX_MAX_LENGTH           = 65536
	PREFIX_MAX_HEADER_LENGTH    = 4096
)
View Source
const (
	TLS_PASSTHROUGH_NONCE_SIZE   = 16
	TLS_PASSTHROUGH_KEY_SIZE     = 32
	TLS_PASSTHROUGH_TIME_PERIOD  = 20 * time.Minute
	TLS_PASSTHROUGH_HISTORY_TTL  = TLS_PASSTHROUGH_TIME_PERIOD * 3
	TLS_PASSTHROUGH_MESSAGE_SIZE = 32
)

Variables

This section is empty.

Functions

func MakeTLSPassthroughMessage

func MakeTLSPassthroughMessage(
	useTimeFactor bool, obfuscatedKey string) ([]byte, error)

MakeTLSPassthroughMessage generates a unique TLS passthrough message using the passthrough key derived from a master obfuscated key.

The passthrough message demonstrates knowledge of the obfuscated key. When useTimeFactor is set, the message will also reflect the current time period, limiting how long it remains valid.

The configurable useTimeFactor enables support for legacy clients and servers which don't use the time factor.

func VerifyTLSPassthroughMessage

func VerifyTLSPassthroughMessage(
	useTimeFactor bool, obfuscatedKey string, message []byte) bool

VerifyTLSPassthroughMessage checks that the specified passthrough message was generated using the passthrough key.

useTimeFactor must be set to the same value used in MakeTLSPassthroughMessage.

func WrapConnWithSkipReader

func WrapConnWithSkipReader(conn net.Conn) net.Conn

Types

type OSSHPrefixHeader

type OSSHPrefixHeader struct {
	SpecName string
}

OSSHPrefixHeader is the prefix header. It is written by the client when a prefix is applied, and read by the server to determine the prefix-spec to use.

type OSSHPrefixSpec

type OSSHPrefixSpec struct {
	Name string
	Spec transforms.Spec
	Seed *prng.Seed
}

type OSSHPrefixSplitConfig

type OSSHPrefixSplitConfig struct {
	Seed     *prng.Seed
	MinDelay time.Duration
	MaxDelay time.Duration
}

OSSHPrefixSplitConfig are parameters for splitting the preamble into two writes: prefix followed by rest of the preamble.

type ObfuscatedSSHConn

type ObfuscatedSSHConn struct {
	net.Conn
	// contains filtered or unexported fields
}

ObfuscatedSSHConn wraps a Conn and applies the obfuscated SSH protocol to the traffic on the connection: https://github.com/brl/obfuscated-openssh/blob/master/README.obfuscation

ObfuscatedSSHConn is used to add obfuscation to golang's stock "ssh" client and server without modification to that standard library code. The underlying connection must be used for SSH traffic. This code injects the obfuscated seed message, applies obfuscated stream cipher transformations, and performs minimal parsing of the SSH protocol to determine when to stop obfuscation (after the first SSH_MSG_NEWKEYS is sent and received).

WARNING: doesn't fully conform to net.Conn concurrency semantics: there's no synchronization of access to the read/writeBuffers, so concurrent calls to one of Read or Write will result in undefined behavior.

func NewClientObfuscatedSSHConn

func NewClientObfuscatedSSHConn(
	conn net.Conn,
	obfuscationKeyword string,
	obfuscationPaddingPRNGSeed *prng.Seed,
	obfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters,
	prefixSpec *OSSHPrefixSpec,
	osshPrefixSplitConfig *OSSHPrefixSplitConfig,
	minPadding, maxPadding *int) (*ObfuscatedSSHConn, error)

NewClientObfuscatedSSHConn creates a client ObfuscatedSSHConn. See documentation in NewObfuscatedSSHConn.

func NewObfuscatedSSHConn

func NewObfuscatedSSHConn(
	mode ObfuscatedSSHConnMode,
	conn net.Conn,
	obfuscationKeyword string,
	obfuscationPaddingPRNGSeed *prng.Seed,
	obfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters,
	clientPrefixSpec *OSSHPrefixSpec,
	serverPrefixSepcs transforms.Specs,
	osshPrefixSplitConfig *OSSHPrefixSplitConfig,
	minPadding, maxPadding *int,
	seedHistory *SeedHistory,
	irregularLogger func(
		clientIP string,
		err error,
		logFields common.LogFields)) (*ObfuscatedSSHConn, error)

NewObfuscatedSSHConn creates a new ObfuscatedSSHConn. The underlying conn must be used for SSH traffic and must have transferred no traffic.

In client mode, NewObfuscatedSSHConn does not block or initiate network I/O. The obfuscation seed message is sent when Write() is first called.

In server mode, NewObfuscatedSSHConn cannot completely initialize itself without the seed message from the client to derive obfuscation keys. So NewObfuscatedSSHConn blocks on reading the client seed message from the underlying conn.

obfuscationPaddingPRNGSeed is required and used only in OBFUSCATION_CONN_MODE_CLIENT mode and allows for optional replay of the same padding: both in the initial obfuscator message and in the SSH KEX sequence. In OBFUSCATION_CONN_MODE_SERVER mode, the server obtains its PRNG seed from the client's initial obfuscator message, resulting in the server replaying its padding as well.

seedHistory and irregularLogger are optional ObfuscatorConfig parameters used only in OBFUSCATION_CONN_MODE_SERVER.

func NewServerObfuscatedSSHConn

func NewServerObfuscatedSSHConn(
	conn net.Conn,
	obfuscationKeyword string,
	seedHistory *SeedHistory,
	serverPrefixSpecs transforms.Specs,
	irregularLogger func(
		clientIP string,
		err error,
		logFields common.LogFields)) (*ObfuscatedSSHConn, error)

NewServerObfuscatedSSHConn creates a server ObfuscatedSSHConn. See documentation in NewObfuscatedSSHConn.

func (*ObfuscatedSSHConn) Close

func (conn *ObfuscatedSSHConn) Close() error

func (*ObfuscatedSSHConn) GetDerivedPRNG

func (conn *ObfuscatedSSHConn) GetDerivedPRNG(salt string) (*prng.PRNG, error)

GetDerivedPRNG creates a new PRNG with a seed derived from the ObfuscatedSSHConn padding seed and distinguished by the salt, which should be a unique identifier for each usage context.

In OBFUSCATION_CONN_MODE_SERVER mode, the ObfuscatedSSHConn padding seed is obtained from the client, so derived PRNGs may be used to replay sequences post-initial obfuscator message.

func (*ObfuscatedSSHConn) GetMetrics

func (conn *ObfuscatedSSHConn) GetMetrics() common.LogFields

GetMetrics implements the common.MetricsSource interface.

func (*ObfuscatedSSHConn) IsOSSHPrefixStream

func (conn *ObfuscatedSSHConn) IsOSSHPrefixStream() bool

IsOSSHPrefixedStream returns true if client wrote a prefix to the Obfuscated SSH stream, or the server read a prefixed Obfuscated SSH stream.

func (*ObfuscatedSSHConn) Read

func (conn *ObfuscatedSSHConn) Read(buffer []byte) (int, error)

Read wraps standard Read, transparently applying the obfuscation transformations.

func (*ObfuscatedSSHConn) SetOSSHPrefixSplitConfig

func (conn *ObfuscatedSSHConn) SetOSSHPrefixSplitConfig(minDelay, maxDelay time.Duration) error

SetOSSHPrefixSplitConfig sets the OSSHPrefixSplitConfig for the server. This must be called before any data is written.

func (*ObfuscatedSSHConn) Write

func (conn *ObfuscatedSSHConn) Write(buffer []byte) (int, error)

Write wraps standard Write, transparently applying the obfuscation transformations.

type ObfuscatedSSHConnMode

type ObfuscatedSSHConnMode int

type ObfuscatedSSHReadState

type ObfuscatedSSHReadState int

type ObfuscatedSSHWriteState

type ObfuscatedSSHWriteState int

type Obfuscator

type Obfuscator struct {
	// contains filtered or unexported fields
}

Obfuscator implements the seed message, key derivation, and stream ciphers for: https://github.com/brl/obfuscated-openssh/blob/master/README.obfuscation

Limitation: the RC4 cipher is vulnerable to ciphertext malleability and the "magic" value provides only weak authentication due to its small size. Increasing the size of the magic field will break compatibility with legacy clients. New protocols and schemes should not use this obfuscator.

func NewClientObfuscator

func NewClientObfuscator(
	config *ObfuscatorConfig) (obfuscator *Obfuscator, err error)

NewClientObfuscator creates a new Obfuscator, staging a seed message to be sent to the server (by the caller) and initializing stream ciphers to obfuscate data.

ObfuscatorConfig.PaddingPRNGSeed allows for optional replay of the obfuscator padding and must not be nil.

func NewServerObfuscator

func NewServerObfuscator(
	config *ObfuscatorConfig, clientIP string, clientReader io.Reader) (obfuscator *Obfuscator, err error)

NewServerObfuscator creates a new Obfuscator, reading a seed message directly from the clientReader and initializing stream ciphers to obfuscate data.

ObfuscatorConfig.PaddingPRNGSeed is not used, as the server obtains a PRNG seed from the client's initial obfuscator message; this scheme allows for optional replay of the downstream obfuscator padding.

The clientIP value is used by the SeedHistory, which retains client IP values for a short time. See SeedHistory documentation.

func (*Obfuscator) GetDerivedPRNG

func (obfuscator *Obfuscator) GetDerivedPRNG(salt string) (*prng.PRNG, error)

GetDerivedPRNG creates a new PRNG with a seed derived from the obfuscator padding seed and distinguished by the salt, which should be a unique identifier for each usage context.

For NewServerObfuscator, the obfuscator padding seed is obtained from the client, so derived PRNGs may be used to replay sequences post-initial obfuscator message.

func (*Obfuscator) GetDerivedPRNGSeed

func (obfuscator *Obfuscator) GetDerivedPRNGSeed(salt string) (*prng.Seed, error)

GetDerivedPRNGSeed creates a new PRNG seed derived from the obfuscator padding seed and distinguished by the salt, which should be a unique identifier for each usage context.

For NewServerObfuscator, the obfuscator padding seed is obtained from the client, so derived seeds may be used to replay sequences post-initial obfuscator message.

func (*Obfuscator) GetPaddingLength

func (obfuscator *Obfuscator) GetPaddingLength() int

GetPaddingLength returns the client seed message padding length. Only valid for NewClientObfuscator.

func (*Obfuscator) ObfuscateClientToServer

func (obfuscator *Obfuscator) ObfuscateClientToServer(buffer []byte)

ObfuscateClientToServer applies the client RC4 stream to the bytes in buffer.

func (*Obfuscator) ObfuscateServerToClient

func (obfuscator *Obfuscator) ObfuscateServerToClient(buffer []byte)

ObfuscateServerToClient applies the server RC4 stream to the bytes in buffer.

func (*Obfuscator) SendPreamble

func (obfuscator *Obfuscator) SendPreamble() ([]byte, int)

SendPreamble returns the preamble created in NewObfuscatorClient or NewServerObfuscator, removing the reference so that it may be garbage collected.

type ObfuscatorConfig

type ObfuscatorConfig struct {
	IsOSSH                              bool
	Keyword                             string
	ClientPrefixSpec                    *OSSHPrefixSpec
	ServerPrefixSpecs                   transforms.Specs
	OSSHPrefixSplitConfig               *OSSHPrefixSplitConfig
	PaddingPRNGSeed                     *prng.Seed
	MinPadding                          *int
	MaxPadding                          *int
	ObfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters

	SeedHistory       *SeedHistory
	StrictHistoryMode bool
	IrregularLogger   func(clientIP string, err error, logFields common.LogFields)
}

ObfuscatorConfig specifies an Obfuscator configuration.

type SeedHistory

type SeedHistory struct {
	// contains filtered or unexported fields
}

SeedHistory maintains a history of recently observed obfuscation seed values. This history is used to identify duplicate seed messages.

As a heurististic to exclude expected duplicates, due to, for example, meek retries, the source client IP is retained for comparison for a short duration -- long enough to cover meek retries without retaining client IPs in memory long past a client connection lifetime.

func NewSeedHistory

func NewSeedHistory(config *SeedHistoryConfig) *SeedHistory

NewSeedHistory creates a new SeedHistory. Config is optional.

func (*SeedHistory) AddNew

func (h *SeedHistory) AddNew(
	strictMode bool,
	clientIP string,
	seedType string,
	seed []byte) (bool, *common.LogFields)

AddNew calls AddNewWithTTL using the SeedTTL that was specified in the SeedHistoryConfig.

func (*SeedHistory) AddNewWithTTL

func (h *SeedHistory) AddNewWithTTL(
	strictMode bool,
	clientIP string,
	seedType string,
	seed []byte,
	TTL time.Duration) (bool, *common.LogFields)

AddNewWithTTL adds a new seed value to the history, set to expire with the specified TTL. If the seed value is already in the history, and an expected case such as a meek retry is ruled out (or strictMode is on), AddNew returns false.

When a duplicate seed is found, a common.LogFields instance is returned, populated with event data. Log fields may be returned in either the false or true case.

type SeedHistoryConfig

type SeedHistoryConfig struct {
	SeedTTL            time.Duration
	SeedMaxEntries     int
	ClientIPTTL        time.Duration
	ClientIPMaxEntries int
}

type SkipReader

type SkipReader struct {
	net.Conn
	// contains filtered or unexported fields
}

func (*SkipReader) Read

func (sr *SkipReader) Read(b []byte) (int, error)

func (*SkipReader) SkipUpToToken

func (sr *SkipReader) SkipUpToToken(
	token []byte, readSize, maxSearchSize int) error

SkipUpToToken reads from the underlying conn initially len(token) bytes, and then readSize bytes at a time up to maxSearchSize until token is found, or error. If the token is found, stream is rewound to end of the token.

Note that maxSearchSize is not a strict limit on the total number of bytes read.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL