github.com/iDigitalFlame/xmt@v0.5.4/c2/cfg/profile.go (about)

     1  // Copyright (C) 2020 - 2023 iDigitalFlame
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  //
    16  
    17  package cfg
    18  
    19  import (
    20  	"context"
    21  	"io"
    22  	"net"
    23  	"time"
    24  
    25  	"github.com/iDigitalFlame/xmt/data"
    26  	"github.com/iDigitalFlame/xmt/util/xerr"
    27  )
    28  
    29  // DefaultSleep is the default sleep Time when the provided sleep value is
    30  // empty or negative.
    31  const DefaultSleep = time.Duration(60) * time.Second
    32  
    33  // DefaultJitter is the default Jitter value when the provided jitter value
    34  // is negative.
    35  const DefaultJitter uint8 = 10
    36  
    37  var (
    38  	// ErrNotAListener is an error that can be returned by a call to a Profile's
    39  	// 'Listen' function when that operation is disabled.
    40  	ErrNotAListener = xerr.Sub("not a Listener", 0x47)
    41  	// ErrNotAConnector is an error that can be returned by a call to a
    42  	// Profile's 'Connect' function when that operation is disabled.
    43  	ErrNotAConnector = xerr.Sub("not a Connector", 0x48)
    44  )
    45  
    46  // Static is a simple static Profile implementation.
    47  //
    48  // This struct fills all the simple values for a Profile without anything
    49  // Fancy.
    50  //
    51  // The single letter attributes represent the values that are used.
    52  //
    53  // If 'S' or 'J' are omitted or zero values, they will be replaced with the
    54  // DefaultJitter and DefaultSleep values respectively.
    55  //
    56  // If the 'L' or 'C' values are omitted or nil, they will disable that function
    57  // of this Profile.
    58  type Static struct {
    59  	_ [0]func()
    60  	// W is the Wrapper
    61  	W Wrapper
    62  	// T is the Transform
    63  	T Transform
    64  	// L is the Acceptor or Server Listener Connector
    65  	L Accepter
    66  	// C is the Connector or Client Connector
    67  	C Connector
    68  	// K is the KillDate
    69  	K *time.Time
    70  	// A is the WorHours
    71  	A *WorkHours
    72  	// H is the Target Host or Listen Address
    73  	H string
    74  	// P is the valid Server PublicKeys that can be used as FNV-32 hashes
    75  	P []uint32
    76  	// S is the Sleep duration
    77  	S time.Duration
    78  	// J is the Jitter percentage
    79  	J int8
    80  }
    81  
    82  // Profile is an interface that defines a C2 connection.
    83  //
    84  // This is used for setting the specifics that wil be used to listen by servers
    85  // and for connections by clients.
    86  type Profile interface {
    87  	Jitter() int8
    88  	Switch(bool) bool
    89  	Sleep() time.Duration
    90  	WorkHours() *WorkHours
    91  	KillDate() (time.Time, bool)
    92  	TrustedKey(data.PublicKey) bool
    93  	Next() (string, Wrapper, Transform)
    94  	Connect(context.Context, string) (net.Conn, error)
    95  	Listen(context.Context, string) (net.Listener, error)
    96  }
    97  
    98  // Wrapper is an interface that wraps the binary streams into separate stream
    99  // types. This allows for using encryption or compression (or both!).
   100  type Wrapper interface {
   101  	Unwrap(io.Reader) (io.Reader, error)
   102  	Wrap(io.WriteCloser) (io.WriteCloser, error)
   103  }
   104  type stackCloser struct {
   105  	_ [0]func()
   106  	s io.WriteCloser
   107  	io.WriteCloser
   108  }
   109  
   110  // Accepter is an interface that can be used to create listening sockets.
   111  //
   112  // This interface defines a single function that returns a listener based on an
   113  // accept address string.
   114  //
   115  // The supplied Context can be used to close the listening socket.
   116  type Accepter interface {
   117  	Listen(context.Context, string) (net.Listener, error)
   118  }
   119  
   120  // Connector is an interface that can be used to connect to listening sockets.
   121  //
   122  // This interface defines a single function that returns a Connected socket
   123  // based on the connection string.
   124  //
   125  // The supplied Context can be used to close the connecting socket or interrupt
   126  // blocking connections.
   127  type Connector interface {
   128  	Connect(context.Context, string) (net.Conn, error)
   129  }
   130  
   131  // Transform is an interface that can modify the data BEFORE it is written or
   132  // AFTER is read from a Connection.
   133  //
   134  // Transforms may be used to mask and unmask communications as benign protocols
   135  // such as DNS, FTP or HTTP.
   136  type Transform interface {
   137  	Read([]byte, io.Writer) error
   138  	Write([]byte, io.Writer) error
   139  }
   140  
   141  // MultiWrapper is an alias for an array of Wrappers.
   142  //
   143  // This will preform the wrapper/unwrapping operations in the order of the
   144  // array.
   145  //
   146  // This is automatically created by some Profile instances when multiple
   147  // Wrappers are present.
   148  type MultiWrapper []Wrapper
   149  
   150  // Jitter fulfils the Profile interface.
   151  func (s Static) Jitter() int8 {
   152  	if s.J < 0 || s.J > 100 {
   153  		return int8(DefaultJitter)
   154  	}
   155  	return s.J
   156  }
   157  
   158  // Switch is function that will indicate to the caller if the 'Next' function
   159  // needs to be called. Calling this function has the potential to advance the
   160  // Profile group, if available.
   161  //
   162  // The supplied boolean must be true if the last call to 'Connect' ot 'Listen'
   163  // resulted in an error or if a forced switch if warranted.
   164  // This indicates to the Profile is "dirty" and a switchover must be done.
   165  //
   166  // It is recommended to call the 'Next' function after if the result of this
   167  // function is true.
   168  //
   169  // Static Profile variants may always return 'false' to prevent allocations.
   170  func (Static) Switch(_ bool) bool {
   171  	return false
   172  }
   173  func (s *stackCloser) Close() error {
   174  	if err := s.WriteCloser.Close(); err != nil {
   175  		return err
   176  	}
   177  	return s.s.Close()
   178  }
   179  
   180  // Sleep returns a value that indicates the amount of time a Session should wait
   181  // before attempting communication again, modified by Jitter (if enabled).
   182  //
   183  // Sleep MUST be greater than zero (0), any value that is zero or less is
   184  // ignored and indicates that this profile does not set a Sleep value and will
   185  // use the system default '60s'.
   186  func (s Static) Sleep() time.Duration {
   187  	if s.S <= 0 {
   188  		return DefaultSleep
   189  	}
   190  	return s.S
   191  }
   192  
   193  // WorkHours fulfils the Profile interface. Empty WorkHours values indicate that
   194  // there is no workhours set and a nil value indicates that there is no WorkHours
   195  // in this Profile.
   196  func (s Static) WorkHours() *WorkHours {
   197  	return s.A
   198  }
   199  
   200  // KillDate fulfils the Profile interface.
   201  //
   202  // A valid or empty time.Time value along with a True will indicate that this
   203  // Profile has a KillDate set. If the boolean is false, this indicates that no
   204  // KilDate is specified in this Profile and the 'time.Time' will be ignored.
   205  func (s Static) KillDate() (time.Time, bool) {
   206  	if s.K == nil {
   207  		return time.Time{}, false
   208  	}
   209  	return *s.K, true
   210  }
   211  
   212  // TrustedKey returns true if the supplied Server PublicKey is trusted.
   213  // Empty PublicKeys will always return false.
   214  //
   215  // This function returns true if no trusted PublicKey hashes are configured or
   216  // the hash was found.
   217  func (s Static) TrustedKey(k data.PublicKey) bool {
   218  	if k.Empty() {
   219  		return false
   220  	}
   221  	if len(s.P) == 0 {
   222  		return true
   223  	}
   224  	h := k.Hash()
   225  	for i := range s.P {
   226  		if s.P[i] == h {
   227  			return true
   228  		}
   229  	}
   230  	return false
   231  }
   232  
   233  // Next is a function call that can be used to grab the Profile's current target
   234  // along with the appropriate Wrapper and Transform.
   235  //
   236  // Implementations of a Profile are recommend to ensure that this function does
   237  // not affect how the Profile currently works until a call to 'Switch' as this
   238  // WILL be called on startup of a Session.
   239  func (s Static) Next() (string, Wrapper, Transform) {
   240  	return s.H, s.W, s.T
   241  }
   242  
   243  // Unwrap satisfies the Wrapper interface.
   244  func (m MultiWrapper) Unwrap(r io.Reader) (io.Reader, error) {
   245  	var (
   246  		o   = r
   247  		err error
   248  	)
   249  	for x := len(m) - 1; x >= 0; x-- {
   250  		if o, err = m[x].Unwrap(o); err != nil {
   251  			return nil, err
   252  		}
   253  	}
   254  	return o, nil
   255  }
   256  
   257  // Wrap satisfies the Wrapper interface.
   258  func (m MultiWrapper) Wrap(w io.WriteCloser) (io.WriteCloser, error) {
   259  	var (
   260  		o   = w
   261  		k   io.WriteCloser
   262  		err error
   263  	)
   264  	for x := len(m) - 1; x >= 0; x-- {
   265  		if k, err = m[x].Wrap(o); err != nil {
   266  			return nil, err
   267  		}
   268  		o = &stackCloser{s: o, WriteCloser: k}
   269  	}
   270  	return o, nil
   271  }
   272  
   273  // Connect is a function that will preform a Connection attempt against the
   274  // supplied address string.
   275  //
   276  // This function may return an error if a connection could not be made or if
   277  // this Profile does not support Client-side connections.
   278  //
   279  // It is recommended for implementations to implement using the passed Context
   280  // to stop in-flight calls.
   281  func (s Static) Connect(x context.Context, a string) (net.Conn, error) {
   282  	if s.C == nil {
   283  		return nil, ErrNotAConnector
   284  	}
   285  	return s.C.Connect(x, a)
   286  }
   287  
   288  // Listen is a function that will attempt to create a listening connection on
   289  // the supplied address string.
   290  //
   291  // This function may return an error if a listener could not be created or if
   292  // this Profile does not support Server-side connections.
   293  //
   294  // It is recommended for implementations to implement using the passed Context
   295  // to stop running Listeners.
   296  func (s Static) Listen(x context.Context, a string) (net.Listener, error) {
   297  	if s.L == nil {
   298  		return nil, ErrNotAListener
   299  	}
   300  	return s.L.Listen(x, a)
   301  }