mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
Expand i18n coverage
This commit is contained in:
@@ -164,17 +164,17 @@ func (p *ActiveMQPlugin) authenticateSTOMP(conn net.Conn, username, password str
|
||||
|
||||
_ = conn.SetWriteDeadline(time.Now().Add(timeout))
|
||||
if _, err := conn.Write([]byte(stompConnect)); err != nil {
|
||||
return false, fmt.Errorf("STOMP请求发送失败: %w", err)
|
||||
return false, fmt.Errorf("%s: %w", i18n.GetText("activemq_stomp_send_failed"), err)
|
||||
}
|
||||
|
||||
_ = conn.SetReadDeadline(time.Now().Add(timeout))
|
||||
response := make([]byte, 1024)
|
||||
n, err := conn.Read(response)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("STOMP响应读取失败: %w", err)
|
||||
return false, fmt.Errorf("%s: %w", i18n.GetText("activemq_stomp_read_failed"), err)
|
||||
}
|
||||
if n == 0 {
|
||||
return false, fmt.Errorf("STOMP无响应数据")
|
||||
return false, fmt.Errorf("%s", i18n.GetText("activemq_stomp_empty_response"))
|
||||
}
|
||||
|
||||
responseStr := string(response[:n])
|
||||
@@ -182,7 +182,7 @@ func (p *ActiveMQPlugin) authenticateSTOMP(conn net.Conn, username, password str
|
||||
if strings.Contains(responseStr, "CONNECTED") {
|
||||
return true, nil
|
||||
} else if strings.Contains(responseStr, "ERROR") {
|
||||
errorMsg := "STOMP认证错误"
|
||||
errorMsg := i18n.GetText("activemq_stomp_auth_error")
|
||||
if strings.Contains(responseStr, "Authentication failed") {
|
||||
errorMsg = "Authentication failed"
|
||||
} else if strings.Contains(responseStr, "Access denied") {
|
||||
@@ -193,7 +193,7 @@ func (p *ActiveMQPlugin) authenticateSTOMP(conn net.Conn, username, password str
|
||||
return false, fmt.Errorf("%s", errorMsg)
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("STOMP未知响应格式")
|
||||
return false, fmt.Errorf("%s", i18n.GetText("activemq_stomp_unknown_response"))
|
||||
}
|
||||
|
||||
// identifyService ActiveMQ服务识别
|
||||
@@ -236,7 +236,7 @@ func (p *ActiveMQPlugin) identifyService(ctx context.Context, info *common.HostI
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "activemq",
|
||||
Error: fmt.Errorf("无响应数据"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("activemq_stomp_empty_response")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ func (p *CassandraPlugin) tryNoAuthConnection(ctx context.Context, info *common.
|
||||
Type: plugins.ResultTypeService,
|
||||
Success: true,
|
||||
Service: "cassandra",
|
||||
Banner: fmt.Sprintf("Cassandra (无认证, 集群: %s)", dummy),
|
||||
Banner: i18n.Tr("cassandra_no_auth_cluster", dummy),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ func (p *CassandraPlugin) identifyService(ctx context.Context, info *common.Host
|
||||
state.IncrementTCPSuccessPacketCount()
|
||||
|
||||
if opcode == cqlOpAuthChl {
|
||||
banner := "Cassandra (需要认证)"
|
||||
banner := i18n.GetText("cassandra_auth_required")
|
||||
common.LogSuccess(i18n.Tr("cassandra_service", target, banner))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "cassandra", Banner: banner}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, s
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "elasticsearch",
|
||||
VulInfo: "未授权访问",
|
||||
VulInfo: i18n.GetText("unauthorized_access"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,27 +108,26 @@ type NetworkInfo struct {
|
||||
// Summary 返回网络信息摘要
|
||||
func (ni *NetworkInfo) Summary() string {
|
||||
if !ni.Valid {
|
||||
return "网络发现失败"
|
||||
return i18n.GetText("findnet_discovery_failed")
|
||||
}
|
||||
|
||||
var parts []string
|
||||
if ni.Hostname != "" {
|
||||
parts = append(parts, fmt.Sprintf("主机名: %s", ni.Hostname))
|
||||
parts = append(parts, i18n.Tr("findnet_hostname", ni.Hostname))
|
||||
}
|
||||
if len(ni.IPv4Addrs) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("IPv4: %d个", len(ni.IPv4Addrs)))
|
||||
parts = append(parts, i18n.Tr("findnet_ipv4_count", len(ni.IPv4Addrs)))
|
||||
}
|
||||
if len(ni.IPv6Addrs) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("IPv6: %d个", len(ni.IPv6Addrs)))
|
||||
parts = append(parts, i18n.Tr("findnet_ipv6_count", len(ni.IPv6Addrs)))
|
||||
}
|
||||
|
||||
if len(parts) == 0 {
|
||||
return "网络信息收集完成"
|
||||
return i18n.GetText("findnet_complete")
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
|
||||
// RPC数据包定义
|
||||
var (
|
||||
rpcBuffer1, _ = hex.DecodeString("05000b03100000004800000001000000b810b810000000000100000000000100c4fefc9960521b10bbcb00aa0021347a00000000045d888aeb1cc9119fe808002b10486002000000")
|
||||
@@ -140,24 +139,24 @@ var (
|
||||
func (p *FindNetPlugin) performNetworkDiscovery(conn net.Conn) (*NetworkInfo, error) {
|
||||
// 发送第一个RPC请求
|
||||
if _, err := conn.Write(rpcBuffer1); err != nil {
|
||||
return nil, fmt.Errorf("发送RPC请求1失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("findnet_rpc_request1_failed"), err)
|
||||
}
|
||||
|
||||
// 读取响应
|
||||
reply := make([]byte, 4096)
|
||||
if _, err := conn.Read(reply); err != nil {
|
||||
return nil, fmt.Errorf("读取RPC响应1失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("findnet_rpc_response1_failed"), err)
|
||||
}
|
||||
|
||||
// 发送第二个RPC请求
|
||||
if _, err := conn.Write(rpcBuffer2); err != nil {
|
||||
return nil, fmt.Errorf("发送RPC请求2失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("findnet_rpc_request2_failed"), err)
|
||||
}
|
||||
|
||||
// 读取网络信息响应
|
||||
n, err := conn.Read(reply)
|
||||
if err != nil || n < 42 {
|
||||
return nil, fmt.Errorf("读取RPC响应2失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("findnet_rpc_response2_failed"), err)
|
||||
}
|
||||
|
||||
// 解析响应数据
|
||||
|
||||
@@ -202,7 +202,7 @@ func (p *FTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostIn
|
||||
_ = result.Conn.Close()
|
||||
|
||||
var output strings.Builder
|
||||
output.WriteString(fmt.Sprintf("FTP %s 匿名访问 - %s:%s", target, cred.Username, cred.Password))
|
||||
output.WriteString(i18n.Tr("ftp_anonymous_access_detail", target, cred.Username, cred.Password))
|
||||
if len(fileList) > 0 {
|
||||
for _, file := range fileList {
|
||||
output.WriteString(fmt.Sprintf("\n [->] %s", file))
|
||||
@@ -216,7 +216,7 @@ func (p *FTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostIn
|
||||
Service: "ftp",
|
||||
Username: cred.Username,
|
||||
Password: cred.Password,
|
||||
Banner: "FTP匿名访问",
|
||||
Banner: i18n.GetText("ftp_anonymous_banner"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ func (p *KafkaPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
if err != nil {
|
||||
state.IncrementTCPFailedPacketCount()
|
||||
if p.isKafkaError(err) {
|
||||
banner := "Kafka (需要认证)"
|
||||
banner := i18n.GetText("kafka_auth_required")
|
||||
common.LogSuccess(i18n.Tr("kafka_service", target, banner))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "kafka", Banner: banner}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ func (p *LDAPPlugin) doLDAPAuth(ctx context.Context, info *common.HostInfo, cred
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeAuth,
|
||||
Error: fmt.Errorf("所有DN格式都失败"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("ldap_all_dn_failed")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ func (p *MemcachedPlugin) Scan(ctx context.Context, info *common.HostInfo, sessi
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "memcached",
|
||||
Error: fmt.Errorf("无法访问Memcached服务"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("memcached_access_failed")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (p *MemcachedPlugin) identifyService(ctx context.Context, info *common.Host
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "memcached",
|
||||
Error: fmt.Errorf("无法连接到Memcached服务"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("memcached_connect_failed")),
|
||||
}
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
@@ -49,7 +49,7 @@ func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "mongodb",
|
||||
VulInfo: "未授权访问",
|
||||
VulInfo: i18n.GetText("unauthorized_access"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,9 +148,9 @@ func (p *MongoDBPlugin) doMongoDBAuth(ctx context.Context, info *common.HostInfo
|
||||
// ── MongoDB wire protocol 工具 ──────────────────────────────────
|
||||
|
||||
const (
|
||||
opMsg uint32 = 2013
|
||||
opQuery uint32 = 2004
|
||||
opReply uint32 = 1
|
||||
opMsg uint32 = 2013
|
||||
opQuery uint32 = 2004
|
||||
opReply uint32 = 1
|
||||
)
|
||||
|
||||
var mongoRequestID uint32
|
||||
@@ -376,11 +376,11 @@ func (p *MongoDBPlugin) identifyService(ctx context.Context, info *common.HostIn
|
||||
|
||||
if isUnauth {
|
||||
common.LogVuln(i18n.Tr("mongodb_unauth", target))
|
||||
return &ScanResult{Type: plugins.ResultTypeVuln, Success: true, Service: "mongodb", VulInfo: "未授权访问"}
|
||||
return &ScanResult{Type: plugins.ResultTypeVuln, Success: true, Service: "mongodb", VulInfo: i18n.GetText("unauthorized_access")}
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("mongodb_auth_required", target))
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "mongodb", Banner: "需要认证"}
|
||||
return &ScanResult{Type: plugins.ResultTypeService, Success: true, Service: "mongodb", Banner: i18n.GetText("auth_required")}
|
||||
}
|
||||
|
||||
func (p *MongoDBPlugin) mongodbUnauth(ctx context.Context, info *common.HostInfo, session *common.ScanSession) (bool, error) {
|
||||
@@ -439,7 +439,7 @@ func (p *MongoDBPlugin) checkMongoAuth(ctx context.Context, address string, pack
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return "", fmt.Errorf("收到空响应")
|
||||
return "", fmt.Errorf("%s", i18n.GetText("empty_response_received"))
|
||||
}
|
||||
|
||||
return string(reply[:count]), nil
|
||||
|
||||
+47
-47
@@ -43,7 +43,7 @@ func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "ms17010",
|
||||
Error: fmt.Errorf("MS17010漏洞检测仅支持445端口"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("ms17010_port_only")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,14 +71,14 @@ func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "ms17010",
|
||||
Banner: fmt.Sprintf("MS17-010漏洞 (%s)", osVersion),
|
||||
Banner: i18n.Tr("ms17010_vuln_banner", osVersion),
|
||||
}
|
||||
}
|
||||
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "ms17010",
|
||||
Error: fmt.Errorf("目标不存在MS17-010漏洞"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("ms17010_not_vulnerable")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,12 +89,12 @@ func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, cred
|
||||
common.LogSuccess(i18n.Tr("ms17010_start", target))
|
||||
|
||||
var output strings.Builder
|
||||
output.WriteString(fmt.Sprintf("=== MS17-010漏洞利用结果 - %s ===\n", target))
|
||||
output.WriteString(i18n.Tr("ms17010_exploit_header", target) + "\n")
|
||||
|
||||
// 首先确认漏洞存在
|
||||
vulnerable, osVersion, hasBackdoor, err := p.checkMS17010Vulnerability(ctx, info.Host, session)
|
||||
if err != nil {
|
||||
output.WriteString(fmt.Sprintf("\n[漏洞检测失败] %v\n", err))
|
||||
output.WriteString("\n" + i18n.Tr("ms17010_exploit_check_failed", err) + "\n")
|
||||
return &ExploitResult{
|
||||
Success: false,
|
||||
Output: output.String(),
|
||||
@@ -103,58 +103,58 @@ func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, cred
|
||||
}
|
||||
|
||||
if !vulnerable {
|
||||
output.WriteString("\n[漏洞状态] 目标不存在MS17-010漏洞\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_not_vulnerable") + "\n")
|
||||
return &ExploitResult{
|
||||
Success: false,
|
||||
Output: output.String(),
|
||||
Error: fmt.Errorf("目标不存在MS17-010漏洞"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("ms17010_not_vulnerable")),
|
||||
}
|
||||
}
|
||||
|
||||
output.WriteString("\n[漏洞确认] ✅ MS17-010漏洞存在\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_confirmed") + "\n")
|
||||
if osVersion != "" {
|
||||
output.WriteString(fmt.Sprintf("[操作系统] %s\n", osVersion))
|
||||
output.WriteString(i18n.Tr("ms17010_exploit_os", osVersion) + "\n")
|
||||
}
|
||||
|
||||
if hasBackdoor {
|
||||
output.WriteString("\n[后门检测] ⚠️ 发现DOUBLEPULSAR后门\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_backdoor_found") + "\n")
|
||||
} else {
|
||||
output.WriteString("\n[后门检测] 未发现DOUBLEPULSAR后门\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_backdoor_not_found") + "\n")
|
||||
}
|
||||
|
||||
// 如果有Shellcode配置,执行实际利用
|
||||
if config.Shellcode != "" {
|
||||
output.WriteString(fmt.Sprintf("\n[利用模式] %s\n", config.Shellcode))
|
||||
output.WriteString("[利用状态] 开始执行EternalBlue攻击...\n")
|
||||
output.WriteString("\n" + i18n.Tr("ms17010_exploit_mode", config.Shellcode) + "\n")
|
||||
output.WriteString(i18n.GetText("ms17010_exploit_start_attack") + "\n")
|
||||
|
||||
// 执行实际的MS17010利用
|
||||
err = p.executeMS17010Exploit(info, session)
|
||||
if err != nil {
|
||||
output.WriteString(fmt.Sprintf("[利用结果] ❌ 利用失败: %v\n", err))
|
||||
output.WriteString(i18n.Tr("ms17010_exploit_failed", err) + "\n")
|
||||
return &ExploitResult{
|
||||
Success: false,
|
||||
Output: output.String(),
|
||||
Error: err,
|
||||
}
|
||||
}
|
||||
output.WriteString("[利用结果] ✅ 漏洞利用成功完成\n")
|
||||
output.WriteString(i18n.GetText("ms17010_exploit_success") + "\n")
|
||||
|
||||
// 根据不同类型提供后续操作建议
|
||||
switch config.Shellcode {
|
||||
case "bind":
|
||||
output.WriteString("\n[连接建议] 使用以下命令连接Bind Shell:\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_bind_hint") + "\n")
|
||||
output.WriteString(fmt.Sprintf(" nc %s 64531\n", info.Host))
|
||||
case "add":
|
||||
output.WriteString("\n[访问建议] 已添加管理员账户,可以通过以下方式连接:\n")
|
||||
output.WriteString(" 用户名: sysadmin 密码: 1qaz@WSX!@#4\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_add_hint") + "\n")
|
||||
output.WriteString(i18n.GetText("ms17010_exploit_add_credential") + "\n")
|
||||
output.WriteString(fmt.Sprintf(" RDP: mstsc /v:%s\n", info.Host))
|
||||
case "guest":
|
||||
output.WriteString("\n[访问建议] 已激活Guest账户,可以直接远程连接\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_guest_hint") + "\n")
|
||||
}
|
||||
} else {
|
||||
output.WriteString("\n[利用模式] 仅检测模式 (未配置Shellcode)\n")
|
||||
output.WriteString("[建议] 可使用 -sc 参数配置Shellcode进行实际利用\n")
|
||||
output.WriteString(" 支持的模式: bind, add, guest 或自定义shellcode\n")
|
||||
output.WriteString("\n" + i18n.GetText("ms17010_exploit_detect_only") + "\n")
|
||||
output.WriteString(i18n.GetText("ms17010_exploit_shellcode_hint") + "\n")
|
||||
output.WriteString(i18n.GetText("ms17010_exploit_supported_modes") + "\n")
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("ms17010_complete", target))
|
||||
@@ -171,17 +171,17 @@ func (p *MS17010Plugin) Exploit(ctx context.Context, info *common.HostInfo, cred
|
||||
func aesDecrypt(crypted string, key string) (string, error) {
|
||||
cryptedBytes, err := base64.StdEncoding.DecodeString(crypted)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("base64解码失败: %w", err)
|
||||
return "", fmt.Errorf("%s: %w", i18n.GetText("ms17010_base64_decode_failed"), err)
|
||||
}
|
||||
|
||||
keyBytes := []byte(key)
|
||||
block, err := aes.NewCipher(keyBytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建AES密码块失败: %w", err)
|
||||
return "", fmt.Errorf("%s: %w", i18n.GetText("ms17010_aes_cipher_failed"), err)
|
||||
}
|
||||
|
||||
if len(cryptedBytes) < aes.BlockSize {
|
||||
return "", fmt.Errorf("密文长度过短")
|
||||
return "", fmt.Errorf("%s", i18n.GetText("ms17010_ciphertext_too_short"))
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, keyBytes[:aes.BlockSize])
|
||||
@@ -190,12 +190,12 @@ func aesDecrypt(crypted string, key string) (string, error) {
|
||||
// 移除PKCS7填充
|
||||
padding := int(cryptedBytes[len(cryptedBytes)-1])
|
||||
if padding > len(cryptedBytes) || padding > aes.BlockSize {
|
||||
return "", fmt.Errorf("无效的填充")
|
||||
return "", fmt.Errorf("%s", i18n.GetText("ms17010_invalid_padding"))
|
||||
}
|
||||
|
||||
for i := len(cryptedBytes) - padding; i < len(cryptedBytes); i++ {
|
||||
if cryptedBytes[i] != byte(padding) {
|
||||
return "", fmt.Errorf("填充验证失败")
|
||||
return "", fmt.Errorf("%s", i18n.GetText("ms17010_padding_check_failed"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,42 +293,42 @@ func (p *MS17010Plugin) checkMS17010Vulnerability(ctx context.Context, ip string
|
||||
func (p *MS17010Plugin) checkMS17010VulnerabilityAt(ctx context.Context, address string, session *common.ScanSession) (bool, string, bool, error) {
|
||||
conn, err := session.DialTCP(ctx, "tcp", address, session.Config.Timeout)
|
||||
if err != nil {
|
||||
return false, "", false, fmt.Errorf("连接错误: %w", err)
|
||||
return false, "", false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_connection_error"), err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
if err = conn.SetDeadline(time.Now().Add(session.Config.Timeout)); err != nil {
|
||||
return false, "", false, fmt.Errorf("设置超时错误: %w", err)
|
||||
return false, "", false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_set_timeout_error"), err)
|
||||
}
|
||||
|
||||
// SMB协议协商
|
||||
if _, err = conn.Write(negotiateProtocolRequest); err != nil {
|
||||
return false, "", false, fmt.Errorf("发送协议请求错误: %w", err)
|
||||
return false, "", false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_send_protocol_error"), err)
|
||||
}
|
||||
|
||||
reply := make([]byte, 1024)
|
||||
n, readErr := conn.Read(reply)
|
||||
if readErr != nil || n < 36 {
|
||||
// 连接被关闭或响应不完整,通常表示目标不支持SMBv1
|
||||
return false, "", false, fmt.Errorf("目标可能不支持SMBv1")
|
||||
return false, "", false, fmt.Errorf("%s", i18n.GetText("ms17010_smbv1_unsupported"))
|
||||
}
|
||||
|
||||
if binary.LittleEndian.Uint32(reply[9:13]) != 0 {
|
||||
return false, "", false, fmt.Errorf("SMBv1协议协商被拒绝")
|
||||
return false, "", false, fmt.Errorf("%s", i18n.GetText("ms17010_smbv1_rejected"))
|
||||
}
|
||||
|
||||
// 建立会话
|
||||
if _, err = conn.Write(sessionSetupRequest); err != nil {
|
||||
return false, "", false, fmt.Errorf("发送会话请求错误: %w", err)
|
||||
return false, "", false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_send_session_error"), err)
|
||||
}
|
||||
|
||||
n, readErr = conn.Read(reply)
|
||||
if readErr != nil || n < 36 {
|
||||
return false, "", false, fmt.Errorf("SMB会话建立失败")
|
||||
return false, "", false, fmt.Errorf("%s", i18n.GetText("ms17010_session_failed"))
|
||||
}
|
||||
|
||||
if binary.LittleEndian.Uint32(reply[9:13]) != 0 {
|
||||
return false, "", false, fmt.Errorf("SMB会话被拒绝")
|
||||
return false, "", false, fmt.Errorf("%s", i18n.GetText("ms17010_session_rejected"))
|
||||
}
|
||||
|
||||
// 提取系统信息
|
||||
@@ -354,15 +354,15 @@ func (p *MS17010Plugin) checkMS17010VulnerabilityAt(ctx context.Context, address
|
||||
treeConnect[33] = userID[1]
|
||||
|
||||
if _, err = conn.Write(treeConnect); err != nil {
|
||||
return false, osVersion, false, fmt.Errorf("发送树连接请求错误: %w", err)
|
||||
return false, osVersion, false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_send_tree_error"), err)
|
||||
}
|
||||
|
||||
n, readErr = conn.Read(reply)
|
||||
if readErr != nil || n < 36 {
|
||||
if readErr != nil {
|
||||
return false, osVersion, false, fmt.Errorf("读取树连接响应错误: %w", readErr)
|
||||
return false, osVersion, false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_read_tree_error"), readErr)
|
||||
}
|
||||
return false, osVersion, false, fmt.Errorf("树连接响应不完整")
|
||||
return false, osVersion, false, fmt.Errorf("%s", i18n.GetText("ms17010_tree_response_incomplete"))
|
||||
}
|
||||
|
||||
// 命名管道请求
|
||||
@@ -374,15 +374,15 @@ func (p *MS17010Plugin) checkMS17010VulnerabilityAt(ctx context.Context, address
|
||||
transNamedPipe[33] = userID[1]
|
||||
|
||||
if _, err = conn.Write(transNamedPipe); err != nil {
|
||||
return false, osVersion, false, fmt.Errorf("发送管道请求错误: %w", err)
|
||||
return false, osVersion, false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_send_pipe_error"), err)
|
||||
}
|
||||
|
||||
n, readErr = conn.Read(reply)
|
||||
if readErr != nil || n < 36 {
|
||||
if readErr != nil {
|
||||
return false, osVersion, false, fmt.Errorf("读取管道响应错误: %w", readErr)
|
||||
return false, osVersion, false, fmt.Errorf("%s: %w", i18n.GetText("ms17010_read_pipe_error"), readErr)
|
||||
}
|
||||
return false, osVersion, false, fmt.Errorf("管道响应不完整")
|
||||
return false, osVersion, false, fmt.Errorf("%s", i18n.GetText("ms17010_pipe_response_incomplete"))
|
||||
}
|
||||
|
||||
// 漏洞检测 - 关键检查点
|
||||
@@ -420,7 +420,7 @@ func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *co
|
||||
var err error
|
||||
sc, err = aesDecrypt(scEnc, defaultKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解密bind shellcode失败: %w", err)
|
||||
return fmt.Errorf("%s: %w", i18n.GetText("ms17010_bind_shellcode_decrypt_failed"), err)
|
||||
}
|
||||
|
||||
case "add":
|
||||
@@ -429,7 +429,7 @@ func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *co
|
||||
var err error
|
||||
sc, err = aesDecrypt(scEnc, defaultKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解密add shellcode失败: %w", err)
|
||||
return fmt.Errorf("%s: %w", i18n.GetText("ms17010_add_shellcode_decrypt_failed"), err)
|
||||
}
|
||||
|
||||
case "guest":
|
||||
@@ -438,7 +438,7 @@ func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *co
|
||||
var err error
|
||||
sc, err = aesDecrypt(scEnc, defaultKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解密guest shellcode失败: %w", err)
|
||||
return fmt.Errorf("%s: %w", i18n.GetText("ms17010_guest_shellcode_decrypt_failed"), err)
|
||||
}
|
||||
|
||||
case "cs":
|
||||
@@ -450,7 +450,7 @@ func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *co
|
||||
if strings.Contains(shellcode, "file:") {
|
||||
read, err := os.ReadFile(shellcode[5:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取Shellcode文件失败: %w", err)
|
||||
return fmt.Errorf("%s: %w", i18n.GetText("ms17010_shellcode_file_read_failed"), err)
|
||||
}
|
||||
sc = fmt.Sprintf("%x", read)
|
||||
} else {
|
||||
@@ -460,13 +460,13 @@ func (p *MS17010Plugin) executeMS17010Exploit(info *common.HostInfo, session *co
|
||||
|
||||
// 验证shellcode有效性
|
||||
if len(sc) < 20 {
|
||||
return fmt.Errorf("无效的Shellcode")
|
||||
return fmt.Errorf("%s", i18n.GetText("ms17010_invalid_shellcode"))
|
||||
}
|
||||
|
||||
// 解码shellcode
|
||||
scBytes, err := hex.DecodeString(sc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("shellcode解码失败: %w", err)
|
||||
return fmt.Errorf("%s: %w", i18n.GetText("ms17010_shellcode_decode_failed"), err)
|
||||
}
|
||||
|
||||
if err = eternalBlue(net.JoinHostPort(info.Host, "445"), 12, 12, scBytes); err != nil {
|
||||
|
||||
@@ -117,7 +117,7 @@ func (p *Neo4jPlugin) doNeo4jAuth(ctx context.Context, info *common.HostInfo, cr
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeUnknown,
|
||||
Error: fmt.Errorf("未知错误,状态码: %d", resp.StatusCode),
|
||||
Error: fmt.Errorf(i18n.GetText("unknown_status_code")+": %d", resp.StatusCode),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-14
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins"
|
||||
)
|
||||
|
||||
@@ -39,7 +40,7 @@ func (p *NetBIOSPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "netbios",
|
||||
Error: fmt.Errorf("NetBIOS插件仅支持137和139端口"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("netbios_port_only")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +67,7 @@ func (p *NetBIOSPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "netbios",
|
||||
Error: fmt.Errorf("未发现有效的NetBIOS信息"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("netbios_info_not_found")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ func (p *NetBIOSPlugin) Scan(ctx context.Context, info *common.HostInfo, session
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeService,
|
||||
Type: plugins.ResultTypeService,
|
||||
Service: "netbios",
|
||||
Banner: netbiosInfo.Summary(),
|
||||
}
|
||||
@@ -164,7 +165,7 @@ func (p *NetBIOSPlugin) queryNetBIOSNames(host string, config *common.Config, st
|
||||
|
||||
conn, err := net.DialTimeout("udp", target, config.Timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接NetBIOS名称服务失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_name_connect_failed"), err)
|
||||
}
|
||||
state.IncrementUDPPacketCount()
|
||||
defer func() { _ = conn.Close() }()
|
||||
@@ -173,13 +174,13 @@ func (p *NetBIOSPlugin) queryNetBIOSNames(host string, config *common.Config, st
|
||||
|
||||
_, err = conn.Write(queryPacket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送NetBIOS查询失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_query_send_failed"), err)
|
||||
}
|
||||
|
||||
response := make([]byte, 1024)
|
||||
n, err := conn.Read(response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取NetBIOS响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_response_read_failed"), err)
|
||||
}
|
||||
|
||||
return p.parseNetBIOSNames(response[:n])
|
||||
@@ -191,7 +192,7 @@ func (p *NetBIOSPlugin) queryNetBIOSSession(ctx context.Context, host string, se
|
||||
|
||||
conn, err := session.DialTCP(ctx, "tcp", target, session.Config.Timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("连接NetBIOS会话服务失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_session_connect_failed"), err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
@@ -212,13 +213,13 @@ func (p *NetBIOSPlugin) queryNetBIOSSession(ctx context.Context, host string, se
|
||||
|
||||
_, err = conn.Write(smbNegotiate1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMB协商1失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_smb_negotiate_send_failed"), err)
|
||||
}
|
||||
|
||||
response1 := make([]byte, 1024)
|
||||
_, err = conn.Read(response1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取SMB协商1响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_smb_negotiate_read_failed"), err)
|
||||
}
|
||||
|
||||
// 发送Session Setup请求
|
||||
@@ -244,13 +245,13 @@ func (p *NetBIOSPlugin) queryNetBIOSSession(ctx context.Context, host string, se
|
||||
|
||||
_, err = conn.Write(smbSessionSetup)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMB Session Setup失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_smb_session_send_failed"), err)
|
||||
}
|
||||
|
||||
response2 := make([]byte, 2048)
|
||||
n, err := conn.Read(response2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取SMB Session Setup响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("netbios_smb_session_read_failed"), err)
|
||||
}
|
||||
|
||||
return p.parseNetBIOSSession(response2[:n])
|
||||
@@ -261,13 +262,13 @@ func (p *NetBIOSPlugin) parseNetBIOSNames(data []byte) (*NetBIOSInfo, error) {
|
||||
info := &NetBIOSInfo{Valid: false}
|
||||
|
||||
if len(data) < 57 {
|
||||
return info, fmt.Errorf("NetBIOS响应数据过短")
|
||||
return info, fmt.Errorf("%s", i18n.GetText("netbios_response_too_short"))
|
||||
}
|
||||
|
||||
// 获取名称记录数量
|
||||
numNames := int(data[56])
|
||||
if numNames == 0 {
|
||||
return info, fmt.Errorf("没有NetBIOS名称记录")
|
||||
return info, fmt.Errorf("%s", i18n.GetText("netbios_no_name_records"))
|
||||
}
|
||||
|
||||
nameData := data[57:]
|
||||
@@ -333,7 +334,7 @@ func (p *NetBIOSPlugin) parseNetBIOSSession(data []byte) (*NetBIOSInfo, error) {
|
||||
info := &NetBIOSInfo{Valid: false}
|
||||
|
||||
if len(data) < 47 {
|
||||
return info, fmt.Errorf("SMB响应数据过短")
|
||||
return info, fmt.Errorf("%s", i18n.GetText("netbios_smb_response_too_short"))
|
||||
}
|
||||
|
||||
info.Valid = true
|
||||
|
||||
@@ -101,7 +101,7 @@ func (p *OraclePlugin) doOracleAuth(ctx context.Context, info *common.HostInfo,
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeNetwork,
|
||||
Error: fmt.Errorf("无法连接到Oracle数据库"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("oracle_connect_failed")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ func (p *OraclePlugin) testUnauthorizedAccess(ctx context.Context, info *common.
|
||||
Service: "oracle",
|
||||
Username: cred.Username,
|
||||
Password: cred.Password,
|
||||
Banner: "未授权访问 - 默认账户",
|
||||
Banner: i18n.GetText("oracle_default_account_banner"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,11 +184,11 @@ func (p *PostgreSQLPlugin) testUnauthorizedAccess(ctx context.Context, info *com
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "postgresql",
|
||||
VulInfo: "未授权访问(trust认证)",
|
||||
VulInfo: i18n.GetText("postgresql_trust_unauth"),
|
||||
}
|
||||
}
|
||||
|
||||
vulInfo := fmt.Sprintf("未授权访问(trust认证) - %s", version)
|
||||
vulInfo := i18n.Tr("postgresql_trust_unauth_version", version)
|
||||
if len(vulInfo) > 100 {
|
||||
vulInfo = vulInfo[:100] + "..."
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func (p *RabbitMQPlugin) doRabbitMQAuth(ctx context.Context, info *common.HostIn
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeUnknown,
|
||||
Error: fmt.Errorf("意外响应状态码: %d", resp.StatusCode),
|
||||
Error: fmt.Errorf(i18n.GetText("unexpected_status_code")+": %d", resp.StatusCode),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ func (p *RabbitMQPlugin) testUnauthorizedAccess(ctx context.Context, info *commo
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "rabbitmq",
|
||||
Banner: "未授权访问 - guest默认密码",
|
||||
Banner: i18n.GetText("rabbitmq_guest_default_password"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
common.LogSuccess(i18n.Tr("rdp_service", target, banner))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeService,
|
||||
Type: plugins.ResultTypeService,
|
||||
Service: "rdp",
|
||||
Banner: banner,
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeCredential,
|
||||
Type: plugins.ResultTypeCredential,
|
||||
Service: "rdp",
|
||||
Username: cred.Username,
|
||||
Password: cred.Password,
|
||||
@@ -144,7 +144,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("%s", i18n.GetText("rdp_port_closed")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ func (p *RDPPlugin) logOSInfo(target string, osInfo map[string]any) {
|
||||
// buildBanner 构建服务识别Banner
|
||||
func (p *RDPPlugin) buildBanner(osInfo map[string]any) string {
|
||||
if len(osInfo) == 0 {
|
||||
return "RDP远程桌面服务"
|
||||
return i18n.GetText("rdp_remote_desktop_service")
|
||||
}
|
||||
|
||||
osVersion := p.extractStringField(osInfo, "OsVerion")
|
||||
@@ -256,7 +256,7 @@ func (p *RDPPlugin) buildBanner(osInfo map[string]any) string {
|
||||
return fmt.Sprintf("RDP (Hostname:%s)", hostname)
|
||||
}
|
||||
|
||||
return "RDP远程桌面服务"
|
||||
return i18n.GetText("rdp_remote_desktop_service")
|
||||
}
|
||||
|
||||
// extractStringField 安全提取字符串字段
|
||||
|
||||
@@ -170,7 +170,7 @@ func (p *RedisPlugin) doRedisAuth(ctx context.Context, info *common.HostInfo, cr
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeUnknown,
|
||||
Error: fmt.Errorf("redis PING测试失败: %s", strings.TrimSpace(responseStr)),
|
||||
Error: fmt.Errorf("%s", i18n.Tr("redis_ping_failed", strings.TrimSpace(responseStr))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ func (p *RedisPlugin) testUnauthorizedAccess(ctx context.Context, info *common.H
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Success: true,
|
||||
Service: "redis",
|
||||
VulInfo: "未授权访问",
|
||||
VulInfo: i18n.GetText("unauthorized_access"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,13 +288,13 @@ func (p *RedisPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
var banner string
|
||||
|
||||
if strings.Contains(responseStr, "PONG") {
|
||||
banner = "Redis服务 (PONG响应)"
|
||||
banner = i18n.GetText("redis_service_pong")
|
||||
} else if strings.Contains(responseStr, "-NOAUTH") {
|
||||
banner = "Redis服务 (需要认证)"
|
||||
banner = i18n.GetText("redis_service_auth_required")
|
||||
} else if strings.Contains(responseStr, "-ERR") {
|
||||
banner = "Redis服务 (协议响应)"
|
||||
banner = i18n.GetText("redis_service_protocol_response")
|
||||
} else {
|
||||
banner = "Redis服务"
|
||||
banner = i18n.GetText("redis_service_plain")
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("redis_service_identified", target, banner)) //nolint:govet
|
||||
@@ -552,10 +552,10 @@ func (p *RedisPlugin) writeKey(conn net.Conn, filename string) (flag bool, text
|
||||
// 读取密钥文件
|
||||
key, err := p.readFile(filename)
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("读取密钥文件 %s 失败: %v", filename, err), err
|
||||
return false, i18n.Tr("redis_key_file_read_failed", filename, err), err
|
||||
}
|
||||
if len(key) == 0 {
|
||||
return false, fmt.Sprintf("密钥文件 %s 为空", filename), nil
|
||||
return false, i18n.Tr("redis_key_file_empty", filename), nil
|
||||
}
|
||||
|
||||
// 写入密钥
|
||||
@@ -596,7 +596,7 @@ func (p *RedisPlugin) writeCron(conn net.Conn, host string) (flag bool, text str
|
||||
// 解析目标地址
|
||||
target := strings.Split(host, ":")
|
||||
if len(target) < 2 {
|
||||
return false, "主机地址格式错误", nil
|
||||
return false, i18n.GetText("redis_host_format_invalid"), nil
|
||||
}
|
||||
scanIp, scanPort := target[0], target[1]
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ func (p *RsyncPlugin) doRsyncAuth(ctx context.Context, info *common.HostInfo, cr
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeNetwork,
|
||||
Error: fmt.Errorf("无法连接到Rsync服务"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("rsync_connect_failed")),
|
||||
}
|
||||
}
|
||||
modules := p.getModules(conn, session.Config)
|
||||
@@ -120,7 +120,7 @@ func (p *RsyncPlugin) doRsyncAuth(ctx context.Context, info *common.HostInfo, cr
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeUnknown,
|
||||
Error: fmt.Errorf("无法获取模块列表"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("rsync_modules_failed")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ func (p *RsyncPlugin) testUnauthorizedAccess(ctx context.Context, info *common.H
|
||||
modules := p.getModules(conn, session.Config)
|
||||
|
||||
if len(modules) > 0 {
|
||||
banner := fmt.Sprintf("未授权访问 - 可用模块: %s", strings.Join(modules, ", "))
|
||||
banner := i18n.Tr("rsync_unauth_modules", strings.Join(modules, ", "))
|
||||
return &ScanResult{
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeService,
|
||||
@@ -328,7 +328,7 @@ func (p *RsyncPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "rsync",
|
||||
Error: fmt.Errorf("无法连接到Rsync服务"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("rsync_connect_failed")),
|
||||
}
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
@@ -363,12 +363,12 @@ func (p *RsyncPlugin) identifyService(ctx context.Context, info *common.HostInfo
|
||||
lines := strings.Split(responseStr, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "@RSYNCD:") {
|
||||
banner = fmt.Sprintf("Rsync服务 (%s)", strings.TrimSpace(line))
|
||||
banner = i18n.Tr("rsync_service_info", strings.TrimSpace(line))
|
||||
break
|
||||
}
|
||||
}
|
||||
if banner == "" {
|
||||
banner = "Rsync文件同步服务"
|
||||
banner = i18n.GetText("rsync_file_sync_service")
|
||||
}
|
||||
} else {
|
||||
return &ScanResult{
|
||||
|
||||
@@ -34,7 +34,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "smb",
|
||||
Error: fmt.Errorf("SMB插件仅支持139和445端口"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("smb_port_only")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
return &ScanResult{
|
||||
Success: false,
|
||||
Service: "smb",
|
||||
Error: fmt.Errorf("SMB协议探测失败: %w", err),
|
||||
Error: fmt.Errorf("%s: %w", i18n.GetText("smb_probe_failed"), err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, session *co
|
||||
if result := p.testUnauthorizedAccess(ctx, info, auth, config, state, session); result != nil && result.Success {
|
||||
var successMsg string
|
||||
if config.Credentials.Domain != "" {
|
||||
successMsg = fmt.Sprintf("SMB %s 未授权访问 - %s\\%s:%s", target, config.Credentials.Domain, result.Username, result.Password)
|
||||
successMsg = i18n.Tr("smb_unauth_domain_access", target, config.Credentials.Domain, result.Username, result.Password)
|
||||
} else {
|
||||
successMsg = fmt.Sprintf("SMB %s 未授权访问 - %s:%s", target, result.Username, result.Password)
|
||||
successMsg = i18n.Tr("smb_unauth_access", target, result.Username, result.Password)
|
||||
}
|
||||
common.LogVuln(successMsg)
|
||||
return result
|
||||
@@ -143,7 +143,7 @@ func (p *SmbPlugin) testUnauthorizedAccess(ctx context.Context, info *common.Hos
|
||||
if displayUser == "" {
|
||||
displayUser = "<empty>"
|
||||
}
|
||||
output.WriteString(fmt.Sprintf("SMB %s 匿名访问 - %s:%s", target, displayUser, cred.Password))
|
||||
output.WriteString(i18n.Tr("smb_anonymous_access_detail", target, displayUser, cred.Password))
|
||||
for _, share := range shareInfo {
|
||||
output.WriteString(fmt.Sprintf("\n%s", share))
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (p *SmbPlugin) testUnauthorizedAccess(ctx context.Context, info *common.Hos
|
||||
Service: "smb",
|
||||
Username: cred.Username,
|
||||
Password: cred.Password,
|
||||
Banner: "SMB匿名访问",
|
||||
Banner: i18n.GetText("smb_anonymous_banner"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,13 +216,13 @@ func probeTarget(ctx context.Context, host string, port int, timeout time.Durati
|
||||
// 首先尝试SMBv1协商
|
||||
_, err = conn.Write(smbv1NegotiatePacket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMBv1协商包失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv1_negotiate_send_failed"), err)
|
||||
}
|
||||
|
||||
// 读取SMBv1协商响应
|
||||
r1, err := readSMBMessage(conn)
|
||||
if err != nil {
|
||||
common.LogDebug(fmt.Sprintf("读取SMBv1协商响应失败: %v", err))
|
||||
common.LogDebug(i18n.Tr("smbv1_negotiate_read_failed", err))
|
||||
}
|
||||
|
||||
// 检查是否支持SMBv1
|
||||
@@ -239,12 +239,12 @@ func probeSMBv1(conn net.Conn, target string, timeout time.Duration) (*SMBTarget
|
||||
// 发送Session Setup请求
|
||||
_, err := conn.Write(smbv1SessionSetupPacket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMBv1 Session Setup失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv1_session_send_failed"), err)
|
||||
}
|
||||
|
||||
ret, err := readSMBMessage(conn)
|
||||
if err != nil || len(ret) < 47 {
|
||||
return nil, fmt.Errorf("读取SMBv1 Session Setup响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv1_session_read_failed"), err)
|
||||
}
|
||||
|
||||
info := &SMBTarget{
|
||||
@@ -301,12 +301,12 @@ func probeSMBv2(ctx context.Context, target string, timeout time.Duration, sessi
|
||||
// 发送SMBv2协商包
|
||||
_, err = conn2.Write(smbv2NegotiatePacket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMBv2协商包失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv2_negotiate_send_failed"), err)
|
||||
}
|
||||
|
||||
r2, err := readSMBMessage(conn2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取SMBv2协商响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv2_negotiate_read_failed"), err)
|
||||
}
|
||||
|
||||
// 构建NTLM数据包
|
||||
@@ -322,23 +322,23 @@ func probeSMBv2(ctx context.Context, target string, timeout time.Duration, sessi
|
||||
// 发送Session Setup
|
||||
_, err = conn2.Write(smbv2SessionSetupPacket)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMBv2 Session Setup失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv2_session_send_failed"), err)
|
||||
}
|
||||
|
||||
_, err = readSMBMessage(conn2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取SMBv2 Session Setup响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv2_session_read_failed"), err)
|
||||
}
|
||||
|
||||
// 发送NTLM协商包
|
||||
_, err = conn2.Write(ntlmData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送SMBv2 NTLM包失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv2_ntlm_send_failed"), err)
|
||||
}
|
||||
|
||||
ret, err := readSMBMessage(conn2)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取SMBv2 NTLM响应失败: %w", err)
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("smbv2_ntlm_read_failed"), err)
|
||||
}
|
||||
|
||||
ntlmOff := bytes.Index(ret, []byte("NTLMSSP"))
|
||||
@@ -455,7 +455,7 @@ func (a *SMB1Authenticator) Authenticate(ctx context.Context, host string, port
|
||||
return &AuthResult{
|
||||
Success: false,
|
||||
ErrorType: ErrorTypeNetwork,
|
||||
Error: fmt.Errorf("连接超时"),
|
||||
Error: fmt.Errorf("%s", i18n.GetText("connection_timeout")),
|
||||
}, nil
|
||||
case <-ctx.Done():
|
||||
go func() {
|
||||
@@ -701,13 +701,13 @@ func readSMBMessage(conn net.Conn) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
if n != 4 {
|
||||
return nil, fmt.Errorf("NetBIOS头部长度不足: %d", n)
|
||||
return nil, fmt.Errorf(i18n.GetText("netbios_header_too_short")+": %d", n)
|
||||
}
|
||||
|
||||
messageLength := int(headerBuf[0])<<24 | int(headerBuf[1])<<16 | int(headerBuf[2])<<8 | int(headerBuf[3])
|
||||
|
||||
if messageLength > 1024*1024 {
|
||||
return nil, fmt.Errorf("消息长度过大: %d", messageLength)
|
||||
return nil, fmt.Errorf(i18n.GetText("message_length_too_large")+": %d", messageLength)
|
||||
}
|
||||
|
||||
if messageLength == 0 {
|
||||
|
||||
@@ -269,7 +269,7 @@ func (p *SMTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostI
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "smtp",
|
||||
Banner: "未授权访问 - 允许匿名邮件发送",
|
||||
Banner: i18n.GetText("smtp_anonymous_mail_allowed"),
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -321,7 +321,7 @@ func (p *SMTPPlugin) testOpenRelay(ctx context.Context, info *common.HostInfo, s
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "smtp",
|
||||
Banner: "未授权访问 - 开放中继",
|
||||
Banner: i18n.GetText("smtp_open_relay"),
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -386,7 +386,7 @@ func (p *SMTPPlugin) testVRFYCommand(ctx context.Context, info *common.HostInfo,
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "smtp",
|
||||
Banner: fmt.Sprintf("未授权访问 - VRFY命令枚举用户(%s)", user),
|
||||
Banner: i18n.Tr("smtp_vrfy_user_enum", user),
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -456,7 +456,7 @@ func (p *SMTPPlugin) testEXPNCommand(ctx context.Context, info *common.HostInfo,
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "smtp",
|
||||
Banner: fmt.Sprintf("未授权访问 - EXPN命令枚举邮件列表(%s)", list),
|
||||
Banner: i18n.Tr("smtp_expn_list_enum", list),
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -522,7 +522,7 @@ func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo,
|
||||
var banner string
|
||||
|
||||
if serverInfo != "" {
|
||||
banner = fmt.Sprintf("SMTP邮件服务 (%s)", serverInfo)
|
||||
banner = i18n.Tr("smtp_mail_service_info", serverInfo)
|
||||
} else {
|
||||
conn, err := session.DialTCP(ctx, "tcp", target, session.Config.Timeout)
|
||||
if err != nil {
|
||||
@@ -533,7 +533,7 @@ func (p *SMTPPlugin) identifyService(ctx context.Context, info *common.HostInfo,
|
||||
}
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
banner = "SMTP邮件服务"
|
||||
banner = i18n.GetText("smtp_mail_service")
|
||||
}
|
||||
|
||||
common.LogSuccess(i18n.Tr("smtp_service", target, banner))
|
||||
|
||||
@@ -167,11 +167,11 @@ func classifySSHErrorType(err error) ErrorType {
|
||||
|
||||
// SSH 特有的网络/临时错误(需要重试)
|
||||
sshNetworkErrors := append(CommonNetworkErrors,
|
||||
"handshake failed", // 握手失败,可能是服务端限流
|
||||
"ssh: disconnect", // SSH 主动断开
|
||||
"connection closed", // 连接被关闭
|
||||
"max startups", // SSH MaxStartups 限制
|
||||
"too many authentication", // 认证次数过多
|
||||
"handshake failed", // 握手失败,可能是服务端限流
|
||||
"ssh: disconnect", // SSH 主动断开
|
||||
"connection closed", // 连接被关闭
|
||||
"max startups", // SSH MaxStartups 限制
|
||||
"too many authentication", // 认证次数过多
|
||||
)
|
||||
|
||||
return ClassifyError(err, sshAuthErrors, sshNetworkErrors)
|
||||
@@ -268,7 +268,7 @@ func (p *SSHPlugin) readSSHBanner(conn net.Conn, config *common.Config) string {
|
||||
if matched := sshBannerRegex.FindStringSubmatch(bannerStr); len(matched) >= 3 {
|
||||
return fmt.Sprintf("SSH %s (%s)", matched[1], matched[2])
|
||||
}
|
||||
return fmt.Sprintf("SSH服务: %s", bannerStr)
|
||||
return i18n.Tr("ssh_service_banner", bannerStr)
|
||||
}
|
||||
|
||||
return ""
|
||||
|
||||
@@ -249,7 +249,7 @@ func (p *TelnetPlugin) testUnauthAccess(ctx context.Context, info *common.HostIn
|
||||
Success: true,
|
||||
Type: plugins.ResultTypeVuln,
|
||||
Service: "telnet",
|
||||
Banner: "Telnet远程终端服务 (未授权访问)",
|
||||
Banner: i18n.GetText("telnet_unauth_service"),
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -541,21 +541,21 @@ func (p *TelnetPlugin) identifyService(ctx context.Context, info *common.HostInf
|
||||
var banner string
|
||||
|
||||
if p.isShellPrompt(cleaned) {
|
||||
banner = "Telnet远程终端服务 (未授权访问)"
|
||||
banner = i18n.GetText("telnet_unauth_service")
|
||||
} else if strings.Contains(cleanedLower, "login") ||
|
||||
strings.Contains(cleanedLower, "username") ||
|
||||
strings.Contains(cleanedLower, "user") {
|
||||
banner = "Telnet远程终端服务 (需要认证)"
|
||||
banner = i18n.GetText("telnet_auth_required")
|
||||
} else if strings.Contains(cleanedLower, "password") {
|
||||
banner = "Telnet远程终端服务 (只需密码)"
|
||||
banner = i18n.GetText("telnet_password_only")
|
||||
} else if cleaned != "" {
|
||||
displayCleaned := cleaned
|
||||
if len(displayCleaned) > 50 {
|
||||
displayCleaned = displayCleaned[:50] + "..."
|
||||
}
|
||||
banner = fmt.Sprintf("Telnet远程终端服务 (自定义欢迎: %s)", displayCleaned)
|
||||
banner = i18n.Tr("telnet_custom_welcome", displayCleaned)
|
||||
} else {
|
||||
banner = "Telnet远程终端服务"
|
||||
banner = i18n.GetText("telnet_remote_terminal_service")
|
||||
}
|
||||
|
||||
if p.isShellPrompt(cleaned) {
|
||||
|
||||
Reference in New Issue
Block a user