687 lines
17 KiB
Go
Executable File
687 lines
17 KiB
Go
Executable File
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
type DBConfig struct {
|
|
DbDriver string `json:"dbdriver"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
DBName string `json:"dbname"`
|
|
}
|
|
|
|
type Webconfig struct {
|
|
Port int `json:"port"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
type Config struct {
|
|
ZmapFile string `json:"zmapfile"`
|
|
Channelfeeders int `json:"channelfeeders"`
|
|
DB DBConfig `json:"db"`
|
|
Web Webconfig `json:"web"`
|
|
}
|
|
|
|
func loadConfig(path string) (*Config, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
var cfg Config
|
|
if err := json.NewDecoder(f).Decode(&cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
type BannerStore interface {
|
|
InsertHTTPBanner(ip string, listening bool, title, resp, errStr, headers, serverHeader, contentType, contentLength, redirect, metaTags, bodySnippet string) error
|
|
InsertSSHBanner(ip string, listening bool, firstline string) error
|
|
InsertTelnetBanner(ip string, listening bool, firstline, fullbanner string) error
|
|
}
|
|
type SQLBannerStore struct {
|
|
db *sql.DB
|
|
insertHTTP *sql.Stmt
|
|
insertSSH *sql.Stmt
|
|
insertTelnet *sql.Stmt
|
|
}
|
|
|
|
func (s *SQLBannerStore) InsertHTTPBanner(
|
|
ip string,
|
|
listening bool,
|
|
title, resp, errStr, headers,
|
|
serverHeader, contentType, contentLength, redirect, metaTags, bodySnippet string,
|
|
) error {
|
|
_, err := s.insertHTTP.Exec(ip, listening, title, resp, errStr, headers,
|
|
serverHeader, contentType, contentLength, redirect, metaTags, bodySnippet)
|
|
return err
|
|
}
|
|
|
|
func (s *SQLBannerStore) InsertSSHBanner(ip string, listening bool, firstline string) error {
|
|
_, err := s.insertSSH.Exec(ip, listening, firstline)
|
|
return err
|
|
}
|
|
|
|
func (s *SQLBannerStore) InsertTelnetBanner(ip string, listening bool, firstline, fullbanner string) error {
|
|
_, err := s.insertTelnet.Exec(ip, listening, firstline, fullbanner)
|
|
return err
|
|
}
|
|
|
|
var (
|
|
db *sql.DB
|
|
defaultFile = "zmaplist.txt"
|
|
store BannerStore
|
|
excluded []ipRange
|
|
allowed []ipRange
|
|
)
|
|
|
|
const red = "\033[31m"
|
|
const reset = "\033[0m"
|
|
const green = "\033[32m"
|
|
const blue = "\033[34m"
|
|
|
|
type ipRange struct {
|
|
start uint32
|
|
end uint32
|
|
}
|
|
|
|
func ipToUint32(ip net.IP) uint32 {
|
|
ip = ip.To4()
|
|
return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3])
|
|
}
|
|
|
|
func uint32ToIP(n uint32) net.IP {
|
|
return net.IPv4(
|
|
byte(n>>24),
|
|
byte(n>>16),
|
|
byte(n>>8),
|
|
byte(n),
|
|
)
|
|
}
|
|
func parseCIDRRange(cidr string) ipRange {
|
|
_, network, _ := net.ParseCIDR(cidr)
|
|
start := ipToUint32(network.IP)
|
|
mask := ipToUint32(net.IP(network.Mask))
|
|
hostBits := ^mask
|
|
end := start | hostBits
|
|
return ipRange{start, end}
|
|
}
|
|
func main() {
|
|
blocks := []string{
|
|
"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
|
|
"127.0.0.0/8", "169.254.0.0/16", "100.64.0.0/10",
|
|
"192.0.0.0/24", "192.0.2.0/24", "192.88.99.0/24",
|
|
"198.18.0.0/15", "198.51.100.0/24", "203.0.113.0/24",
|
|
"224.0.0.0/4", "240.0.0.0/4", "255.255.255.255/32",
|
|
// US Gov / DoD legacy
|
|
"6.0.0.0/8", "7.0.0.0/8", "11.0.0.0/8",
|
|
"21.0.0.0/8", "22.0.0.0/8", "26.0.0.0/8",
|
|
"28.0.0.0/8", "29.0.0.0/8", "30.0.0.0/8",
|
|
"33.0.0.0/8", "55.0.0.0/8",
|
|
}
|
|
for _, b := range blocks {
|
|
excluded = append(excluded, parseCIDRRange(b))
|
|
}
|
|
for first := 1; first <= 255; first++ {
|
|
cidr := fmt.Sprintf("%d.0.0.0/8", first)
|
|
r := parseCIDRRange(cidr)
|
|
skip := false
|
|
for _, ex := range excluded {
|
|
if r.start >= ex.start && r.start <= ex.end {
|
|
skip = true
|
|
break
|
|
}
|
|
}
|
|
if !skip {
|
|
allowed = append(allowed, r)
|
|
}
|
|
}
|
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
cfg, err := loadConfig("config.json")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
switch cfg.DB.DbDriver {
|
|
case "mysql":
|
|
store, err = sqlInitMySQL(fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s", cfg.DB.Username, cfg.DB.Password, cfg.DB.DBName))
|
|
case "sqlite":
|
|
store, err = sqlInitSQLite(cfg.DB.DBName)
|
|
default:
|
|
log.Fatalf("Unsupported driver: %s", cfg.DB.DbDriver)
|
|
}
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println("Starting IP scan")
|
|
ConcurrentScan(defaultFile, cfg)
|
|
fmt.Println("Finished IP scan!")
|
|
return
|
|
}
|
|
|
|
func sqlInitMySQL(connargs string) (*SQLBannerStore, error) {
|
|
// open connection
|
|
var err error
|
|
db, err = sql.Open("mysql", connargs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// create tables
|
|
tables := []string{
|
|
`CREATE TABLE IF NOT EXISTS httpbanners (
|
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
|
Ip VARCHAR(255) NOT NULL UNIQUE,
|
|
Scanned_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
IsListening TINYINT(1) NOT NULL,
|
|
httptitle TEXT,
|
|
Httpresp TEXT,
|
|
Headers TEXT,
|
|
Error TEXT,
|
|
ServerHeader TEXT,
|
|
ContentType TEXT,
|
|
ContentLength TEXT,
|
|
Redirect TEXT,
|
|
MetaTags TEXT,
|
|
BodySnippet TEXT
|
|
)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS sshbanners (
|
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
|
Ip VARCHAR(255) NOT NULL UNIQUE,
|
|
scanned_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
IsListening TINYINT(1) NOT NULL,
|
|
firstline TEXT
|
|
)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS telnetbanners (
|
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
|
Ip VARCHAR(255) NOT NULL UNIQUE,
|
|
Scanned_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
IsListening TINYINT(1) NOT NULL,
|
|
firstline TEXT,
|
|
fullbanner TEXT
|
|
)`,
|
|
}
|
|
|
|
for _, stmt := range tables {
|
|
if _, err := db.Exec(stmt); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// prepare statements
|
|
httpStmt, err := db.Prepare(`
|
|
INSERT INTO httpbanners
|
|
(Ip, IsListening, httptitle, Httpresp, Error, Headers,
|
|
ServerHeader, ContentType, ContentLength, Redirect, MetaTags, BodySnippet)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
IsListening=VALUES(IsListening),
|
|
httptitle=VALUES(httptitle),
|
|
Httpresp=VALUES(Httpresp),
|
|
Error=VALUES(Error),
|
|
Headers=VALUES(Headers),
|
|
ServerHeader=VALUES(ServerHeader),
|
|
ContentType=VALUES(ContentType),
|
|
ContentLength=VALUES(ContentLength),
|
|
Redirect=VALUES(Redirect),
|
|
MetaTags=VALUES(MetaTags),
|
|
BodySnippet=VALUES(BodySnippet)
|
|
`)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sshStmt, err := db.Prepare(`
|
|
INSERT INTO sshbanners (Ip, IsListening, firstline)
|
|
VALUES (?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
IsListening=VALUES(IsListening),
|
|
firstline=VALUES(firstline)
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
telnetStmt, err := db.Prepare(`
|
|
INSERT INTO telnetbanners (Ip, IsListening, firstline, fullbanner)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
IsListening=VALUES(IsListening),
|
|
firstline=VALUES(firstline),
|
|
fullbanner=VALUES(fullbanner)
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &SQLBannerStore{
|
|
db: db,
|
|
insertHTTP: httpStmt,
|
|
insertSSH: sshStmt,
|
|
insertTelnet: telnetStmt,
|
|
}, nil
|
|
}
|
|
|
|
func sqlInitSQLite(dbname string) (*SQLBannerStore, error) {
|
|
db, err := sql.Open("sqlite3", dbname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tables := []string{
|
|
`CREATE TABLE IF NOT EXISTS httpbanners (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
Ip TEXT NOT NULL UNIQUE,
|
|
Scanned_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
IsListening INTEGER NOT NULL,
|
|
httptitle TEXT,
|
|
Httpresp TEXT,
|
|
Headers TEXT,
|
|
Error TEXT,
|
|
ServerHeader TEXT,
|
|
ContentType TEXT,
|
|
ContentLength TEXT,
|
|
Redirect TEXT,
|
|
MetaTags TEXT,
|
|
BodySnippet TEXT
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS sshbanners (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
Ip TEXT NOT NULL UNIQUE,
|
|
Scanned_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
IsListening INTEGER NOT NULL,
|
|
firstline TEXT
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS telnetbanners (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
Ip TEXT NOT NULL UNIQUE,
|
|
Scanned_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
IsListening INTEGER NOT NULL,
|
|
firstline TEXT,
|
|
fullbanner TEXT
|
|
)`,
|
|
}
|
|
|
|
for _, stmt := range tables {
|
|
if _, err := db.Exec(stmt); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
httpStmt, err := db.Prepare(`
|
|
INSERT INTO httpbanners
|
|
(Ip, IsListening, httptitle, Httpresp, Error, Headers,
|
|
ServerHeader, ContentType, ContentLength, Redirect, MetaTags, BodySnippet)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON CONFLICT(Ip) DO UPDATE SET
|
|
IsListening=excluded.IsListening,
|
|
httptitle=excluded.httptitle,
|
|
Httpresp=excluded.Httpresp,
|
|
Error=excluded.Error,
|
|
Headers=excluded.Headers,
|
|
ServerHeader=excluded.ServerHeader,
|
|
ContentType=excluded.ContentType,
|
|
ContentLength=excluded.ContentLength,
|
|
Redirect=excluded.Redirect,
|
|
MetaTags=excluded.MetaTags,
|
|
BodySnippet=excluded.BodySnippet
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sshStmt, err := db.Prepare(`
|
|
INSERT INTO sshbanners (Ip, IsListening, firstline)
|
|
VALUES (?, ?, ?)
|
|
ON CONFLICT(Ip) DO UPDATE SET
|
|
IsListening=excluded.IsListening,
|
|
firstline=excluded.firstline
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
telnetStmt, err := db.Prepare(`
|
|
INSERT INTO telnetbanners (Ip, IsListening, firstline, fullbanner)
|
|
VALUES (?, ?, ?, ?)
|
|
ON CONFLICT(Ip) DO UPDATE SET
|
|
IsListening=excluded.IsListening,
|
|
firstline=excluded.firstline,
|
|
fullbanner=excluded.fullbanner
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &SQLBannerStore{
|
|
db: db,
|
|
insertHTTP: httpStmt,
|
|
insertSSH: sshStmt,
|
|
insertTelnet: telnetStmt,
|
|
}, nil
|
|
}
|
|
func isPrivateIP(ip net.IP) bool { // blacklisted CIDR ranges from being scanned, you can also put your own IPs, a long as its in CIDR notation
|
|
var privateBlocks = []string{
|
|
"10.0.0.0/8", // RFC1918 private
|
|
"172.16.0.0/12", // RFC1918 private
|
|
"192.168.0.0/16", // RFC1918 private
|
|
"127.0.0.0/8", // loopback
|
|
"169.254.0.0/16", // link-local
|
|
"100.64.0.0/10", // carrier-grade NAT
|
|
"192.0.0.0/24", // IANA special use
|
|
"192.0.2.0/24", // TEST-NET-1
|
|
"192.88.99.0/24", // deprecated 6to4 relay
|
|
"198.18.0.0/15", // benchmarking
|
|
"198.51.100.0/24", // TEST-NET-2
|
|
"203.0.113.0/24", // TEST-NET-3
|
|
"224.0.0.0/4", // multicast
|
|
"240.0.0.0/4", // reserved
|
|
"255.255.255.255/32", // broadcast
|
|
}
|
|
|
|
// US Government / DoD legacy ranges (policy exclusions)
|
|
var govBlocks = []string{
|
|
"6.0.0.0/8",
|
|
"7.0.0.0/8",
|
|
"11.0.0.0/8",
|
|
"21.0.0.0/8",
|
|
"22.0.0.0/8",
|
|
"26.0.0.0/8",
|
|
"28.0.0.0/8",
|
|
"29.0.0.0/8",
|
|
"30.0.0.0/8",
|
|
"33.0.0.0/8",
|
|
"55.0.0.0/8",
|
|
}
|
|
for _, block := range append(privateBlocks, govBlocks...) {
|
|
_, cidr, _ := net.ParseCIDR(block)
|
|
if cidr.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
func generateRandomPublicIP() string {
|
|
r := allowed[rand.Intn(len(allowed))]
|
|
ip := r.start + uint32(rand.Intn(int(r.end-r.start+1)))
|
|
return uint32ToIP(ip).String()
|
|
}
|
|
func grabbanner(ip string) {
|
|
ports := []int{80, 8080, 8088}
|
|
client := http.Client{Timeout: 1500 * time.Millisecond}
|
|
|
|
for _, port := range ports {
|
|
url := fmt.Sprintf("http://%s:%d", ip, port)
|
|
resp, err := client.Get(url)
|
|
if err != nil {
|
|
//store.InsertHTTPBanner(fmt.Sprintf("%s:%d", ip, port),false, "", "", err.Error(), "", "", "", "", "", "", "")
|
|
continue
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Limit body size to avoid huge pages
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 8192))
|
|
if err != nil {
|
|
store.InsertHTTPBanner(fmt.Sprintf("%s:%d", ip, port), true, "", "", err.Error(), "", "", "", "", "", "", "")
|
|
continue
|
|
}
|
|
|
|
htmlStr := string(body)
|
|
title := extractTitle(htmlStr)
|
|
metas := extractMeta(htmlStr)
|
|
|
|
// Collect headers
|
|
var sb strings.Builder
|
|
for k, v := range resp.Header {
|
|
sb.WriteString(fmt.Sprintf("%s: %s\n", k, strings.Join(v, ", ")))
|
|
}
|
|
headers := sb.String()
|
|
|
|
// Extra metadata
|
|
server := resp.Header.Get("Server")
|
|
contentType := resp.Header.Get("Content-Type")
|
|
contentLength := resp.Header.Get("Content-Length")
|
|
redirect := resp.Header.Get("Location")
|
|
|
|
// Truncate body snippet
|
|
snippet := htmlStr
|
|
if len(snippet) > 2000 {
|
|
snippet = snippet[:2000]
|
|
}
|
|
|
|
fmt.Println(green+"[ HTTP BANNER ] target found", url, reset)
|
|
store.InsertHTTPBanner(fmt.Sprintf("%s:%d", ip, port), true,
|
|
title,
|
|
fmt.Sprintf("%d %s", resp.StatusCode, resp.Status),
|
|
"", headers,
|
|
server, contentType, contentLength, redirect,
|
|
fmt.Sprint(metas), snippet)
|
|
}
|
|
}
|
|
|
|
func extractTitle(htmlStr string) string { // finally it works..except im trusting the go devs html package for this
|
|
doc, err := html.Parse(strings.NewReader(htmlStr))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
var f func(*html.Node) string
|
|
f = func(n *html.Node) string {
|
|
if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil {
|
|
return n.FirstChild.Data
|
|
}
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
if t := f(c); t != "" {
|
|
return t
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
return f(doc)
|
|
}
|
|
func extractMeta(htmlStr string) map[string]string {
|
|
metas := make(map[string]string)
|
|
doc, err := html.Parse(strings.NewReader(htmlStr))
|
|
if err != nil {
|
|
return metas
|
|
}
|
|
var f func(*html.Node)
|
|
f = func(n *html.Node) {
|
|
if n.Type == html.ElementNode && n.Data == "meta" {
|
|
var name, content string
|
|
for _, attr := range n.Attr {
|
|
if attr.Key == "name" {
|
|
name = attr.Val
|
|
}
|
|
if attr.Key == "content" {
|
|
content = attr.Val
|
|
}
|
|
}
|
|
if name != "" {
|
|
metas[name] = content
|
|
}
|
|
}
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
f(c)
|
|
}
|
|
}
|
|
f(doc)
|
|
return metas
|
|
}
|
|
func startGenerators(n int, httpJobs, sshJobs, telnetJobs chan string) {
|
|
for i := 0; i < n; i++ {
|
|
go func(seed int64) {
|
|
rand.Seed(time.Now().UnixNano() + seed)
|
|
for {
|
|
ip := generateRandomPublicIP()
|
|
select {
|
|
case httpJobs <- ip:
|
|
default:
|
|
}
|
|
select {
|
|
case sshJobs <- ip:
|
|
default:
|
|
}
|
|
select {
|
|
case telnetJobs <- ip:
|
|
default:
|
|
// meh..
|
|
}
|
|
}
|
|
}(int64(i))
|
|
}
|
|
}
|
|
|
|
func ConcurrentScan(filename string, conf *Config) { // the big main loop
|
|
httpJobs := make(chan string, 5700)
|
|
telnetJobs := make(chan string, 5000)
|
|
sshJobs := make(chan string, 5000)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
var httpCount, sshCount, telnetCount uint64
|
|
// helper to spawn workers
|
|
spawn := func(n int, jobs chan string, fn func(string), counter *uint64) {
|
|
for w := 0; w < n; w++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for ip := range jobs {
|
|
fn(ip)
|
|
atomic.AddUint64(counter, 1)
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
// spawn workers
|
|
spawn(340, httpJobs, grabbanner, &httpCount)
|
|
spawn(150, sshJobs, func(ip string) { BannerSSH(fmt.Sprintf("%s:22", ip)) }, &sshCount)
|
|
spawn(150, telnetJobs, BannerTelnet, &telnetCount)
|
|
// feed ZMap file if present
|
|
file, err := os.Open(filename)
|
|
if err == nil {
|
|
scanner := bufio.NewScanner(file)
|
|
fed := 0
|
|
for scanner.Scan() {
|
|
ip := strings.TrimSpace(scanner.Text())
|
|
if ip == "" {
|
|
continue
|
|
}
|
|
httpJobs <- ip
|
|
telnetJobs <- ip
|
|
sshJobs <- ip
|
|
fed++
|
|
}
|
|
file.Close()
|
|
if fed > 0 {
|
|
fmt.Printf("Fed %d IPs from ZMap file\n", fed)
|
|
} else {
|
|
fmt.Println("ZMap file contained no usable IPs")
|
|
}
|
|
} else {
|
|
fmt.Println("No ZMap file found")
|
|
}
|
|
go startGenerators(conf.Channelfeeders, httpJobs, sshJobs, telnetJobs) // parallel feeders
|
|
go func() {
|
|
ticker := time.NewTicker(20 * time.Second)
|
|
defer ticker.Stop()
|
|
for range ticker.C {
|
|
fmt.Printf("[STATUS] HTTP:%d SSH:%d TELNET:%d | Queues H:%d S:%d T:%d\n",
|
|
atomic.LoadUint64(&httpCount),
|
|
atomic.LoadUint64(&sshCount),
|
|
atomic.LoadUint64(&telnetCount),
|
|
len(httpJobs), len(sshJobs), len(telnetJobs))
|
|
|
|
}
|
|
}()
|
|
wg.Wait()
|
|
}
|
|
|
|
func BannerTelnet(ip string) {
|
|
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:23", ip), 1*time.Second)
|
|
if err != nil {
|
|
// host not listening
|
|
// store.InsertTelnetBanner(ip, false, "", "")
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
conn.SetReadDeadline(time.Now().Add(4 * time.Second))
|
|
|
|
buf := make([]byte, 2048)
|
|
var sb bytes.Buffer
|
|
for {
|
|
n, err := conn.Read(buf)
|
|
if err != nil {
|
|
break
|
|
}
|
|
sb.Write(buf[:n])
|
|
}
|
|
|
|
clean := stripTelnetControls(sb.Bytes())
|
|
finalbanner := strings.TrimSpace(string(clean))
|
|
firstLine := strings.SplitN(finalbanner, "\n", 2)[0]
|
|
|
|
fmt.Println(blue+"[TELNET SCANNER] HOST FOUND", ip, reset)
|
|
store.InsertTelnetBanner(ip, true, firstLine, finalbanner)
|
|
}
|
|
|
|
func BannerSSH(ip string) {
|
|
conn, err := net.DialTimeout("tcp", ip, 1500*time.Millisecond)
|
|
if err != nil {
|
|
// host not listening
|
|
//store.InsertSSHBanner(ip, false, "")
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
reader := bufio.NewReader(conn)
|
|
banner, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
// connection established but banner read failed
|
|
//store.InsertSSHBanner(ip, true, "")
|
|
return
|
|
}
|
|
|
|
// success: got a banner line
|
|
store.InsertSSHBanner(ip, true, banner)
|
|
}
|
|
|
|
// StripTelnetControls used to remove the binary data from telnet connections to extract strings
|
|
func stripTelnetControls(data []byte) []byte { // the ANNOYING IAC CODES
|
|
out := make([]byte, 0, len(data))
|
|
i := 0
|
|
for i < len(data) {
|
|
if data[i] == 0xFF && i+2 < len(data) {
|
|
i += 3
|
|
continue
|
|
}
|
|
out = append(out, data[i])
|
|
i++
|
|
}
|
|
return out
|
|
}
|