├── .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 |
--------------------------------------------------------------------------------