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 }