mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
修复凭证测试器计数器、消除panic、补齐i18n
- credential_tester: testCredentialWithRetry返回ErrorType,修复网络错误计数器永久不递增的bug - scanner: os.Exit(1)改为return,defer Cleanup可正常执行 - probe_parser: 5处panic改为error返回,调用链透传到init() - common库: parsers/initialize/network/session共17处硬编码中文改用i18n - services插件: 18个文件115处硬编码中文改用i18n - locale: 补齐service/parser/network相关~25个中英文键
This commit is contained in:
@@ -213,6 +213,36 @@ parse_error_empty_input:
|
||||
other: "Input parameters are empty"
|
||||
parse_error_parser_not_init:
|
||||
other: "Parser not initialized"
|
||||
parser_read_hosts_failed:
|
||||
other: "Failed to read hosts file"
|
||||
parser_parse_host_failed:
|
||||
other: "Failed to parse host"
|
||||
parser_parse_exclude_failed:
|
||||
other: "Failed to parse exclude hosts"
|
||||
parser_no_valid_hosts:
|
||||
other: "No valid hosts found"
|
||||
parser_cidr_failed:
|
||||
other: "CIDR parse failed {{.Arg1}}"
|
||||
parser_ip_range_failed:
|
||||
other: "IP range parse failed {{.Arg1}}"
|
||||
parser_invalid_ip_range_fmt:
|
||||
other: "Invalid IP range format: {{.Arg1}}"
|
||||
parser_invalid_start_ip:
|
||||
other: "Invalid start IP: {{.Arg1}}"
|
||||
parser_invalid_end_ip:
|
||||
other: "Invalid end IP: {{.Arg1}}"
|
||||
parser_invalid_ip_end_val:
|
||||
other: "Invalid IP range end value: {{.Arg1}}"
|
||||
parser_invalid_ip_range_val:
|
||||
other: "Invalid IP range: {{.Arg1}}-{{.Arg2}}"
|
||||
parser_invalid_ip_fmt:
|
||||
other: "Invalid IP address format: {{.Arg1}}"
|
||||
parser_ipv4_only:
|
||||
other: "IPv4 addresses only"
|
||||
parser_start_gt_end:
|
||||
other: "Start IP greater than end IP"
|
||||
network_rate_limited:
|
||||
other: "Rate limited: {{.Arg1}}"
|
||||
target_local_mode:
|
||||
other: "Local scan mode"
|
||||
param_conflict_ao_icmp_both:
|
||||
@@ -817,3 +847,25 @@ web_shutting_down:
|
||||
other: "Web server shutting down..."
|
||||
web_mode_not_supported:
|
||||
other: "Web mode not supported in this build, rebuild with: go build -tags web"
|
||||
|
||||
# ========================= Service Plugin Common Messages =========================
|
||||
service_no_credentials:
|
||||
other: "No available test credentials"
|
||||
service_not_identified:
|
||||
other: "Cannot identify as {{.Arg1}} service"
|
||||
service_port_restriction:
|
||||
other: "{{.Arg1}} plugin only supports port {{.Arg2}}"
|
||||
service_target_unreachable:
|
||||
other: "Target unreachable: {{.Arg1}}"
|
||||
service_unauthorized:
|
||||
other: "Unauthorized access"
|
||||
service_auth_failed:
|
||||
other: "Authentication failed"
|
||||
service_connection_failed:
|
||||
other: "Connection failed: {{.Arg1}}"
|
||||
service_no_weak_pass:
|
||||
other: "No weak password found"
|
||||
service_no_test_creds:
|
||||
other: "No credentials to test"
|
||||
service_conn_port_failed:
|
||||
other: "Port connection failed: {{.Arg1}}"
|
||||
|
||||
@@ -213,6 +213,36 @@ parse_error_empty_input:
|
||||
other: "输入参数为空"
|
||||
parse_error_parser_not_init:
|
||||
other: "解析器未初始化"
|
||||
parser_read_hosts_failed:
|
||||
other: "读取主机文件失败"
|
||||
parser_parse_host_failed:
|
||||
other: "解析主机失败"
|
||||
parser_parse_exclude_failed:
|
||||
other: "解析排除主机失败"
|
||||
parser_no_valid_hosts:
|
||||
other: "没有找到有效的主机"
|
||||
parser_cidr_failed:
|
||||
other: "CIDR解析失败 {{.Arg1}}"
|
||||
parser_ip_range_failed:
|
||||
other: "IP范围解析失败 {{.Arg1}}"
|
||||
parser_invalid_ip_range_fmt:
|
||||
other: "无效的IP范围格式: {{.Arg1}}"
|
||||
parser_invalid_start_ip:
|
||||
other: "无效的起始IP地址: {{.Arg1}}"
|
||||
parser_invalid_end_ip:
|
||||
other: "无效的结束IP地址: {{.Arg1}}"
|
||||
parser_invalid_ip_end_val:
|
||||
other: "无效的IP范围结束值: {{.Arg1}}"
|
||||
parser_invalid_ip_range_val:
|
||||
other: "无效的IP范围: {{.Arg1}}-{{.Arg2}}"
|
||||
parser_invalid_ip_fmt:
|
||||
other: "无效的IP地址格式: {{.Arg1}}"
|
||||
parser_ipv4_only:
|
||||
other: "仅支持IPv4地址范围"
|
||||
parser_start_gt_end:
|
||||
other: "起始IP大于结束IP"
|
||||
network_rate_limited:
|
||||
other: "发包受限: {{.Arg1}}"
|
||||
target_local_mode:
|
||||
other: "本地扫描模式"
|
||||
param_conflict_ao_icmp_both:
|
||||
@@ -796,6 +826,8 @@ webscan_response_parse_failed:
|
||||
# Main 入口
|
||||
param_error:
|
||||
other: "参数错误: {{.Arg1}}"
|
||||
param_exclusive:
|
||||
other: "参数 {{.Arg1}} 互斥,请只指定一个扫描目标\n -h: 网络主机扫描\n -u: Web URL扫描\n -local: 本地信息收集"
|
||||
error_generic:
|
||||
other: "错误: {{.Arg1}}"
|
||||
init_failed:
|
||||
@@ -814,3 +846,25 @@ web_shutting_down:
|
||||
other: "Web服务器正在关闭..."
|
||||
web_mode_not_supported:
|
||||
other: "当前版本不支持Web模式,请使用 -tags web 重新编译"
|
||||
|
||||
# ========================= 服务插件通用消息 =========================
|
||||
service_no_credentials:
|
||||
other: "没有可用的测试凭据"
|
||||
service_not_identified:
|
||||
other: "无法识别为{{.Arg1}}服务"
|
||||
service_port_restriction:
|
||||
other: "{{.Arg1}}插件仅支持{{.Arg2}}端口"
|
||||
service_target_unreachable:
|
||||
other: "目标不可达: {{.Arg1}}"
|
||||
service_unauthorized:
|
||||
other: "未授权访问"
|
||||
service_auth_failed:
|
||||
other: "认证失败"
|
||||
service_connection_failed:
|
||||
other: "连接失败: {{.Arg1}}"
|
||||
service_no_weak_pass:
|
||||
other: "未发现弱密码"
|
||||
service_no_test_creds:
|
||||
other: "无凭据可测试"
|
||||
service_conn_port_failed:
|
||||
other: "连接端口失败: {{.Arg1}}"
|
||||
|
||||
@@ -2,6 +2,8 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -80,7 +82,7 @@ func ValidateExclusiveParams(info *HostInfo) error {
|
||||
}
|
||||
|
||||
if paramCount > 1 {
|
||||
return fmt.Errorf("参数 %s 互斥,请只指定一个扫描目标\n -h: 网络主机扫描\n -u: Web URL扫描\n -local: 本地信息收集", activeParam)
|
||||
return fmt.Errorf(i18n.Tr("param_exclusive", activeParam))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+3
-2
@@ -17,6 +17,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/proxy"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
@@ -109,7 +110,7 @@ func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.
|
||||
// 检查发包限制 - 在代理连接前进行控制
|
||||
if canSend, reason := CanSendPacket(); !canSend {
|
||||
LogError(fmt.Sprintf("TCP连接 %s 受限: %s", address, reason))
|
||||
return nil, fmt.Errorf("发包受限: %s", reason)
|
||||
return nil, fmt.Errorf(i18n.Tr("network_rate_limited", reason))
|
||||
}
|
||||
|
||||
// 获取全局拨号器(复用,避免重复创建)
|
||||
@@ -166,7 +167,7 @@ func SafeHTTPDo(client *http.Client, req *http.Request) (*http.Response, error)
|
||||
// 检查发包限制
|
||||
if canSend, reason := CanSendPacket(); !canSend {
|
||||
LogError(fmt.Sprintf("HTTP请求 %s 受限: %s", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("发包受限: %s", reason)
|
||||
return nil, fmt.Errorf(i18n.Tr("network_rate_limited", reason))
|
||||
}
|
||||
|
||||
// 执行HTTP请求
|
||||
|
||||
+15
-14
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/config"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -37,7 +38,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
|
||||
if filename != "" {
|
||||
fileHosts, err := ReadLinesFromFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取主机文件失败: %w", err)
|
||||
return nil, fmt.Errorf(i18n.GetText("parser_read_hosts_failed")+": %w", err)
|
||||
}
|
||||
for _, h := range fileHosts {
|
||||
parsed, err := parseHostString(h)
|
||||
@@ -52,7 +53,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
|
||||
if host != "" {
|
||||
hostList, err := parseHostString(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析主机失败: %w", err)
|
||||
return nil, fmt.Errorf(i18n.GetText("parser_parse_host_failed")+": %w", err)
|
||||
}
|
||||
hosts = append(hosts, hostList...)
|
||||
}
|
||||
@@ -61,7 +62,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
|
||||
if len(nohosts) > 0 && nohosts[0] != "" {
|
||||
excludeList, err := parseHostString(nohosts[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析排除主机失败: %w", err)
|
||||
return nil, fmt.Errorf(i18n.GetText("parser_parse_exclude_failed")+": %w", err)
|
||||
}
|
||||
hosts = excludeFromList(hosts, excludeList)
|
||||
}
|
||||
@@ -71,7 +72,7 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
|
||||
sort.Strings(hosts)
|
||||
|
||||
if len(hosts) == 0 {
|
||||
return nil, fmt.Errorf("没有找到有效的主机")
|
||||
return nil, fmt.Errorf(i18n.GetText("parser_no_valid_hosts"))
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
@@ -109,13 +110,13 @@ func parseHostString(host string) ([]string, error) {
|
||||
case strings.Contains(h, "/"):
|
||||
cidrHosts, err := parseIPCIDR(h, SimpleMaxHosts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CIDR解析失败 %s: %w", h, err)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_cidr_failed", h)+": %w", err)
|
||||
}
|
||||
hosts = append(hosts, cidrHosts...)
|
||||
case strings.Contains(h, "-") && !strings.Contains(h, ":") && looksLikeIPRange(h):
|
||||
rangeHosts, err := parseIPRangeString(h, SimpleMaxHosts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("IP范围解析失败 %s: %w", h, err)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_ip_range_failed", h)+": %w", err)
|
||||
}
|
||||
hosts = append(hosts, rangeHosts...)
|
||||
default:
|
||||
@@ -333,7 +334,7 @@ func looksLikeIPRange(s string) bool {
|
||||
func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
|
||||
parts := strings.Split(rangeStr, "-")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("无效的IP范围格式: %s", rangeStr)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_range_fmt", rangeStr))
|
||||
}
|
||||
|
||||
startIPStr := strings.TrimSpace(parts[0])
|
||||
@@ -341,7 +342,7 @@ func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
|
||||
|
||||
startIP := net.ParseIP(startIPStr)
|
||||
if startIP == nil {
|
||||
return nil, fmt.Errorf("无效的起始IP地址: %s", startIPStr)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_invalid_start_ip", startIPStr))
|
||||
}
|
||||
|
||||
// 处理简写格式 (如: 192.168.1.1-100)
|
||||
@@ -352,7 +353,7 @@ func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
|
||||
// 处理完整格式 (如: 192.168.1.1-192.168.1.100)
|
||||
endIP := net.ParseIP(endIPStr)
|
||||
if endIP == nil {
|
||||
return nil, fmt.Errorf("无效的结束IP地址: %s", endIPStr)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_invalid_end_ip", endIPStr))
|
||||
}
|
||||
|
||||
return parseIPFullRange(startIP, endIP, maxTargets)
|
||||
@@ -362,18 +363,18 @@ func parseIPRangeString(rangeStr string, maxTargets int) ([]string, error) {
|
||||
func parseIPShortRange(startIPStr, endSuffix string, maxTargets int) ([]string, error) {
|
||||
endNum, err := strconv.Atoi(endSuffix)
|
||||
if err != nil || endNum > 255 {
|
||||
return nil, fmt.Errorf("无效的IP范围结束值: %s", endSuffix)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_end_val", endSuffix))
|
||||
}
|
||||
|
||||
ipParts := strings.Split(startIPStr, ".")
|
||||
if len(ipParts) != 4 {
|
||||
return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_fmt", startIPStr))
|
||||
}
|
||||
|
||||
prefixIP := strings.Join(ipParts[0:3], ".")
|
||||
startNum, err := strconv.Atoi(ipParts[3])
|
||||
if err != nil || startNum > endNum {
|
||||
return nil, fmt.Errorf("无效的IP范围: %s-%s", startIPStr, endSuffix)
|
||||
return nil, fmt.Errorf(i18n.Tr("parser_invalid_ip_range_val", startIPStr, endSuffix))
|
||||
}
|
||||
|
||||
var allIP []string
|
||||
@@ -394,14 +395,14 @@ func parseIPFullRange(startIP, endIP net.IP, maxTargets int) ([]string, error) {
|
||||
start4 := startIP.To4()
|
||||
end4 := endIP.To4()
|
||||
if start4 == nil || end4 == nil {
|
||||
return nil, fmt.Errorf("仅支持IPv4地址范围")
|
||||
return nil, fmt.Errorf(i18n.GetText("parser_ipv4_only"))
|
||||
}
|
||||
|
||||
startInt := (int(start4[0]) << 24) | (int(start4[1]) << 16) | (int(start4[2]) << 8) | int(start4[3])
|
||||
endInt := (int(end4[0]) << 24) | (int(end4[1]) << 16) | (int(end4[2]) << 8) | int(end4[3])
|
||||
|
||||
if startInt > endInt {
|
||||
return nil, fmt.Errorf("起始IP大于结束IP")
|
||||
return nil, fmt.Errorf(i18n.GetText("parser_start_gt_end"))
|
||||
}
|
||||
|
||||
var ips []string
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/shadow1ng/fscan/common/proxy"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
)
|
||||
|
||||
// ScanSession 封装单次扫描的全部上下文
|
||||
@@ -38,7 +39,7 @@ func (s *ScanSession) DialTCP(ctx context.Context, network, address string, time
|
||||
// 检查发包限制
|
||||
if ok, err := CanSendPacketWith(s.Config, s.State); !ok {
|
||||
LogError(fmt.Sprintf("TCP连接 %s 受限: %s", address, err.Error()))
|
||||
return nil, fmt.Errorf("发包受限: %s", err.Error())
|
||||
return nil, fmt.Errorf(i18n.Tr("network_rate_limited", err.Error()))
|
||||
}
|
||||
|
||||
// 获取 dialer
|
||||
|
||||
@@ -29,22 +29,20 @@ func (p *Probe) getDirectiveSyntax(data string) (directive Directive) {
|
||||
return directive
|
||||
}
|
||||
|
||||
// 解析探测器信息
|
||||
func (p *Probe) parseProbeInfo(probeStr string) {
|
||||
// parseProbeInfo 解析探测器信息,返回错误替代 panic
|
||||
func (p *Probe) parseProbeInfo(probeStr string) error {
|
||||
// 提取协议和其他信息
|
||||
proto := probeStr[:4]
|
||||
other := probeStr[4:]
|
||||
|
||||
// 验证协议类型
|
||||
if proto != "TCP " && proto != "UDP " {
|
||||
errMsg := "探测器协议必须是 TCP 或 UDP"
|
||||
panic(errMsg)
|
||||
return fmt.Errorf("探测器协议必须是 TCP 或 UDP")
|
||||
}
|
||||
|
||||
// 验证其他信息不为空
|
||||
if len(other) == 0 {
|
||||
errMsg := "nmap-service-probes - 探测器名称无效"
|
||||
panic(errMsg)
|
||||
return fmt.Errorf("nmap-service-probes - 探测器名称无效")
|
||||
}
|
||||
|
||||
// 解析指令
|
||||
@@ -55,6 +53,7 @@ func (p *Probe) parseProbeInfo(probeStr string) {
|
||||
p.Data = strings.Split(directive.DirectiveStr, directive.Delimiter)[0]
|
||||
p.Protocol = strings.ToLower(strings.TrimSpace(proto))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 从字符串解析探测器信息
|
||||
@@ -69,7 +68,9 @@ func (p *Probe) fromString(data string) error {
|
||||
}
|
||||
|
||||
probeStr := lines[0]
|
||||
p.parseProbeInfo(probeStr)
|
||||
if err := p.parseProbeInfo(probeStr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 解析匹配规则和其他配置
|
||||
var matchs []Match
|
||||
@@ -154,8 +155,8 @@ func (p *Probe) parseFallback(data string) {
|
||||
p.Fallback = data[len("fallback")+1:]
|
||||
}
|
||||
|
||||
// 从内容解析探测器规则
|
||||
func (v *VScan) parseProbesFromContent(content string) {
|
||||
// parseProbesFromContent 从内容解析探测器规则,返回错误替代 panic
|
||||
func (v *VScan) parseProbesFromContent(content string) error {
|
||||
var probes []Probe
|
||||
var lines []string
|
||||
|
||||
@@ -171,8 +172,7 @@ func (v *VScan) parseProbesFromContent(content string) {
|
||||
|
||||
// 验证文件内容
|
||||
if len(lines) == 0 {
|
||||
errMsg := "读取nmap-service-probes文件失败: 内容为空"
|
||||
panic(errMsg)
|
||||
return fmt.Errorf("读取nmap-service-probes文件失败: 内容为空")
|
||||
}
|
||||
|
||||
// 检查Exclude指令
|
||||
@@ -182,16 +182,14 @@ func (v *VScan) parseProbesFromContent(content string) {
|
||||
excludeCount++
|
||||
}
|
||||
if excludeCount > 1 {
|
||||
errMsg := "nmap-service-probes文件中只允许有一个Exclude指令"
|
||||
panic(errMsg)
|
||||
return fmt.Errorf("nmap-service-probes文件中只允许有一个Exclude指令")
|
||||
}
|
||||
}
|
||||
|
||||
// 验证第一行格式
|
||||
firstLine := lines[0]
|
||||
if !strings.HasPrefix(firstLine, "Exclude ") && !strings.HasPrefix(firstLine, "Probe ") {
|
||||
errMsg := "解析错误: 首行必须以\"Probe \"或\"Exclude \"开头"
|
||||
panic(errMsg)
|
||||
return fmt.Errorf("解析错误: 首行必须以\"Probe \"或\"Exclude \"开头")
|
||||
}
|
||||
|
||||
// 处理Exclude指令
|
||||
@@ -214,6 +212,7 @@ func (v *VScan) parseProbesFromContent(content string) {
|
||||
}
|
||||
|
||||
v.AllProbes = probes
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将探测器转换为名称映射
|
||||
|
||||
@@ -19,13 +19,16 @@ var (
|
||||
vscanOnce sync.Once
|
||||
)
|
||||
|
||||
// Init 初始化VScan对象
|
||||
func (vs *VScan) Init() {
|
||||
vs.parseProbesFromContent(ProbeString)
|
||||
// Init 初始化VScan对象,返回错误替代panic
|
||||
func (vs *VScan) Init() error {
|
||||
if err := vs.parseProbesFromContent(ProbeString); err != nil {
|
||||
return err
|
||||
}
|
||||
vs.parseProbesToMapKName()
|
||||
vs.SetusedProbes()
|
||||
vs.compileFallbacks() // 编译 fallback 数组
|
||||
vs.preDecodeProbeData() // 预解码探针数据
|
||||
vs.compileFallbacks()
|
||||
vs.preDecodeProbeData()
|
||||
return nil
|
||||
}
|
||||
|
||||
// preDecodeProbeData 预解码所有探针的 Data 字段,避免运行时重复解码
|
||||
@@ -100,10 +103,14 @@ func (vs *VScan) compileFallbacks() {
|
||||
}
|
||||
|
||||
// InitializeGlobalVScan 初始化全局VScan实例(线程安全,只执行一次)
|
||||
func InitializeGlobalVScan() {
|
||||
func InitializeGlobalVScan() error {
|
||||
var initErr error
|
||||
vscanOnce.Do(func() {
|
||||
globalVScan = VScan{}
|
||||
globalVScan.Init()
|
||||
if err := globalVScan.Init(); err != nil {
|
||||
initErr = err
|
||||
return
|
||||
}
|
||||
|
||||
// 获取并检查 NULL 探测器
|
||||
if nullProbe, ok := globalVScan.ProbesMapKName["NULL"]; ok {
|
||||
@@ -115,6 +122,7 @@ func InitializeGlobalVScan() {
|
||||
globalCommon = &genericProbe
|
||||
}
|
||||
})
|
||||
return initErr
|
||||
}
|
||||
|
||||
// GetGlobalVScan 获取全局VScan实例
|
||||
@@ -136,5 +144,5 @@ func GetCommonProbe() *Probe {
|
||||
}
|
||||
|
||||
func init() {
|
||||
InitializeGlobalVScan()
|
||||
_ = InitializeGlobalVScan()
|
||||
}
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ func RunScan(ctx context.Context, info common.HostInfo, session *common.ScanSess
|
||||
// 初始化HTTP客户端(静默,无需日志)
|
||||
if err := lib.Inithttp(config); err != nil {
|
||||
common.LogError(i18n.Tr("http_client_init_failed", err))
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
// 选择策略
|
||||
|
||||
@@ -218,7 +218,7 @@ func (p *ActiveMQPlugin) identifyService(ctx context.Context, info *common.HostI
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "activemq",
|
||||
Error: fmt.Errorf("无法发送STOMP请求: %w", writeErr),
|
||||
Error: fmt.Errorf("STOMP request send failed: %w", writeErr),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ func (p *ActiveMQPlugin) identifyService(ctx context.Context, info *common.HostI
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "activemq",
|
||||
Error: fmt.Errorf("无法读取响应: %w", err),
|
||||
Error: fmt.Errorf("Failed to read response: %w", err),
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
@@ -267,7 +267,7 @@ func (p *ActiveMQPlugin) identifyService(ctx context.Context, info *common.HostI
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "activemq",
|
||||
Error: fmt.Errorf("无法识别为ActiveMQ STOMP服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "ActiveMQ STOMP")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ func (p *CassandraPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "cassandra",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins"
|
||||
)
|
||||
|
||||
@@ -147,7 +148,7 @@ func TestCredentialsConcurrently(
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: serviceName,
|
||||
Error: fmt.Errorf("无凭据可测试"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_test_creds")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ func TestCredentialsConcurrently(
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: serviceName,
|
||||
Error: fmt.Errorf("目标不可达: %w", err),
|
||||
Error: fmt.Errorf(i18n.Tr("service_target_unreachable", "%w"), err),
|
||||
}
|
||||
}
|
||||
_ = preConn.Close()
|
||||
@@ -222,7 +223,7 @@ func TestCredentialsConcurrently(
|
||||
Type: plugins.ResultTypeCredential, // 标记这是凭据测试结果
|
||||
Success: false,
|
||||
Service: serviceName,
|
||||
Error: fmt.Errorf("未发现弱密码"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_weak_pass")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,14 +256,14 @@ func workerTestCredentials(
|
||||
}
|
||||
|
||||
// 带重试的凭据测试
|
||||
result := testCredentialWithRetry(ctx, cred, authFn, serviceName, testConfig)
|
||||
result, errType := testCredentialWithRetry(ctx, cred, authFn, serviceName, testConfig)
|
||||
if result != nil && result.Success {
|
||||
resultChan <- result
|
||||
return
|
||||
}
|
||||
|
||||
// 跟踪连续网络错误
|
||||
if result != nil && result.Error != nil {
|
||||
if errType == ErrorTypeNetwork {
|
||||
consecutiveNetErrors++
|
||||
} else {
|
||||
consecutiveNetErrors = 0
|
||||
@@ -277,12 +278,12 @@ func testCredentialWithRetry(
|
||||
authFn AuthFunc,
|
||||
serviceName string,
|
||||
testConfig ConcurrentTestConfig,
|
||||
) *ScanResult {
|
||||
) (*ScanResult, ErrorType) {
|
||||
for attempt := 0; attempt < testConfig.MaxRetries; attempt++ {
|
||||
// 检查是否应该停止
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
return nil, ErrorTypeUnknown
|
||||
default:
|
||||
}
|
||||
|
||||
@@ -298,14 +299,14 @@ func testCredentialWithRetry(
|
||||
Service: serviceName,
|
||||
Username: cred.Username,
|
||||
Password: cred.Password,
|
||||
}
|
||||
}, ErrorTypeUnknown
|
||||
}
|
||||
|
||||
// 根据错误类型决定是否重试
|
||||
switch result.ErrorType {
|
||||
case ErrorTypeAuth:
|
||||
// 认证错误(密码错误),不重试
|
||||
return nil
|
||||
return nil, result.ErrorType
|
||||
case ErrorTypeNetwork, ErrorTypeUnknown:
|
||||
// 网络错误或未知错误,可以重试(可能是服务端限流等临时问题)
|
||||
if attempt < testConfig.MaxRetries-1 {
|
||||
@@ -313,13 +314,13 @@ func testCredentialWithRetry(
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
timer.Stop()
|
||||
return nil
|
||||
return nil, result.ErrorType
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, ErrorTypeNetwork
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -51,7 +51,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "elasticsearch",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "elasticsearch",
|
||||
Error: fmt.Errorf("未发现弱密码"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_weak_pass")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func (p *ElasticsearchPlugin) identifyService(ctx context.Context, info *common.
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "elasticsearch",
|
||||
Error: fmt.Errorf("无法识别为Elasticsearch服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "Elasticsearch")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins"
|
||||
)
|
||||
|
||||
@@ -45,7 +46,7 @@ func (p *FindNetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "findnet",
|
||||
Error: fmt.Errorf("FindNet插件仅支持RPC端口135"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_port_restriction", "FindNet", "135")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ func (p *FindNetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "findnet",
|
||||
Error: fmt.Errorf("连接RPC端口失败: %w", err),
|
||||
Error: fmt.Errorf(i18n.Tr("service_conn_port_failed", "%w"), err),
|
||||
}
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
@@ -43,7 +43,7 @@ func (p *FTPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "ftp",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ func (p *KafkaPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "kafka",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func (p *KafkaPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "kafka",
|
||||
Error: fmt.Errorf("无法识别为Kafka服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "Kafka")),
|
||||
}
|
||||
}
|
||||
state.IncrementTCPSuccessPacketCount()
|
||||
|
||||
@@ -44,7 +44,7 @@ func (p *LDAPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *c
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "ldap",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ func (p *MemcachedPlugin) testUnauthorizedAccess(ctx context.Context, info *comm
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "memcached",
|
||||
Banner: "未授权访问",
|
||||
Banner: i18n.GetText("service_unauthorized"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ func (p *MemcachedPlugin) identifyService(ctx context.Context, info *common.Host
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "memcached",
|
||||
Error: fmt.Errorf("无法识别为Memcached服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "Memcached")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "mongodb",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,14 +237,14 @@ func (p *MongoDBPlugin) mongodbUnauth(ctx context.Context, info *common.HostInfo
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("无法识别为MongoDB服务")
|
||||
return false, fmt.Errorf(i18n.Tr("service_not_identified", "MongoDB"))
|
||||
}
|
||||
|
||||
// checkMongoAuth 检查MongoDB认证状态
|
||||
func (p *MongoDBPlugin) checkMongoAuth(ctx context.Context, address string, packet []byte, session *common.ScanSession) (string, error) {
|
||||
conn, err := session.DialTCP(ctx, "tcp", address, session.Config.Timeout)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("连接失败: %w", err)
|
||||
return "", fmt.Errorf(i18n.Tr("service_connection_failed", "%w"), err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func (p *MSSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "mssql",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ func (p *MSSQLPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "mssql",
|
||||
Error: fmt.Errorf("无法识别为MSSQL服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "MSSQL")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "mysql",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func (p *MySQLPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "mysql",
|
||||
Error: fmt.Errorf("无法识别为MySQL服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "MySQL")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "neo4j",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ func (p *Neo4jPlugin) doNeo4jAuth(ctx context.Context, info *common.HostInfo, cr
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeAuth,
|
||||
Error: fmt.Errorf("认证失败,状态码: %d", resp.StatusCode),
|
||||
Error: fmt.Errorf(i18n.GetText("service_auth_failed")+": %d", resp.StatusCode),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ func (p *Neo4jPlugin) testUnauthorizedAccess(ctx context.Context, info *common.H
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "neo4j",
|
||||
Banner: "未授权访问",
|
||||
Banner: i18n.GetText("service_unauthorized"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ func (p *Neo4jPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "neo4j",
|
||||
Error: fmt.Errorf("无法识别为Neo4j服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "Neo4j")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ func (p *OraclePlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "oracle",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ func (p *PostgreSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, sess
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "postgresql",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ func (p *PostgreSQLPlugin) identifyService(ctx context.Context, info *common.Hos
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "postgresql",
|
||||
Error: fmt.Errorf("无法识别为PostgreSQL服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "PostgreSQL")),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -46,7 +46,7 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, sessio
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "rabbitmq",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (p *RabbitMQPlugin) doRabbitMQAuth(ctx context.Context, info *common.HostIn
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeAuth,
|
||||
Error: fmt.Errorf("认证失败,状态码: %d", resp.StatusCode),
|
||||
Error: fmt.Errorf(i18n.GetText("service_auth_failed")+": %d", resp.StatusCode),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ func (p *RabbitMQPlugin) testUnauthorizedAccess(ctx context.Context, info *commo
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "rabbitmq",
|
||||
Banner: "未授权访问",
|
||||
Banner: i18n.GetText("service_unauthorized"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,7 +307,7 @@ func (p *RabbitMQPlugin) testManagementInterface(ctx context.Context, info *comm
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "rabbitmq",
|
||||
Error: fmt.Errorf("无法识别为RabbitMQ服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "RabbitMQ")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "rdp",
|
||||
Error: fmt.Errorf("RDP认证失败"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_auth_failed")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ func (p *RedisPlugin) doRedisAuth(ctx context.Context, info *common.HostInfo, cr
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: errType,
|
||||
Error: fmt.Errorf("redis认证失败: %s", strings.TrimSpace(responseStr)),
|
||||
Error: fmt.Errorf(i18n.GetText("service_auth_failed")+": %s", strings.TrimSpace(responseStr)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (p *RsyncPlugin) Scan(ctx context.Context, info *common.HostInfo, session *
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "rsync",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +374,7 @@ func (p *RsyncPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "rsync",
|
||||
Error: fmt.Errorf("无法识别为Rsync服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "Rsync")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/hirochachacha/go-smb2"
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/stacktitan/smb/smb"
|
||||
)
|
||||
|
||||
@@ -206,7 +207,7 @@ func probeTarget(ctx context.Context, host string, port int, timeout time.Durati
|
||||
|
||||
conn, err := session.DialTCP(ctx, "tcp", target, timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接失败: %w", err)
|
||||
return nil, fmt.Errorf(i18n.Tr("service_connection_failed", "%w"), err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
@@ -291,7 +292,7 @@ func probeSMBv1(conn net.Conn, target string, timeout time.Duration) (*SMBTarget
|
||||
func probeSMBv2(ctx context.Context, target string, timeout time.Duration, session *common.ScanSession) (*SMBTarget, error) {
|
||||
conn2, err := session.DialTCP(ctx, "tcp", target, timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SMBv2连接失败: %w", err)
|
||||
return nil, fmt.Errorf(i18n.Tr("service_connection_failed", "%w"), err)
|
||||
}
|
||||
defer func() { _ = conn2.Close() }()
|
||||
|
||||
@@ -436,7 +437,7 @@ func (a *SMB1Authenticator) Authenticate(ctx context.Context, host string, port
|
||||
resultChan <- &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeAuth,
|
||||
Error: fmt.Errorf("认证失败:用户名或密码错误"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_auth_failed")),
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -507,7 +508,7 @@ func (a *SMB2Authenticator) Authenticate(ctx context.Context, host string, port
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: classifySMBError(err),
|
||||
Error: fmt.Errorf("SMB2认证失败: %w", err),
|
||||
Error: fmt.Errorf(i18n.Tr("service_connection_failed", "%w"), err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -662,7 +663,7 @@ func classifySMBError(err error) ErrorType {
|
||||
"smb: wrong password",
|
||||
"smb: login failed",
|
||||
"smb: unauthorized",
|
||||
"smb2认证失败",
|
||||
i18n.GetText("service_auth_failed"),
|
||||
"ntlm authentication failed",
|
||||
"ntlm auth failed",
|
||||
// NT Status codes
|
||||
|
||||
@@ -45,7 +45,7 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *c
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "smtp",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ func (p *SSHPlugin) identifyService(ctx context.Context, info *common.HostInfo,
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "ssh",
|
||||
Error: fmt.Errorf("无法识别为SSH服务"),
|
||||
Error: fmt.Errorf(i18n.Tr("service_not_identified", "SSH")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func (p *TelnetPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "telnet",
|
||||
Error: fmt.Errorf("没有可用的测试凭据"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_no_credentials")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func (p *TelnetPlugin) doTelnetAuth(ctx context.Context, info *common.HostInfo,
|
||||
resultChan <- &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeAuth,
|
||||
Error: fmt.Errorf("认证失败"),
|
||||
Error: fmt.Errorf(i18n.GetText("service_auth_failed")),
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -182,7 +182,7 @@ func (p *VNCPlugin) testUnauthAccess(ctx context.Context, info *common.HostInfo,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "vnc",
|
||||
Banner: "未授权访问",
|
||||
Banner: i18n.GetText("service_unauthorized"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user