├── .gitignore ├── LICENSE ├── README.md ├── commandManager.go ├── commands.go ├── fakeshell └── fakeshell.go ├── handler.go ├── main.go └── remoteClient.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | sshForShits 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 traetox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | sshForShits 2 | =========== 3 | 4 | Framework for a high interaction SSH honeypot. The provided "shell" is just a go routine with callbacks which handle command output. 5 | 6 | 7 | The emulated commands are extremely limited right now, but we welcome more interactive replacements. 8 | Checkout the commands.go file to add or modify some. 9 | 10 | 11 | A web frontend specifically designed to show shits that got shells can be found at https://github.com/remasis/sshForShitsGooey 12 | 13 | A live instance is running at http://shellsforshits.com 14 | 15 | Thanks remasis for rolling the GUI, because I have no sense of design... 16 | -------------------------------------------------------------------------------- /commandManager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | type processor func(args []string, out io.Writer, state *State) error 10 | 11 | type hndler struct { 12 | funcs map[string]processor 13 | state *State 14 | } 15 | 16 | type State struct { 17 | Files []FakeFile //fake files which are "introduced to the session" 18 | History []string //listing of previous commands for the '!!' and what not 19 | } 20 | 21 | type FakeFile struct { 22 | Path string 23 | Content string 24 | } 25 | 26 | func NewHandler() *hndler { 27 | return &hndler{ 28 | funcs: make(map[string]processor, 3), 29 | } 30 | } 31 | 32 | func (h *hndler) Register(command string, proc processor) error { 33 | _, ok := h.funcs[command] 34 | if ok { 35 | return errors.New("Already registered") 36 | } 37 | h.funcs[command] = proc 38 | return nil 39 | } 40 | 41 | func (h *hndler) Handle(cmd string, args []string, out io.Writer) (bool, error) { 42 | hnd, ok := h.funcs[cmd] 43 | if !ok { 44 | //no handler ready for that command 45 | return false, nil 46 | } 47 | err := hnd(args, out, h.state) 48 | if err != nil { 49 | h.state.History = append(h.state.History, cmd+strings.Join(args, " ")) 50 | } 51 | return true, err 52 | } 53 | -------------------------------------------------------------------------------- /commands.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | func registerCommands(hnd *hndler) error { 10 | if err := hnd.Register("whoami", whoami); err != nil { 11 | return err 12 | } 13 | if err := hnd.Register("id", id); err != nil { 14 | return err 15 | } 16 | if err := hnd.Register("curl", curl); err != nil { 17 | return err 18 | } 19 | if err := hnd.Register("ls", ls); err != nil { 20 | return err 21 | } 22 | return nil 23 | } 24 | 25 | func whoami(args []string, out io.Writer, state *State) error { 26 | _, err := fmt.Fprintf(out, "root\n") 27 | return err 28 | } 29 | 30 | func id(args []string, out io.Writer, state *State) error { 31 | _, err := fmt.Fprintf(out, "uid=0(root) gid=0(root) groups=0(root)\n") 32 | return err 33 | } 34 | 35 | func curl(args []string, out io.Writer, state *State) error { 36 | fileOut := false 37 | st := "" 38 | for i := range args { 39 | if args[i] == "-o" || args[i] == "-O" || args[i] == "--output" || args[i] == "--remote-name" { 40 | fileOut = true 41 | } 42 | } 43 | if fileOut { 44 | st = ` 45 | % Total % Received % Xferd Average Speed Time Time Time Current 46 | Dload Upload Total Spent Left Speed 47 | 100 219 100 219 0 0 490000 0 --:--:-- --:--:-- --:--:-- 49243 48 | ` 49 | } else { 50 | st = ` 51 | 301 Moved 52 |

301 Moved

