github.com/iDigitalFlame/xmt@v0.5.4/c2/server.go (about) 1 //go:build !implant 2 // +build !implant 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package c2 21 22 import ( 23 "context" 24 "strings" 25 "sync" 26 "sync/atomic" 27 28 "github.com/PurpleSec/logx" 29 30 "github.com/iDigitalFlame/xmt/c2/cfg" 31 "github.com/iDigitalFlame/xmt/c2/cout" 32 "github.com/iDigitalFlame/xmt/com" 33 "github.com/iDigitalFlame/xmt/data" 34 "github.com/iDigitalFlame/xmt/device" 35 "github.com/iDigitalFlame/xmt/util/bugtrack" 36 "github.com/iDigitalFlame/xmt/util/xerr" 37 ) 38 39 // Server is the manager for all C2 Listener and Sessions connection and states. 40 // This struct also manages all events and connection changes. 41 type Server struct { 42 log cout.Log 43 ctx context.Context 44 events chan event 45 active map[string]*Listener 46 ch chan struct{} 47 48 Oneshot func(*com.Packet) 49 New func(*Session) 50 new chan *Listener 51 52 Shutdown func(*Session) 53 delSession chan uint32 54 delListener chan string 55 sessions map[uint32]*Session 56 cancel context.CancelFunc 57 lock sync.RWMutex 58 init sync.Once 59 run uint32 60 61 Keys data.KeyPair 62 } 63 64 // Wait will block until the current Server is closed and shutdown. 65 func (s *Server) Wait() { 66 <-s.ch 67 } 68 func (s *Server) listen() { 69 if atomic.SwapUint32(&s.run, 1) != 0 { 70 return 71 } 72 if bugtrack.Enabled { 73 defer bugtrack.Recover("c2.Server.listen()") 74 } 75 if cout.Enabled { 76 s.log.Info("Server-side event processing thread started!") 77 } 78 if s.Keys.Empty() { 79 if s.Keys.Fill(); cout.Enabled { 80 s.log.Info("Generating new server KeyPair..") 81 } 82 } 83 if cout.Enabled { 84 s.log.Trace("Server loaded keys! PublicKey: %s", s.Keys.Public) 85 } 86 if bugtrack.Enabled { 87 bugtrack.Track("c2.(*Server).listen(): Server KeyPair loaded! PublicKey: %s", s.Keys.Public) 88 } 89 for { 90 select { 91 case <-s.ctx.Done(): 92 s.shutdown() 93 return 94 case l := <-s.new: 95 s.active[l.name] = l 96 case r := <-s.delListener: 97 delete(s.active, r) 98 case i := <-s.delSession: 99 s.lock.Lock() 100 if v, ok := s.sessions[i]; ok { 101 if s.Shutdown != nil { 102 s.queue(event{s: v, sf: s.Shutdown}) 103 } 104 if delete(s.sessions, i); cout.Enabled { 105 s.log.Debug("[%s] Removed closed Session 0x%X.", v.parent.name, i) 106 } 107 } 108 s.lock.Unlock() 109 case e := <-s.events: 110 e.process(s.log) 111 } 112 } 113 } 114 func (s *Server) shutdown() { 115 s.cancel() 116 for _, v := range s.sessions { 117 v.Close() 118 } 119 for _, v := range s.active { 120 v.Close() 121 } 122 for len(s.active) > 0 { 123 delete(s.active, <-s.delListener) 124 } 125 if s.active = nil; atomic.SwapUint32(&s.run, 2) == 2 { 126 return 127 } 128 if cout.Enabled { 129 s.log.Debug("Stopping event processor.") 130 } 131 close(s.new) 132 close(s.delListener) 133 close(s.delSession) 134 close(s.events) 135 close(s.ch) 136 } 137 138 // Close stops the processing thread from this Server and releases all associated 139 // resources. 140 // 141 // This will signal the shutdown of all attached Listeners and Sessions. 142 func (s *Server) Close() error { 143 if s.cancel(); atomic.LoadUint32(&s.run) == 0 { 144 s.shutdown() 145 } 146 <-s.ch 147 return nil 148 } 149 func (s *Server) queue(e event) { 150 s.events <- e 151 } 152 153 // IsActive returns true if this Server is still able to Process events. 154 func (s *Server) IsActive() bool { 155 select { 156 case <-s.ch: 157 return false 158 case <-s.ctx.Done(): 159 return false 160 default: 161 return true 162 } 163 } 164 165 // NewServer creates a new Server instance for managing C2 Listeners and Sessions. 166 // 167 // If the supplied Log is nil, the 'logx.NOP' log will be used. 168 func NewServer(l logx.Log) *Server { 169 return NewServerContext(context.Background(), l) 170 } 171 172 // SetLog will set the internal logger used by the Server and any underlying 173 // Listeners, Sessions and Proxies. 174 // 175 // This function is a NOP if the logger is nil or logging is not enabled via the 176 // 'implant' build tag. 177 func (s *Server) SetLog(l logx.Log) { 178 s.log.Set(l) 179 } 180 181 // Sessions returns an array of all the current Sessions connected to Listeners 182 // running on this Server instance. 183 func (s *Server) Sessions() []*Session { 184 s.lock.RLock() 185 l := make([]*Session, 0, len(s.sessions)) 186 for _, v := range s.sessions { 187 l = append(l, v) 188 } 189 s.lock.RUnlock() 190 return l 191 } 192 193 // Done returns a channel that's closed when this Server is closed. 194 // 195 // This can be used to monitor a Server's status using a select statement. 196 func (s *Server) Done() <-chan struct{} { 197 return s.ch 198 } 199 200 // Listeners returns all the Listeners current active on this Server. 201 func (s *Server) Listeners() []*Listener { 202 l := make([]*Listener, 0, len(s.active)) 203 for _, v := range s.active { 204 l = append(l, v) 205 } 206 return l 207 } 208 209 // Listener returns the lister with the provided name if it exists, nil 210 // otherwise. 211 func (s *Server) Listener(n string) *Listener { 212 if len(n) == 0 { 213 return nil 214 } 215 return s.active[n] 216 } 217 218 // Session returns the Session that matches the specified Device ID. 219 // 220 // This function will return nil if no matching Device ID is found. 221 func (s *Server) Session(i device.ID) *Session { 222 if i.Empty() { 223 return nil 224 } 225 s.lock.RLock() 226 v := s.sessions[i.Hash()] 227 s.lock.RUnlock() 228 return v 229 } 230 231 // Remove removes and closes the Session and releases all it's associated 232 // resources from this server instance. 233 // 234 // If shutdown is false, this does not close the Session on the client's end and 235 // will just remove the entry, but can be re-added and if the client connects 236 // again. 237 // 238 // If shutdown is true, this will trigger a Shutdown packet to be sent to close 239 // down the client and will wait until the client acknowledges the shutdown 240 // request before removing. 241 func (s *Server) Remove(i device.ID, shutdown bool) { 242 if !shutdown { 243 if !s.IsActive() { 244 return 245 } 246 s.delSession <- i.Hash() 247 return 248 } 249 if !s.IsActive() { 250 return 251 } 252 s.lock.RLock() 253 v, ok := s.sessions[i.Hash()] 254 if s.lock.RUnlock(); !ok { 255 return 256 } 257 v.Close() 258 } 259 260 // NewServerContext creates a new Server instance for managing C2 Listeners and 261 // Sessions. 262 // 263 // If the supplied Log is nil, the 'logx.NOP' log will be used. 264 // 265 // This function will use the supplied Context as the base context for 266 // cancellation. 267 func NewServerContext(x context.Context, l logx.Log) *Server { 268 s := &Server{ 269 ch: make(chan struct{}), 270 log: cout.New(l), 271 new: make(chan *Listener, 4), 272 active: make(map[string]*Listener), 273 events: make(chan event, maxEvents), 274 sessions: make(map[uint32]*Session), 275 delSession: make(chan uint32, 64), 276 delListener: make(chan string, 16), 277 } 278 s.ctx, s.cancel = context.WithCancel(x) 279 return s 280 } 281 282 // Listen adds the Listener under the name provided. A Listener struct to 283 // control and receive callback functions is added to assist in managing 284 // connections to this Listener. 285 func (s *Server) Listen(name, addr string, p cfg.Profile) (*Listener, error) { 286 return s.ListenContext(s.ctx, name, addr, p) 287 } 288 289 // ListenContext adds the Listener under the name and address provided. A Listener 290 // struct to control and receive callback functions is added to assist in managing 291 // connections to this Listener. 292 // 293 // This function version allows for overriding the Context passed to the Session. 294 func (s *Server) ListenContext(x context.Context, name, addr string, p cfg.Profile) (*Listener, error) { 295 if p == nil { 296 return nil, ErrInvalidProfile 297 } 298 if len(name) == 0 { 299 return nil, xerr.Sub("empty Listener name", 0x4A) 300 } 301 n := strings.ToLower(name) 302 if _, ok := s.active[n]; ok { 303 return nil, xerr.Sub("listener already exists", 0x4B) 304 } 305 h, w, t := p.Next() 306 if len(addr) > 0 { 307 h = addr 308 } 309 if len(h) == 0 { 310 return nil, ErrNoHost 311 } 312 v, err := p.Listen(x, h) 313 if err != nil { 314 return nil, xerr.Wrap("unable to listen", err) 315 } else if v == nil { 316 return nil, xerr.Sub("unable to listen", 0x49) 317 } 318 l := &Listener{ 319 ch: make(chan struct{}), 320 name: n, 321 listener: v, 322 connection: connection{s: s, m: s, p: p, w: w, t: t, log: s.log}, 323 } 324 if s.init.Do(func() { go s.listen() }); cout.Enabled { 325 s.log.Info(`[%s] Added Listener on "%s"!`, n, h) 326 } 327 l.ctx, l.cancel = context.WithCancel(x) 328 s.new <- l 329 go l.listen() 330 return l, nil 331 }