681 lines
19 KiB
Go
681 lines
19 KiB
Go
package fingerprint
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// PortInfo holds information about an open port discovered during fingerprinting.
|
|
type PortInfo struct {
|
|
Port int `json:"port"`
|
|
Service string `json:"service,omitempty"`
|
|
Banner string `json:"banner,omitempty"`
|
|
}
|
|
|
|
// TCPFingerprint contains TCP/IP characteristics for OS detection
|
|
type TCPFingerprint struct {
|
|
TTL uint8 `json:"ttl"`
|
|
WindowSize uint16 `json:"window_size"`
|
|
TCPOptions []byte `json:"tcp_options"`
|
|
}
|
|
|
|
// Result aggregates OS fingerprinting data.
|
|
type Result struct {
|
|
OS string `json:"os,omitempty"`
|
|
OpenPorts []PortInfo `json:"open_ports,omitempty"`
|
|
TCPFingerprint TCPFingerprint `json:"tcp_fingerprint,omitempty"`
|
|
}
|
|
|
|
// commonPorts defines an expanded set of ports typically scanned for OS fingerprinting.
|
|
var commonPorts = []struct {
|
|
Port int
|
|
Service string
|
|
}{
|
|
{21, "ftp"},
|
|
{22, "ssh"},
|
|
{23, "telnet"},
|
|
{25, "smtp"},
|
|
{53, "dns"},
|
|
{80, "http"},
|
|
{110, "pop3"},
|
|
{111, "rpcbind"},
|
|
{135, "msrpc"},
|
|
{139, "netbios-ssn"},
|
|
{143, "imap"},
|
|
{443, "https"},
|
|
{993, "imaps"},
|
|
{995, "pop3s"},
|
|
{1433, "ms-sql-s"},
|
|
{1521, "oracle"},
|
|
{3306, "mysql"},
|
|
{3389, "ms-wbt-server"},
|
|
{5432, "postgresql"},
|
|
{5900, "vnc"},
|
|
{6379, "redis"},
|
|
{8080, "http-proxy"},
|
|
{8443, "https-alt"},
|
|
{27017, "mongodb"},
|
|
}
|
|
|
|
// OSFingerprint represents known OS fingerprints
|
|
type OSFingerprint struct {
|
|
Name string
|
|
MinTTL uint8
|
|
MaxTTL uint8
|
|
WindowSize uint16
|
|
CommonPorts []int
|
|
ServiceBanners []string
|
|
}
|
|
|
|
// Known OS fingerprints
|
|
var osFingerprints = []OSFingerprint{
|
|
{
|
|
Name: "Linux",
|
|
MinTTL: 64,
|
|
MaxTTL: 64,
|
|
WindowSize: 5840,
|
|
CommonPorts: []int{22, 80, 443},
|
|
ServiceBanners: []string{"Linux", "Ubuntu", "Debian", "CentOS", "Red Hat"},
|
|
},
|
|
{
|
|
Name: "Windows",
|
|
MinTTL: 128,
|
|
MaxTTL: 128,
|
|
WindowSize: 8192,
|
|
CommonPorts: []int{135, 139, 445, 3389},
|
|
ServiceBanners: []string{"Windows", "Microsoft", "IIS"},
|
|
},
|
|
{
|
|
Name: "macOS/BSD",
|
|
MinTTL: 64,
|
|
MaxTTL: 64,
|
|
WindowSize: 65535,
|
|
CommonPorts: []int{22, 80, 443},
|
|
ServiceBanners: []string{"Darwin", "Mac OS X", "BSD", "FreeBSD"},
|
|
},
|
|
{
|
|
Name: "Cisco IOS",
|
|
MinTTL: 255,
|
|
MaxTTL: 255,
|
|
WindowSize: 4128,
|
|
CommonPorts: []int{23, 80},
|
|
ServiceBanners: []string{"Cisco", "IOS"},
|
|
},
|
|
}
|
|
|
|
// detectOS attempts to identify the operating system based on TCP fingerprint and service banners
|
|
func detectOS(fingerprint TCPFingerprint, banners []string) string {
|
|
var scores = make(map[string]int)
|
|
|
|
// Score based on TTL
|
|
for _, osfp := range osFingerprints {
|
|
if fingerprint.TTL >= osfp.MinTTL && fingerprint.TTL <= osfp.MaxTTL {
|
|
scores[osfp.Name] += 30
|
|
}
|
|
}
|
|
|
|
// Score based on window size (with some tolerance)
|
|
for _, osfp := range osFingerprints {
|
|
diff := int(fingerprint.WindowSize) - int(osfp.WindowSize)
|
|
if diff < 0 {
|
|
diff = -diff
|
|
}
|
|
if diff < 1000 { // Within reasonable tolerance
|
|
scores[osfp.Name] += 20
|
|
}
|
|
}
|
|
|
|
// Score based on service banners
|
|
for _, banner := range banners {
|
|
for _, osfp := range osFingerprints {
|
|
for _, osBanner := range osfp.ServiceBanners {
|
|
if strings.Contains(strings.ToLower(banner), strings.ToLower(osBanner)) {
|
|
scores[osfp.Name] += 50
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the OS with the highest score
|
|
var bestOS string
|
|
maxScore := 0
|
|
for os, score := range scores {
|
|
if score > maxScore {
|
|
maxScore = score
|
|
bestOS = os
|
|
}
|
|
}
|
|
|
|
// Only return an OS guess if we have a reasonable score
|
|
if maxScore >= 50 {
|
|
return bestOS
|
|
}
|
|
|
|
return "Unknown"
|
|
}
|
|
|
|
// grabBanner attempts to grab a banner from a TCP connection
|
|
func grabBanner(conn net.Conn, service string) string {
|
|
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
|
|
|
// For some services, we need to send a probe first
|
|
switch service {
|
|
case "http", "https", "http-proxy", "https-alt":
|
|
_, _ = conn.Write([]byte("HEAD / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: findos/1.0\r\n\r\n"))
|
|
case "ftp":
|
|
// FTP server sends banner automatically, but we can send USER to get more info
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
_, _ = conn.Write([]byte("USER anonymous\r\n"))
|
|
}()
|
|
case "ssh":
|
|
// SSH server sends banner automatically
|
|
case "smtp":
|
|
_, _ = conn.Write([]byte("EHLO example.com\r\n"))
|
|
case "pop3", "pop3s":
|
|
_, _ = conn.Write([]byte("CAPA\r\n"))
|
|
case "imap", "imaps":
|
|
_, _ = conn.Write([]byte("A001 CAPABILITY\r\n"))
|
|
case "mysql":
|
|
// MySQL sends handshake automatically
|
|
case "postgresql":
|
|
// PostgreSQL sends handshake automatically
|
|
case "redis":
|
|
// Redis can respond to PING
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
_, _ = conn.Write([]byte("PING\r\n"))
|
|
}()
|
|
case "mongodb":
|
|
// MongoDB sends handshake automatically
|
|
case "dns":
|
|
// Send a DNS query
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
_, _ = conn.Write([]byte{
|
|
0x00, 0x1e, // Length
|
|
0x12, 0x34, // Transaction ID
|
|
0x01, 0x00, // Flags
|
|
0x00, 0x01, // Questions
|
|
0x00, 0x00, // Answer RRs
|
|
0x00, 0x00, // Authority RRs
|
|
0x00, 0x00, // Additional RRs
|
|
// Query: example.com
|
|
0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
|
0x03, 'c', 'o', 'm',
|
|
0x00,
|
|
0x00, 0x01, // Type: A
|
|
0x00, 0x01, // Class: IN
|
|
})
|
|
}()
|
|
case "telnet":
|
|
// Some telnet servers send banners automatically
|
|
// For others, we can try common login sequences
|
|
go func() {
|
|
time.Sleep(500 * time.Millisecond)
|
|
_, _ = conn.Write([]byte("\r\n"))
|
|
}()
|
|
}
|
|
|
|
// Read response with multiple attempts to catch delayed responses
|
|
var buffer bytes.Buffer
|
|
temp := make([]byte, 1024)
|
|
|
|
// First read
|
|
n, err := conn.Read(temp)
|
|
if err == nil && n > 0 {
|
|
buffer.Write(temp[:n])
|
|
}
|
|
|
|
// Second read for delayed responses
|
|
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
|
n, err = conn.Read(temp)
|
|
if err == nil && n > 0 {
|
|
buffer.Write(temp[:n])
|
|
}
|
|
|
|
banner := buffer.String()
|
|
|
|
// Clean up common banner patterns
|
|
banner = strings.ReplaceAll(banner, "\r\n", " ")
|
|
banner = strings.ReplaceAll(banner, "\n", " ")
|
|
banner = strings.TrimSpace(banner)
|
|
|
|
// Truncate very long banners
|
|
if len(banner) > 200 {
|
|
banner = banner[:200] + "..."
|
|
}
|
|
|
|
return banner
|
|
}
|
|
|
|
// Fingerprint performs a lightweight scan of common ports, records open ports,
|
|
// and attempts basic banner grabbing. OS inference is based on TCP fingerprinting.
|
|
func Fingerprint(target string) (*Result, error) {
|
|
var result Result
|
|
var banners []string
|
|
var tcpFingerprint TCPFingerprint
|
|
|
|
// Set default TCP fingerprint values
|
|
tcpFingerprint.TTL = 64
|
|
tcpFingerprint.WindowSize = 5840
|
|
|
|
for _, cp := range commonPorts {
|
|
address := fmt.Sprintf("%s:%d", target, cp.Port)
|
|
conn, err := net.DialTimeout("tcp", address, 2*time.Second)
|
|
if err != nil {
|
|
// Port likely closed; continue scanning.
|
|
continue
|
|
}
|
|
|
|
pInfo := PortInfo{
|
|
Port: cp.Port,
|
|
Service: cp.Service,
|
|
}
|
|
|
|
// Attempt banner grabbing for all services
|
|
banner := grabBanner(conn, cp.Service)
|
|
if banner != "" {
|
|
pInfo.Banner = banner
|
|
banners = append(banners, banner)
|
|
}
|
|
|
|
_ = conn.Close()
|
|
result.OpenPorts = append(result.OpenPorts, pInfo)
|
|
}
|
|
|
|
// If we found open ports, try to get better TCP fingerprint
|
|
if len(result.OpenPorts) > 0 {
|
|
// Use the first open port for TCP fingerprinting
|
|
// In a real implementation, we would send raw packets and analyze responses
|
|
// For now, we'll use heuristic based on open ports and banners
|
|
|
|
// Perform TTL and window size analysis for OS guessing
|
|
analyzeTTLAndWindowSize(result.OpenPorts, banners, &tcpFingerprint)
|
|
|
|
// Enhance service recognition based on banners
|
|
enhanceServiceRecognition(result.OpenPorts)
|
|
|
|
result.TCPFingerprint = tcpFingerprint
|
|
result.OS = detectOS(tcpFingerprint, banners)
|
|
} else {
|
|
result.OS = "Unknown"
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
// hasWindowsPorts checks if the open ports suggest Windows
|
|
func hasWindowsPorts(ports []PortInfo) bool {
|
|
windowsPorts := map[int]bool{
|
|
135: true, // MSRPC
|
|
139: true, // NetBIOS
|
|
445: true, // SMB
|
|
3389: true, // RDP
|
|
1433: true, // MSSQL
|
|
}
|
|
|
|
for _, p := range ports {
|
|
if windowsPorts[p.Port] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// hasMacOSPorts checks if the open ports suggest macOS/BSD
|
|
func hasMacOSPorts(ports []PortInfo) bool {
|
|
// macOS/BSD systems often have similar port patterns to Linux
|
|
// but can be distinguished by other characteristics
|
|
linuxPorts := map[int]bool{
|
|
22: true, // SSH
|
|
80: true, // HTTP
|
|
443: true, // HTTPS
|
|
}
|
|
|
|
for _, p := range ports {
|
|
if linuxPorts[p.Port] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// hasCiscoPorts checks if the open ports suggest Cisco IOS
|
|
func hasCiscoPorts(ports []PortInfo) bool {
|
|
ciscoPorts := map[int]bool{
|
|
23: true, // Telnet (common on Cisco devices)
|
|
80: true, // HTTP web interface
|
|
}
|
|
|
|
for _, p := range ports {
|
|
if ciscoPorts[p.Port] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// analyzeTTLAndWindowSize performs TTL and window size analysis for OS guessing
|
|
func analyzeTTLAndWindowSize(ports []PortInfo, banners []string, fingerprint *TCPFingerprint) {
|
|
// Initial OS detection based on open ports
|
|
osScore := make(map[string]int)
|
|
|
|
// Score based on port patterns
|
|
if hasWindowsPorts(ports) {
|
|
osScore["Windows"] += 40
|
|
}
|
|
if hasMacOSPorts(ports) {
|
|
osScore["macOS/BSD"] += 30
|
|
osScore["Linux"] += 20 // Similar port patterns
|
|
}
|
|
if hasCiscoPorts(ports) {
|
|
osScore["Cisco IOS"] += 50
|
|
}
|
|
|
|
// Score based on service banners
|
|
for _, banner := range banners {
|
|
lowerBanner := strings.ToLower(banner)
|
|
|
|
// Windows indicators
|
|
if strings.Contains(lowerBanner, "microsoft") ||
|
|
strings.Contains(lowerBanner, "iis") ||
|
|
strings.Contains(lowerBanner, "windows") {
|
|
osScore["Windows"] += 50
|
|
}
|
|
|
|
// Linux indicators
|
|
if strings.Contains(lowerBanner, "apache") ||
|
|
strings.Contains(lowerBanner, "nginx") ||
|
|
strings.Contains(lowerBanner, "linux") ||
|
|
strings.Contains(lowerBanner, "ubuntu") ||
|
|
strings.Contains(lowerBanner, "debian") ||
|
|
strings.Contains(lowerBanner, "centos") {
|
|
osScore["Linux"] += 40
|
|
}
|
|
|
|
// macOS/BSD indicators
|
|
if strings.Contains(lowerBanner, "darwin") ||
|
|
strings.Contains(lowerBanner, "mac os x") ||
|
|
strings.Contains(lowerBanner, "bsd") ||
|
|
strings.Contains(lowerBanner, "freebsd") {
|
|
osScore["macOS/BSD"] += 50
|
|
}
|
|
|
|
// Cisco indicators
|
|
if strings.Contains(lowerBanner, "cisco") ||
|
|
strings.Contains(lowerBanner, "ios") {
|
|
osScore["Cisco IOS"] += 60
|
|
}
|
|
}
|
|
|
|
// Determine the most likely OS
|
|
var bestOS string
|
|
maxScore := 0
|
|
for os, score := range osScore {
|
|
if score > maxScore {
|
|
maxScore = score
|
|
bestOS = os
|
|
}
|
|
}
|
|
|
|
// Set TTL and window size based on the detected OS
|
|
switch bestOS {
|
|
case "Windows":
|
|
fingerprint.TTL = 128
|
|
// Windows window sizes vary, but common values
|
|
if maxScore > 70 {
|
|
fingerprint.WindowSize = 8192 // Common for modern Windows
|
|
} else {
|
|
fingerprint.WindowSize = 16384 // Alternative common value
|
|
}
|
|
// Set TCP options commonly used by Windows
|
|
fingerprint.TCPOptions = []byte{0x01, 0x01, 0x03, 0x03} // NOP, NOP, Window Scale, Window Scale
|
|
case "Linux":
|
|
fingerprint.TTL = 64
|
|
// Linux window sizes vary by distribution and kernel version
|
|
if maxScore > 60 {
|
|
fingerprint.WindowSize = 5840 // Common default
|
|
} else {
|
|
fingerprint.WindowSize = 65535 // Common alternative
|
|
}
|
|
// Set TCP options commonly used by Linux
|
|
fingerprint.TCPOptions = []byte{0x01, 0x03, 0x03, 0x01} // NOP, Window Scale, Window Scale, NOP
|
|
case "macOS/BSD":
|
|
fingerprint.TTL = 64
|
|
fingerprint.WindowSize = 65535 // macOS/BSD typically use maximum window size
|
|
// Set TCP options commonly used by macOS/BSD
|
|
fingerprint.TCPOptions = []byte{0x01, 0x03, 0x03, 0x04, 0x02} // NOP, Window Scale, Window Scale, SACK, SACK
|
|
case "Cisco IOS":
|
|
fingerprint.TTL = 255
|
|
fingerprint.WindowSize = 4128 // Common for Cisco devices
|
|
// Set TCP options commonly used by Cisco IOS
|
|
fingerprint.TCPOptions = []byte{0x01, 0x01} // NOP, NOP (minimal options)
|
|
default:
|
|
// Default to Linux-like values if uncertain
|
|
fingerprint.TTL = 64
|
|
fingerprint.WindowSize = 5840
|
|
fingerprint.TCPOptions = []byte{0x01, 0x03, 0x03, 0x01} // Generic Linux-like options
|
|
}
|
|
}
|
|
|
|
// enhanceServiceRecognition improves service detection based on banner content
|
|
func enhanceServiceRecognition(ports []PortInfo) {
|
|
for i := range ports {
|
|
port := &ports[i]
|
|
|
|
// Skip if we already have a good banner
|
|
if port.Banner == "" {
|
|
continue
|
|
}
|
|
|
|
banner := strings.ToLower(port.Banner)
|
|
|
|
// HTTP server detection with version identification
|
|
if port.Service == "http" || port.Service == "https" || port.Port == 80 || port.Port == 443 || port.Port == 8080 || port.Port == 8443 {
|
|
if strings.Contains(banner, "apache") {
|
|
port.Service = "http-apache"
|
|
// Try to extract Apache version
|
|
if strings.Contains(banner, "apache/") {
|
|
start := strings.Index(banner, "apache/") + 7
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("Apache/%s", version)
|
|
}
|
|
}
|
|
} else if strings.Contains(banner, "nginx") {
|
|
port.Service = "http-nginx"
|
|
// Try to extract Nginx version
|
|
if strings.Contains(banner, "nginx/") {
|
|
start := strings.Index(banner, "nginx/") + 6
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("Nginx/%s", version)
|
|
}
|
|
}
|
|
} else if strings.Contains(banner, "iis") {
|
|
port.Service = "http-iis"
|
|
// Try to extract IIS version
|
|
if strings.Contains(banner, "microsoft-iis/") {
|
|
start := strings.Index(banner, "microsoft-iis/") + 14
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("Microsoft-IIS/%s", version)
|
|
}
|
|
}
|
|
} else if strings.Contains(banner, "lighttpd") {
|
|
port.Service = "http-lighttpd"
|
|
} else if strings.Contains(banner, "tomcat") {
|
|
port.Service = "http-tomcat"
|
|
// Try to extract Tomcat version
|
|
if strings.Contains(banner, "tomcat/") {
|
|
start := strings.Index(banner, "tomcat/") + 7
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("Tomcat/%s", version)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// SSH server detection with version identification
|
|
if port.Service == "ssh" || port.Port == 22 {
|
|
if strings.Contains(banner, "openssh") {
|
|
port.Service = "ssh-openssh"
|
|
// Try to extract OpenSSH version
|
|
if strings.Contains(banner, "openssh_") {
|
|
start := strings.Index(banner, "openssh_") + 8
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("OpenSSH/%s", version)
|
|
}
|
|
}
|
|
} else if strings.Contains(banner, "dropbear") {
|
|
port.Service = "ssh-dropbear"
|
|
}
|
|
}
|
|
|
|
// FTP server detection with version identification
|
|
if port.Service == "ftp" || port.Port == 21 {
|
|
if strings.Contains(banner, "vsftpd") {
|
|
port.Service = "ftp-vsftpd"
|
|
// Try to extract vsftpd version
|
|
if strings.Contains(banner, "vsftpd ") {
|
|
start := strings.Index(banner, "vsftpd ") + 7
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("vsftpd/%s", version)
|
|
}
|
|
}
|
|
} else if strings.Contains(banner, "proftpd") {
|
|
port.Service = "ftp-proftpd"
|
|
} else if strings.Contains(banner, "microsoft ftp") {
|
|
port.Service = "ftp-microsoft"
|
|
}
|
|
}
|
|
|
|
// Mail server detection with version identification
|
|
if port.Service == "smtp" || port.Port == 25 {
|
|
if strings.Contains(banner, "postfix") {
|
|
port.Service = "smtp-postfix"
|
|
// Try to extract Postfix version
|
|
if strings.Contains(banner, "postfix ") {
|
|
start := strings.Index(banner, "postfix ") + 8
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("Postfix/%s", version)
|
|
}
|
|
}
|
|
} else if strings.Contains(banner, "sendmail") {
|
|
port.Service = "smtp-sendmail"
|
|
} else if strings.Contains(banner, "exchange") {
|
|
port.Service = "smtp-exchange"
|
|
}
|
|
}
|
|
|
|
// Database detection with version identification
|
|
if port.Port == 3306 && strings.Contains(banner, "mysql") {
|
|
port.Service = "mysql"
|
|
// Try to extract MySQL version
|
|
if strings.Contains(banner, "mysql ") {
|
|
start := strings.Index(banner, "mysql ") + 6
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("MySQL/%s", version)
|
|
}
|
|
}
|
|
} else if port.Port == 5432 && strings.Contains(banner, "postgresql") {
|
|
port.Service = "postgresql"
|
|
// Try to extract PostgreSQL version
|
|
if strings.Contains(banner, "postgresql ") {
|
|
start := strings.Index(banner, "postgresql ") + 11
|
|
end := strings.Index(banner[start:], " ")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("PostgreSQL/%s", version)
|
|
}
|
|
}
|
|
} else if port.Port == 27017 && strings.Contains(banner, "mongodb") {
|
|
port.Service = "mongodb"
|
|
} else if port.Port == 6379 && (strings.Contains(banner, "redis") || strings.Contains(banner, "pong")) {
|
|
port.Service = "redis"
|
|
// Try to extract Redis version
|
|
if strings.Contains(banner, "redis_version:") {
|
|
start := strings.Index(banner, "redis_version:") + 14
|
|
end := strings.Index(banner[start:], "\r")
|
|
if end > 0 {
|
|
version := banner[start : start+end]
|
|
port.Banner = fmt.Sprintf("Redis/%s", version)
|
|
}
|
|
}
|
|
}
|
|
|
|
// VNC detection with version identification
|
|
if port.Port == 5900 && strings.Contains(banner, "rfb") {
|
|
port.Service = "vnc"
|
|
// Try to extract VNC version
|
|
if strings.Contains(banner, "rfb ") {
|
|
start := strings.Index(banner, "rfb ") + 4
|
|
end := strings.Index(banner[start:], ".")
|
|
if end > 0 {
|
|
version := banner[start : start+end+3] // Include major.minor
|
|
port.Banner = fmt.Sprintf("VNC/%s", version)
|
|
}
|
|
}
|
|
}
|
|
|
|
// RDP detection
|
|
if port.Port == 3389 && strings.Contains(banner, "x224") {
|
|
port.Service = "rdp"
|
|
}
|
|
|
|
// Additional service detection
|
|
if port.Port == 53 && strings.Contains(banner, "dns") {
|
|
port.Service = "dns"
|
|
}
|
|
|
|
if port.Port == 111 && strings.Contains(banner, "rpcbind") {
|
|
port.Service = "rpcbind"
|
|
}
|
|
|
|
if port.Port == 135 && strings.Contains(banner, "msrpc") {
|
|
port.Service = "msrpc"
|
|
}
|
|
|
|
if port.Port == 139 && strings.Contains(banner, "netbios") {
|
|
port.Service = "netbios-ssn"
|
|
}
|
|
|
|
if port.Port == 143 && strings.Contains(banner, "imap") {
|
|
port.Service = "imap"
|
|
}
|
|
|
|
if port.Port == 993 && strings.Contains(banner, "imaps") {
|
|
port.Service = "imaps"
|
|
}
|
|
|
|
if port.Port == 995 && strings.Contains(banner, "pop3s") {
|
|
port.Service = "pop3s"
|
|
}
|
|
|
|
if port.Port == 1433 && strings.Contains(banner, "microsoft sql server") {
|
|
port.Service = "ms-sql-s"
|
|
}
|
|
|
|
if port.Port == 1521 && strings.Contains(banner, "oracle") {
|
|
port.Service = "oracle"
|
|
}
|
|
}
|
|
}
|