53 | The document has moved 54 | here. 55 | ` 56 | } 57 | _, err := fmt.Fprintf(out, "%s\n", st) 58 | return err 59 | } 60 | 61 | func ls(args []string, out io.Writer, state *State) error { 62 | var dirs []string 63 | var err error 64 | for i := range args { 65 | if strings.HasPrefix(args[i], "-") { 66 | continue 67 | } 68 | dirs = append(dirs, args[i]) 69 | } 70 | if len(dirs) == 0 { 71 | _, err := fmt.Fprintf(out, ". ..\n") 72 | return err 73 | } 74 | for i := range dirs { 75 | _, err = fmt.Fprintf(out, "ls: cannot access %s: No such file or directory\n", dirs[i]) 76 | } 77 | return err 78 | } 79 | -------------------------------------------------------------------------------- /fakeshell/fakeshell.go: -------------------------------------------------------------------------------- 1 | package fakeshell 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | type fakeShell struct { 13 | stdin io.Reader 14 | stdout io.Writer 15 | stderr io.Writer 16 | closer io.Closer 17 | prompt string 18 | h Handler 19 | doneChan chan error 20 | mtx *sync.Mutex 21 | running bool 22 | ca CommandAdd 23 | } 24 | 25 | type Handler interface { 26 | Handle(string, []string, io.Writer) (bool, error) 27 | } 28 | 29 | type CommandAdd interface { 30 | AddCommand(string, string) error 31 | } 32 | 33 | func New(prompt string, hn Handler, ca CommandAdd) *fakeShell { 34 | return &fakeShell{ 35 | prompt: prompt, 36 | doneChan: make(chan error, 2), 37 | mtx: &sync.Mutex{}, 38 | running: false, 39 | h: hn, 40 | ca: ca, 41 | } 42 | } 43 | 44 | func (f *fakeShell) Wait() error { 45 | f.mtx.Lock() 46 | if !f.running { 47 | f.mtx.Unlock() 48 | return errors.New("not running") 49 | } 50 | f.mtx.Unlock() 51 | return <-f.doneChan 52 | } 53 | 54 | func (f *fakeShell) SetReadWriteCloser(rd io.Reader, wr io.Writer, cl io.Closer) { 55 | f.stdin = rd 56 | f.stdout = wr 57 | f.stderr = wr 58 | f.closer = cl 59 | } 60 | 61 | func (f *fakeShell) Start() error { 62 | f.mtx.Lock() 63 | defer f.mtx.Unlock() 64 | if f.running { 65 | return errors.New("already running") 66 | } 67 | f.running = true 68 | go f.routine() 69 | return nil 70 | } 71 | 72 | func (f *fakeShell) routine() { 73 | bb := buffer{} 74 | rdr := bufio.NewReader(f.stdin) 75 | for { 76 | fmt.Fprintf(f.stdout, "%s", f.prompt) 77 | ln, err := rdr.ReadString('\n') 78 | if err != nil { 79 | break 80 | } 81 | ln = strings.TrimRight(ln, "\n\r") 82 | if len(ln) == 0 { 83 | continue 84 | } 85 | if ln == "exit" { 86 | f.ca.AddCommand(f.prompt+ln, "") 87 | break 88 | } 89 | flds := strings.Fields(ln) 90 | cmd := flds[0] 91 | var args []string 92 | if len(flds) > 1 { 93 | args = flds[1:len(flds)] 94 | } else { 95 | args = nil 96 | } 97 | bb.Reset() 98 | wtr := io.MultiWriter(&bb, f.stdout) 99 | ok, err := f.h.Handle(cmd, args, wtr) 100 | if err != nil { 101 | break 102 | } 103 | if !ok { 104 | fmt.Fprintf(wtr, "sh: %s: command not found\n", cmd) 105 | } 106 | f.ca.AddCommand(f.prompt+ln, bb.String()) 107 | } 108 | f.running = false 109 | f.doneChan <- f.closer.Close() 110 | } 111 | 112 | type buffer struct { 113 | buf []byte 114 | } 115 | 116 | func (b *buffer) Write(x []byte) (int, error) { 117 | b.buf = append(b.buf, x...) 118 | return len(x), nil 119 | } 120 | 121 | func (b *buffer) Reset() { 122 | b.buf = nil 123 | } 124 | 125 | func (b *buffer) String() string { 126 | if len(b.buf) == 0 { 127 | return "" 128 | } 129 | return string(b.buf) 130 | } 131 | -------------------------------------------------------------------------------- /handler.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "sync" 7 | "time" 8 | 9 | "github.com/traetox/pty" 10 | "github.com/traetox/sshForShits/fakeshell" 11 | "golang.org/x/crypto/ssh" 12 | ) 13 | 14 | const ( 15 | pmpt = "sh-4.3$ " 16 | ) 17 | 18 | func mainHandler(conn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request, handler fakeshell.Handler, dg datagram) { 19 | wg := sync.WaitGroup{} 20 | wg.Add(2) 21 | go handleRequests(reqs, &wg) 22 | go handleChannels(chans, &wg, handler, dg) 23 | wg.Wait() 24 | conn.Close() 25 | conn.Conn.Close() 26 | } 27 | 28 | func handleRequests(reqs <-chan *ssh.Request, wg *sync.WaitGroup) { 29 | defer wg.Done() 30 | for _ = range reqs { 31 | } 32 | } 33 | 34 | func handleChannels(chans <-chan ssh.NewChannel, wg *sync.WaitGroup, handler fakeshell.Handler, dg datagram) { 35 | defer wg.Done() 36 | // Service the incoming Channel channel. 37 | for newChannel := range chans { 38 | // Channels have a type, depending on the application level 39 | // protocol intended. In the case of a shell, the type is 40 | // "session" and ServerShell may be used to present a simple 41 | // terminal interface. 42 | if t := newChannel.ChannelType(); t != "session" { 43 | newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t)) 44 | continue 45 | } 46 | channel, requests, err := newChannel.Accept() 47 | if err != nil { 48 | errLog.Printf("Failed to Accept new channel: %v", err) 49 | continue 50 | } 51 | 52 | //fire up our fake shell 53 | c := fakeshell.New("sh-4.3$ ", handler, &dg) 54 | f, err := pty.StartFaker(c) 55 | if err != nil { 56 | errLog.Printf("Failed to start faker: %v", err) 57 | continue 58 | } 59 | 60 | //teardown session 61 | var once sync.Once 62 | close := func() { 63 | channel.Close() 64 | c.Wait() 65 | f.Close() //close the PTY device 66 | dg.Logout = time.Now().Format(time.RFC3339Nano) 67 | if len(dg.ShellActivity) > 0 { 68 | if err := activityClient.Write(dg); err != nil { 69 | errLog.Printf("Failed to write session: %v", err) 70 | if err := activityClient.Login(); err != nil { 71 | errLog.Printf("Failed to re-login after Write error: %v", err) 72 | } 73 | } 74 | } 75 | } 76 | 77 | //pipe session to bash and vice-versa 78 | go func() { 79 | io.Copy(channel, f) 80 | once.Do(close) 81 | }() 82 | go func() { 83 | io.Copy(f, channel) 84 | once.Do(close) 85 | }() 86 | 87 | // Sessions have out-of-band requests such as "shell", "pty-req" and "env" 88 | go func(in <-chan *ssh.Request) { 89 | for req := range in { 90 | switch req.Type { 91 | case "shell": 92 | // We don't accept any commands (Payload), 93 | // only the default shell. 94 | if len(req.Payload) == 0 { 95 | req.Reply(true, nil) 96 | } else { 97 | req.Reply(false, nil) 98 | } 99 | case "pty-req": 100 | // Responding 'ok' here will let the client 101 | // know we have a pty ready for input 102 | req.Reply(true, nil) 103 | case "window-change": 104 | continue //no response 105 | } 106 | } 107 | }(requests) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io/ioutil" 6 | "log" 7 | "net" 8 | "os" 9 | "time" 10 | 11 | "golang.org/x/crypto/ssh" 12 | ) 13 | 14 | var ( 15 | logFile = flag.String("l", "/tmp/attempts.log", "Log file to log SSH attempts") 16 | errFile = flag.String("e", "/tmp/shellsForShits.log", "Log file to log errors") 17 | bindAddr = flag.String("b", "", "Address to bind to, if nil, bind all") 18 | portNum = flag.String("p", "22", "Port to bind to") 19 | keyFile = flag.String("k", "/tmp/hostkey", "Host keyfile for the server") 20 | remoteServer = flag.String("s", "ds063140.mongolab.com:63140", "mongodb server") 21 | mong_user = flag.String("user", "", "mongodb user") 22 | mong_pass = flag.String("pass", "", "mongodb password") 23 | db = flag.String("db", "sshforshits", "mongodb database") 24 | pcoll = flag.String("pcoll", "pwns", "mongodb shell activity collection") 25 | acoll = flag.String("acoll", "attempts", "mongodb login attempts collection") 26 | versionBanner = flag.String("v", "SSH-2.0-OpenSSH_6.0p1 Debian-4+deb7u2", "Banner to present") 27 | logger *log.Logger 28 | errLog *log.Logger 29 | hostPrivateKeySigner ssh.Signer 30 | activityClient *shellActivityClient 31 | attemptChan chan attempt 32 | ) 33 | 34 | func init() { 35 | flag.Parse() 36 | if *remoteServer == "" { 37 | log.Panic("Invalid API server url") 38 | } 39 | if *mong_user == "" || *mong_pass == "" || *db == "" || *pcoll == "" || *acoll == "" { 40 | log.Panic("Empty mongo info") 41 | } 42 | if *logFile != "" { 43 | fout, err := os.OpenFile(*logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) 44 | if err != nil { 45 | panic(err) 46 | } 47 | logger = log.New(fout, "", log.LstdFlags) 48 | } else { 49 | logger = log.New(ioutil.Discard, "", log.LstdFlags) 50 | } 51 | if *errFile != "" { 52 | fout, err := os.OpenFile(*errFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) 53 | if err != nil { 54 | panic(err) 55 | } 56 | errLog = log.New(fout, "", log.LstdFlags) 57 | } else { 58 | errLog = log.New(ioutil.Discard, "", log.LstdFlags) 59 | } 60 | 61 | hostPrivateKey, err := ioutil.ReadFile(*keyFile) 62 | if err != nil { 63 | panic(err) 64 | } 65 | 66 | hostPrivateKeySigner, err = ssh.ParsePrivateKey(hostPrivateKey) 67 | if err != nil { 68 | panic(err) 69 | } 70 | 71 | attemptChan = make(chan attempt, 16) 72 | } 73 | 74 | func passAuth(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { 75 | var host string 76 | var err error 77 | if host, _, err = net.SplitHostPort(conn.RemoteAddr().String()); err != nil { 78 | host = conn.RemoteAddr().String() 79 | } 80 | logger.Printf("%s %s:%s\n", host, conn.User(), string(pass)) 81 | creds := map[string]string{} 82 | creds["username"] = conn.User() 83 | creds["password"] = string(pass) 84 | perm := ssh.Permissions{ 85 | CriticalOptions: nil, 86 | Extensions: creds, 87 | } 88 | attemptChan <- attempt{ 89 | User: conn.User(), 90 | Pass: string(pass), 91 | TS: time.Now().Format(time.RFC3339Nano), 92 | Origin: conn.RemoteAddr().String(), 93 | } 94 | return &perm, nil 95 | } 96 | 97 | func main() { 98 | var err error 99 | var port string 100 | config := ssh.ServerConfig{ 101 | PasswordCallback: passAuth, 102 | ServerVersion: *versionBanner, 103 | } 104 | config.AddHostKey(hostPrivateKeySigner) 105 | if *portNum == "" { 106 | port = "2222" 107 | } else { 108 | port = *portNum 109 | } 110 | hnd := NewHandler() 111 | registerCommands(hnd) 112 | activityClient, err = NewShellActivityClient(*remoteServer, *db, *pcoll, *acoll, *mong_user, *mong_pass) 113 | if err != nil { 114 | panic(err) 115 | } 116 | if err = activityClient.Login(); err != nil { 117 | errLog.Printf("Failed to login, all writes will be cached until we can actually login") 118 | errLog.Printf("Error: \"%v\"\n", err) 119 | } 120 | go attempter(activityClient) 121 | 122 | socket, err := net.Listen("tcp", *bindAddr+":"+port) 123 | if err != nil { 124 | panic(err) 125 | } 126 | defer socket.Close() 127 | for { 128 | conn, err := socket.Accept() 129 | if err != nil { 130 | continue 131 | } 132 | 133 | // From a standard TCP connection to an encrypted SSH connection 134 | sshConn, chans, reqs, err := ssh.NewServerConn(conn, &config) 135 | if err != nil { 136 | conn.Close() 137 | continue 138 | } 139 | if sshConn.Permissions == nil { 140 | errLog.Printf("Failed to get ssh permissions\n") 141 | sshConn.Close() 142 | conn.Close() 143 | continue 144 | } 145 | if sshConn.Permissions.Extensions == nil { 146 | errLog.Printf("ssh permissions extensions are nil") 147 | sshConn.Close() 148 | conn.Close() 149 | continue 150 | } 151 | username, ok := sshConn.Permissions.Extensions["username"] 152 | if !ok { 153 | errLog.Printf("ssh permission extensions is missing the username") 154 | sshConn.Close() 155 | conn.Close() 156 | continue 157 | } 158 | password, ok := sshConn.Permissions.Extensions["password"] 159 | if !ok { 160 | errLog.Printf("ssh permission extensions is missing the password") 161 | sshConn.Close() 162 | conn.Close() 163 | continue 164 | } 165 | dg := datagram{ 166 | Login: time.Now().Format(time.RFC3339Nano), 167 | Src: sshConn.Conn.RemoteAddr().String(), 168 | Dst: sshConn.Conn.LocalAddr().String(), 169 | User: username, 170 | Pass: password, 171 | } 172 | go mainHandler(sshConn, chans, reqs, hnd, dg) 173 | } 174 | } 175 | 176 | func attempter(sac *shellActivityClient) { 177 | var ats []attempt 178 | for at := range attemptChan { 179 | if err := sac.WriteAttempt(at); err != nil { 180 | ats = append(ats, at) 181 | if err = sac.Login(); err != nil { 182 | time.Sleep(10 * time.Second) 183 | continue 184 | } 185 | } 186 | //a write worked, try to clear the backlog 187 | i := 0 188 | wloop: 189 | for i = 0; i < len(ats); i++ { 190 | if err := sac.WriteAttempt(ats[i]); err != nil { 191 | break wloop 192 | } 193 | } 194 | if i == len(ats) { 195 | //we got everything out 196 | ats = nil 197 | } else { 198 | ats = ats[i:len(ats)] 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /remoteClient.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "errors" 6 | "fmt" 7 | "sync" 8 | "time" 9 | 10 | "gopkg.in/mgo.v2" 11 | "gopkg.in/mgo.v2/bson" 12 | ) 13 | 14 | const ( 15 | defaultTimeout = time.Second * 2 16 | ) 17 | 18 | type datagram struct { 19 | Login string `json:"login"` 20 | Logout string `json:"logout"` 21 | Src string `json:"src"` 22 | Dst string `json:"dst"` 23 | User string `json:"user"` 24 | Pass string `json:"pass"` 25 | ShellActivity []command `json:"shell_activity"` 26 | } 27 | 28 | type command struct { 29 | TS string `json:"ts"` 30 | Cmd string `json:"cmd"` 31 | Resp string `json:"resp"` 32 | } 33 | 34 | type attempt struct { 35 | TS string `json:"ts"` 36 | Pass string `json:"pass"` 37 | User string `json:"user"` 38 | Origin string `json:"origin"` 39 | } 40 | 41 | type shellActivityClient struct { 42 | user string 43 | pass string 44 | dst string 45 | db string 46 | pwnColl string 47 | attemptColl string 48 | session *mgo.Session 49 | status bool 50 | headers map[string]string 51 | mtx *sync.Mutex 52 | cache *list.List 53 | } 54 | 55 | func NewShellActivityClient(dst, db, pwnColl, attemptColl, user, pass string) (*shellActivityClient, error) { 56 | return &shellActivityClient{ 57 | user: user, 58 | pass: pass, 59 | dst: dst, 60 | db: db, 61 | pwnColl: pwnColl, 62 | attemptColl: attemptColl, 63 | headers: make(map[string]string, 1), 64 | mtx: &sync.Mutex{}, 65 | cache: list.New(), 66 | }, nil 67 | } 68 | 69 | //do the actual login work 70 | func (sac *shellActivityClient) login() error { 71 | var err error 72 | uri := fmt.Sprintf("mongodb://%s:%s@%s/%s", sac.user, sac.pass, sac.dst, sac.db) 73 | sac.session, err = mgo.Dial(uri) 74 | if err != nil { 75 | return err 76 | } 77 | sac.status = true 78 | return nil 79 | } 80 | 81 | //Login will renew creds with the remote server 82 | //it is totally ok to hit this over and over 83 | func (sac *shellActivityClient) Login() error { 84 | sac.mtx.Lock() 85 | defer sac.mtx.Unlock() 86 | return sac.login() 87 | } 88 | 89 | func (sac *shellActivityClient) Close() error { 90 | sac.mtx.Lock() 91 | defer sac.mtx.Unlock() 92 | if sac.status && sac.session != nil { 93 | //attempt to send everything in the cache 94 | for { 95 | dg, err := sac.popCache() 96 | if err != nil || dg == nil { 97 | break 98 | } 99 | if err = sac.sendDatagram(dg); err != nil { 100 | break 101 | } 102 | } 103 | } 104 | sac.session.Close() 105 | sac.status = false 106 | return nil 107 | } 108 | 109 | func (sac *shellActivityClient) sendAttempt(at attempt) error { 110 | collection := sac.session.DB(sac.db).C(sac.attemptColl) 111 | if err := collection.Insert(at); err != nil { 112 | return err 113 | } 114 | return nil 115 | } 116 | 117 | func (sac *shellActivityClient) WriteAttempt(at attempt) error { 118 | sac.mtx.Lock() 119 | defer sac.mtx.Unlock() 120 | if !sac.status { 121 | return errors.New("Failed to push attempt") 122 | } 123 | return sac.sendAttempt(at) 124 | } 125 | 126 | func (sac *shellActivityClient) sendDatagram(dg *datagram) error { 127 | collection := sac.session.DB(sac.db).C(sac.pwnColl) 128 | if err := collection.Insert(dg); err != nil { 129 | return err 130 | } 131 | return nil 132 | } 133 | 134 | func (sac *shellActivityClient) Write(dg datagram) error { 135 | sac.mtx.Lock() 136 | defer sac.mtx.Unlock() 137 | if !sac.status { 138 | sac.pushCache(dg) 139 | return errors.New("Client closed") 140 | } 141 | for { 142 | d, err := sac.popCache() 143 | if err != nil { 144 | return err 145 | } 146 | //cache is empty 147 | if d == nil { 148 | break 149 | } 150 | if err := sac.sendDatagram(d); err != nil { 151 | sac.pushCache(*d) 152 | return err 153 | } 154 | } 155 | return sac.sendDatagram(&dg) 156 | } 157 | 158 | func (sac *shellActivityClient) Cache(dg datagram) error { 159 | sac.mtx.Lock() 160 | defer sac.mtx.Unlock() 161 | if !sac.status { 162 | return errors.New("Client closed") 163 | } 164 | return sac.pushCache(dg) 165 | } 166 | 167 | func (sac *shellActivityClient) pushCache(dg datagram) error { 168 | if sac.cache.PushBack(dg) == nil { 169 | return errors.New("Failed push") 170 | } 171 | return nil 172 | } 173 | 174 | func (sac *shellActivityClient) popCache() (*datagram, error) { 175 | if sac.cache.Len() <= 0 { 176 | return nil, nil 177 | } 178 | e := sac.cache.Front() 179 | if e == nil { 180 | return nil, nil 181 | } 182 | dg, ok := e.Value.(datagram) 183 | if !ok { 184 | return nil, errors.New("Invalid item in cache") 185 | } 186 | return &dg, nil 187 | } 188 | 189 | func (dg *datagram) AddCommand(cmd, resp string) error { 190 | ts := time.Now().Format(time.RFC3339Nano) 191 | dg.ShellActivity = append(dg.ShellActivity, command{ts, cmd, resp}) 192 | return nil 193 | } 194 | 195 | type msg struct { 196 | Id bson.ObjectId `bson:"_id"` 197 | Msg string `bson:"msg"` 198 | Count int `bson:"count"` 199 | } 200 | --------------------------------------------------------------------------------