github.com/iDigitalFlame/xmt@v0.5.4/com/wc2/server.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 wc2 18 19 import ( 20 "context" 21 "crypto/tls" 22 "net" 23 "net/http" 24 "net/http/cookiejar" 25 "os" 26 "sync/atomic" 27 "time" 28 29 "github.com/iDigitalFlame/xmt/com" 30 "github.com/iDigitalFlame/xmt/util/xerr" 31 ) 32 33 // Server is a C2 profile that mimics a standard web server and client setup. 34 // This struct inherits the http.Server struct and can be used to serve real 35 // files and pages. 36 // 37 // Use the Target Rules a URL mapping that can be used by clients to access the 38 // C2 functions. 39 type Server struct { 40 Target Target 41 t transport 42 ch chan complete 43 tls *tls.Config 44 handler *http.ServeMux 45 client *http.Client 46 rules []Rule 47 48 Timeout time.Duration 49 done uint32 50 } 51 type directory string 52 53 // Close terminates this Web instance and signals all current listeners and 54 // clients to disconnect. This will close all connections related to this struct. 55 // 56 // This function does not block. 57 func (s *Server) Close() error { 58 if atomic.LoadUint32(&s.done) == 0 { 59 atomic.StoreUint32(&s.done, 1) 60 close(s.ch) 61 } 62 return nil 63 } 64 65 // TargetAsRule will take the server 'Target' and create a matching ruleset using 66 // the 'Rule' function. 67 func (s *Server) TargetAsRule() { 68 if s.Target.empty() { 69 return 70 } 71 s.Rule(s.Target.Rule()) 72 } 73 74 // Rule adds the specified rules to the Web instance to assist in determine real 75 // and C2 traffic. 76 func (s *Server) Rule(r ...Rule) { 77 if len(r) == 0 { 78 return 79 } 80 if s.rules == nil { 81 s.rules = make([]Rule, 0, len(r)) 82 } 83 s.rules = append(s.rules, r...) 84 } 85 86 // Client returns the internal 'http.Client' struct to allow for extra configuration. 87 // To prevent any issues, it is recommended to NOT overrite or change the Transport 88 // of this Client. 89 // 90 // The return value will ALWAYS be non-nil. 91 func (s *Server) Client() *http.Client { 92 return s.client 93 } 94 95 // NewServer creates a new Web C2 server instance. This can be passed to the Listen 96 // function of a 'c2.Server' to serve a Web Server that also acts as a C2 server. 97 // 98 // This struct supports all the default Golang http.Server functions and can be 99 // used to serve real web pages. Rules must be defined using the 'Rule' function 100 // to allow the server to differentiate between C2 and real traffic. 101 func NewServer(t time.Duration) *Server { 102 return NewServerTLS(t, nil) 103 } 104 105 // Serve attempts to serve the specified filesystem path 'f' at the URL mapped 106 // path 'p'. This function will determine if the path represents a file or 107 // directory and will call 'ServeFile' or 'ServeDirectory' depending on the path 108 // result. This function will return an error if the filesystem path does not 109 // exist or is invalid. 110 func (s *Server) Serve(p, f string) error { 111 i, err := os.Stat(f) 112 if err != nil { 113 return err 114 } 115 if i.IsDir() { 116 s.handler.Handle(p, http.FileServer(http.Dir(f))) 117 return nil 118 } 119 s.handler.Handle(p, http.FileServer(directory(f))) 120 return nil 121 } 122 123 // Transport returns the internal 'http.Transport' struct to allow for extra 124 // configuration. To prevent any issues, it is recommended to NOT overrite or 125 // change any of the 'Dial*' functions of this Transoport. 126 // 127 // The return value will ALWAYS be non-nil. 128 func (s *Server) Transport() *http.Transport { 129 return s.t.Transport 130 } 131 132 // ServeFile attempts to serve the specified filesystem path 'f' at the URL mapped 133 // path 'p'. This function is used to serve files and will return an error if the 134 // filesystem path does not exist or the path destination is not a file. 135 func (s *Server) ServeFile(p, f string) error { 136 i, err := os.Stat(f) 137 if err != nil { 138 return err 139 } 140 if !i.IsDir() { 141 s.handler.Handle(p, http.FileServer(directory(f))) 142 return nil 143 } 144 return xerr.Sub("not a file", 0x35) 145 } 146 147 // Handle registers the handler for the given pattern. If a handler already exists 148 // for pattern, Handle panics. 149 func (s *Server) Handle(p string, h http.Handler) { 150 s.handler.Handle(p, h) 151 } 152 153 // ServeDirectory attempts to serve the specified filesystem path 'f' at the URL 154 // mapped path 'p'. This function is used to serve directories and will return 155 // an error if the filesystem path does not exist or the path destination is not 156 // a directory. 157 func (s *Server) ServeDirectory(p, f string) error { 158 i, err := os.Stat(f) 159 if err != nil { 160 return err 161 } 162 if i.IsDir() { 163 s.handler.Handle(p, http.FileServer(http.Dir(f))) 164 return nil 165 } 166 return xerr.Sub("not a directory", 0x36) 167 } 168 func (d directory) Open(_ string) (http.File, error) { 169 // 0 - READONLY 170 return os.OpenFile(string(d), 0, 0) 171 } 172 173 // NewServerTLS creates a new TLS wrapped Web C2 server instance. This can be passed 174 // to the Listen function of a 'c2.Server' to serve a Web Server that also acts 175 // as a C2 server. 176 // 177 // This struct supports all the default Golang http.Server 178 // functions and can be used to serve real web pages. Rules must be defined 179 // using the 'Rule' function to allow the server to differentiate between C2 180 // and real traffic. 181 func NewServerTLS(t time.Duration, c *tls.Config) *Server { 182 if t <= 0 { 183 t = com.DefaultTimeout 184 } 185 s := &Server{ 186 ch: make(chan complete, 1), 187 tls: c, 188 handler: new(http.ServeMux), 189 Timeout: t, 190 } 191 var ( 192 j, _ = cookiejar.New(nil) 193 x = newTransport(t) 194 ) 195 s.t.hook(x) 196 s.t.Transport = x 197 s.client = &http.Client{Jar: j, Transport: x} 198 return s 199 } 200 201 // Connect creates a C2 client connector that uses the same properties of the 202 // Web struct parent. 203 func (s *Server) Connect(x context.Context, a string) (net.Conn, error) { 204 return s.t.connect(x, &s.Target, s.client, a) 205 } 206 207 // Listen returns a new C2 listener for this Web instance. This function creates 208 // a separate server, but still shares the handler for the base Web instance that 209 // it's created from. 210 func (s *Server) Listen(x context.Context, a string) (net.Listener, error) { 211 if s.tls != nil && (len(s.tls.Certificates) == 0 || s.tls.GetCertificate == nil) { 212 return nil, com.ErrInvalidTLSConfig 213 } 214 v, err := com.ListenConfig.Listen(x, com.NameTCP, a) 215 if err != nil { 216 return nil, err 217 } 218 l := &listener{ 219 p: s, 220 ch: make(chan complete, 1), 221 pch: s.ch, 222 new: make(chan *conn, 64), 223 ctx: x, 224 rules: make([]Rule, len(s.rules)), 225 Server: &http.Server{ 226 Addr: a, 227 TLSConfig: s.tls, 228 ReadTimeout: s.Timeout, 229 IdleTimeout: 0, 230 WriteTimeout: s.Timeout, 231 ReadHeaderTimeout: s.Timeout, 232 }, 233 } 234 l.Handler = l 235 baseContext(l, l.context) 236 if copy(l.rules, s.rules); s.tls != nil { 237 if len(s.tls.NextProtos) == 0 { 238 s.tls.NextProtos = []string{"http/1.1"} // Prevent using http2 for websockets 239 } 240 go l.listen(tls.NewListener(v, s.tls)) 241 } else { 242 go l.listen(v) 243 } 244 return l, nil 245 } 246 247 // HandleFunc registers the handler function for the given pattern. 248 func (s *Server) HandleFunc(p string, h func(http.ResponseWriter, *http.Request)) { 249 s.handler.HandleFunc(p, h) 250 }