Skip to content

Instantly share code, notes, and snippets.

@kyberorg
Last active April 29, 2021 07:17
Honeypod drafting

Honeypod draft

About

Name: honeypod

A simple SSH honeypot written on Go. Strictly not a honeypot as it doesnt trap or jail anything, it simply collects data on attempts to login to a generic SSH server open to the internet.

The tool runs an SSH server that rejects all login attempts. There is no session created it just allows a login attempt and records the user name and password and source IP for later analysis.

Envs

  • DB_TYPE - sqlite, postgres, nodb
  • DB_FILE - (sqlite only).
  • DB_HOST
  • DB_PORT
  • DB_NAME
  • DB_USER
  • DB_PASS
  • GEOIP_COUNTRY_FILE
  • GEOIP_CITY_FILE
  • LOG_LOCAL_IPS=true/false (default: false)
  • CONFIG_FILE

Roadmap

Version 1.0

  • MVP (honeypod, json logs, geoip)

  • Module struct: params jne

  • Systemd daemon

  • Grafana Dashboard and loki config manual

  • Raw Metrics

  • Internals: HostKeyProvider, config

  • ELK

  • Record data to Database (SQLite and Postgres)

  • Docker

  • Readme

  • Branding: logo jne

Version 1.1

  • Multiport support

Other

  • Simple API to expose data (w/ Gin Gonic)

Roadmap своими словами

Базовая версия

Слушаем порт. Пришло сообщение -делаем struct NameHere и кидаем в канал.

Горутина, которая делает логи конвёртит эту структуру в другую (ака json) и срёт в логи. Конвертилка взаимодействует с функцией, которая с айпи делает геоданные

Ещё 2 идеи

В main или даже в init: конфигурим аппу (флаги и все дела)

Надо как-то фоново парсить поля запроса и сразу давать ответ клиенту, иначе мы тупые терминалы или клиент сорвётся во время парса.

Эмблема

Посередине го-суслик (аля траефик) На вход: три строки (username,password,1.2.3.4) На выход: строки JSON log, ELK, SQLite, Slon (iz Postgres) Вверх и вниз: Prometheus Metrics, Raw metrics

{
"node_hostname": "server.domain.tld",
"data" : {
"username" : "admin",
"password" : "admin"
},
"remote" : {
"ip" : "1.2.3.4",
"geoip" : {
"coodrinates" : {
"lat" : 123.45,
"lon" : 54.32
},
"country" : {
"code" : "CN",
"name" "China"
},
"region" : {
"code" : "ZH",
"name" : "Shanhai Region"
},
"city" : {
"name" : "Shanhai"
}
}
},
"time" : {
"local" : "08/Apr/2021:11:26:39 +0300",
"iso8601" : "2021-04-08T11:26:39+03:00"
}
}
package main
import (
"errors"
"fmt"
"github.com/gliderlabs/ssh"
"github.com/kyberorg/honeypot/cmd/honeypot/config"
gossh "golang.org/x/crypto/ssh"
"io"
"io/ioutil"
"log"
"net"
"os"
"time"
)
var (
DeadlineTimeout = 30 * time.Second
IdleTimeout = 10 * time.Second
)
func main() {
// load application configurations
if err := config.LoadConfig("./config"); err != nil {
panic(fmt.Errorf("invalid application configuration: %s", err))
}
fmt.Println(config.Config.ConfigVar)
// Load a hostkey so a new one isnt generated every time
home := os.Getenv("HOME")
privateBytes, err := ioutil.ReadFile(home + "/.ssh/id_rsa")
if err != nil {
log.Fatal("Failed to load private key (" + home + "/.ssh/id_rsa)")
}
private, err := gossh.ParsePrivateKey(privateBytes)
if err != nil {
log.Fatal("Failed to parse private key")
}
ssh.Handle(func(s ssh.Session) {
log.Println("new connection")
//игнорим - ибо оно нам на хрен не сдалось
_, _ = io.WriteString(s, fmt.Sprintf("Hello %s\n", s.User()))
log.Println("connection closed")
})
log.Println("starting ssh server on port 2222....")
log.Printf("connections will only last %s\n", DeadlineTimeout)
log.Printf("and timeout after %s of no activity\n", IdleTimeout)
server := &ssh.Server{
Addr: "0.0.0.0:2222",
MaxTimeout: DeadlineTimeout,
IdleTimeout: IdleTimeout,
PasswordHandler: passwordHandler,
}
server.AddHostKey(private)
log.Fatal(server.ListenAndServe())
}
func passwordHandler(ctx ssh.Context, password string) bool {
ipAddr, _ := ParseIP(ctx.RemoteAddr().String())
log.Printf("User: %s, Password: %s, IP: %s", ctx.User(), password, ipAddr)
// Small delay to simulate "real" ssh server
time.Sleep(2 * time.Second)
return false
}
func ParseIP(s string) (string, error) {
ip, _, err := net.SplitHostPort(s)
if err == nil {
return ip, nil
}
ip2 := net.ParseIP(s)
if ip2 == nil {
return "", errors.New("invalid IP")
}
return ip2.String(), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment