Skip to content

Instantly share code, notes, and snippets.

@ezr
Created July 5, 2021 16:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ezr/224840e81883d6be08b9b7c45e2cff8f to your computer and use it in GitHub Desktop.
Save ezr/224840e81883d6be08b9b7c45e2cff8f to your computer and use it in GitHub Desktop.
A fake ssh server that logs usernames/passwords that get submitted
// A fake SSH server that logs passwords
// tweaked from https://gist.github.com/jpillora/b480fde82bff51a06238
//
// # generate server keypair with
// ssh-keygen -t rsa
// the server expects to find id_rsa in the PWD
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"strconv"
"golang.org/x/crypto/ssh"
)
func main() {
port := flag.Int("port", 22, "port to listen on")
addr := flag.String("addr", "0.0.0.0", "address to listen on")
flag.Parse()
listenPort := strconv.Itoa(*port)
logFile, err := os.OpenFile("ssh.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
// A ssh.ServerConn is created by passing an existing net.Conn and a ssh.ServerConfig
// to ssh.NewServerConn, in effect, upgrading the net.Con into an ssh.ServerConn
config := &ssh.ServerConfig{
// function to run when a client attempts a password login
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
log.Println("[password attempt]", c.User(), string(pass), c.RemoteAddr())
return nil, fmt.Errorf("password rejected for %q", c.User())
},
// Deny any attempt to authenticate with a key
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
log.Println("[pubkey attempt]", key.Type(), ssh.FingerprintSHA256(key))
return nil, fmt.Errorf("denied")
},
}
// You can generate a keypair with 'ssh-keygen -t rsa'
privateBytes, err := ioutil.ReadFile("id_rsa")
if err != nil {
log.Fatal("Failed to load private key (./id_rsa)")
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
log.Fatal("Failed to parse private key")
}
// this is needed for an initial handshake even if the client is only using password auth
config.AddHostKey(private)
// Once a ServerConfig has been configured, connections can be accepted.
listenAddr := *addr + ":" + listenPort
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
log.Fatalf("Failed to listen on %s (%s)", listenAddr, err)
}
// Accept all connections
log.Println("Listening on", listenAddr)
for {
tcpConn, err := listener.Accept()
if err != nil {
log.Printf("Failed to accept incoming connection (%s)", err)
continue
}
// Before use, a handshake must be performed on the incoming net.Conn.
//sshConn, chans, reqs, err := ssh.NewServerConn(tcpConn, config)
sshConn, _, reqs, err := ssh.NewServerConn(tcpConn, config)
if err != nil {
log.Printf("Failed to handshake (%s)", err)
continue
}
log.Printf("New SSH connection from %s (%s)", sshConn.RemoteAddr(), sshConn.ClientVersion())
// Discard all global out-of-band Requests
go ssh.DiscardRequests(reqs)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment