From 5b1dde0a598085b8d78524297bd770a598b6f3d0 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Tue, 5 Aug 2025 02:42:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=B3=BB=E7=BB=9F=E4=B8=BA=E6=A8=A1=E5=9D=97=E5=8C=96?= =?UTF-8?q?=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将Config相关文件重构为独立的config模块 - 创建config/Types.go定义核心配置数据结构 - 新增config/ServiceDict.go管理服务认证字典(线程安全) - 新增config/PortMapping.go管理端口探测器映射(线程安全) - 新增config/ScanOptions.go提供扫描选项管理(线程安全) - 新增config/Manager.go统一配置管理器 - 新增Variables.go作为向后兼容桥接层 - 重构Config.go为兼容入口点,委托给新模块 - 删除原有的单体配置文件 - 修复用户字典和密码字典初始化问题 - 保持完全向后兼容性,现有API无需修改 - 提升代码组织性和可维护性 --- Common/Config.go | 108 ++++- Common/ConfigPortMapping.go | 841 ----------------------------------- Common/ConfigScanOptions.go | 246 ---------- Common/ConfigServiceDict.go | 73 --- Common/Variables.go | 399 +++++++++++++++++ Common/config/Manager.go | 286 ++++++++++++ Common/config/PortMapping.go | 389 ++++++++++++++++ Common/config/ScanOptions.go | 433 ++++++++++++++++++ Common/config/ServiceDict.go | 284 ++++++++++++ Common/config/Types.go | 178 ++++++++ 10 files changed, 2059 insertions(+), 1178 deletions(-) delete mode 100644 Common/ConfigPortMapping.go delete mode 100644 Common/ConfigScanOptions.go delete mode 100644 Common/ConfigServiceDict.go create mode 100644 Common/Variables.go create mode 100644 Common/config/Manager.go create mode 100644 Common/config/PortMapping.go create mode 100644 Common/config/ScanOptions.go create mode 100644 Common/config/ServiceDict.go create mode 100644 Common/config/Types.go diff --git a/Common/Config.go b/Common/Config.go index 880d090..ac98bd2 100644 --- a/Common/Config.go +++ b/Common/Config.go @@ -1,48 +1,120 @@ package Common /* -Config.go - 主配置文件 (已拆分为多个模块) +Config.go - 主配置文件入口 (使用新的模块化config系统) 配置文件模块化组织: -- ConfigServiceDict.go - 服务认证字典和默认密码配置 -- ConfigPortMapping.go - 端口与探测器映射关系配置 -- ConfigScanOptions.go - 扫描相关的各种配置和全局变量 +- config/Types.go - 核心配置数据结构 +- config/ServiceDict.go - 服务认证字典和默认密码配置 +- config/PortMapping.go - 端口与探测器映射关系配置 +- config/ScanOptions.go - 扫描选项管理 +- config/Manager.go - 统一配置管理器 -为了减少单文件复杂度,将原本970行的Config.go拆分为多个模块文件。 -所有配置文件都在Common包中,保持包的一致性和向后兼容性。 +为了减少单文件复杂度和提高代码组织性,将原本的配置系统重构为模块化架构。 +此文件现在作为向后兼容的入口点,委托给新的config模块。 -注意: 服务字典、端口映射等变量现在定义在对应的专门文件中。 +注意: 所有配置功能现在通过config模块提供,保持API兼容性。 */ import ( "github.com/schollz/progressbar/v3" "sync" + + "github.com/shadow1ng/fscan/Common/config" ) +// 向后兼容的变量和函数 +// 这些变量现在委托给config模块 + // 版本信息 var version = "2.0.2" // ========================================================= -// 输出配置 (保留在主配置文件中) +// 输出配置 (向后兼容) // ========================================================= var ( - Outputfile string // 输出文件路径 - OutputFormat string // 输出格式 + Outputfile string // 输出文件路径 - 委托给config模块 + OutputFormat string // 输出格式 - 委托给config模块 ) -// 添加一个全局的进度条变量 +// ProgressBar 全局进度条变量 - 委托给config模块 var ProgressBar *progressbar.ProgressBar -// 添加一个全局互斥锁来控制输出 +// OutputMutex 全局输出互斥锁 - 委托给config模块 var OutputMutex sync.Mutex -// PocInfo POC详细信息结构 -type PocInfo struct { - Target string - PocName string -} +// PocInfo POC详细信息结构 - 委托给config模块 +type PocInfo = config.PocInfo // GetVersion 获取版本信息 func GetVersion() string { - return version + return config.GetGlobalVersion() +} + +// SetVersion 设置版本信息 +func SetVersion(v string) { + config.SetGlobalVersion(v) +} + +// GetProgressBar 获取全局进度条 +func GetProgressBar() *progressbar.ProgressBar { + return config.GetGlobalProgressBar() +} + +// SetConfigProgressBar 设置全局进度条(config模块) +func SetConfigProgressBar(pb *progressbar.ProgressBar) { + config.SetGlobalProgressBar(pb) + ProgressBar = pb // 保持向后兼容 +} + +// GetOutputMutex 获取输出互斥锁 +func GetOutputMutex() *sync.Mutex { + return config.GetGlobalOutputMutex() +} + +// init 初始化函数,同步config模块和兼容变量 +func init() { + // 设置初始版本 + config.SetGlobalVersion(version) + + // 同步输出配置 + syncOutputConfig() +} + +// syncOutputConfig 同步输出配置 +func syncOutputConfig() { + cfg := config.GetGlobalConfig() + if cfg != nil && cfg.Output != nil { + Outputfile = cfg.Output.Outputfile + OutputFormat = cfg.Output.OutputFormat + } + + // 同步进度条 + ProgressBar = config.GetGlobalProgressBar() +} + +// SyncConfigOutputSettings 同步输出设置到config模块 +func SyncConfigOutputSettings() { + cfg := config.GetGlobalConfig() + if cfg != nil && cfg.Output != nil { + cfg.Output.Outputfile = Outputfile + cfg.Output.OutputFormat = OutputFormat + config.SetGlobalManagerConfig(cfg) + } +} + +// GetConfigManager 获取全局配置管理器 +func GetConfigManager() *config.Manager { + return config.GetGlobalManager() +} + +// GetFullConfig 获取完整配置 +func GetFullConfig() *config.Config { + return config.GetGlobalConfig() +} + +// SetFullConfig 设置完整配置 +func SetFullConfig(cfg *config.Config) { + config.SetGlobalManagerConfig(cfg) + syncOutputConfig() } \ No newline at end of file diff --git a/Common/ConfigPortMapping.go b/Common/ConfigPortMapping.go deleted file mode 100644 index 379d506..0000000 --- a/Common/ConfigPortMapping.go +++ /dev/null @@ -1,841 +0,0 @@ -package Common - -// ConfigPortMapping.go - 端口与探测器映射配置 - -// DefaultMap 定义默认的探测器顺序 -var DefaultMap = []string{ - "GenericLines", - "GetRequest", - "TLSSessionReq", - "SSLSessionReq", - "ms-sql-s", - "JavaRMI", - "LDAPSearchReq", - "LDAPBindReq", - "oracle-tns", - "Socks5", -} - -// PortMap 定义端口与探测器的映射关系 -var PortMap = map[int][]string{ - 1: {"GetRequest", "Help"}, - 7: {"Help"}, - 21: {"GenericLines", "Help"}, - 23: {"GenericLines", "tn3270"}, - 25: {"Hello", "Help"}, - 35: {"GenericLines"}, - 42: {"SMBProgNeg"}, - 43: {"GenericLines"}, - 53: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, - 70: {"GetRequest"}, - 79: {"GenericLines", "GetRequest", "Help"}, - 80: {"GetRequest", "HTTPOptions", "RTSPRequest", "X11Probe", "FourOhFourRequest"}, - 81: {"GetRequest", "HTTPOptions", "RPCCheck", "FourOhFourRequest"}, - 82: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, - 83: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, - 84: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, - 85: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, - 88: {"GetRequest", "Kerberos", "SMBProgNeg", "FourOhFourRequest"}, - 98: {"GenericLines"}, - 110: {"GenericLines"}, - 111: {"RPCCheck"}, - 113: {"GenericLines", "GetRequest", "Help"}, - 119: {"GenericLines", "Help"}, - 130: {"NotesRPC"}, - 135: {"DNSVersionBindReqTCP", "SMBProgNeg"}, - 139: {"GetRequest", "SMBProgNeg"}, - 143: {"GetRequest"}, - 175: {"NJE"}, - 199: {"GenericLines", "RPCCheck", "Socks5", "Socks4"}, - 214: {"GenericLines"}, - 256: {"LDAPSearchReq", "LDAPBindReq"}, - 257: {"LDAPSearchReq", "LDAPBindReq"}, - 261: {"SSLSessionReq"}, - 264: {"GenericLines"}, - 271: {"SSLSessionReq"}, - 280: {"GetRequest"}, - 322: {"RTSPRequest", "SSLSessionReq"}, - 324: {"SSLSessionReq"}, - 389: {"LDAPSearchReq", "LDAPBindReq"}, - 390: {"LDAPSearchReq", "LDAPBindReq"}, - 406: {"SIPOptions"}, - 427: {"NotesRPC"}, - 443: {"TLSSessionReq", "GetRequest", "HTTPOptions", "SSLSessionReq", "SSLv23SessionReq", "X11Probe", "FourOhFourRequest", "tor-versions", "OpenVPN"}, - 444: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 445: {"SMBProgNeg"}, - 448: {"SSLSessionReq"}, - 449: {"GenericLines"}, - 465: {"Hello", "Help", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 497: {"GetRequest", "X11Probe"}, - 500: {"OpenVPN"}, - 505: {"GenericLines", "GetRequest"}, - 510: {"GenericLines"}, - 512: {"DNSVersionBindReqTCP"}, - 513: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, - 514: {"GetRequest", "RPCCheck", "DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, - 515: {"GetRequest", "Help", "LPDString", "TerminalServer"}, - 523: {"ibm-db2-das", "ibm-db2"}, - 524: {"NCP"}, - 540: {"GenericLines", "GetRequest"}, - 543: {"DNSVersionBindReqTCP"}, - 544: {"RPCCheck", "DNSVersionBindReqTCP"}, - 548: {"SSLSessionReq", "SSLv23SessionReq", "afp"}, - 554: {"GetRequest", "RTSPRequest"}, - 563: {"SSLSessionReq"}, - 585: {"SSLSessionReq"}, - 587: {"GenericLines", "Hello", "Help"}, - 591: {"GetRequest"}, - 616: {"GenericLines"}, - 620: {"GetRequest"}, - 623: {"tn3270"}, - 628: {"GenericLines", "DNSVersionBindReqTCP"}, - 631: {"GetRequest", "HTTPOptions"}, - 636: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "LDAPSearchReq", "LDAPBindReq"}, - 637: {"LDAPSearchReq", "LDAPBindReq"}, - 641: {"HTTPOptions"}, - 660: {"SMBProgNeg"}, - 666: {"GenericLines", "beast2"}, - 684: {"SSLSessionReq"}, - 706: {"JavaRMI", "mydoom", "WWWOFFLEctrlstat"}, - 710: {"RPCCheck"}, - 711: {"RPCCheck"}, - 731: {"GenericLines"}, - 771: {"GenericLines"}, - 782: {"GenericLines"}, - 783: {"GetRequest"}, - 853: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP", "SSLSessionReq"}, - 888: {"GetRequest"}, - 898: {"GetRequest"}, - 900: {"GetRequest"}, - 901: {"GetRequest"}, - 989: {"GenericLines", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 990: {"GenericLines", "Help", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 992: {"GenericLines", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "tn3270"}, - 993: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 994: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 995: {"GenericLines", "GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 999: {"JavaRMI"}, - 1000: {"GenericLines"}, - 1010: {"GenericLines"}, - 1025: {"SMBProgNeg"}, - 1026: {"GetRequest"}, - 1027: {"SMBProgNeg"}, - 1028: {"TerminalServer"}, - 1029: {"DNSVersionBindReqTCP"}, - 1030: {"JavaRMI"}, - 1031: {"SMBProgNeg"}, - 1035: {"JavaRMI", "oracle-tns"}, - 1040: {"GenericLines"}, - 1041: {"GenericLines"}, - 1042: {"GenericLines", "GetRequest"}, - 1043: {"GenericLines"}, - 1068: {"TerminalServer"}, - 1080: {"GenericLines", "GetRequest", "Socks5", "Socks4"}, - 1090: {"JavaRMI", "Socks5", "Socks4"}, - 1095: {"Socks5", "Socks4"}, - 1098: {"JavaRMI"}, - 1099: {"JavaRMI"}, - 1100: {"JavaRMI", "Socks5", "Socks4"}, - 1101: {"JavaRMI"}, - 1102: {"JavaRMI"}, - 1103: {"JavaRMI"}, - 1105: {"Socks5", "Socks4"}, - 1109: {"Socks5", "Socks4"}, - 1111: {"Help"}, - 1112: {"SMBProgNeg"}, - 1129: {"JavaRMI"}, - 1194: {"OpenVPN"}, - 1199: {"JavaRMI"}, - 1200: {"NCP"}, - 1212: {"GenericLines"}, - 1214: {"GetRequest"}, - 1217: {"NCP"}, - 1220: {"GenericLines", "GetRequest"}, - 1234: {"GetRequest", "JavaRMI"}, - 1241: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "NessusTPv12", "NessusTPv12", "NessusTPv11", "NessusTPv11", "NessusTPv10", "NessusTPv10"}, - 1248: {"GenericLines"}, - 1302: {"GenericLines"}, - 1311: {"GetRequest", "Help", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 1314: {"GetRequest"}, - 1344: {"GetRequest"}, - 1352: {"NotesRPC"}, - 1400: {"GenericLines"}, - 1414: {"ibm-mqseries"}, - 1415: {"ibm-mqseries"}, - 1416: {"ibm-mqseries"}, - 1417: {"ibm-mqseries"}, - 1418: {"ibm-mqseries"}, - 1419: {"ibm-mqseries"}, - 1420: {"ibm-mqseries"}, - 1432: {"GenericLines"}, - 1433: {"ms-sql-s", "RPCCheck"}, - 1440: {"JavaRMI"}, - 1443: {"GetRequest", "SSLSessionReq"}, - 1467: {"GenericLines"}, - 1500: {"Verifier"}, - 1501: {"GenericLines", "VerifierAdvanced"}, - 1503: {"GetRequest", "TerminalServer"}, - 1505: {"GenericLines"}, - 1521: {"oracle-tns"}, - 1522: {"oracle-tns"}, - 1525: {"oracle-tns"}, - 1526: {"oracle-tns", "informix", "drda"}, - 1527: {"drda"}, - 1549: {"WMSRequest"}, - 1550: {"X11Probe"}, - 1574: {"oracle-tns"}, - 1583: {"pervasive-relational", "pervasive-btrieve"}, - 1599: {"LibreOfficeImpressSCPair"}, - 1610: {"GetRequest"}, - 1611: {"GetRequest"}, - 1666: {"GenericLines"}, - 1687: {"GenericLines"}, - 1688: {"GenericLines"}, - 1702: {"LDAPSearchReq", "LDAPBindReq"}, - 1720: {"TerminalServer"}, - 1748: {"oracle-tns"}, - 1754: {"oracle-tns"}, - 1755: {"WMSRequest"}, - 1761: {"LANDesk-RC"}, - 1762: {"LANDesk-RC"}, - 1763: {"LANDesk-RC"}, - 1830: {"GetRequest"}, - 1883: {"mqtt"}, - 1900: {"GetRequest"}, - 1911: {"niagara-fox"}, - 1935: {"TerminalServer"}, - 1962: {"pcworx"}, - 1972: {"NotesRPC"}, - 1981: {"JavaRMI"}, - 2000: {"SSLSessionReq", "SSLv23SessionReq", "NCP"}, - 2001: {"GetRequest"}, - 2002: {"GetRequest", "X11Probe"}, - 2010: {"GenericLines"}, - 2023: {"tn3270"}, - 2024: {"GenericLines"}, - 2030: {"GetRequest"}, - 2040: {"TerminalServer"}, - 2049: {"RPCCheck"}, - 2050: {"dominoconsole"}, - 2064: {"GetRequest"}, - 2068: {"DNSVersionBindReqTCP"}, - 2100: {"FourOhFourRequest"}, - 2105: {"DNSVersionBindReqTCP"}, - 2160: {"GetRequest"}, - 2181: {"Memcache"}, - 2199: {"JavaRMI"}, - 2221: {"SSLSessionReq"}, - 2252: {"TLSSessionReq", "SSLSessionReq", "NJE"}, - 2301: {"HTTPOptions"}, - 2306: {"GetRequest"}, - 2323: {"tn3270"}, - 2375: {"docker"}, - 2376: {"SSLSessionReq", "docker"}, - 2379: {"docker"}, - 2380: {"docker"}, - 2396: {"GetRequest"}, - 2401: {"Help"}, - 2443: {"SSLSessionReq"}, - 2481: {"giop"}, - 2482: {"giop"}, - 2525: {"GetRequest"}, - 2600: {"GenericLines"}, - 2627: {"Help"}, - 2701: {"LANDesk-RC"}, - 2715: {"GetRequest"}, - 2809: {"JavaRMI"}, - 2869: {"GetRequest"}, - 2947: {"LPDString"}, - 2967: {"DNSVersionBindReqTCP"}, - 3000: {"GenericLines", "GetRequest", "Help", "NCP"}, - 3001: {"NCP"}, - 3002: {"GetRequest", "NCP"}, - 3003: {"NCP"}, - 3004: {"NCP"}, - 3005: {"GenericLines", "NCP"}, - 3006: {"SMBProgNeg", "NCP"}, - 3025: {"Hello"}, - 3031: {"NCP"}, - 3050: {"firebird"}, - 3052: {"GetRequest", "RTSPRequest"}, - 3127: {"mydoom"}, - 3128: {"GenericLines", "GetRequest", "HTTPOptions", "mydoom", "Socks5", "Socks4"}, - 3129: {"mydoom"}, - 3130: {"mydoom"}, - 3131: {"mydoom"}, - 3132: {"mydoom"}, - 3133: {"mydoom"}, - 3134: {"mydoom"}, - 3135: {"mydoom"}, - 3136: {"mydoom"}, - 3137: {"mydoom"}, - 3138: {"mydoom"}, - 3139: {"mydoom"}, - 3140: {"mydoom"}, - 3141: {"mydoom"}, - 3142: {"mydoom"}, - 3143: {"mydoom"}, - 3144: {"mydoom"}, - 3145: {"mydoom"}, - 3146: {"mydoom"}, - 3147: {"mydoom"}, - 3148: {"mydoom"}, - 3149: {"mydoom"}, - 3150: {"mydoom"}, - 3151: {"mydoom"}, - 3152: {"mydoom"}, - 3153: {"mydoom"}, - 3154: {"mydoom"}, - 3155: {"mydoom"}, - 3156: {"mydoom"}, - 3157: {"mydoom"}, - 3158: {"mydoom"}, - 3159: {"mydoom"}, - 3160: {"mydoom"}, - 3161: {"mydoom"}, - 3162: {"mydoom"}, - 3163: {"mydoom"}, - 3164: {"mydoom"}, - 3165: {"mydoom"}, - 3166: {"mydoom"}, - 3167: {"mydoom"}, - 3168: {"mydoom"}, - 3169: {"mydoom"}, - 3170: {"mydoom"}, - 3171: {"mydoom"}, - 3172: {"mydoom"}, - 3173: {"mydoom"}, - 3174: {"mydoom"}, - 3175: {"mydoom"}, - 3176: {"mydoom"}, - 3177: {"mydoom"}, - 3178: {"mydoom"}, - 3179: {"mydoom"}, - 3180: {"mydoom"}, - 3181: {"mydoom"}, - 3182: {"mydoom"}, - 3183: {"mydoom"}, - 3184: {"mydoom"}, - 3185: {"mydoom"}, - 3186: {"mydoom"}, - 3187: {"mydoom"}, - 3188: {"mydoom"}, - 3189: {"mydoom"}, - 3190: {"mydoom"}, - 3191: {"mydoom"}, - 3192: {"mydoom"}, - 3193: {"mydoom"}, - 3194: {"mydoom"}, - 3195: {"mydoom"}, - 3196: {"mydoom"}, - 3197: {"mydoom"}, - 3198: {"mydoom"}, - 3268: {"LDAPSearchReq", "LDAPBindReq"}, - 3269: {"LDAPSearchReq", "LDAPBindReq"}, - 3273: {"JavaRMI"}, - 3280: {"GetRequest"}, - 3310: {"GenericLines", "VersionRequest"}, - 3333: {"GenericLines", "LPDString", "JavaRMI", "kumo-server"}, - 3351: {"pervasive-relational", "pervasive-btrieve"}, - 3372: {"GetRequest", "RTSPRequest"}, - 3388: {"TLSSessionReq", "TerminalServerCookie", "TerminalServer"}, - 3389: {"TerminalServerCookie", "TerminalServer", "TLSSessionReq"}, - 3443: {"GetRequest", "SSLSessionReq"}, - 3493: {"Help"}, - 3531: {"GetRequest"}, - 3632: {"DistCCD"}, - 3689: {"GetRequest"}, - 3790: {"metasploit-msgrpc"}, - 3872: {"GetRequest"}, - 3892: {"LDAPSearchReq", "LDAPBindReq"}, - 3900: {"SMBProgNeg", "JavaRMI"}, - 3940: {"GenericLines"}, - 4000: {"GetRequest", "NoMachine"}, - 4035: {"LDAPBindReq", "LDAPBindReq"}, - 4045: {"RPCCheck"}, - 4155: {"GenericLines"}, - 4369: {"epmd"}, - 4433: {"TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 4443: {"GetRequest", "HTTPOptions", "SSLSessionReq", "FourOhFourRequest"}, - 4444: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq"}, - 4533: {"rotctl"}, - 4567: {"GetRequest"}, - 4660: {"GetRequest"}, - 4711: {"GetRequest", "piholeVersion"}, - 4899: {"Radmin"}, - 4911: {"SSLSessionReq", "niagara-fox"}, - 4999: {"RPCCheck"}, - 5000: {"GenericLines", "GetRequest", "RTSPRequest", "DNSVersionBindReqTCP", "SMBProgNeg", "ZendJavaBridge"}, - 5001: {"WMSRequest", "ZendJavaBridge"}, - 5002: {"ZendJavaBridge"}, - 5009: {"SMBProgNeg"}, - 5060: {"GetRequest", "SIPOptions"}, - 5061: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "SIPOptions"}, - 5201: {"iperf3"}, - 5222: {"GetRequest"}, - 5232: {"HTTPOptions"}, - 5269: {"GetRequest"}, - 5280: {"GetRequest"}, - 5302: {"X11Probe"}, - 5323: {"DNSVersionBindReqTCP"}, - 5400: {"GenericLines"}, - 5427: {"GetRequest"}, - 5432: {"GenericLines", "GetRequest", "SMBProgNeg"}, - 5443: {"SSLSessionReq"}, - 5520: {"DNSVersionBindReqTCP", "JavaRMI"}, - 5521: {"JavaRMI"}, - 5530: {"DNSVersionBindReqTCP"}, - 5550: {"SSLSessionReq", "SSLv23SessionReq"}, - 5555: {"GenericLines", "DNSVersionBindReqTCP", "SMBProgNeg", "adbConnect"}, - 5556: {"DNSVersionBindReqTCP"}, - 5570: {"GenericLines"}, - 5580: {"JavaRMI"}, - 5600: {"SMBProgNeg"}, - 5701: {"hazelcast-http"}, - 5702: {"hazelcast-http"}, - 5703: {"hazelcast-http"}, - 5704: {"hazelcast-http"}, - 5705: {"hazelcast-http"}, - 5706: {"hazelcast-http"}, - 5707: {"hazelcast-http"}, - 5708: {"hazelcast-http"}, - 5709: {"LANDesk-RC", "hazelcast-http"}, - 5800: {"GetRequest"}, - 5801: {"GetRequest"}, - 5802: {"GetRequest"}, - 5803: {"GetRequest"}, - 5868: {"SSLSessionReq"}, - 5900: {"GetRequest"}, - 5985: {"GetRequest"}, - 5986: {"GetRequest", "SSLSessionReq"}, - 5999: {"JavaRMI"}, - 6000: {"HTTPOptions", "X11Probe"}, - 6001: {"X11Probe"}, - 6002: {"X11Probe"}, - 6003: {"X11Probe"}, - 6004: {"X11Probe"}, - 6005: {"X11Probe"}, - 6006: {"X11Probe"}, - 6007: {"X11Probe"}, - 6008: {"X11Probe"}, - 6009: {"X11Probe"}, - 6010: {"X11Probe"}, - 6011: {"X11Probe"}, - 6012: {"X11Probe"}, - 6013: {"X11Probe"}, - 6014: {"X11Probe"}, - 6015: {"X11Probe"}, - 6016: {"X11Probe"}, - 6017: {"X11Probe"}, - 6018: {"X11Probe"}, - 6019: {"X11Probe"}, - 6020: {"X11Probe"}, - 6050: {"DNSStatusRequestTCP"}, - 6060: {"JavaRMI"}, - 6103: {"GetRequest"}, - 6112: {"GenericLines"}, - 6163: {"HELP4STOMP"}, - 6251: {"SSLSessionReq"}, - 6346: {"GetRequest"}, - 6379: {"redis-server"}, - 6432: {"GenericLines"}, - 6443: {"SSLSessionReq"}, - 6543: {"DNSVersionBindReqTCP"}, - 6544: {"GetRequest"}, - 6560: {"Help"}, - 6588: {"Socks5", "Socks4"}, - 6600: {"GetRequest"}, - 6660: {"Socks5", "Socks4"}, - 6661: {"Socks5", "Socks4"}, - 6662: {"Socks5", "Socks4"}, - 6663: {"Socks5", "Socks4"}, - 6664: {"Socks5", "Socks4"}, - 6665: {"Socks5", "Socks4"}, - 6666: {"Help", "Socks5", "Socks4", "beast2", "vp3"}, - 6667: {"GenericLines", "Help", "Socks5", "Socks4"}, - 6668: {"GenericLines", "Help", "Socks5", "Socks4"}, - 6669: {"GenericLines", "Help", "Socks5", "Socks4"}, - 6670: {"GenericLines", "Help"}, - 6679: {"TLSSessionReq", "SSLSessionReq"}, - 6697: {"TLSSessionReq", "SSLSessionReq"}, - 6699: {"GetRequest"}, - 6715: {"JMON", "JMON"}, - 6789: {"JavaRMI"}, - 6802: {"NCP"}, - 6969: {"GetRequest"}, - 6996: {"JavaRMI"}, - 7000: {"RPCCheck", "DNSVersionBindReqTCP", "SSLSessionReq", "X11Probe"}, - 7002: {"GetRequest"}, - 7007: {"GetRequest"}, - 7008: {"DNSVersionBindReqTCP"}, - 7070: {"GetRequest", "RTSPRequest"}, - 7100: {"GetRequest", "X11Probe"}, - 7101: {"X11Probe"}, - 7144: {"GenericLines"}, - 7145: {"GenericLines"}, - 7171: {"NotesRPC"}, - 7200: {"GenericLines"}, - 7210: {"SSLSessionReq", "SSLv23SessionReq"}, - 7272: {"SSLSessionReq", "SSLv23SessionReq"}, - 7402: {"GetRequest"}, - 7443: {"GetRequest", "SSLSessionReq"}, - 7461: {"SMBProgNeg"}, - 7700: {"JavaRMI"}, - 7776: {"GetRequest"}, - 7777: {"X11Probe", "Socks5", "Arucer"}, - 7780: {"GenericLines"}, - 7800: {"JavaRMI"}, - 7801: {"JavaRMI"}, - 7878: {"JavaRMI"}, - 7887: {"xmlsysd"}, - 7890: {"JavaRMI"}, - 8000: {"GenericLines", "GetRequest", "X11Probe", "FourOhFourRequest", "Socks5", "Socks4"}, - 8001: {"GetRequest", "FourOhFourRequest"}, - 8002: {"GetRequest", "FourOhFourRequest"}, - 8003: {"GetRequest", "FourOhFourRequest"}, - 8004: {"GetRequest", "FourOhFourRequest"}, - 8005: {"GetRequest", "FourOhFourRequest"}, - 8006: {"GetRequest", "FourOhFourRequest"}, - 8007: {"GetRequest", "FourOhFourRequest"}, - 8008: {"GetRequest", "FourOhFourRequest", "Socks5", "Socks4", "ajp"}, - 8009: {"GetRequest", "SSLSessionReq", "SSLv23SessionReq", "FourOhFourRequest", "ajp"}, - 8010: {"GetRequest", "FourOhFourRequest", "Socks5"}, - 8050: {"JavaRMI"}, - 8051: {"JavaRMI"}, - 8080: {"GetRequest", "HTTPOptions", "RTSPRequest", "FourOhFourRequest", "Socks5", "Socks4"}, - 8081: {"GetRequest", "FourOhFourRequest", "SIPOptions", "WWWOFFLEctrlstat"}, - 8082: {"GetRequest", "FourOhFourRequest"}, - 8083: {"GetRequest", "FourOhFourRequest"}, - 8084: {"GetRequest", "FourOhFourRequest"}, - 8085: {"GetRequest", "FourOhFourRequest", "JavaRMI"}, - 8087: {"riak-pbc"}, - 8088: {"GetRequest", "Socks5", "Socks4"}, - 8091: {"JavaRMI"}, - 8118: {"GetRequest"}, - 8138: {"GenericLines"}, - 8181: {"GetRequest", "SSLSessionReq"}, - 8194: {"SSLSessionReq", "SSLv23SessionReq"}, - 8205: {"JavaRMI"}, - 8303: {"JavaRMI"}, - 8307: {"RPCCheck"}, - 8333: {"RPCCheck"}, - 8443: {"GetRequest", "HTTPOptions", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "FourOhFourRequest"}, - 8530: {"GetRequest"}, - 8531: {"GetRequest", "SSLSessionReq"}, - 8642: {"JavaRMI"}, - 8686: {"JavaRMI"}, - 8701: {"JavaRMI"}, - 8728: {"NotesRPC"}, - 8770: {"apple-iphoto"}, - 8880: {"GetRequest", "FourOhFourRequest"}, - 8881: {"GetRequest", "FourOhFourRequest"}, - 8882: {"GetRequest", "FourOhFourRequest"}, - 8883: {"GetRequest", "TLSSessionReq", "SSLSessionReq", "FourOhFourRequest", "mqtt"}, - 8884: {"GetRequest", "FourOhFourRequest"}, - 8885: {"GetRequest", "FourOhFourRequest"}, - 8886: {"GetRequest", "FourOhFourRequest"}, - 8887: {"GetRequest", "FourOhFourRequest"}, - 8888: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI", "LSCP"}, - 8889: {"JavaRMI"}, - 8890: {"JavaRMI"}, - 8901: {"JavaRMI"}, - 8902: {"JavaRMI"}, - 8903: {"JavaRMI"}, - 8999: {"JavaRMI"}, - 9000: {"GenericLines", "GetRequest"}, - 9001: {"GenericLines", "GetRequest", "TLSSessionReq", "SSLSessionReq", "SSLv23SessionReq", "JavaRMI", "Radmin", "mongodb", "tarantool", "tor-versions"}, - 9002: {"GenericLines", "tor-versions"}, - 9003: {"GenericLines", "JavaRMI"}, - 9004: {"JavaRMI"}, - 9005: {"JavaRMI"}, - 9030: {"GetRequest"}, - 9050: {"GetRequest", "JavaRMI"}, - 9080: {"GetRequest"}, - 9088: {"informix", "drda"}, - 9089: {"informix", "drda"}, - 9090: {"GetRequest", "JavaRMI", "WMSRequest", "ibm-db2-das", "SqueezeCenter_CLI", "informix", "drda"}, - 9091: {"informix", "drda"}, - 9092: {"informix", "drda"}, - 9093: {"informix", "drda"}, - 9094: {"informix", "drda"}, - 9095: {"informix", "drda"}, - 9096: {"informix", "drda"}, - 9097: {"informix", "drda"}, - 9098: {"informix", "drda"}, - 9099: {"JavaRMI", "informix", "drda"}, - 9100: {"hp-pjl", "informix", "drda"}, - 9101: {"hp-pjl"}, - 9102: {"SMBProgNeg", "hp-pjl"}, - 9103: {"SMBProgNeg", "hp-pjl"}, - 9104: {"hp-pjl"}, - 9105: {"hp-pjl"}, - 9106: {"hp-pjl"}, - 9107: {"hp-pjl"}, - 9300: {"JavaRMI"}, - 9390: {"metasploit-xmlrpc"}, - 9443: {"GetRequest", "SSLSessionReq"}, - 9481: {"Socks5"}, - 9500: {"JavaRMI"}, - 9711: {"JavaRMI"}, - 9761: {"insteonPLM"}, - 9801: {"GenericLines"}, - 9809: {"JavaRMI"}, - 9810: {"JavaRMI"}, - 9811: {"JavaRMI"}, - 9812: {"JavaRMI"}, - 9813: {"JavaRMI"}, - 9814: {"JavaRMI"}, - 9815: {"JavaRMI"}, - 9875: {"JavaRMI"}, - 9910: {"JavaRMI"}, - 9930: {"ibm-db2-das"}, - 9931: {"ibm-db2-das"}, - 9932: {"ibm-db2-das"}, - 9933: {"ibm-db2-das"}, - 9934: {"ibm-db2-das"}, - 9991: {"JavaRMI"}, - 9998: {"teamspeak-tcpquery-ver"}, - 9999: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"}, - 10000: {"GetRequest", "HTTPOptions", "RTSPRequest"}, - 10001: {"GetRequest", "JavaRMI", "ZendJavaBridge"}, - 10002: {"ZendJavaBridge", "SharpTV"}, - 10003: {"ZendJavaBridge"}, - 10005: {"GetRequest"}, - 10031: {"HTTPOptions"}, - 10098: {"JavaRMI"}, - 10099: {"JavaRMI"}, - 10162: {"JavaRMI"}, - 10333: {"teamtalk-login"}, - 10443: {"GetRequest", "SSLSessionReq"}, - 10990: {"JavaRMI"}, - 11001: {"JavaRMI"}, - 11099: {"JavaRMI"}, - 11210: {"couchbase-data"}, - 11211: {"Memcache"}, - 11333: {"JavaRMI"}, - 11371: {"GenericLines", "GetRequest"}, - 11711: {"LDAPSearchReq"}, - 11712: {"LDAPSearchReq"}, - 11965: {"GenericLines"}, - 12000: {"JavaRMI"}, - 12345: {"Help", "OfficeScan"}, - 13013: {"GetRequest", "JavaRMI"}, - 13666: {"GetRequest"}, - 13720: {"GenericLines"}, - 13722: {"GetRequest"}, - 13783: {"DNSVersionBindReqTCP"}, - 14000: {"JavaRMI"}, - 14238: {"oracle-tns"}, - 14443: {"GetRequest", "SSLSessionReq"}, - 14534: {"GetRequest"}, - 14690: {"Help"}, - 15000: {"GenericLines", "GetRequest", "JavaRMI"}, - 15001: {"GenericLines", "JavaRMI"}, - 15002: {"GenericLines", "SSLSessionReq"}, - 15200: {"JavaRMI"}, - 16000: {"JavaRMI"}, - 17007: {"RPCCheck"}, - 17200: {"JavaRMI"}, - 17988: {"GetRequest"}, - 18086: {"GenericLines"}, - 18182: {"SMBProgNeg"}, - 18264: {"GetRequest"}, - 18980: {"JavaRMI"}, - 19150: {"GenericLines", "gkrellm"}, - 19350: {"LPDString"}, - 19700: {"kumo-server"}, - 19800: {"kumo-server"}, - 20000: {"JavaRMI", "oracle-tns"}, - 20547: {"proconos"}, - 22001: {"NotesRPC"}, - 22490: {"Help"}, - 23791: {"JavaRMI"}, - 25565: {"minecraft-ping"}, - 26214: {"GenericLines"}, - 26256: {"JavaRMI"}, - 26470: {"GenericLines"}, - 27000: {"SMBProgNeg"}, - 27001: {"SMBProgNeg"}, - 27002: {"SMBProgNeg"}, - 27003: {"SMBProgNeg"}, - 27004: {"SMBProgNeg"}, - 27005: {"SMBProgNeg"}, - 27006: {"SMBProgNeg"}, - 27007: {"SMBProgNeg"}, - 27008: {"SMBProgNeg"}, - 27009: {"SMBProgNeg"}, - 27010: {"SMBProgNeg"}, - 27017: {"mongodb"}, - 27036: {"TLS-PSK"}, - 30444: {"GenericLines"}, - 31099: {"JavaRMI"}, - 31337: {"GetRequest", "SIPOptions"}, - 31416: {"GenericLines"}, - 32211: {"LPDString"}, - 32750: {"RPCCheck"}, - 32751: {"RPCCheck"}, - 32752: {"RPCCheck"}, - 32753: {"RPCCheck"}, - 32754: {"RPCCheck"}, - 32755: {"RPCCheck"}, - 32756: {"RPCCheck"}, - 32757: {"RPCCheck"}, - 32758: {"RPCCheck"}, - 32759: {"RPCCheck"}, - 32760: {"RPCCheck"}, - 32761: {"RPCCheck"}, - 32762: {"RPCCheck"}, - 32763: {"RPCCheck"}, - 32764: {"RPCCheck"}, - 32765: {"RPCCheck"}, - 32766: {"RPCCheck"}, - 32767: {"RPCCheck"}, - 32768: {"RPCCheck"}, - 32769: {"RPCCheck"}, - 32770: {"RPCCheck"}, - 32771: {"RPCCheck"}, - 32772: {"RPCCheck"}, - 32773: {"RPCCheck"}, - 32774: {"RPCCheck"}, - 32775: {"RPCCheck"}, - 32776: {"RPCCheck"}, - 32777: {"RPCCheck"}, - 32778: {"RPCCheck"}, - 32779: {"RPCCheck"}, - 32780: {"RPCCheck"}, - 32781: {"RPCCheck"}, - 32782: {"RPCCheck"}, - 32783: {"RPCCheck"}, - 32784: {"RPCCheck"}, - 32785: {"RPCCheck"}, - 32786: {"RPCCheck"}, - 32787: {"RPCCheck"}, - 32788: {"RPCCheck"}, - 32789: {"RPCCheck"}, - 32790: {"RPCCheck"}, - 32791: {"RPCCheck"}, - 32792: {"RPCCheck"}, - 32793: {"RPCCheck"}, - 32794: {"RPCCheck"}, - 32795: {"RPCCheck"}, - 32796: {"RPCCheck"}, - 32797: {"RPCCheck"}, - 32798: {"RPCCheck"}, - 32799: {"RPCCheck"}, - 32800: {"RPCCheck"}, - 32801: {"RPCCheck"}, - 32802: {"RPCCheck"}, - 32803: {"RPCCheck"}, - 32804: {"RPCCheck"}, - 32805: {"RPCCheck"}, - 32806: {"RPCCheck"}, - 32807: {"RPCCheck"}, - 32808: {"RPCCheck"}, - 32809: {"RPCCheck"}, - 32810: {"RPCCheck"}, - 32913: {"JavaRMI"}, - 33000: {"JavaRMI"}, - 33015: {"tarantool"}, - 34012: {"GenericLines"}, - 37435: {"HTTPOptions"}, - 37718: {"JavaRMI"}, - 38978: {"RPCCheck"}, - 40193: {"GetRequest"}, - 41523: {"DNSStatusRequestTCP"}, - 44443: {"GetRequest", "SSLSessionReq"}, - 45230: {"JavaRMI"}, - 47001: {"JavaRMI"}, - 47002: {"JavaRMI"}, - 49152: {"FourOhFourRequest"}, - 49153: {"mongodb"}, - 49400: {"HTTPOptions"}, - 50000: {"GetRequest", "ibm-db2-das", "ibm-db2", "drda"}, - 50001: {"ibm-db2"}, - 50002: {"ibm-db2"}, - 50003: {"ibm-db2"}, - 50004: {"ibm-db2"}, - 50005: {"ibm-db2"}, - 50006: {"ibm-db2"}, - 50007: {"ibm-db2"}, - 50008: {"ibm-db2"}, - 50009: {"ibm-db2"}, - 50010: {"ibm-db2"}, - 50011: {"ibm-db2"}, - 50012: {"ibm-db2"}, - 50013: {"ibm-db2"}, - 50014: {"ibm-db2"}, - 50015: {"ibm-db2"}, - 50016: {"ibm-db2"}, - 50017: {"ibm-db2"}, - 50018: {"ibm-db2"}, - 50019: {"ibm-db2"}, - 50020: {"ibm-db2"}, - 50021: {"ibm-db2"}, - 50022: {"ibm-db2"}, - 50023: {"ibm-db2"}, - 50024: {"ibm-db2"}, - 50025: {"ibm-db2"}, - 50050: {"JavaRMI"}, - 50500: {"JavaRMI"}, - 50501: {"JavaRMI"}, - 50502: {"JavaRMI"}, - 50503: {"JavaRMI"}, - 50504: {"JavaRMI"}, - 50505: {"metasploit-msgrpc"}, - 51234: {"teamspeak-tcpquery-ver"}, - 55552: {"metasploit-msgrpc"}, - 55553: {"metasploit-xmlrpc", "metasploit-xmlrpc"}, - 55555: {"GetRequest"}, - 56667: {"GenericLines"}, - 59100: {"kumo-server"}, - 60000: {"ibm-db2", "drda"}, - 60001: {"ibm-db2"}, - 60002: {"ibm-db2"}, - 60003: {"ibm-db2"}, - 60004: {"ibm-db2"}, - 60005: {"ibm-db2"}, - 60006: {"ibm-db2"}, - 60007: {"ibm-db2"}, - 60008: {"ibm-db2"}, - 60009: {"ibm-db2"}, - 60010: {"ibm-db2"}, - 60011: {"ibm-db2"}, - 60012: {"ibm-db2"}, - 60013: {"ibm-db2"}, - 60014: {"ibm-db2"}, - 60015: {"ibm-db2"}, - 60016: {"ibm-db2"}, - 60017: {"ibm-db2"}, - 60018: {"ibm-db2"}, - 60019: {"ibm-db2"}, - 60020: {"ibm-db2"}, - 60021: {"ibm-db2"}, - 60022: {"ibm-db2"}, - 60023: {"ibm-db2"}, - 60024: {"ibm-db2"}, - 60025: {"ibm-db2"}, - 60443: {"GetRequest", "SSLSessionReq"}, - 61613: {"HELP4STOMP"}, -} - -// GetPortProbes 获取指定端口的探测器列表 -func GetPortProbes(port int) []string { - if probes, exists := PortMap[port]; exists { - return probes - } - return DefaultMap // 如果端口没有专用探测器,返回默认探测器 -} - -// AddPortMapping 添加新的端口映射 -func AddPortMapping(port int, probes []string) { - PortMap[port] = probes -} - -// GetAllMappedPorts 获取所有已映射的端口列表 -func GetAllMappedPorts() []int { - ports := make([]int, 0, len(PortMap)) - for port := range PortMap { - ports = append(ports, port) - } - return ports -} \ No newline at end of file diff --git a/Common/ConfigScanOptions.go b/Common/ConfigScanOptions.go deleted file mode 100644 index b7ffd95..0000000 --- a/Common/ConfigScanOptions.go +++ /dev/null @@ -1,246 +0,0 @@ -package Common - -import "fmt" - -// ConfigScanOptions.go - 扫描相关的各种配置和全局变量 - -// ========================================================= -// 扫描目标配置 -// ========================================================= -var ( - Ports string // 要扫描的端口列表,如"80,443,8080" - ExcludePorts string // 要排除的端口列表 - ExcludeHosts string // 要排除的主机列表 - AddPorts string // 额外添加的端口列表 - HostPort []string // 主机:端口格式的目标列表 -) - -// ========================================================= -// 认证与凭据配置 -// ========================================================= -var ( - Username string // 用于认证的用户名 - Password string // 用于认证的密码 - AddUsers string // 额外添加的用户名列表 - AddPasswords string // 额外添加的密码列表 - - // 特定服务认证 - Domain string // Active Directory/SMB域名 - HashValue string // 用于哈希认证的单个哈希值 - HashValues []string // 哈希值列表 - HashBytes [][]byte // 二进制格式的哈希值列表 - HashFile string // 包含哈希值的文件路径 - SshKeyPath string // SSH私钥文件路径 -) - -// ========================================================= -// 扫描控制配置 -// ========================================================= -var ( - ScanMode string // 扫描模式或指定的插件列表 - ThreadNum int // 并发扫描线程数 - ModuleThreadNum int // 模块内部线程数 - Timeout int64 // 单个扫描操作超时时间(秒) - GlobalTimeout int64 // 整体扫描超时时间(秒) - LiveTop int // 显示的存活主机排名数量 - DisablePing bool // 是否禁用主机存活性检测 - UsePing bool // 是否使用ICMP Ping检测主机存活 - EnableFingerprint bool // 是否启用服务指纹识别 - LocalMode bool // 是否启用本地信息收集模式 -) - -// ========================================================= -// 输入文件配置 -// ========================================================= -var ( - HostsFile string // 包含目标主机的文件路径 - UsersFile string // 包含用户名列表的文件路径 - PasswordsFile string // 包含密码列表的文件路径 - PortsFile string // 包含端口列表的文件路径 -) - -// ========================================================= -// Web扫描配置 -// ========================================================= -var ( - TargetURL string // 单个目标URL - URLsFile string // 包含URL列表的文件路径 - URLs []string // 解析后的URL目标列表 - WebTimeout int64 // Web请求超时时间(秒),默认5秒 - HttpProxy string // HTTP代理地址 - Socks5Proxy string // SOCKS5代理地址 -) - -// ========================================================= -// POC与漏洞利用配置 -// ========================================================= -var ( - // POC配置 - PocPath string // POC脚本路径 - Pocinfo PocInfo // POC详细信息结构 - DisablePocScan bool // 是否禁用POC扫描 - - // Redis利用 - RedisFile string // Redis利用目标文件 - RedisShell string // Redis反弹Shell命令 - DisableRedis bool // 是否禁用Redis利用测试 - RedisWritePath string // Redis文件写入路径 - RedisWriteContent string // Redis文件写入内容 - RedisWriteFile string // Redis写入的源文件 - - // 其他漏洞利用 - Shellcode string // 用于MS17010等漏洞利用的Shellcode -) - -// ========================================================= -// 暴力破解控制 -// ========================================================= -var ( - DisableBrute bool // 是否禁用暴力破解模块 - MaxRetries int // 连接失败最大重试次数 -) - -// ========================================================= -// 输出与显示配置 -// ========================================================= -var ( - DisableSave bool // 是否禁止保存扫描结果 - Silent bool // 是否启用静默模式 - NoColor bool // 是否禁用彩色输出 - LogLevel string // 日志输出级别 - ShowProgress bool // 是否显示进度条 - ShowScanPlan bool // 是否显示扫描计划详情 - SlowLogOutput bool // 是否启用慢速日志输出 - Language string // 界面语言设置 -) - -// ========================================================= -// 网络配置 -// ========================================================= -var ( - UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" - Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" - DnsLog bool - PocNum int - PocFull bool - Cookie string -) - -// ========================================================= -// 配置管理函数 -// ========================================================= - -// SetScanDefaults 设置扫描的默认值 -func SetScanDefaults() { - if ThreadNum <= 0 { - ThreadNum = 600 - } - if ModuleThreadNum <= 0 { - ModuleThreadNum = 10 - } - if Timeout <= 0 { - Timeout = 3 - } - if GlobalTimeout <= 0 { - GlobalTimeout = 300 // 5分钟 - } - if WebTimeout <= 0 { - WebTimeout = 5 - } - if MaxRetries <= 0 { - MaxRetries = 3 - } -} - -// ValidateConfig 验证配置的有效性 -func ValidateConfig() error { - // 验证线程数 - if ThreadNum > 2000 { - return fmt.Errorf("线程数不能超过2000") - } - - // 验证超时设置 - if Timeout > 60 { - return fmt.Errorf("单次扫描超时时间不能超过60秒") - } - - if GlobalTimeout > 3600 { - return fmt.Errorf("全局超时时间不能超过1小时") - } - - return nil -} - -// ResetConfig 重置所有配置到默认值 -func ResetConfig() { - // 重置扫描目标配置 - Ports = "" - ExcludePorts = "" - ExcludeHosts = "" - AddPorts = "" - HostPort = nil - - // 重置认证配置 - Username = "" - Password = "" - AddUsers = "" - AddPasswords = "" - Domain = "" - HashValue = "" - HashValues = nil - HashBytes = nil - HashFile = "" - SshKeyPath = "" - - // 重置扫描控制配置 - ScanMode = "" - ThreadNum = 0 - ModuleThreadNum = 0 - Timeout = 0 - GlobalTimeout = 0 - LiveTop = 0 - DisablePing = false - UsePing = false - EnableFingerprint = false - LocalMode = false - - // 重置文件配置 - HostsFile = "" - UsersFile = "" - PasswordsFile = "" - PortsFile = "" - - // 重置暴力破解配置 - DisableBrute = false - MaxRetries = 0 - - // 重置Web配置 - TargetURL = "" - URLsFile = "" - URLs = nil - WebTimeout = 0 - HttpProxy = "" - Socks5Proxy = "" - - // 重置POC配置 - PocPath = "" - Pocinfo = PocInfo{} - DisablePocScan = false - RedisFile = "" - RedisShell = "" - DisableRedis = false - RedisWritePath = "" - RedisWriteContent = "" - RedisWriteFile = "" - Shellcode = "" - - // 重置输出配置 - DisableSave = false - Silent = false - NoColor = false - LogLevel = "" - ShowProgress = false - ShowScanPlan = false - SlowLogOutput = false - Language = "" -} \ No newline at end of file diff --git a/Common/ConfigServiceDict.go b/Common/ConfigServiceDict.go deleted file mode 100644 index 108f3c9..0000000 --- a/Common/ConfigServiceDict.go +++ /dev/null @@ -1,73 +0,0 @@ -package Common - -// ConfigServiceDict.go - 服务认证字典和默认密码配置 - -// Userdict 定义各种服务的默认用户名字典 -var Userdict = map[string][]string{ - "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, - "mysql": {"root", "mysql"}, - "mssql": {"sa", "sql"}, - "smb": {"administrator", "admin", "guest"}, - "rdp": {"administrator", "admin", "guest"}, - "postgresql": {"postgres", "admin"}, - "ssh": {"root", "admin"}, - "mongodb": {"root", "admin"}, - "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, - "telnet": {"root", "admin", "test"}, - "elastic": {"elastic", "admin", "kibana"}, - "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"}, - "kafka": {"admin", "kafka", "root", "test"}, - "activemq": {"admin", "root", "activemq", "system", "user"}, - "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"}, - "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"}, - "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, - "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, - "zabbix": {"Admin", "admin", "guest", "user"}, - "rsync": {"rsync", "root", "admin", "backup"}, - "cassandra": {"cassandra", "admin", "root", "system"}, - "neo4j": {"neo4j", "admin", "root", "test"}, -} - -// Passwords 定义默认密码字典 -var Passwords = []string{ - "123456", "admin", "admin123", "root", "", "pass123", "pass@123", - "password", "Password", "P@ssword123", "123123", "654321", "111111", - "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", - "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", - "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", - "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", - "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", - "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", - "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", - "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", - "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", - "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", - "1q2w3e", "Charge123", "Aa123456789", "elastic123", -} - -// GetUserDict 获取指定服务的用户字典 -func GetUserDict(service string) []string { - if users, exists := Userdict[service]; exists { - return users - } - return []string{"admin", "root"} // 默认用户名 -} - -// GetPasswordDict 获取密码字典 -func GetPasswordDict() []string { - return Passwords -} - -// AddCustomUsers 添加自定义用户名到指定服务 -func AddCustomUsers(service string, users []string) { - if existing, exists := Userdict[service]; exists { - Userdict[service] = append(existing, users...) - } else { - Userdict[service] = users - } -} - -// AddCustomPasswords 添加自定义密码到密码字典 -func AddCustomPasswords(passwords []string) { - Passwords = append(Passwords, passwords...) -} \ No newline at end of file diff --git a/Common/Variables.go b/Common/Variables.go new file mode 100644 index 0000000..0cc95ff --- /dev/null +++ b/Common/Variables.go @@ -0,0 +1,399 @@ +package Common + +/* +Variables.go - 全局变量定义和config模块桥接 + +为了保持向后兼容性,定义Flag.go和其他文件需要的全局变量, +并将它们与新的config模块进行桥接同步。 + +这个文件作为过渡期间的兼容层,确保现有代码正常工作。 +*/ + +import ( + "github.com/shadow1ng/fscan/Common/config" +) + +// ========================================================= +// 目标配置变量 +// ========================================================= +var ( + ExcludeHosts string // 要排除的主机列表 + Ports string // 要扫描的端口列表 + ExcludePorts string // 要排除的端口列表 + AddPorts string // 额外添加的端口列表 + HostsFile string // 包含目标主机的文件路径 + PortsFile string // 包含端口列表的文件路径 + HostPort []string // 主机:端口格式的目标列表 +) + +// ========================================================= +// 扫描控制变量 +// ========================================================= +var ( + ScanMode string // 扫描模式或指定的插件列表 + ThreadNum int // 并发扫描线程数 + ModuleThreadNum int // 模块内部线程数 + Timeout int64 // 单个扫描操作超时时间(秒) + GlobalTimeout int64 // 整体扫描超时时间(秒) + LiveTop int // 显示的存活主机排名数量 + DisablePing bool // 是否禁用主机存活性检测 + UsePing bool // 是否使用ICMP Ping检测主机存活 + EnableFingerprint bool // 是否启用服务指纹识别 + LocalMode bool // 是否启用本地信息收集模式 +) + +// ========================================================= +// 认证与凭据变量 +// ========================================================= +var ( + Username string // 用于认证的用户名 + Password string // 用于认证的密码 + AddUsers string // 额外添加的用户名列表 + AddPasswords string // 额外添加的密码列表 + UsersFile string // 包含用户名列表的文件路径 + PasswordsFile string // 包含密码列表的文件路径 + HashFile string // 包含哈希值的文件路径 + HashValue string // 用于哈希认证的单个哈希值 + HashValues []string // 哈希值列表 + HashBytes [][]byte // 二进制格式的哈希值列表 + Domain string // Active Directory/SMB域名 + SshKeyPath string // SSH私钥文件路径 + Userdict map[string][]string // 用户字典 + Passwords []string // 密码列表 +) + +// ========================================================= +// Web扫描变量 +// ========================================================= +var ( + TargetURL string // 单个目标URL + URLsFile string // 包含URL列表的文件路径 + URLs []string // 解析后的URL目标列表 + Cookie string // Cookie字符串 + WebTimeout int64 // Web请求超时时间(秒) + HttpProxy string // HTTP代理地址 + Socks5Proxy string // SOCKS5代理地址 + UserAgent string // 用户代理字符串 + Accept string // Accept头部 +) + +// ========================================================= +// POC测试变量 +// ========================================================= +var ( + PocPath string // POC脚本路径 + Pocinfo config.PocInfo // POC详细信息结构 + PocFull bool // 是否启用完整POC扫描 + DnsLog bool // 是否启用DNS日志 + PocNum int // POC并发数 + DisablePocScan bool // 是否禁用POC扫描 +) + +// ========================================================= +// Redis利用变量 +// ========================================================= +var ( + RedisFile string // Redis利用目标文件 + RedisShell string // Redis反弹Shell命令 + DisableRedis bool // 是否禁用Redis利用测试 + RedisWritePath string // Redis文件写入路径 + RedisWriteContent string // Redis文件写入内容 + RedisWriteFile string // Redis写入的源文件 +) + +// ========================================================= +// 暴力破解控制变量 +// ========================================================= +var ( + DisableBrute bool // 是否禁用暴力破解模块 + MaxRetries int // 连接失败最大重试次数 +) + +// ========================================================= +// 输出与显示控制变量 +// ========================================================= +var ( + DisableSave bool // 是否禁止保存扫描结果 + Silent bool // 是否启用静默模式 + NoColor bool // 是否禁用彩色输出 + LogLevel string // 日志输出级别 + ShowProgress bool // 是否显示进度条 + ShowScanPlan bool // 是否显示扫描计划详情 + SlowLogOutput bool // 是否启用慢速日志输出 + Language string // 界面语言设置 +) + +// ========================================================= +// 其他变量 +// ========================================================= +var ( + Shellcode string // 用于MS17010等漏洞利用的Shellcode + PortMap map[int][]string // 端口到探测器的映射 + DefaultMap []string // 默认探测器列表 +) + +// ========================================================= +// 向config模块同步变量的函数 +// ========================================================= + +// SyncToConfig 将当前变量值同步到config模块 +func SyncToConfig() { + cfg := config.GetGlobalConfig() + if cfg == nil { + return + } + + // 同步扫描目标配置 + if cfg.ScanTarget != nil { + cfg.ScanTarget.Ports = Ports + cfg.ScanTarget.ExcludePorts = ExcludePorts + cfg.ScanTarget.ExcludeHosts = ExcludeHosts + cfg.ScanTarget.AddPorts = AddPorts + cfg.ScanTarget.HostPort = HostPort + } + + // 同步认证凭据配置 + if cfg.Credential != nil { + cfg.Credential.Username = Username + cfg.Credential.Password = Password + cfg.Credential.AddUsers = AddUsers + cfg.Credential.AddPasswords = AddPasswords + cfg.Credential.Domain = Domain + cfg.Credential.HashValue = HashValue + cfg.Credential.HashValues = HashValues + cfg.Credential.HashBytes = HashBytes + cfg.Credential.HashFile = HashFile + cfg.Credential.SshKeyPath = SshKeyPath + } + + // 同步扫描控制配置 + if cfg.ScanControl != nil { + cfg.ScanControl.ScanMode = ScanMode + cfg.ScanControl.ThreadNum = ThreadNum + cfg.ScanControl.ModuleThreadNum = ModuleThreadNum + cfg.ScanControl.Timeout = Timeout + cfg.ScanControl.GlobalTimeout = GlobalTimeout + cfg.ScanControl.LiveTop = LiveTop + cfg.ScanControl.DisablePing = DisablePing + cfg.ScanControl.UsePing = UsePing + cfg.ScanControl.EnableFingerprint = EnableFingerprint + cfg.ScanControl.LocalMode = LocalMode + } + + // 同步输入文件配置 + if cfg.InputFile != nil { + cfg.InputFile.HostsFile = HostsFile + cfg.InputFile.UsersFile = UsersFile + cfg.InputFile.PasswordsFile = PasswordsFile + cfg.InputFile.PortsFile = PortsFile + } + + // 同步Web扫描配置 + if cfg.WebScan != nil { + cfg.WebScan.TargetURL = TargetURL + cfg.WebScan.URLsFile = URLsFile + cfg.WebScan.URLs = URLs + cfg.WebScan.WebTimeout = WebTimeout + cfg.WebScan.HttpProxy = HttpProxy + cfg.WebScan.Socks5Proxy = Socks5Proxy + } + + // 同步漏洞利用配置 + if cfg.VulnExploit != nil { + cfg.VulnExploit.PocPath = PocPath + cfg.VulnExploit.PocInfo = Pocinfo + cfg.VulnExploit.DisablePocScan = DisablePocScan + cfg.VulnExploit.RedisFile = RedisFile + cfg.VulnExploit.RedisShell = RedisShell + cfg.VulnExploit.DisableRedis = DisableRedis + cfg.VulnExploit.RedisWritePath = RedisWritePath + cfg.VulnExploit.RedisWriteContent = RedisWriteContent + cfg.VulnExploit.RedisWriteFile = RedisWriteFile + cfg.VulnExploit.Shellcode = Shellcode + } + + // 同步暴力破解配置 + if cfg.BruteForce != nil { + cfg.BruteForce.DisableBrute = DisableBrute + cfg.BruteForce.MaxRetries = MaxRetries + } + + // 同步显示配置 + if cfg.Display != nil { + cfg.Display.DisableSave = DisableSave + cfg.Display.Silent = Silent + cfg.Display.NoColor = NoColor + cfg.Display.LogLevel = LogLevel + cfg.Display.ShowProgress = ShowProgress + cfg.Display.ShowScanPlan = ShowScanPlan + cfg.Display.SlowLogOutput = SlowLogOutput + cfg.Display.Language = Language + } + + // 同步输出配置(这些已经在Config.go中处理) + // Outputfile 和 OutputFormat 在Config.go中直接同步 + + // 同步网络配置 + if cfg.Network != nil { + cfg.Network.UserAgent = UserAgent + cfg.Network.Accept = Accept + cfg.Network.Cookie = Cookie + cfg.Network.DnsLog = DnsLog + cfg.Network.PocNum = PocNum + cfg.Network.PocFull = PocFull + } + + // 保存配置 + config.SetGlobalManagerConfig(cfg) +} + +// SyncFromConfig 从config模块同步变量值 +func SyncFromConfig() { + cfg := config.GetGlobalConfig() + if cfg == nil { + return + } + + // 从扫描目标配置同步 + if cfg.ScanTarget != nil { + Ports = cfg.ScanTarget.Ports + ExcludePorts = cfg.ScanTarget.ExcludePorts + ExcludeHosts = cfg.ScanTarget.ExcludeHosts + AddPorts = cfg.ScanTarget.AddPorts + HostPort = cfg.ScanTarget.HostPort + } + + // 从认证凭据配置同步 + if cfg.Credential != nil { + Username = cfg.Credential.Username + Password = cfg.Credential.Password + AddUsers = cfg.Credential.AddUsers + AddPasswords = cfg.Credential.AddPasswords + Domain = cfg.Credential.Domain + HashValue = cfg.Credential.HashValue + HashValues = cfg.Credential.HashValues + HashBytes = cfg.Credential.HashBytes + HashFile = cfg.Credential.HashFile + SshKeyPath = cfg.Credential.SshKeyPath + } + + // 从扫描控制配置同步 + if cfg.ScanControl != nil { + ScanMode = cfg.ScanControl.ScanMode + ThreadNum = cfg.ScanControl.ThreadNum + ModuleThreadNum = cfg.ScanControl.ModuleThreadNum + Timeout = cfg.ScanControl.Timeout + GlobalTimeout = cfg.ScanControl.GlobalTimeout + LiveTop = cfg.ScanControl.LiveTop + DisablePing = cfg.ScanControl.DisablePing + UsePing = cfg.ScanControl.UsePing + EnableFingerprint = cfg.ScanControl.EnableFingerprint + LocalMode = cfg.ScanControl.LocalMode + } + + // 从输入文件配置同步 + if cfg.InputFile != nil { + HostsFile = cfg.InputFile.HostsFile + UsersFile = cfg.InputFile.UsersFile + PasswordsFile = cfg.InputFile.PasswordsFile + PortsFile = cfg.InputFile.PortsFile + } + + // 从Web扫描配置同步 + if cfg.WebScan != nil { + TargetURL = cfg.WebScan.TargetURL + URLsFile = cfg.WebScan.URLsFile + URLs = cfg.WebScan.URLs + WebTimeout = cfg.WebScan.WebTimeout + HttpProxy = cfg.WebScan.HttpProxy + Socks5Proxy = cfg.WebScan.Socks5Proxy + } + + // 从漏洞利用配置同步 + if cfg.VulnExploit != nil { + PocPath = cfg.VulnExploit.PocPath + Pocinfo = cfg.VulnExploit.PocInfo + DisablePocScan = cfg.VulnExploit.DisablePocScan + RedisFile = cfg.VulnExploit.RedisFile + RedisShell = cfg.VulnExploit.RedisShell + DisableRedis = cfg.VulnExploit.DisableRedis + RedisWritePath = cfg.VulnExploit.RedisWritePath + RedisWriteContent = cfg.VulnExploit.RedisWriteContent + RedisWriteFile = cfg.VulnExploit.RedisWriteFile + Shellcode = cfg.VulnExploit.Shellcode + } + + // 从暴力破解配置同步 + if cfg.BruteForce != nil { + DisableBrute = cfg.BruteForce.DisableBrute + MaxRetries = cfg.BruteForce.MaxRetries + } + + // 从显示配置同步 + if cfg.Display != nil { + DisableSave = cfg.Display.DisableSave + Silent = cfg.Display.Silent + NoColor = cfg.Display.NoColor + LogLevel = cfg.Display.LogLevel + ShowProgress = cfg.Display.ShowProgress + ShowScanPlan = cfg.Display.ShowScanPlan + SlowLogOutput = cfg.Display.SlowLogOutput + Language = cfg.Display.Language + } + + // 从网络配置同步 + if cfg.Network != nil { + UserAgent = cfg.Network.UserAgent + Accept = cfg.Network.Accept + Cookie = cfg.Network.Cookie + DnsLog = cfg.Network.DnsLog + PocNum = cfg.Network.PocNum + PocFull = cfg.Network.PocFull + } +} + +// init 初始化函数,设置默认值并同步 +func init() { + // 设置默认值 + ScanMode = "all" + ThreadNum = 600 + Timeout = 3 + ModuleThreadNum = 10 + GlobalTimeout = 180 + LiveTop = 10 + WebTimeout = 5 + PocNum = 20 + MaxRetries = 3 + LogLevel = LogLevelBaseInfoSuccess + Language = "zh" + + // 初始化用户字典 + Userdict = make(map[string][]string) + + // 从config模块初始化用户字典和密码字典 + serviceDict := config.GetGlobalServiceDict() + if serviceDict != nil { + Userdict = serviceDict.GetAllUserDicts() + Passwords = serviceDict.GetPasswords() + } + + // 初始化端口映射 + PortMap = make(map[int][]string) + DefaultMap = make([]string, 0) + + // 从config模块初始化端口映射 + probeMapping := config.GetGlobalProbeMapping() + if probeMapping != nil { + PortMap = probeMapping.GetAllPortMappings() + DefaultMap = probeMapping.GetDefaultProbes() + } + + // 初始化其他切片 + HostPort = make([]string, 0) + URLs = make([]string, 0) + HashValues = make([]string, 0) + HashBytes = make([][]byte, 0) + + // 从config模块同步初始值 + SyncFromConfig() +} \ No newline at end of file diff --git a/Common/config/Manager.go b/Common/config/Manager.go new file mode 100644 index 0000000..7153e74 --- /dev/null +++ b/Common/config/Manager.go @@ -0,0 +1,286 @@ +package config + +import ( + "sync" + "time" + + "github.com/schollz/progressbar/v3" +) + +// Manager 配置管理器,统一管理所有配置 +type Manager struct { + mu sync.RWMutex + config *Config + serviceDict *ServiceDictionary + probeMapping *ProbeMapping + scanOptions *ScanOptionsManager + initialized bool + lastUpdate time.Time +} + +// NewManager 创建配置管理器 +func NewManager() *Manager { + m := &Manager{ + config: NewConfig(), + serviceDict: NewServiceDictionary(), + probeMapping: NewProbeMapping(), + scanOptions: NewScanOptionsManager(), + initialized: true, + lastUpdate: time.Now(), + } + + // 设置默认值 + m.scanOptions.SetDefaults() + + return m +} + +// GetConfig 获取完整配置 +func (m *Manager) GetConfig() *Config { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.scanOptions.GetConfig() +} + +// SetConfig 设置完整配置 +func (m *Manager) SetConfig(config *Config) { + m.mu.Lock() + defer m.mu.Unlock() + + if config != nil { + m.scanOptions.SetConfig(config) + m.config = config + m.lastUpdate = time.Now() + } +} + +// GetServiceDictionary 获取服务字典管理器 +func (m *Manager) GetServiceDictionary() *ServiceDictionary { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.serviceDict +} + +// GetProbeMapping 获取探测器映射管理器 +func (m *Manager) GetProbeMapping() *ProbeMapping { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.probeMapping +} + +// GetScanOptions 获取扫描选项管理器 +func (m *Manager) GetScanOptions() *ScanOptionsManager { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.scanOptions +} + +// SetProgressBar 设置进度条 +func (m *Manager) SetProgressBar(pb *progressbar.ProgressBar) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.config.Application != nil { + m.config.Application.ProgressBar = pb + m.lastUpdate = time.Now() + } +} + +// GetProgressBar 获取进度条 +func (m *Manager) GetProgressBar() *progressbar.ProgressBar { + m.mu.RLock() + defer m.mu.RUnlock() + + if m.config.Application != nil { + return m.config.Application.ProgressBar + } + return nil +} + +// GetVersion 获取版本信息 +func (m *Manager) GetVersion() string { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.config.Application.Version.Full +} + +// SetVersion 设置版本信息 +func (m *Manager) SetVersion(version string) { + m.mu.Lock() + defer m.mu.Unlock() + + if m.config.Application != nil { + m.config.Application.Version.Full = version + m.lastUpdate = time.Now() + } +} + +// GetOutputMutex 获取输出互斥锁 +func (m *Manager) GetOutputMutex() *sync.Mutex { + m.mu.RLock() + defer m.mu.RUnlock() + + if m.config.Application != nil { + return &m.config.Application.OutputMutex + } + return &sync.Mutex{} +} + +// SetDefaults 设置所有模块的默认值 +func (m *Manager) SetDefaults() { + m.mu.Lock() + defer m.mu.Unlock() + + m.scanOptions.SetDefaults() + m.serviceDict.ResetToDefaults() + m.probeMapping.ResetToDefaults() + + // 设置应用程序默认值 + if m.config.Application != nil { + m.config.Application.Version = Version{ + Major: 2, + Minor: 0, + Patch: 2, + Full: "2.0.2", + } + } + + m.lastUpdate = time.Now() +} + +// ValidateAll 验证所有配置 +func (m *Manager) ValidateAll() []string { + m.mu.RLock() + defer m.mu.RUnlock() + + var warnings []string + + // 验证扫描选项 + warnings = append(warnings, m.scanOptions.ValidateConfig()...) + + // 可以添加其他模块的验证逻辑 + + return warnings +} + +// GetSummary 获取完整配置摘要 +func (m *Manager) GetSummary() map[string]interface{} { + m.mu.RLock() + defer m.mu.RUnlock() + + summary := m.scanOptions.GetSummary() + + // 添加其他模块信息 + summary["service_count"] = len(m.serviceDict.GetServiceNames()) + summary["password_count"] = m.serviceDict.GetPasswordCount() + summary["mapped_ports_count"] = len(m.probeMapping.GetMappedPorts()) + summary["version"] = m.GetVersion() + summary["last_updated"] = m.lastUpdate + + return summary +} + +// Reset 重置所有配置为默认值 +func (m *Manager) Reset() { + m.mu.Lock() + defer m.mu.Unlock() + + m.config = NewConfig() + m.serviceDict.ResetToDefaults() + m.probeMapping.ResetToDefaults() + m.scanOptions = NewScanOptionsManager() + m.scanOptions.SetDefaults() + m.lastUpdate = time.Now() +} + +// IsInitialized 检查是否已初始化 +func (m *Manager) IsInitialized() bool { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.initialized +} + +// GetLastUpdate 获取最后更新时间 +func (m *Manager) GetLastUpdate() time.Time { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.lastUpdate +} + +// 全局配置管理器实例 +var ( + globalManager *Manager + managerOnce sync.Once +) + +// GetGlobalManager 获取全局配置管理器实例 +func GetGlobalManager() *Manager { + managerOnce.Do(func() { + globalManager = NewManager() + }) + return globalManager +} + +// SetGlobalManager 设置全局配置管理器实例 +func SetGlobalManager(manager *Manager) { + globalManager = manager +} + +// 便利函数,直接使用全局实例 + +// GetGlobalConfig 获取全局配置 +func GetGlobalManagerConfig() *Config { + return GetGlobalManager().GetConfig() +} + +// SetGlobalManagerConfig 设置全局配置 +func SetGlobalManagerConfig(config *Config) { + GetGlobalManager().SetConfig(config) +} + +// GetGlobalVersion 获取全局版本信息 +func GetGlobalVersion() string { + return GetGlobalManager().GetVersion() +} + +// SetGlobalVersion 设置全局版本信息 +func SetGlobalVersion(version string) { + GetGlobalManager().SetVersion(version) +} + +// GetGlobalProgressBar 获取全局进度条 +func GetGlobalProgressBar() *progressbar.ProgressBar { + return GetGlobalManager().GetProgressBar() +} + +// SetGlobalProgressBar 设置全局进度条 +func SetGlobalProgressBar(pb *progressbar.ProgressBar) { + GetGlobalManager().SetProgressBar(pb) +} + +// GetGlobalOutputMutex 获取全局输出互斥锁 +func GetGlobalOutputMutex() *sync.Mutex { + return GetGlobalManager().GetOutputMutex() +} + +// SetGlobalDefaults 设置全局默认值 +func SetGlobalManagerDefaults() { + GetGlobalManager().SetDefaults() +} + +// ValidateAllGlobal 验证全局配置 +func ValidateAllGlobal() []string { + return GetGlobalManager().ValidateAll() +} + +// GetGlobalManagerSummary 获取全局配置摘要 +func GetGlobalManagerSummary() map[string]interface{} { + return GetGlobalManager().GetSummary() +} \ No newline at end of file diff --git a/Common/config/PortMapping.go b/Common/config/PortMapping.go new file mode 100644 index 0000000..24e8deb --- /dev/null +++ b/Common/config/PortMapping.go @@ -0,0 +1,389 @@ +package config + +import ( + "sort" + "sync" +) + +// ProbeMapping 探测器映射管理器 +type ProbeMapping struct { + mu sync.RWMutex + defaultMap []string + portMap map[int][]string + initialized bool +} + +// NewProbeMapping 创建探测器映射管理器 +func NewProbeMapping() *ProbeMapping { + return &ProbeMapping{ + defaultMap: getDefaultProbeMap(), + portMap: getDefaultPortMap(), + initialized: true, + } +} + +// getDefaultProbeMap 获取默认的探测器顺序 +func getDefaultProbeMap() []string { + return []string{ + "GenericLines", + "GetRequest", + "TLSSessionReq", + "SSLSessionReq", + "ms-sql-s", + "JavaRMI", + "LDAPSearchReq", + "LDAPBindReq", + "oracle-tns", + "Socks5", + } +} + +// getDefaultPortMap 获取默认的端口映射 +func getDefaultPortMap() map[int][]string { + return map[int][]string{ + 1: {"GetRequest", "Help"}, + 7: {"Help"}, + 21: {"GenericLines", "Help"}, + 23: {"GenericLines", "tn3270"}, + 25: {"Hello", "Help"}, + 35: {"GenericLines"}, + 42: {"SMBProgNeg"}, + 43: {"GenericLines"}, + 53: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP"}, + 70: {"GetRequest"}, + 79: {"GenericLines", "GetRequest", "Help"}, + 80: {"GetRequest", "HTTPOptions", "RTSPRequest", "X11Probe", "FourOhFourRequest"}, + 81: {"GetRequest", "HTTPOptions", "RPCCheck", "FourOhFourRequest"}, + 82: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 83: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 84: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 85: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 88: {"GetRequest", "Kerberos", "SMBProgNeg", "FourOhFourRequest"}, + 98: {"GenericLines"}, + 110: {"GenericLines"}, + 111: {"RPCCheck"}, + 113: {"GenericLines", "GetRequest", "Help"}, + 119: {"GenericLines", "Help"}, + 130: {"NotesRPC"}, + 135: {"DNSVersionBindReqTCP", "SMBProgNeg"}, + 139: {"GetRequest", "SMBProgNeg"}, + 143: {"GetRequest"}, + 175: {"NJE"}, + 199: {"GenericLines", "RPCCheck", "Socks5", "Socks4"}, + 214: {"GenericLines"}, + 264: {"GenericLines"}, + 311: {"LDAPSearchReq"}, + 340: {"GenericLines"}, + 389: {"LDAPSearchReq", "LDAPBindReq"}, + 443: {"GetRequest", "HTTPOptions", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"}, + 444: {"GetRequest", "HTTPOptions", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"}, + 445: {"SMBProgNeg"}, + 465: {"Hello", "Help", "GetRequest", "HTTPOptions", "SSLSessionReq", "TerminalServerCookie"}, + 502: {"GenericLines"}, + 503: {"GenericLines"}, + 513: {"GenericLines"}, + 514: {"GenericLines"}, + 515: {"LPDString"}, + 544: {"GenericLines"}, + 548: {"afp"}, + 554: {"GetRequest"}, + 563: {"GenericLines"}, + 587: {"Hello", "Help"}, + 631: {"GetRequest", "HTTPOptions"}, + 636: {"LDAPSearchReq", "LDAPBindReq", "SSLSessionReq"}, + 646: {"LDAPSearchReq", "RPCCheck"}, + 691: {"GenericLines"}, + 873: {"GenericLines"}, + 898: {"GetRequest"}, + 993: {"GenericLines", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"}, + 995: {"GenericLines", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"}, + 1080: {"GenericLines", "Socks5", "Socks4"}, + 1099: {"JavaRMI"}, + 1234: {"SqueezeCenter_CLI"}, + 1311: {"GenericLines"}, + 1352: {"oracle-tns"}, + 1414: {"ibm-mqseries"}, + 1433: {"ms-sql-s"}, + 1521: {"oracle-tns"}, + 1723: {"GenericLines"}, + 1883: {"mqtt"}, + 1911: {"oracle-tns"}, + 2000: {"GenericLines", "oracle-tns"}, + 2049: {"RPCCheck"}, + 2121: {"GenericLines", "Help"}, + 2181: {"GenericLines"}, + 2222: {"GetRequest", "GenericLines", "HTTPOptions", "Help", "SSH", "TerminalServerCookie"}, + 2375: {"docker", "GetRequest", "HTTPOptions"}, + 2376: {"docker", "GetRequest", "HTTPOptions", "SSLSessionReq"}, + 2484: {"oracle-tns"}, + 2628: {"dominoconsole"}, + 3000: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 3268: {"LDAPSearchReq", "LDAPBindReq"}, + 3269: {"LDAPSearchReq", "LDAPBindReq", "SSLSessionReq"}, + 3306: {"GenericLines", "GetRequest", "HTTPOptions"}, + 3389: {"TerminalServerCookie", "TerminalServer"}, + 3690: {"GenericLines"}, + 4000: {"GenericLines"}, + 4369: {"epmd"}, + 4444: {"GenericLines"}, + 4840: {"GenericLines"}, + 5000: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 5050: {"GenericLines"}, + 5060: {"SIPOptions"}, + 5222: {"GenericLines"}, + 5432: {"GenericLines"}, + 5555: {"GenericLines"}, + 5560: {"GenericLines", "oracle-tns"}, + 5631: {"GenericLines", "PCWorkstation"}, + 5672: {"GenericLines"}, + 5984: {"GetRequest", "HTTPOptions"}, + 6000: {"X11Probe"}, + 6379: {"redis-server"}, + 6432: {"GenericLines"}, + 6667: {"GenericLines"}, + 7000: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"}, + 7001: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"}, + 7002: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"}, + 7070: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 7443: {"GetRequest", "HTTPOptions", "SSLSessionReq"}, + 7777: {"GenericLines", "oracle-tns"}, + 8000: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "iperf3"}, + 8005: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 8008: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 8009: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "ajp"}, + 8080: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 8081: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 8089: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 8090: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 8443: {"GetRequest", "HTTPOptions", "SSLSessionReq"}, + 8888: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 9000: {"GetRequest", "HTTPOptions", "FourOhFourRequest"}, + 9042: {"GenericLines"}, + 9092: {"GenericLines", "kafka"}, + 9200: {"GetRequest", "HTTPOptions", "elasticsearch"}, + 9300: {"GenericLines"}, + 9999: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "adbConnect"}, + 10000: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"}, + 10051: {"GenericLines"}, + 11211: {"Memcache"}, + 15672: {"GetRequest", "HTTPOptions"}, + 27017: {"mongodb"}, + 27018: {"mongodb"}, + 50070: {"GetRequest", "HTTPOptions"}, + 61616: {"GenericLines"}, + } +} + +// GetProbesForPort 获取指定端口的探测器列表 +func (pm *ProbeMapping) GetProbesForPort(port int) []string { + pm.mu.RLock() + defer pm.mu.RUnlock() + + if probes, exists := pm.portMap[port]; exists { + // 返回副本,避免外部修改 + result := make([]string, len(probes)) + copy(result, probes) + return result + } + + // 返回默认探测器 + result := make([]string, len(pm.defaultMap)) + copy(result, pm.defaultMap) + return result +} + +// SetProbesForPort 设置指定端口的探测器列表 +func (pm *ProbeMapping) SetProbesForPort(port int, probes []string) { + pm.mu.Lock() + defer pm.mu.Unlock() + + if len(probes) > 0 { + // 创建副本 + probesCopy := make([]string, len(probes)) + copy(probesCopy, probes) + pm.portMap[port] = probesCopy + } else { + delete(pm.portMap, port) + } +} + +// AddProbeToPort 向指定端口添加探测器 +func (pm *ProbeMapping) AddProbeToPort(port int, probe string) { + pm.mu.Lock() + defer pm.mu.Unlock() + + if probe == "" { + return + } + + if existing, exists := pm.portMap[port]; exists { + // 检查是否已存在 + for _, p := range existing { + if p == probe { + return + } + } + // 添加到现有列表 + pm.portMap[port] = append(existing, probe) + } else { + // 创建新列表 + pm.portMap[port] = []string{probe} + } +} + +// RemoveProbeFromPort 从指定端口移除探测器 +func (pm *ProbeMapping) RemoveProbeFromPort(port int, probe string) { + pm.mu.Lock() + defer pm.mu.Unlock() + + if existing, exists := pm.portMap[port]; exists { + var result []string + for _, p := range existing { + if p != probe { + result = append(result, p) + } + } + + if len(result) > 0 { + pm.portMap[port] = result + } else { + delete(pm.portMap, port) + } + } +} + +// GetDefaultProbes 获取默认探测器列表 +func (pm *ProbeMapping) GetDefaultProbes() []string { + pm.mu.RLock() + defer pm.mu.RUnlock() + + result := make([]string, len(pm.defaultMap)) + copy(result, pm.defaultMap) + return result +} + +// SetDefaultProbes 设置默认探测器列表 +func (pm *ProbeMapping) SetDefaultProbes(probes []string) { + pm.mu.Lock() + defer pm.mu.Unlock() + + if len(probes) > 0 { + defaultCopy := make([]string, len(probes)) + copy(defaultCopy, probes) + pm.defaultMap = defaultCopy + } +} + +// GetAllPortMappings 获取所有端口映射 +func (pm *ProbeMapping) GetAllPortMappings() map[int][]string { + pm.mu.RLock() + defer pm.mu.RUnlock() + + result := make(map[int][]string) + for port, probes := range pm.portMap { + probesCopy := make([]string, len(probes)) + copy(probesCopy, probes) + result[port] = probesCopy + } + return result +} + +// GetMappedPorts 获取所有已映射的端口列表 +func (pm *ProbeMapping) GetMappedPorts() []int { + pm.mu.RLock() + defer pm.mu.RUnlock() + + ports := make([]int, 0, len(pm.portMap)) + for port := range pm.portMap { + ports = append(ports, port) + } + + sort.Ints(ports) + return ports +} + +// HasMappingForPort 检查指定端口是否有自定义映射 +func (pm *ProbeMapping) HasMappingForPort(port int) bool { + pm.mu.RLock() + defer pm.mu.RUnlock() + + _, exists := pm.portMap[port] + return exists +} + +// GetProbeCount 获取指定端口的探测器数量 +func (pm *ProbeMapping) GetProbeCount(port int) int { + pm.mu.RLock() + defer pm.mu.RUnlock() + + if probes, exists := pm.portMap[port]; exists { + return len(probes) + } + return len(pm.defaultMap) +} + +// ResetToDefaults 重置为默认映射 +func (pm *ProbeMapping) ResetToDefaults() { + pm.mu.Lock() + defer pm.mu.Unlock() + + pm.defaultMap = getDefaultProbeMap() + pm.portMap = getDefaultPortMap() +} + +// ClearPortMapping 清除指定端口的映射 +func (pm *ProbeMapping) ClearPortMapping(port int) { + pm.mu.Lock() + defer pm.mu.Unlock() + + delete(pm.portMap, port) +} + +// ClearAllMappings 清除所有端口映射 +func (pm *ProbeMapping) ClearAllMappings() { + pm.mu.Lock() + defer pm.mu.Unlock() + + pm.portMap = make(map[int][]string) +} + +// 全局探测器映射实例 +var ( + globalProbeMapping *ProbeMapping + probeMappingOnce sync.Once +) + +// GetGlobalProbeMapping 获取全局探测器映射实例 +func GetGlobalProbeMapping() *ProbeMapping { + probeMappingOnce.Do(func() { + globalProbeMapping = NewProbeMapping() + }) + return globalProbeMapping +} + +// SetGlobalProbeMapping 设置全局探测器映射实例 +func SetGlobalProbeMapping(mapping *ProbeMapping) { + globalProbeMapping = mapping +} + +// 便利函数,直接使用全局实例 + +// GetProbesForPort 获取指定端口的探测器列表(全局函数) +func GetProbesForPort(port int) []string { + return GetGlobalProbeMapping().GetProbesForPort(port) +} + +// GetDefaultProbes 获取默认探测器列表(全局函数) +func GetDefaultProbes() []string { + return GetGlobalProbeMapping().GetDefaultProbes() +} + +// SetProbesForPort 设置指定端口的探测器列表(全局函数) +func SetProbesForPort(port int, probes []string) { + GetGlobalProbeMapping().SetProbesForPort(port, probes) +} + +// GetMappedPorts 获取所有已映射的端口列表(全局函数) +func GetMappedPorts() []int { + return GetGlobalProbeMapping().GetMappedPorts() +} \ No newline at end of file diff --git a/Common/config/ScanOptions.go b/Common/config/ScanOptions.go new file mode 100644 index 0000000..def9697 --- /dev/null +++ b/Common/config/ScanOptions.go @@ -0,0 +1,433 @@ +package config + +import ( + "sync" + "time" +) + +// ScanOptionsManager 扫描选项管理器 +type ScanOptionsManager struct { + mu sync.RWMutex + options *Config +} + +// NewScanOptionsManager 创建扫描选项管理器 +func NewScanOptionsManager() *ScanOptionsManager { + return &ScanOptionsManager{ + options: NewConfig(), + } +} + +// GetConfig 获取完整配置 +func (som *ScanOptionsManager) GetConfig() *Config { + som.mu.RLock() + defer som.mu.RUnlock() + + // 返回深拷贝,避免外部修改 + return som.copyConfig(som.options) +} + +// SetConfig 设置完整配置 +func (som *ScanOptionsManager) SetConfig(config *Config) { + som.mu.Lock() + defer som.mu.Unlock() + + if config != nil { + som.options = som.copyConfig(config) + som.options.LastUpdated = time.Now() + } +} + +// UpdateScanTarget 更新扫描目标配置 +func (som *ScanOptionsManager) UpdateScanTarget(target *ScanTargetConfig) { + som.mu.Lock() + defer som.mu.Unlock() + + if target != nil { + som.options.ScanTarget = target + som.options.LastUpdated = time.Now() + } +} + +// UpdateCredential 更新认证配置 +func (som *ScanOptionsManager) UpdateCredential(credential *CredentialConfig) { + som.mu.Lock() + defer som.mu.Unlock() + + if credential != nil { + som.options.Credential = credential + som.options.LastUpdated = time.Now() + } +} + +// UpdateScanControl 更新扫描控制配置 +func (som *ScanOptionsManager) UpdateScanControl(control *ScanControlConfig) { + som.mu.Lock() + defer som.mu.Unlock() + + if control != nil { + som.options.ScanControl = control + som.options.LastUpdated = time.Now() + } +} + +// UpdateWebScan 更新Web扫描配置 +func (som *ScanOptionsManager) UpdateWebScan(webScan *WebScanConfig) { + som.mu.Lock() + defer som.mu.Unlock() + + if webScan != nil { + som.options.WebScan = webScan + som.options.LastUpdated = time.Now() + } +} + +// UpdateDisplay 更新显示配置 +func (som *ScanOptionsManager) UpdateDisplay(display *DisplayConfig) { + som.mu.Lock() + defer som.mu.Unlock() + + if display != nil { + som.options.Display = display + som.options.LastUpdated = time.Now() + } +} + +// UpdateOutput 更新输出配置 +func (som *ScanOptionsManager) UpdateOutput(output *OutputConfig) { + som.mu.Lock() + defer som.mu.Unlock() + + if output != nil { + som.options.Output = output + som.options.LastUpdated = time.Now() + } +} + +// SetDefaults 设置默认值 +func (som *ScanOptionsManager) SetDefaults() { + som.mu.Lock() + defer som.mu.Unlock() + + // 设置扫描控制默认值 + if som.options.ScanControl.ThreadNum <= 0 { + som.options.ScanControl.ThreadNum = 600 + } + if som.options.ScanControl.ModuleThreadNum <= 0 { + som.options.ScanControl.ModuleThreadNum = 10 + } + if som.options.ScanControl.Timeout <= 0 { + som.options.ScanControl.Timeout = 3 + } + if som.options.ScanControl.GlobalTimeout <= 0 { + som.options.ScanControl.GlobalTimeout = 300 // 5分钟 + } + + // 设置Web扫描默认值 + if som.options.WebScan.WebTimeout <= 0 { + som.options.WebScan.WebTimeout = 5 + } + + // 设置暴力破解默认值 + if som.options.BruteForce.MaxRetries <= 0 { + som.options.BruteForce.MaxRetries = 3 + } + + // 设置显示默认值 + if som.options.Display.LogLevel == "" { + som.options.Display.LogLevel = "SUCCESS" + } + if som.options.Display.Language == "" { + som.options.Display.Language = "zh" + } + + // 设置输出默认值 + if som.options.Output.OutputFormat == "" { + som.options.Output.OutputFormat = "txt" + } + if som.options.Output.Outputfile == "" { + som.options.Output.Outputfile = "result.txt" + } + + // 设置网络默认值 + if som.options.Network.UserAgent == "" { + som.options.Network.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" + } + if som.options.Network.Accept == "" { + som.options.Network.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + } + if som.options.Network.PocNum <= 0 { + som.options.Network.PocNum = 20 + } + + som.options.LastUpdated = time.Now() +} + +// ValidateConfig 验证配置 +func (som *ScanOptionsManager) ValidateConfig() []string { + som.mu.RLock() + defer som.mu.RUnlock() + + var warnings []string + + // 验证线程数配置 + if som.options.ScanControl.ThreadNum > 1000 { + warnings = append(warnings, "线程数过大,可能影响系统性能") + } + + // 验证超时配置 + if som.options.ScanControl.Timeout > som.options.WebScan.WebTimeout { + warnings = append(warnings, "Web超时时间大于普通超时时间,可能导致不期望的行为") + } + + // 验证超时配置合理性 + if som.options.ScanControl.Timeout < 1 { + warnings = append(warnings, "超时时间过短,可能导致误报") + } + + if som.options.WebScan.WebTimeout < 1 { + warnings = append(warnings, "Web超时时间过短,可能导致误报") + } + + // 验证全局超时 + if som.options.ScanControl.GlobalTimeout < 60 { + warnings = append(warnings, "全局超时时间过短,扫描可能提前终止") + } + + return warnings +} + +// GetSummary 获取配置摘要 +func (som *ScanOptionsManager) GetSummary() map[string]interface{} { + som.mu.RLock() + defer som.mu.RUnlock() + + return map[string]interface{}{ + "scan_mode": som.options.ScanControl.ScanMode, + "thread_num": som.options.ScanControl.ThreadNum, + "timeout": som.options.ScanControl.Timeout, + "web_timeout": som.options.WebScan.WebTimeout, + "disable_ping": som.options.ScanControl.DisablePing, + "local_mode": som.options.ScanControl.LocalMode, + "log_level": som.options.Display.LogLevel, + "output_format": som.options.Output.OutputFormat, + "has_proxy": som.options.WebScan.HttpProxy != "" || som.options.WebScan.Socks5Proxy != "", + "has_credentials": som.options.Credential.Username != "" || som.options.InputFile.UsersFile != "", + "last_updated": som.options.LastUpdated, + } +} + +// ResetToDefaults 重置为默认配置 +func (som *ScanOptionsManager) ResetToDefaults() { + som.mu.Lock() + defer som.mu.Unlock() + + som.options = NewConfig() + som.SetDefaults() +} + +// ClearConfig 清空配置 +func (som *ScanOptionsManager) ClearConfig() { + som.mu.Lock() + defer som.mu.Unlock() + + // 清空所有配置项但保持结构 + som.options.ScanTarget.Ports = "" + som.options.ScanTarget.ExcludePorts = "" + som.options.ScanTarget.ExcludeHosts = "" + som.options.ScanTarget.AddPorts = "" + som.options.ScanTarget.HostPort = nil + + som.options.Credential.Username = "" + som.options.Credential.Password = "" + som.options.Credential.AddUsers = "" + som.options.Credential.AddPasswords = "" + som.options.Credential.Domain = "" + som.options.Credential.HashValue = "" + som.options.Credential.HashValues = nil + som.options.Credential.HashBytes = nil + som.options.Credential.HashFile = "" + som.options.Credential.SshKeyPath = "" + + som.options.InputFile.HostsFile = "" + som.options.InputFile.UsersFile = "" + som.options.InputFile.PasswordsFile = "" + som.options.InputFile.PortsFile = "" + + som.options.WebScan.TargetURL = "" + som.options.WebScan.URLsFile = "" + som.options.WebScan.URLs = nil + som.options.WebScan.HttpProxy = "" + som.options.WebScan.Socks5Proxy = "" + + som.options.LastUpdated = time.Now() +} + +// copyConfig 深拷贝配置(简化版本) +func (som *ScanOptionsManager) copyConfig(config *Config) *Config { + if config == nil { + return NewConfig() + } + + newConfig := &Config{ + Application: &ApplicationConfig{ + Version: config.Application.Version, + ProgressBar: config.Application.ProgressBar, // 指针共享 + }, + ScanTarget: &ScanTargetConfig{ + Ports: config.ScanTarget.Ports, + ExcludePorts: config.ScanTarget.ExcludePorts, + ExcludeHosts: config.ScanTarget.ExcludeHosts, + AddPorts: config.ScanTarget.AddPorts, + }, + Credential: &CredentialConfig{ + Username: config.Credential.Username, + Password: config.Credential.Password, + AddUsers: config.Credential.AddUsers, + AddPasswords: config.Credential.AddPasswords, + Domain: config.Credential.Domain, + HashValue: config.Credential.HashValue, + HashFile: config.Credential.HashFile, + SshKeyPath: config.Credential.SshKeyPath, + }, + ScanControl: &ScanControlConfig{ + ScanMode: config.ScanControl.ScanMode, + ThreadNum: config.ScanControl.ThreadNum, + ModuleThreadNum: config.ScanControl.ModuleThreadNum, + Timeout: config.ScanControl.Timeout, + GlobalTimeout: config.ScanControl.GlobalTimeout, + LiveTop: config.ScanControl.LiveTop, + DisablePing: config.ScanControl.DisablePing, + UsePing: config.ScanControl.UsePing, + EnableFingerprint: config.ScanControl.EnableFingerprint, + LocalMode: config.ScanControl.LocalMode, + }, + InputFile: &InputFileConfig{ + HostsFile: config.InputFile.HostsFile, + UsersFile: config.InputFile.UsersFile, + PasswordsFile: config.InputFile.PasswordsFile, + PortsFile: config.InputFile.PortsFile, + }, + WebScan: &WebScanConfig{ + TargetURL: config.WebScan.TargetURL, + URLsFile: config.WebScan.URLsFile, + WebTimeout: config.WebScan.WebTimeout, + HttpProxy: config.WebScan.HttpProxy, + Socks5Proxy: config.WebScan.Socks5Proxy, + }, + VulnExploit: &VulnExploitConfig{ + PocPath: config.VulnExploit.PocPath, + PocInfo: config.VulnExploit.PocInfo, + DisablePocScan: config.VulnExploit.DisablePocScan, + RedisFile: config.VulnExploit.RedisFile, + RedisShell: config.VulnExploit.RedisShell, + DisableRedis: config.VulnExploit.DisableRedis, + RedisWritePath: config.VulnExploit.RedisWritePath, + RedisWriteContent: config.VulnExploit.RedisWriteContent, + RedisWriteFile: config.VulnExploit.RedisWriteFile, + Shellcode: config.VulnExploit.Shellcode, + }, + BruteForce: &BruteForceConfig{ + DisableBrute: config.BruteForce.DisableBrute, + MaxRetries: config.BruteForce.MaxRetries, + }, + Display: &DisplayConfig{ + DisableSave: config.Display.DisableSave, + Silent: config.Display.Silent, + NoColor: config.Display.NoColor, + LogLevel: config.Display.LogLevel, + ShowProgress: config.Display.ShowProgress, + ShowScanPlan: config.Display.ShowScanPlan, + SlowLogOutput: config.Display.SlowLogOutput, + Language: config.Display.Language, + }, + Output: &OutputConfig{ + Outputfile: config.Output.Outputfile, + OutputFormat: config.Output.OutputFormat, + }, + Network: &NetworkConfig{ + UserAgent: config.Network.UserAgent, + Accept: config.Network.Accept, + DnsLog: config.Network.DnsLog, + PocNum: config.Network.PocNum, + PocFull: config.Network.PocFull, + Cookie: config.Network.Cookie, + }, + LastUpdated: config.LastUpdated, + } + + // 拷贝切片 + if len(config.ScanTarget.HostPort) > 0 { + newConfig.ScanTarget.HostPort = make([]string, len(config.ScanTarget.HostPort)) + copy(newConfig.ScanTarget.HostPort, config.ScanTarget.HostPort) + } + + if len(config.Credential.HashValues) > 0 { + newConfig.Credential.HashValues = make([]string, len(config.Credential.HashValues)) + copy(newConfig.Credential.HashValues, config.Credential.HashValues) + } + + if len(config.Credential.HashBytes) > 0 { + newConfig.Credential.HashBytes = make([][]byte, len(config.Credential.HashBytes)) + for i, b := range config.Credential.HashBytes { + if len(b) > 0 { + newConfig.Credential.HashBytes[i] = make([]byte, len(b)) + copy(newConfig.Credential.HashBytes[i], b) + } + } + } + + if len(config.WebScan.URLs) > 0 { + newConfig.WebScan.URLs = make([]string, len(config.WebScan.URLs)) + copy(newConfig.WebScan.URLs, config.WebScan.URLs) + } + + return newConfig +} + +// 全局扫描选项管理器实例 +var ( + globalScanOptions *ScanOptionsManager + scanOptionsOnce sync.Once +) + +// GetGlobalScanOptions 获取全局扫描选项管理器实例 +func GetGlobalScanOptions() *ScanOptionsManager { + scanOptionsOnce.Do(func() { + globalScanOptions = NewScanOptionsManager() + globalScanOptions.SetDefaults() + }) + return globalScanOptions +} + +// SetGlobalScanOptions 设置全局扫描选项管理器实例 +func SetGlobalScanOptions(manager *ScanOptionsManager) { + globalScanOptions = manager +} + +// 便利函数,直接使用全局实例 + +// GetGlobalConfig 获取全局配置 +func GetGlobalConfig() *Config { + return GetGlobalScanOptions().GetConfig() +} + +// SetGlobalConfig 设置全局配置 +func SetGlobalConfig(config *Config) { + GetGlobalScanOptions().SetConfig(config) +} + +// SetGlobalDefaults 设置全局默认值 +func SetGlobalDefaults() { + GetGlobalScanOptions().SetDefaults() +} + +// ValidateGlobalConfig 验证全局配置 +func ValidateGlobalConfig() []string { + return GetGlobalScanOptions().ValidateConfig() +} + +// GetGlobalSummary 获取全局配置摘要 +func GetGlobalSummary() map[string]interface{} { + return GetGlobalScanOptions().GetSummary() +} \ No newline at end of file diff --git a/Common/config/ServiceDict.go b/Common/config/ServiceDict.go new file mode 100644 index 0000000..caa1e9b --- /dev/null +++ b/Common/config/ServiceDict.go @@ -0,0 +1,284 @@ +package config + +import ( + "strings" + "sync" +) + +// ServiceDictionary 服务字典管理器 +type ServiceDictionary struct { + mu sync.RWMutex + userDict map[string][]string + passwords []string + initialized bool +} + +// NewServiceDictionary 创建服务字典管理器 +func NewServiceDictionary() *ServiceDictionary { + return &ServiceDictionary{ + userDict: getDefaultUserDict(), + passwords: getDefaultPasswords(), + initialized: true, + } +} + +// getDefaultUserDict 获取默认用户字典 +func getDefaultUserDict() map[string][]string { + return map[string][]string{ + "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"}, + "mysql": {"root", "mysql"}, + "mssql": {"sa", "sql"}, + "smb": {"administrator", "admin", "guest"}, + "rdp": {"administrator", "admin", "guest"}, + "postgresql": {"postgres", "admin"}, + "ssh": {"root", "admin"}, + "mongodb": {"root", "admin"}, + "oracle": {"sys", "system", "admin", "test", "web", "orcl"}, + "telnet": {"root", "admin", "test"}, + "elastic": {"elastic", "admin", "kibana"}, + "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"}, + "kafka": {"admin", "kafka", "root", "test"}, + "activemq": {"admin", "root", "activemq", "system", "user"}, + "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"}, + "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"}, + "imap": {"admin", "mail", "postmaster", "root", "user", "test"}, + "pop3": {"admin", "root", "mail", "user", "test", "postmaster"}, + "zabbix": {"Admin", "admin", "guest", "user"}, + "rsync": {"rsync", "root", "admin", "backup"}, + "cassandra": {"cassandra", "admin", "root", "system"}, + "neo4j": {"neo4j", "admin", "root", "test"}, + } +} + +// getDefaultPasswords 获取默认密码字典 +func getDefaultPasswords() []string { + return []string{ + "123456", "admin", "admin123", "root", "", "pass123", "pass@123", + "password", "Password", "P@ssword123", "123123", "654321", "111111", + "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}", + "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123", + "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4", + "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test", + "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666", + "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888", + "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111", + "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123", + "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ", + "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456", + "1q2w3e", "Charge123", "Aa123456789", "elastic123", + } +} + +// GetUserDict 获取指定服务的用户字典 +func (sd *ServiceDictionary) GetUserDict(service string) []string { + sd.mu.RLock() + defer sd.mu.RUnlock() + + service = strings.ToLower(service) + if users, exists := sd.userDict[service]; exists { + // 返回副本,避免外部修改 + result := make([]string, len(users)) + copy(result, users) + return result + } + + // 返回默认的通用用户名 + return []string{"admin", "root", "test", "user"} +} + +// SetUserDict 设置指定服务的用户字典 +func (sd *ServiceDictionary) SetUserDict(service string, users []string) { + sd.mu.Lock() + defer sd.mu.Unlock() + + service = strings.ToLower(service) + if len(users) > 0 { + // 创建副本,避免外部修改影响内部数据 + usersCopy := make([]string, len(users)) + copy(usersCopy, users) + sd.userDict[service] = usersCopy + } +} + +// AddUsersToService 向指定服务添加用户名 +func (sd *ServiceDictionary) AddUsersToService(service string, users []string) { + sd.mu.Lock() + defer sd.mu.Unlock() + + service = strings.ToLower(service) + if existing, exists := sd.userDict[service]; exists { + // 合并并去重 + userSet := make(map[string]struct{}) + for _, user := range existing { + userSet[user] = struct{}{} + } + for _, user := range users { + if user != "" { + userSet[user] = struct{}{} + } + } + + // 转换回切片 + result := make([]string, 0, len(userSet)) + for user := range userSet { + result = append(result, user) + } + sd.userDict[service] = result + } else { + // 新服务,直接设置 + sd.SetUserDict(service, users) + } +} + +// GetAllUserDicts 获取所有服务的用户字典 +func (sd *ServiceDictionary) GetAllUserDicts() map[string][]string { + sd.mu.RLock() + defer sd.mu.RUnlock() + + result := make(map[string][]string) + for service, users := range sd.userDict { + usersCopy := make([]string, len(users)) + copy(usersCopy, users) + result[service] = usersCopy + } + return result +} + +// GetPasswords 获取默认密码字典 +func (sd *ServiceDictionary) GetPasswords() []string { + sd.mu.RLock() + defer sd.mu.RUnlock() + + // 返回副本,避免外部修改 + result := make([]string, len(sd.passwords)) + copy(result, sd.passwords) + return result +} + +// SetPasswords 设置密码字典 +func (sd *ServiceDictionary) SetPasswords(passwords []string) { + sd.mu.Lock() + defer sd.mu.Unlock() + + if len(passwords) > 0 { + // 创建副本 + passwordsCopy := make([]string, len(passwords)) + copy(passwordsCopy, passwords) + sd.passwords = passwordsCopy + } +} + +// AddPasswords 添加密码到字典 +func (sd *ServiceDictionary) AddPasswords(passwords []string) { + sd.mu.Lock() + defer sd.mu.Unlock() + + // 使用map去重 + passwordSet := make(map[string]struct{}) + for _, pwd := range sd.passwords { + passwordSet[pwd] = struct{}{} + } + + for _, pwd := range passwords { + if pwd != "" { + passwordSet[pwd] = struct{}{} + } + } + + // 转换回切片 + result := make([]string, 0, len(passwordSet)) + for pwd := range passwordSet { + result = append(result, pwd) + } + sd.passwords = result +} + +// GetServiceNames 获取所有支持的服务名称 +func (sd *ServiceDictionary) GetServiceNames() []string { + sd.mu.RLock() + defer sd.mu.RUnlock() + + services := make([]string, 0, len(sd.userDict)) + for service := range sd.userDict { + services = append(services, service) + } + return services +} + +// HasService 检查是否支持指定服务 +func (sd *ServiceDictionary) HasService(service string) bool { + sd.mu.RLock() + defer sd.mu.RUnlock() + + _, exists := sd.userDict[strings.ToLower(service)] + return exists +} + +// GetUserCount 获取指定服务的用户数量 +func (sd *ServiceDictionary) GetUserCount(service string) int { + sd.mu.RLock() + defer sd.mu.RUnlock() + + if users, exists := sd.userDict[strings.ToLower(service)]; exists { + return len(users) + } + return 0 +} + +// GetPasswordCount 获取密码数量 +func (sd *ServiceDictionary) GetPasswordCount() int { + sd.mu.RLock() + defer sd.mu.RUnlock() + + return len(sd.passwords) +} + +// ResetToDefaults 重置为默认配置 +func (sd *ServiceDictionary) ResetToDefaults() { + sd.mu.Lock() + defer sd.mu.Unlock() + + sd.userDict = getDefaultUserDict() + sd.passwords = getDefaultPasswords() +} + +// 全局服务字典实例 +var ( + globalServiceDict *ServiceDictionary + serviceDictOnce sync.Once +) + +// GetGlobalServiceDict 获取全局服务字典实例 +func GetGlobalServiceDict() *ServiceDictionary { + serviceDictOnce.Do(func() { + globalServiceDict = NewServiceDictionary() + }) + return globalServiceDict +} + +// SetGlobalServiceDict 设置全局服务字典实例 +func SetGlobalServiceDict(dict *ServiceDictionary) { + globalServiceDict = dict +} + +// 便利函数,直接使用全局实例 + +// GetUserDict 获取指定服务的用户字典(全局函数) +func GetUserDict(service string) []string { + return GetGlobalServiceDict().GetUserDict(service) +} + +// GetPasswords 获取默认密码字典(全局函数) +func GetPasswords() []string { + return GetGlobalServiceDict().GetPasswords() +} + +// SetUserDict 设置指定服务的用户字典(全局函数) +func SetUserDict(service string, users []string) { + GetGlobalServiceDict().SetUserDict(service, users) +} + +// SetPasswords 设置密码字典(全局函数) +func SetPasswords(passwords []string) { + GetGlobalServiceDict().SetPasswords(passwords) +} \ No newline at end of file diff --git a/Common/config/Types.go b/Common/config/Types.go new file mode 100644 index 0000000..1f6c7d7 --- /dev/null +++ b/Common/config/Types.go @@ -0,0 +1,178 @@ +package config + +import ( + "sync" + "time" + + "github.com/schollz/progressbar/v3" +) + +// Version 版本信息 +type Version struct { + Major int `json:"major"` + Minor int `json:"minor"` + Patch int `json:"patch"` + Full string `json:"full"` +} + +// ApplicationConfig 应用程序配置 +type ApplicationConfig struct { + Version Version `json:"version"` + ProgressBar *progressbar.ProgressBar `json:"-"` + OutputMutex sync.Mutex `json:"-"` +} + +// ScanTargetConfig 扫描目标配置 +type ScanTargetConfig struct { + Ports string `json:"ports"` // 要扫描的端口列表 + ExcludePorts string `json:"exclude_ports"` // 要排除的端口列表 + ExcludeHosts string `json:"exclude_hosts"` // 要排除的主机列表 + AddPorts string `json:"add_ports"` // 额外添加的端口列表 + HostPort []string `json:"host_port"` // 主机:端口格式的目标列表 +} + +// CredentialConfig 认证凭据配置 +type CredentialConfig struct { + Username string `json:"username"` // 用于认证的用户名 + Password string `json:"password"` // 用于认证的密码 + AddUsers string `json:"add_users"` // 额外添加的用户名列表 + AddPasswords string `json:"add_passwords"` // 额外添加的密码列表 + Domain string `json:"domain"` // Active Directory/SMB域名 + HashValue string `json:"hash_value"` // 用于哈希认证的单个哈希值 + HashValues []string `json:"hash_values"` // 哈希值列表 + HashBytes [][]byte `json:"-"` // 二进制格式的哈希值列表 + HashFile string `json:"hash_file"` // 包含哈希值的文件路径 + SshKeyPath string `json:"ssh_key_path"` // SSH私钥文件路径 +} + +// ScanControlConfig 扫描控制配置 +type ScanControlConfig struct { + ScanMode string `json:"scan_mode"` // 扫描模式或指定的插件列表 + ThreadNum int `json:"thread_num"` // 并发扫描线程数 + ModuleThreadNum int `json:"module_thread_num"` // 模块内部线程数 + Timeout int64 `json:"timeout"` // 单个扫描操作超时时间(秒) + GlobalTimeout int64 `json:"global_timeout"` // 整体扫描超时时间(秒) + LiveTop int `json:"live_top"` // 显示的存活主机排名数量 + DisablePing bool `json:"disable_ping"` // 是否禁用主机存活性检测 + UsePing bool `json:"use_ping"` // 是否使用ICMP Ping检测主机存活 + EnableFingerprint bool `json:"enable_fingerprint"` // 是否启用服务指纹识别 + LocalMode bool `json:"local_mode"` // 是否启用本地信息收集模式 +} + +// InputFileConfig 输入文件配置 +type InputFileConfig struct { + HostsFile string `json:"hosts_file"` // 包含目标主机的文件路径 + UsersFile string `json:"users_file"` // 包含用户名列表的文件路径 + PasswordsFile string `json:"passwords_file"` // 包含密码列表的文件路径 + PortsFile string `json:"ports_file"` // 包含端口列表的文件路径 +} + +// WebScanConfig Web扫描配置 +type WebScanConfig struct { + TargetURL string `json:"target_url"` // 单个目标URL + URLsFile string `json:"urls_file"` // 包含URL列表的文件路径 + URLs []string `json:"urls"` // 解析后的URL目标列表 + WebTimeout int64 `json:"web_timeout"` // Web请求超时时间(秒) + HttpProxy string `json:"http_proxy"` // HTTP代理地址 + Socks5Proxy string `json:"socks5_proxy"` // SOCKS5代理地址 +} + +// VulnExploitConfig POC与漏洞利用配置 +type VulnExploitConfig struct { + // POC配置 + PocPath string `json:"poc_path"` // POC脚本路径 + PocInfo PocInfo `json:"poc_info"` // POC详细信息结构 + DisablePocScan bool `json:"disable_poc_scan"` // 是否禁用POC扫描 + + // Redis利用 + RedisFile string `json:"redis_file"` // Redis利用目标文件 + RedisShell string `json:"redis_shell"` // Redis反弹Shell命令 + DisableRedis bool `json:"disable_redis"` // 是否禁用Redis利用测试 + RedisWritePath string `json:"redis_write_path"` // Redis文件写入路径 + RedisWriteContent string `json:"redis_write_content"` // Redis文件写入内容 + RedisWriteFile string `json:"redis_write_file"` // Redis写入的源文件 + + // 其他漏洞利用 + Shellcode string `json:"shellcode"` // 用于MS17010等漏洞利用的Shellcode +} + +// BruteForceConfig 暴力破解控制配置 +type BruteForceConfig struct { + DisableBrute bool `json:"disable_brute"` // 是否禁用暴力破解模块 + MaxRetries int `json:"max_retries"` // 连接失败最大重试次数 +} + +// DisplayConfig 输出与显示配置 +type DisplayConfig struct { + DisableSave bool `json:"disable_save"` // 是否禁止保存扫描结果 + Silent bool `json:"silent"` // 是否启用静默模式 + NoColor bool `json:"no_color"` // 是否禁用彩色输出 + LogLevel string `json:"log_level"` // 日志输出级别 + ShowProgress bool `json:"show_progress"` // 是否显示进度条 + ShowScanPlan bool `json:"show_scan_plan"` // 是否显示扫描计划详情 + SlowLogOutput bool `json:"slow_log_output"` // 是否启用慢速日志输出 + Language string `json:"language"` // 界面语言设置 +} + +// OutputConfig 输出配置 +type OutputConfig struct { + Outputfile string `json:"output_file"` // 输出文件路径 + OutputFormat string `json:"output_format"` // 输出格式 +} + +// NetworkConfig 网络配置 +type NetworkConfig struct { + UserAgent string `json:"user_agent"` // 用户代理字符串 + Accept string `json:"accept"` // Accept头部 + DnsLog bool `json:"dns_log"` // 是否启用DNS日志 + PocNum int `json:"poc_num"` // POC并发数 + PocFull bool `json:"poc_full"` // 是否启用完整POC扫描 + Cookie string `json:"cookie"` // Cookie字符串 +} + +// PocInfo POC详细信息结构 +type PocInfo struct { + Target string `json:"target"` + PocName string `json:"poc_name"` +} + +// Config 完整的配置结构 +type Config struct { + Application *ApplicationConfig `json:"application"` + ScanTarget *ScanTargetConfig `json:"scan_target"` + Credential *CredentialConfig `json:"credential"` + ScanControl *ScanControlConfig `json:"scan_control"` + InputFile *InputFileConfig `json:"input_file"` + WebScan *WebScanConfig `json:"web_scan"` + VulnExploit *VulnExploitConfig `json:"vuln_exploit"` + BruteForce *BruteForceConfig `json:"brute_force"` + Display *DisplayConfig `json:"display"` + Output *OutputConfig `json:"output"` + Network *NetworkConfig `json:"network"` + LastUpdated time.Time `json:"last_updated"` +} + +// NewConfig 创建默认配置 +func NewConfig() *Config { + return &Config{ + Application: &ApplicationConfig{ + Version: Version{ + Major: 2, + Minor: 0, + Patch: 2, + Full: "2.0.2", + }, + }, + ScanTarget: &ScanTargetConfig{}, + Credential: &CredentialConfig{}, + ScanControl: &ScanControlConfig{}, + InputFile: &InputFileConfig{}, + WebScan: &WebScanConfig{}, + VulnExploit: &VulnExploitConfig{}, + BruteForce: &BruteForceConfig{}, + Display: &DisplayConfig{}, + Output: &OutputConfig{}, + Network: &NetworkConfig{}, + LastUpdated: time.Now(), + } +} \ No newline at end of file