读读 fingerprintx _一个端口指纹识别工具
读读 fingerprintx ,一个端口指纹识别工具
GitHub:https://github.com/praetorian-inc/fingerprintx
fingerprintx是一个类似于httpx的实用程序,它还支持 RDP、SSH、MySQL、PostgreSQL、Kafka 等指纹识别服务。fingerprintx可以与Naabu等端口扫描仪一起使用,对端口扫描期间识别的一组端口进行指纹识别。例如,工程师可能希望扫描 IP 范围,然后快速识别在所有发现的端口上运行的服务。
输入ip+端口,就能输出端口服务指纹相关的信息
支持的协议:
SERVICE | TRANSPORT | SERVICE | TRANSPORT |
---|---|---|---|
HTTP | TCP | REDIS | TCP |
SSH | TCP | MQTT3 | TCP |
MODBUS | TCP | VNC | TCP |
TELNET | TCP | MQTT5 | TCP |
FTP | TCP | RSYNC | TCP |
SMB | TCP | RPC | TCP |
DNS | TCP | OracleDB | TCP |
SMTP | TCP | RTSP | TCP |
PostgreSQL | TCP | MQTT5 | TCP (TLS) |
RDP | TCP | HTTPS | TCP (TLS) |
POP3 | TCP | SMTPS | TCP (TLS) |
KAFKA | TCP | MQTT3 | TCP (TLS) |
MySQL | TCP | RDP | TCP (TLS) |
MSSQL | TCP | POP3S | TCP (TLS) |
LDAP | TCP | LDAPS | TCP (TLS) |
IMAP | TCP | IMAPS | TCP (TLS) |
SNMP | UDP | Kafka | TCP (TLS) |
OPENVPN | UDP | NETBIOS-NS | UDP |
IPSEC | UDP | DHCP | UDP |
STUN | UDP | NTP | UDP |
DNS | UDP |
想看看源码,这些协议是怎么做识别以及怎么组织的。
看官方描述,使用fingerprintx
有一个快速模式fast
该fast模式将仅尝试为每个目标识别与该端口关联的默认服务。例如,如果praetorian.com:8443是输入,则只会https运行插件。如果https未在 上运行praetorian.com:8443,则不会有输出。为什么要这样做?这是在大量主机列表中识别大多数服务的快速方法(想想2/8原则 )。
和nmap的区别
一个在 8080 端口打开的服务器上运行的插件是 http 插件。默认服务方法在最好的情况下减少了扫描时间。大多数情况下,在端口 80、443、22 上运行的服务是 http、https 和 ssh——所以这是fingerprintx首先检查的内容。
插件组织结构
这个项目提供了很好的一个插件架构,fingerprintx的指纹识别是以插件的形式进行的,如ftp识别是一个插件,mysql识别也是一个插件。
插件目录位于pkg/plugins/services
虽然不是动态插件加载,作为go的也值得学习。
插件的接口是
type Plugin interface {
Run(net.Conn, PluginConfig) (*PluginResults, error) // 运行插件,返回结果
PortPriority(uint16) bool // 返回端口的优先级,比如ssh的端口优先级是22,优先级可以让识别更快
Name() string // 返回服务插件的名称
Type() Protocol // 返回该插件的协议类型 TCP或UDP
Priority() int // 插件调用的优先级,数字越大优先级越高
}
所有的插件都要实现这些方法。
看一个简单的插件源码,例如ftp
package ftp
import (
"net"
"regexp"
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
utils "github.com/praetorian-inc/fingerprintx/pkg/plugins/pluginutils"
)
var ftpResponse = regexp.MustCompile(`^\d{3}[- ](.*)\r`)
const FTP = "ftp"
type FTPPlugin struct{}
func init() {
plugins.RegisterPlugin(&FTPPlugin{})
}
func (p *FTPPlugin) Run(conn net.Conn, config plugins.PluginConfig) (*plugins.PluginResults, error) {
response, err := utils.Recv(conn, config.Timeout)
if err != nil {
return nil, err
}
if len(response) == 0 {
return nil, nil
}
matches := ftpResponse.FindStringSubmatch(string(response))
if matches == nil {
return nil, nil
}
return &plugins.PluginResults{
Info: map[string]any{
"banner": string(response),
}}, nil
}
func (p *FTPPlugin) PortPriority(i uint16) bool {
return i == 21
}
func (p *FTPPlugin) Name() string {
return FTP
}
func (p *FTPPlugin) Type() plugins.Protocol {
return plugins.TCP
}
func (p *FTPPlugin) Priority() int {
return 10
}
每个插件初始化时候都会进行默认注册
func init() {
plugins.RegisterPlugin(&FTPPlugin{})
}
跟进RegisterPlugin
函数
var Plugins = make(map[Protocol][]Plugin)
var pluginIDs = make(map[PluginID]bool)
// This function must not be run concurrently.
// This function should only be run once per plugin.
func RegisterPlugin(p Plugin) {
id := CreatePluginID(p)
if pluginIDs[id] {
panic(fmt.Sprintf("plugin: Register called twice for driver %+v\n", id))
}
pluginIDs[id] = true
var pluginList []Plugin
if list, exists := Plugins[p.Type()]; exists {
pluginList = list
} else {
pluginList = make([]Plugin, 0)
}
Plugins[p.Type()] = append(pluginList, p)
}
他会把实例化的类加入到Plugins
这个全局变量中。在程序初始化中,pkg/scan/plugin_list.go
进行初始化所有插件。
后面运行直接遍历Plugins
全局变量的内容即可实现插件化调用了。
识别流程
初始化插件,以及对每个类别的插件进行排序,按照协议类型TCP
、TCPTLS
、UDP
整理
func setupPlugins() {
if len(sortedTCPPlugins) > 0 {
// already sorted
return
}
sortedTCPPlugins = append(sortedTCPPlugins, plugins.Plugins[plugins.TCP]...)
sortedTCPTLSPlugins = append(sortedTCPTLSPlugins, plugins.Plugins[plugins.TCPTLS]...)
sortedUDPPlugins = append(sortedUDPPlugins, plugins.Plugins[plugins.UDP]...)
sort.Slice(sortedTCPPlugins, func(i, j int) bool {
return sortedTCPPlugins[i].Priority() < sortedTCPPlugins[j].Priority()
})
sort.Slice(sortedUDPPlugins, func(i, j int) bool {
return sortedUDPPlugins[i].Priority() < sortedUDPPlugins[j].Priority()
})
sort.Slice(sortedTCPTLSPlugins, func(i, j int) bool {
return sortedTCPTLSPlugins[i].Priority() < sortedTCPTLSPlugins[j].Priority()
})
}
fingerxprint的扫描模式分为快速模式和精准模式,快速模式只检查常用端口对应的服务,所以速度较快,精准模式不在乎性能,只求精准,会将所有插件都运行一遍。
End
fingerprintx readme后面还提到了zgrab2,也是类似的用Go编写的服务指纹识别工具,他和zmap是同一个项目组,后面再看看它的源码。