feat: 优化日志颜色方案,区分漏洞和普通信息

- 新增 LogVuln 级别(红色),用于漏洞和重要发现
- 密码爆破成功、未授权访问、POC漏洞等改用红色显示
- 普通信息(扫描统计等)改为白色
- Web指纹保持绿色
This commit is contained in:
ZacharyZcR
2026-01-17 13:21:43 +08:00
parent c3bff843a3
commit 5ebdbd3aa2
27 changed files with 68 additions and 52 deletions

View File

@@ -72,8 +72,11 @@ func LogBase(msg string) { getGlobalLogger().Base(msg) }
// LogInfo 输出信息日志
func LogInfo(msg string) { getGlobalLogger().Info(msg) }
// LogSuccess 输出成功日志
// LogSuccess 输出成功日志Web指纹等
func LogSuccess(result string) { getGlobalLogger().Success(result) }
// LogVuln 输出漏洞/重要发现日志(密码成功、漏洞等)
func LogVuln(result string) { getGlobalLogger().Vuln(result) }
// LogError 输出错误日志
func LogError(errMsg string) { getGlobalLogger().Error(errMsg) }

View File

@@ -25,7 +25,8 @@ const (
LevelError LogLevel = "ERROR" // 仅显示错误日志
LevelBase LogLevel = "BASE" // 仅显示基础信息日志
LevelInfo LogLevel = "INFO" // 仅显示信息日志
LevelSuccess LogLevel = "SUCCESS" // 仅显示成功日志
LevelSuccess LogLevel = "SUCCESS" // 仅显示成功日志Web指纹等
LevelVuln LogLevel = "VULN" // 漏洞和重要发现(密码成功、漏洞等)
LevelDebug LogLevel = "DEBUG" // 仅显示调试日志
LevelInfoSuccess LogLevel = "INFO_SUCCESS" // 仅显示信息和成功日志
LevelBaseInfoSuccess LogLevel = "BASE_INFO_SUCCESS" // 显示基础、信息和成功日志
@@ -57,6 +58,8 @@ const (
const (
// PrefixSuccess 成功日志前缀
PrefixSuccess = "[+]"
// PrefixVuln 漏洞/重要发现前缀
PrefixVuln = "[!]"
// PrefixInfo 信息日志前缀
PrefixInfo = "[*]"
// PrefixError 错误日志前缀
@@ -87,10 +90,11 @@ const (
// GetDefaultLevelColors 获取默认的日志级别颜色映射
func GetDefaultLevelColors() map[LogLevel]interface{} {
return map[LogLevel]interface{}{
LevelError: color.FgRed, // 错误日志显示红色(危险/错误)
LevelBase: color.FgYellow, // 基础日志显示色(警告/提示
LevelInfo: color.FgCyan, // 信息日志显示色(中性信息)
LevelSuccess: color.FgGreen, // 成功日志显示绿色(成功/通过
LevelDebug: color.FgWhite, // 调试日志显示色(详细信息
LevelError: color.FgRed, // 错误日志显示红色
LevelVuln: color.FgRed, // 漏洞/重要发现显示色(密码成功、漏洞等
LevelBase: color.FgWhite, // 基础日志显示色(普通信息)
LevelInfo: color.FgWhite, // 信息日志显示色(普通信息
LevelSuccess: color.FgGreen, // 成功日志显示绿色(Web指纹等
LevelDebug: color.FgWhite, // 调试日志显示白色
}
}

View File

@@ -95,6 +95,11 @@ func (l *Logger) Success(msg string) {
l.log(LevelSuccess, msg)
}
// Vuln 输出漏洞/重要发现信息
func (l *Logger) Vuln(msg string) {
l.log(LevelVuln, msg)
}
// Error 输出错误信息
func (l *Logger) Error(msg string) {
l.log(LevelError, msg)
@@ -130,6 +135,8 @@ func (l *Logger) shouldLog(level LogLevel) bool {
return true
case LevelError:
return level == LevelError
case LevelVuln:
return level == LevelVuln
case LevelBase:
return level == LevelBase
case LevelInfo:
@@ -139,15 +146,15 @@ func (l *Logger) shouldLog(level LogLevel) bool {
case LevelDebug:
return level == LevelDebug
case LevelInfoSuccess:
return level == LevelInfo || level == LevelSuccess
return level == LevelInfo || level == LevelSuccess || level == LevelVuln
case LevelBaseInfoSuccess:
return level == LevelBase || level == LevelInfo || level == LevelSuccess
return level == LevelBase || level == LevelInfo || level == LevelSuccess || level == LevelVuln
default:
// 向后兼容:字符串"debug"显示所有
if string(l.config.Level) == "debug" {
return true
}
return level == LevelInfo || level == LevelSuccess
return level == LevelInfo || level == LevelSuccess || level == LevelVuln
}
}
@@ -201,6 +208,8 @@ func (l *Logger) formatElapsedTime(elapsed time.Duration) string {
// getLevelPrefix 获取日志级别前缀
func (l *Logger) getLevelPrefix(level LogLevel) string {
switch level {
case LevelVuln:
return PrefixVuln
case LevelSuccess:
return PrefixSuccess
case LevelInfo:

View File

@@ -54,7 +54,7 @@ func (p *ActiveMQPlugin) Scan(ctx context.Context, info *common.HostInfo, config
result := TestCredentialsConcurrently(ctx, credentials, authFn, "activemq", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("activemq_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("activemq_credential", target, result.Username, result.Password))
}
return result

View File

@@ -52,7 +52,7 @@ func (p *CassandraPlugin) Scan(ctx context.Context, info *common.HostInfo, confi
result := TestCredentialsConcurrently(ctx, credentials, authFn, "cassandra", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("cassandra_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("cassandra_credential", target, result.Username, result.Password))
}
return result
@@ -161,7 +161,7 @@ func (p *CassandraPlugin) tryNoAuthConnection(ctx context.Context, info *common.
}
session.Close()
common.LogSuccess(i18n.Tr("cassandra_unauth", target))
common.LogVuln(i18n.Tr("cassandra_unauth", target))
return &ScanResult{
Type: plugins.ResultTypeService,
Success: true,

View File

@@ -34,7 +34,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, c
// 首先检测未授权访问
if p.testCredential(ctx, info, Credential{Username: "", Password: ""}, config, state) {
common.LogSuccess(i18n.Tr("elasticsearch_unauth", target))
common.LogVuln(i18n.Tr("elasticsearch_unauth", target))
return &ScanResult{
Success: true,
Type: plugins.ResultTypeVuln,
@@ -55,7 +55,7 @@ func (p *ElasticsearchPlugin) Scan(ctx context.Context, info *common.HostInfo, c
for _, cred := range credentials {
if p.testCredential(ctx, info, cred, config, state) {
common.LogSuccess(i18n.Tr("elasticsearch_credential", target, cred.Username, cred.Password))
common.LogVuln(i18n.Tr("elasticsearch_credential", target, cred.Username, cred.Password))
return &ScanResult{
Success: true,
Type: plugins.ResultTypeCredential,

View File

@@ -61,7 +61,7 @@ func (p *FTPPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
output.WriteString(fmt.Sprintf("\n [->] %s", file))
}
}
common.LogSuccess(output.String())
common.LogVuln(output.String())
}
return result
@@ -202,7 +202,7 @@ func (p *FTPPlugin) testAnonymousAccess(ctx context.Context, info *common.HostIn
output.WriteString(fmt.Sprintf("\n [->] %s", file))
}
}
common.LogSuccess(output.String())
common.LogVuln(output.String())
return &ScanResult{
Type: plugins.ResultTypeCredential,

View File

@@ -47,7 +47,7 @@ func (p *KafkaPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
result := TestCredentialsConcurrently(ctx, credentials, authFn, "kafka", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("kafka_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("kafka_credential", target, result.Username, result.Password))
}
return result

View File

@@ -46,7 +46,7 @@ func (p *LDAPPlugin) Scan(ctx context.Context, info *common.HostInfo, config *co
result := TestCredentialsConcurrently(ctx, credentials, authFn, "ldap", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("ldap_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("ldap_credential", target, result.Username, result.Password))
}
return result

View File

@@ -33,7 +33,7 @@ func (p *MemcachedPlugin) Scan(ctx context.Context, info *common.HostInfo, confi
// 检测未授权访问
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
common.LogSuccess(i18n.Tr("memcached_unauth", target))
common.LogVuln(i18n.Tr("memcached_unauth", target))
return result
}

View File

@@ -46,7 +46,7 @@ func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo, config
}
if isUnauth {
common.LogSuccess(i18n.Tr("mongodb_unauth", target))
common.LogVuln(i18n.Tr("mongodb_unauth", target))
return &ScanResult{
Type: plugins.ResultTypeVuln,
Success: true,
@@ -72,7 +72,7 @@ func (p *MongoDBPlugin) Scan(ctx context.Context, info *common.HostInfo, config
result := TestCredentialsConcurrently(ctx, credentials, authFn, "mongodb", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("mongodb_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("mongodb_credential", target, result.Username, result.Password))
}
return result
@@ -195,7 +195,7 @@ func (p *MongoDBPlugin) identifyService(ctx context.Context, info *common.HostIn
}
if isUnauth {
common.LogSuccess(i18n.Tr("mongodb_unauth", target))
common.LogVuln(i18n.Tr("mongodb_unauth", target))
return &ScanResult{
Type: plugins.ResultTypeVuln,
Success: true,

View File

@@ -70,7 +70,7 @@ func (p *MS17010Plugin) Scan(ctx context.Context, info *common.HostInfo, config
if osVersion != "" {
msg += fmt.Sprintf(" [%s]", osVersion)
}
common.LogSuccess(msg)
common.LogVuln(msg)
return &ScanResult{
Success: true,

View File

@@ -48,7 +48,7 @@ func (p *MSSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
result := TestCredentialsConcurrently(ctx, credentials, authFn, "mssql", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("mssql_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("mssql_credential", target, result.Username, result.Password))
}
return result

View File

@@ -59,7 +59,7 @@ func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
result := TestCredentialsConcurrently(ctx, credentials, authFn, "mysql", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("mysql_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("mysql_credential", target, result.Username, result.Password))
}
return result

View File

@@ -34,7 +34,7 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
// 先测试未授权访问
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
common.LogSuccess(i18n.Tr("neo4j_unauth", target))
common.LogVuln(i18n.Tr("neo4j_unauth", target))
return result
}
@@ -54,7 +54,7 @@ func (p *Neo4jPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
result := TestCredentialsConcurrently(ctx, credentials, authFn, "neo4j", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("neo4j_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("neo4j_credential", target, result.Username, result.Password))
}
return result

View File

@@ -53,7 +53,7 @@ func (p *OraclePlugin) Scan(ctx context.Context, info *common.HostInfo, config *
result := TestCredentialsConcurrently(ctx, credentials, authFn, "oracle", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("oracle_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("oracle_credential", target, result.Username, result.Password))
}
return result
@@ -172,7 +172,7 @@ func (p *OraclePlugin) testUnauthorizedAccess(ctx context.Context, info *common.
if result.Conn != nil {
_ = result.Conn.Close()
}
common.LogSuccess(i18n.Tr("oracle_default_account", target, cred.Username, cred.Password))
common.LogVuln(i18n.Tr("oracle_default_account", target, cred.Username, cred.Password))
return &ScanResult{
Type: plugins.ResultTypeVuln,
Success: true,

View File

@@ -34,7 +34,7 @@ func (p *PostgreSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, conf
// 先测试未授权访问
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
common.LogSuccess(i18n.Tr("postgresql_vuln", target, result.VulInfo))
common.LogVuln(i18n.Tr("postgresql_vuln", target, result.VulInfo))
return result
}
@@ -54,7 +54,7 @@ func (p *PostgreSQLPlugin) Scan(ctx context.Context, info *common.HostInfo, conf
result := TestCredentialsConcurrently(ctx, credentials, authFn, "postgresql", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("postgresql_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("postgresql_credential", target, result.Username, result.Password))
}
return result

View File

@@ -55,7 +55,7 @@ func (p *RabbitMQPlugin) Scan(ctx context.Context, info *common.HostInfo, config
result := TestCredentialsConcurrently(ctx, credentials, authFn, "rabbitmq", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("rabbitmq_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("rabbitmq_credential", target, result.Username, result.Password))
}
return result

View File

@@ -124,7 +124,7 @@ func (p *RDPPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
}
result := fmt.Sprintf("RDP %s %s\\%s %s", target, displayDomain, cred.Username, cred.Password)
common.LogSuccess(result)
common.LogVuln(result)
return &ScanResult{
Success: true,

View File

@@ -41,7 +41,7 @@ func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
// 首先检查未授权访问
if result := p.testUnauthorizedAccess(ctx, info, config, state); result != nil && result.Success {
common.LogSuccess(i18n.Tr("redis_unauth_success", target)) //nolint:govet
common.LogVuln(i18n.Tr("redis_unauth_success", target)) //nolint:govet
// 如果需要利用,重新建立连接执行
if p.shouldExploit(config) {
@@ -62,7 +62,7 @@ func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
// 如果成功,记录并执行利用
if result.Success {
common.LogSuccess(i18n.Tr("redis_scan_success", target, result.Password)) //nolint:govet
common.LogVuln(i18n.Tr("redis_scan_success", target, result.Password)) //nolint:govet
// 如果需要利用,重新建立连接执行
if p.shouldExploit(config) {
@@ -352,7 +352,7 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
if success, _, writeErr := p.writeCustomFile(conn, dirPath, fileName, config.Redis.WriteContent); writeErr != nil {
common.LogError(i18n.Tr("redis_write_failed", writeErr))
} else if success {
common.LogSuccess(i18n.Tr("redis_write_success", config.Redis.WritePath))
common.LogVuln(i18n.Tr("redis_write_success", config.Redis.WritePath))
}
}
@@ -368,7 +368,7 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
if success, _, writeErr := p.writeCustomFile(conn, dirPath, fileName, string(fileContent)); writeErr != nil {
common.LogError(i18n.Tr("redis_write_failed", writeErr))
} else if success {
common.LogSuccess(i18n.Tr("redis_file_write_success", config.Redis.WriteFile, config.Redis.WritePath))
common.LogVuln(i18n.Tr("redis_file_write_success", config.Redis.WriteFile, config.Redis.WritePath))
}
}
}
@@ -378,7 +378,7 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
if success, _, keyErr := p.writeKey(conn, config.Redis.File); keyErr != nil {
common.LogError(i18n.Tr("redis_ssh_key_failed", keyErr))
} else if success {
common.LogSuccess(i18n.GetText("redis_ssh_key_success"))
common.LogVuln(i18n.GetText("redis_ssh_key_success"))
}
}
@@ -387,7 +387,7 @@ func (p *RedisPlugin) exploit(ctx context.Context, info *common.HostInfo, conn n
if success, _, cronErr := p.writeCron(conn, config.Redis.Shell); cronErr != nil {
common.LogError(i18n.Tr("redis_cron_failed", cronErr))
} else if success {
common.LogSuccess(i18n.GetText("redis_cron_success"))
common.LogVuln(i18n.GetText("redis_cron_success"))
}
}

View File

@@ -74,7 +74,7 @@ func (p *RsyncPlugin) Scan(ctx context.Context, info *common.HostInfo, config *c
result := TestCredentialsConcurrently(ctx, creds, authFn, "rsync", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("rsync_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("rsync_credential", target, result.Username, result.Password))
return result
}

View File

@@ -55,7 +55,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
if smbTarget.Protocol == SMBProtocol2 && info.Port == 445 {
if checkSMBGhost(info.Host, config.Timeout) {
smbTarget.Vulnerable = &SMBVuln{CVE20200796: true}
common.LogSuccess(i18n.Tr("smbghost_vuln", target))
common.LogVuln(i18n.Tr("smbghost_vuln", target))
}
}
@@ -75,7 +75,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
} else {
successMsg = fmt.Sprintf("SMB %s 未授权访问 - %s:%s", target, result.Username, result.Password)
}
common.LogSuccess(successMsg)
common.LogVuln(successMsg)
return result
}
@@ -102,7 +102,7 @@ func (p *SmbPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
} else {
successMsg = fmt.Sprintf("SMB %s %s:%s", target, result.Username, result.Password)
}
common.LogSuccess(successMsg)
common.LogVuln(successMsg)
}
return result

View File

@@ -61,7 +61,7 @@ func (p *SMTPPlugin) Scan(ctx context.Context, info *common.HostInfo, config *co
result := TestCredentialsConcurrently(ctx, creds, authFn, "smtp", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("smtp_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("smtp_credential", target, result.Username, result.Password))
}
return result

View File

@@ -40,7 +40,7 @@ func (p *SSHPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
// 如果指定了SSH密钥优先使用密钥认证
if config.Credentials.SSHKeyPath != "" {
if result := p.scanWithKey(ctx, info, config, state); result != nil && result.Success {
common.LogSuccess(i18n.Tr("ssh_key_auth_success", target, result.Username)) //nolint:govet
common.LogVuln(i18n.Tr("ssh_key_auth_success", target, result.Username)) //nolint:govet
return result
}
}
@@ -70,7 +70,7 @@ func (p *SSHPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
// 记录成功
if result.Success {
common.LogSuccess(i18n.Tr("ssh_pwd_auth_success", target, result.Username, result.Password)) //nolint:govet
common.LogVuln(i18n.Tr("ssh_pwd_auth_success", target, result.Username, result.Password)) //nolint:govet
}
return result

View File

@@ -71,7 +71,7 @@ func (p *TelnetPlugin) Scan(ctx context.Context, info *common.HostInfo, config *
result := TestCredentialsConcurrently(ctx, creds, authFn, "telnet", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("telnet_credential", target, result.Username, result.Password))
common.LogVuln(i18n.Tr("telnet_credential", target, result.Username, result.Password))
}
return result

View File

@@ -29,7 +29,7 @@ func (p *VNCPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
// 检查未授权访问
if result := p.testUnauthAccess(ctx, info, config, state); result != nil && result.Success {
common.LogSuccess(i18n.Tr("vnc_unauth", target))
common.LogVuln(i18n.Tr("vnc_unauth", target))
return result
}
@@ -53,7 +53,7 @@ func (p *VNCPlugin) Scan(ctx context.Context, info *common.HostInfo, config *com
result := TestCredentialsConcurrently(ctx, credentials, authFn, "vnc", testConfig)
if result.Success {
common.LogSuccess(i18n.Tr("vnc_credential", target, result.Password))
common.LogVuln(i18n.Tr("vnc_credential", target, result.Password))
}
return result

View File

@@ -133,7 +133,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
}
// 输出成功日志
common.LogSuccess(logMsg)
common.LogVuln(logMsg)
}
}
}()
@@ -655,7 +655,7 @@ func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, ski
}
// 输出成功日志
common.LogSuccess(logMsg)
common.LogVuln(logMsg)
}
// isFuzz 检查规则是否包含需要Fuzz测试的参数