/
fakessh.go
141 lines (128 loc) · 3.77 KB
/
fakessh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// fakessh is an SSH honeypot server.
// It accepts password attempts and logs them.
// If the attacker guesses a password, they get a very fake shell prompt.
//
// To use it, you first need to generate ssh keys (id_rsa, id_dsa, id_ecdsa)
// which live in the same directory as the fakessh binary.
//
// Then run fakessh on your honeypot host. It will listen on port 2022.
// Forward port 22 on some external IP to 2022 on the honeypot. Then watch
// the logs ... sooner or later the attackers will show up.
//
// Probably someone should publish a DNSBL or other blacklist of known SSH
// attackers.
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
"io/ioutil"
"log"
"net"
)
var (
accounts = map[string]string{
"testuser": "tiger",
"john": "mary",
"mary": "john",
"dave": "joe",
"joe": "dave",
"root": "qm22",
}
)
func main() {
// Configure server.
config := &ssh.ServerConfig{
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
addr := c.RemoteAddr().String()
user := c.User()
goodpass, ok := accounts[user]
if ok && goodpass == string(pass) {
log.Printf("Successful login for %s @ %s", user, addr)
return nil, nil
}
log.Printf("Failed login: %s : %s @ %s\n", user, pass, addr)
return nil, fmt.Errorf("password rejected for %q", user)
},
}
// Load keys.
keyed := false
for _, keyfile := range []string{"id_rsa", "id_dsa", "id_ecdsa"} {
privateBytes, err := ioutil.ReadFile(keyfile)
if err != nil {
log.Println("Failed to load private key:", keyfile)
break
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
log.Println("Failed to parse private key:", keyfile)
break
}
config.AddHostKey(private)
log.Println("Added key:", keyfile)
keyed = true
}
if !keyed {
panic("No key, can't start.")
}
// Ready to listen.
log.Print("Listening!")
listener, err := net.Listen("tcp", "0.0.0.0:2022")
if err != nil {
panic("failed to listen for connection")
}
// Loop forever waiting for connections.
for {
nConn, err := listener.Accept()
if err != nil {
log.Print("Couldn't accept incoming connection: ", err)
}
go handleConn(nConn, config)
}
}
func handleConn(nConn net.Conn, config *ssh.ServerConfig) {
addr := nConn.RemoteAddr().String()
log.Printf("Connection opened from %s", addr)
defer log.Printf("Connection closed from %s", addr)
// Negotiate SSH. This is where passwords are checked.
_, chans, _, err := ssh.NewServerConn(nConn, config)
if err != nil {
log.Printf("Couldn't handshake %s: %s", addr, err)
}
// A ServerConn multiplexes several channels, which must
// themselves be Accepted.
for newChannel := range chans {
// Accept reads from the connection, demultiplexes packets
// to their corresponding channels and returns when a new
// channel request is seen. Some goroutine must always be
// calling Accept; otherwise no messages will be forwarded
// to the channels.
if newChannel.ChannelType() != "session" {
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
continue
}
channel, _, err := newChannel.Accept()
if err != nil {
break
}
// Channels have a type, depending on the application level
// protocol intended. In the case of a shell, the type is
// "session" and ServerShell may be used to present a simple
// terminal interface.
log.Printf("Channel from %s : %s", addr, newChannel.ChannelType())
prompt := "$ "
term := terminal.NewTerminal(channel, prompt)
go func() {
defer log.Printf("Channel closed from %s", addr)
defer channel.Close()
// Just log every line that the attacker sends.
for {
line, err := term.ReadLine()
if err != nil {
break
}
log.Printf("%s > %s", addr, line)
}
}()
}
}