fscan/Plugins/services/telnet/plugin.go
ZacharyZcR 05727a0db7 feat: 实现Telnet远程终端协议专业扫描插件
- 新增Telnet服务插件,支持完整的Telnet协议实现和弱口令检测
- 实现Telnet连接器、利用器和主插件的完整新架构
- 支持智能服务器类型识别(无需认证、仅密码、用户名+密码)
- 集成IAC命令处理、选项协商等完整Telnet协议功能
- 支持多种认证模式和用户名/密码组合暴力破解
- 完善国际化消息支持和Docker测试环境配置
- 测试验证:成功检测root:123456等弱密码组合
2025-08-09 15:42:46 +08:00

184 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package telnet
import (
"context"
"fmt"
"strings"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
)
// TelnetPlugin Telnet服务插件
type TelnetPlugin struct {
*base.ServicePlugin
exploiter *TelnetExploiter
}
// NewTelnetPlugin 创建Telnet插件
func NewTelnetPlugin() *TelnetPlugin {
// 插件元数据
metadata := &base.PluginMetadata{
Name: "telnet",
Version: "2.0.0",
Author: "fscan-team",
Description: "Telnet远程终端协议服务检测和弱口令扫描",
Category: "service",
Ports: []int{23}, // Telnet默认端口
Protocols: []string{"tcp"},
Tags: []string{"telnet", "remote-access", "weak-password", "unauthorized-access"},
}
// 创建连接器和服务插件
connector := NewTelnetConnector()
servicePlugin := base.NewServicePlugin(metadata, connector)
// 创建Telnet插件
plugin := &TelnetPlugin{
ServicePlugin: servicePlugin,
exploiter: NewTelnetExploiter(),
}
// 设置能力
plugin.SetCapabilities([]base.Capability{
base.CapWeakPassword,
base.CapUnauthorized,
})
return plugin
}
// init 自动注册Telnet插件
func init() {
// 创建插件工厂
metadata := &base.PluginMetadata{
Name: "telnet",
Version: "2.0.0",
Author: "fscan-team",
Description: "Telnet远程终端协议服务检测和弱口令扫描",
Category: "service",
Ports: []int{23},
Protocols: []string{"tcp"},
Tags: []string{"telnet", "remote-access", "weak-password", "unauthorized-access"},
}
factory := base.NewSimplePluginFactory(metadata, func() base.Plugin {
return NewTelnetPlugin()
})
base.GlobalPluginRegistry.Register("telnet", factory)
}
// Scan 重写扫描方法进行Telnet服务扫描
func (p *TelnetPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
// 如果禁用了暴力破解,只进行服务识别
if common.DisableBrute {
return p.performServiceIdentification(ctx, info)
}
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 生成凭据进行暴力破解
credentials := p.generateCredentials()
if len(credentials) == 0 {
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("no credentials available"),
}, nil
}
// 遍历凭据进行测试
for _, cred := range credentials {
result, err := p.ScanCredential(ctx, info, cred)
if err == nil && result.Success {
// 检查是否无需认证
if result.Extra != nil && result.Extra["type"] == "unauthorized-access" {
common.LogSuccess(i18n.GetText("telnet_unauthorized_access", target))
} else {
// 认证成功
common.LogSuccess(i18n.GetText("telnet_weak_password_success", target, cred.Username, cred.Password))
}
return &base.ScanResult{
Success: true,
Service: "Telnet",
Credentials: []*base.Credential{cred},
Banner: result.Banner,
Extra: map[string]interface{}{
"service": "Telnet",
"port": info.Ports,
"username": cred.Username,
"password": cred.Password,
"type": "weak-password",
},
}, nil
}
}
// 没有找到有效凭据
return &base.ScanResult{
Success: false,
Service: "Telnet",
Error: fmt.Errorf("authentication failed for all credentials"),
}, nil
}
// performServiceIdentification 执行服务识别
func (p *TelnetPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
// 尝试连接到服务进行基本识别
conn, err := p.GetServiceConnector().Connect(ctx, info)
if err != nil {
return &base.ScanResult{
Success: false,
Error: err,
}, nil
}
defer p.GetServiceConnector().Close(conn)
// 服务识别成功
return &base.ScanResult{
Success: true,
Service: "Telnet",
Banner: "Telnet service detected",
Extra: map[string]interface{}{
"service": "Telnet",
"port": info.Ports,
"type": "service-identification",
},
}, nil
}
// generateCredentials 生成Telnet认证凭据
func (p *TelnetPlugin) generateCredentials() []*base.Credential {
var credentials []*base.Credential
// 获取用户名字典
usernames := common.Userdict["telnet"]
if len(usernames) == 0 {
// 使用默认用户名
usernames = []string{"admin", "root", "user", "test", "guest"}
}
// 获取密码字典
passwords := common.Passwords
if len(passwords) == 0 {
// 使用默认密码
passwords = []string{"", "admin", "root", "123456", "password", "test", "guest"}
}
// 生成凭据组合
for _, username := range usernames {
for _, password := range passwords {
// 处理密码中的用户名占位符
actualPassword := strings.Replace(password, "{user}", username, -1)
credentials = append(credentials, &base.Credential{
Username: username,
Password: actualPassword,
})
}
}
return credentials
}