feat: 优化域探测显示,调整Web扫描逻辑

This commit is contained in:
ZacharyZcR 2025-01-04 11:49:59 +08:00
parent a42ee523b0
commit 75aeee5215
13 changed files with 424 additions and 230 deletions

View File

@ -876,14 +876,15 @@ var (
AddPasswords string // 原PassAdd
// 扫描配置
ScanMode string // 原Scantype
ThreadNum int // 原Threads
UseSynScan bool
Timeout int64 = 3
LiveTop int
DisablePing bool // 原NoPing
UsePing bool // 原Ping
Command string
ScanMode string // 原Scantype
ThreadNum int // 原Threads
UseSynScan bool
Timeout int64 = 3
LiveTop int
DisablePing bool // 原NoPing
UsePing bool // 原Ping
Command string
SkipFingerprint bool
// 本地扫描配置
LocalScan bool
@ -896,17 +897,14 @@ var (
PortsFile string // 原PortFile
// Web配置
TargetURL string // 原URL
URLsFile string // 原UrlFile
URLs []string // 原Urls
WebTimeout int64 = 5
HttpProxy string // 原Proxy
Socks5Proxy string
// POC配置
PocPath string
Pocinfo PocInfo
DisablePoc bool // 原NoPoc
PocPath string
Pocinfo PocInfo
// Redis配置
RedisFile string

View File

@ -185,7 +185,6 @@ func Flag(Info *HostInfo) {
" - Port: 端口扫描模式\n"+
" - ICMP: ICMP存活探测\n"+
" - Local: 本地信息收集\n\n"+
" - UDP: UDP扫描模式\n\n"+
"单个插件模式(小写):\n"+
" Web类: web, fcgi\n"+
" 数据库类: mysql, mssql, redis, mongodb, postgres, oracle, memcached\n"+
@ -199,9 +198,10 @@ func Flag(Info *HostInfo) {
flag.BoolVar(&DisablePing, "np", false, "禁用主机存活探测")
flag.BoolVar(&UsePing, "ping", false, "使用系统ping命令替代ICMP探测")
flag.StringVar(&Command, "c", "", "指定要执行的系统命令(支持ssh和wmiexec)")
flag.BoolVar(&SkipFingerprint, "skip", false, "跳过端口指纹识别")
// 本地扫描配置
flag.BoolVar(&LocalScan, "local", false, "启用本地网段扫描模式")
flag.BoolVar(&LocalScan, "local", false, "启用本地扫描模式")
// 文件配置
flag.StringVar(&HostsFile, "hf", "", "从文件中读取目标主机列表")
@ -211,8 +211,6 @@ func Flag(Info *HostInfo) {
flag.StringVar(&PortsFile, "portf", "", "从文件中读取端口列表")
// Web配置
flag.StringVar(&TargetURL, "u", "", "指定目标URL")
flag.StringVar(&URLsFile, "uf", "", "从文件中读取URL列表")
flag.StringVar(&Cookie, "cookie", "", "设置HTTP请求Cookie")
flag.Int64Var(&WebTimeout, "wt", 5, "设置Web请求超时时间(单位:秒)")
flag.StringVar(&HttpProxy, "proxy", "", "设置HTTP代理服务器")
@ -221,7 +219,6 @@ func Flag(Info *HostInfo) {
// POC配置
flag.StringVar(&PocPath, "pocpath", "", "指定自定义POC文件路径")
flag.StringVar(&Pocinfo.PocName, "pocname", "", "指定要使用的POC名称,如: -pocname weblogic")
flag.BoolVar(&DisablePoc, "nopoc", false, "禁用Web漏洞POC扫描")
flag.BoolVar(&PocFull, "full", false, "启用完整POC扫描(如测试shiro全部100个key)")
flag.BoolVar(&DnsLog, "dns", false, "启用dnslog进行漏洞验证")
flag.IntVar(&PocNum, "num", 20, "设置POC扫描并发数")
@ -248,7 +245,7 @@ func Flag(Info *HostInfo) {
flag.BoolVar(&NoColor, "nocolor", false, "禁用彩色输出显示")
flag.BoolVar(&JsonFormat, "json", false, "以JSON格式输出结果")
flag.StringVar(&LogLevel, "log", LogLevelInfo, "日志输出级别(ALL/SUCCESS/ERROR/INFO/DEBUG)")
flag.BoolVar(&NoProgress, "noprogress", false, "禁用进度条显示")
flag.BoolVar(&NoProgress, "nopg", false, "禁用进度条显示")
flag.Parse()
}

View File

@ -83,9 +83,17 @@ func formatLogMessage(entry *LogEntry) string {
// 修改 printLog 函数
func printLog(entry *LogEntry) {
if LogLevel != LogLevelAll &&
entry.Level != LogLevel &&
!(LogLevel == LogLevelInfo && (entry.Level == LogLevelInfo || entry.Level == LogLevelSuccess)) {
// 默认情况(LogLevelInfo)下打印 INFO、SUCCESS、ERROR
if LogLevel == LogLevelInfo {
if entry.Level != LogLevelInfo &&
entry.Level != LogLevelSuccess &&
entry.Level != LogLevelError {
return
}
} else if LogLevel == LogLevelDebug || LogLevel == LogLevelAll {
// Debug或ALL模式打印所有日志
} else if entry.Level != LogLevel {
// 其他情况只打印指定等级的日志
return
}

View File

@ -114,40 +114,6 @@ func ParsePass(Info *HostInfo) error {
LogInfo(fmt.Sprintf("加载有效哈希值: %d 个", validCount))
}
// 处理直接指定的URL列表
if TargetURL != "" {
urls := strings.Split(TargetURL, ",")
tmpUrls := make(map[string]struct{})
for _, url := range urls {
if url != "" {
if _, ok := tmpUrls[url]; !ok {
tmpUrls[url] = struct{}{}
URLs = append(URLs, url)
}
}
}
LogInfo(fmt.Sprintf("加载URL: %d 个", len(URLs)))
}
// 从文件加载URL列表
if URLsFile != "" {
urls, err := Readfile(URLsFile)
if err != nil {
return fmt.Errorf("读取URL文件失败: %v", err)
}
tmpUrls := make(map[string]struct{})
for _, url := range urls {
if url != "" {
if _, ok := tmpUrls[url]; !ok {
tmpUrls[url] = struct{}{}
URLs = append(URLs, url)
}
}
}
LogInfo(fmt.Sprintf("从文件加载URL: %d 个", len(urls)))
}
// 从文件加载端口列表
if PortsFile != "" {
ports, err := Readfile(PortsFile)
@ -206,7 +172,7 @@ func Readfile(filename string) ([]string, error) {
// ParseInput 解析和验证输入参数配置
func ParseInput(Info *HostInfo) error {
// 检查必要的目标参数
if Info.Host == "" && HostsFile == "" && TargetURL == "" && URLsFile == "" {
if Info.Host == "" && HostsFile == "" {
LogError("未指定扫描目标")
flag.Usage()
return fmt.Errorf("必须指定扫描目标")

View File

@ -18,7 +18,7 @@ const (
// 插件分类映射表 - 所有插件名使用小写
var pluginGroups = map[string][]string{
ModeAll: {
"web", "fcgi", // web类
"webtitle", "webpoc", "fcgi", // web类
"mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类
"oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j", // 数据库类
"ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "modbus", "rsync", // 服务类
@ -26,14 +26,14 @@ var pluginGroups = map[string][]string{
"findnet", // 其他
},
ModeBasic: {
"web", "ftp", "ssh", "smb", "findnet",
"webtitle", "ftp", "ssh", "smb", "findnet",
},
ModeDatabase: {
"mysql", "mssql", "redis", "mongodb",
"postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j",
},
ModeWeb: {
"web", "fcgi",
"webtitle", "webpoc", "fcgi",
},
ModeService: {
"ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "modbus", "rsync",

View File

@ -126,27 +126,23 @@ func PortConnect(addr Addr, results chan<- ScanResult, timeout int64, wg *sync.W
Port: addr.port,
}
// 进行服务识别
if conn != nil {
// 只在未跳过指纹识别时进行服务识别
if !Common.SkipFingerprint && conn != nil {
scanner := NewPortInfoScanner(addr.ip, addr.port, conn, time.Duration(timeout)*time.Second)
if serviceInfo, err := scanner.Identify(); err == nil {
result.Service = serviceInfo
// 打印服务识别信息
var logMsg strings.Builder
logMsg.WriteString(fmt.Sprintf("服务识别 %s => ", address))
// 添加服务名称
if serviceInfo.Name != "unknown" {
logMsg.WriteString(fmt.Sprintf("[%s]", serviceInfo.Name))
}
// 添加版本信息
if serviceInfo.Version != "" {
logMsg.WriteString(fmt.Sprintf(" 版本:%s", serviceInfo.Version))
}
// 添加其他有用的信息
if v, ok := serviceInfo.Extras["vendor_product"]; ok && v != "" {
logMsg.WriteString(fmt.Sprintf(" 产品:%s", v))
}
@ -157,7 +153,6 @@ func PortConnect(addr Addr, results chan<- ScanResult, timeout int64, wg *sync.W
logMsg.WriteString(fmt.Sprintf(" 信息:%s", v))
}
// 如果有Banner且长度合适也输出
if len(serviceInfo.Banner) > 0 && len(serviceInfo.Banner) < 100 {
logMsg.WriteString(fmt.Sprintf(" Banner:[%s]", strings.TrimSpace(serviceInfo.Banner)))
}
@ -166,7 +161,6 @@ func PortConnect(addr Addr, results chan<- ScanResult, timeout int64, wg *sync.W
}
}
// 发送结果
results <- result
}

View File

@ -195,7 +195,7 @@ func init() {
})
// web 相关插件添加 WebPorts 配置
Common.RegisterPlugin("web", Common.ScanPlugin{
Common.RegisterPlugin("webtitle", Common.ScanPlugin{
Name: "WebTitle",
Ports: Common.ParsePortsFromString(Common.WebPorts), // 将 WebPorts 字符串解析为端口数组
ScanFunc: Plugins.WebTitle,

View File

@ -222,16 +222,18 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
// finishScan 完成扫描任务
func finishScan(wg *sync.WaitGroup) {
wg.Wait()
// 确保进度条完成
Common.ProgressBar.Finish()
fmt.Println() // 添加一个换行
// 确保进度条完成,只在存在进度条时调用
if Common.ProgressBar != nil {
Common.ProgressBar.Finish()
fmt.Println() // 添加一个换行
}
Common.LogSuccess(fmt.Sprintf("扫描已完成: %v/%v", Common.End, Common.Num))
}
// Mutex用于保护共享资源的并发访问
var Mutex = &sync.Mutex{}
// AddScan 也需要修改
// AddScan
func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
*ch <- struct{}{}
wg.Add(1)

File diff suppressed because it is too large Load Diff

View File

@ -279,26 +279,30 @@ func IsAdmin() bool {
func MiniDump(info *Common.HostInfo) (err error) {
// 先检查管理员权限
if !IsAdmin() {
Common.LogError("需要管理员权限才能执行此操作")
return fmt.Errorf("需要管理员权限才能执行此操作")
}
pm, err := NewProcessManager()
if err != nil {
Common.LogError(fmt.Sprintf("初始化进程管理器失败: %v", err))
return fmt.Errorf("初始化进程管理器失败: %v", err)
}
// 查找 lsass.exe
pid, err := pm.FindProcess("lsass.exe")
if err != nil {
Common.LogError(fmt.Sprintf("查找进程失败: %v", err))
return fmt.Errorf("查找进程失败: %v", err)
}
fmt.Printf("找到进程 lsass.exe, PID: %d\n", pid)
Common.LogSuccess(fmt.Sprintf("找到进程 lsass.exe, PID: %d", pid))
// 提升权限
if err := pm.ElevatePrivileges(); err != nil {
Common.LogError(fmt.Sprintf("提升权限失败: %v", err))
return fmt.Errorf("提升权限失败: %v", err)
}
fmt.Println("成功提升进程权限")
Common.LogSuccess("成功提升进程权限")
// 创建输出路径
outputPath := filepath.Join(".", fmt.Sprintf("fscan-%d.dmp", pid))
@ -306,9 +310,10 @@ func MiniDump(info *Common.HostInfo) (err error) {
// 执行转储
if err := pm.DumpProcess(pid, outputPath); err != nil {
os.Remove(outputPath)
Common.LogError(fmt.Sprintf("进程转储失败: %v", err))
return fmt.Errorf("进程转储失败: %v", err)
}
fmt.Printf("成功将进程内存转储到文件: %s\n", outputPath)
Common.LogSuccess(fmt.Sprintf("成功将进程内存转储到文件: %s", outputPath))
return nil
}

View File

@ -18,7 +18,7 @@ import (
"golang.org/x/text/encoding/simplifiedchinese"
)
// WebTitle 获取Web标题并执行扫描
// WebTitle 获取Web标题和指纹信息
func WebTitle(info *Common.HostInfo) error {
// 获取网站标题信息
err, CheckData := GOWebTitle(info)
@ -31,10 +31,8 @@ func WebTitle(info *Common.HostInfo) error {
}
}
// 根据配置决定是否执行漏洞扫描
if !Common.DisablePoc && err == nil {
WebScan.WebScan(info)
} else {
// 输出错误信息(如果有)
if err != nil {
errlog := fmt.Sprintf("网站标题 %v %v", info.Url, err)
Common.LogError(errlog)
}

View File

@ -19,22 +19,43 @@ var AllPocs []*lib.Poc
// WebScan 执行Web漏洞扫描
func WebScan(info *Common.HostInfo) {
// 确保POC只初始化一次
once.Do(initpoc)
// 构建扫描信息
var pocinfo = Common.Pocinfo
urlParts := strings.Split(info.Url, "/")
pocinfo.Target = strings.Join(urlParts[:3], "/")
// 执行扫描
if pocinfo.PocName != "" {
// 指定POC扫描
// 自动构建URL
if info.Url == "" {
info.Url = fmt.Sprintf("http://%s:%s", info.Host, info.Ports)
}
urlParts := strings.Split(info.Url, "/")
// 检查切片长度并构建目标URL
if len(urlParts) >= 3 {
pocinfo.Target = strings.Join(urlParts[:3], "/")
} else {
pocinfo.Target = info.Url
}
Common.LogDebug(fmt.Sprintf("扫描目标: %s", pocinfo.Target))
// 如果是直接调用WebPoc没有指定pocName执行所有POC
if pocinfo.PocName == "" && len(info.Infostr) == 0 {
Common.LogDebug("直接调用WebPoc执行所有POC")
Execute(pocinfo)
} else {
// 根据指纹信息选择POC扫描
for _, infostr := range info.Infostr {
pocinfo.PocName = lib.CheckInfoPoc(infostr)
// 根据指纹信息选择性执行POC
if len(info.Infostr) > 0 {
for _, infostr := range info.Infostr {
pocinfo.PocName = lib.CheckInfoPoc(infostr)
if pocinfo.PocName != "" {
Common.LogDebug(fmt.Sprintf("根据指纹 %s 执行对应POC", infostr))
Execute(pocinfo)
}
}
} else if pocinfo.PocName != "" {
// 指定了特定的POC
Common.LogDebug(fmt.Sprintf("执行指定POC: %s", pocinfo.PocName))
Execute(pocinfo)
}
}
@ -42,6 +63,8 @@ func WebScan(info *Common.HostInfo) {
// Execute 执行具体的POC检测
func Execute(PocInfo Common.PocInfo) {
Common.LogDebug(fmt.Sprintf("开始执行POC检测目标: %s", PocInfo.Target))
// 创建基础HTTP请求
req, err := http.NewRequest("GET", PocInfo.Target, nil)
if err != nil {
@ -59,12 +82,16 @@ func Execute(PocInfo Common.PocInfo) {
// 根据名称筛选POC并执行
pocs := filterPoc(PocInfo.PocName)
Common.LogDebug(fmt.Sprintf("筛选到的POC数量: %d", len(pocs)))
lib.CheckMultiPoc(req, pocs, Common.PocNum)
}
// initpoc 初始化POC加载
func initpoc() {
Common.LogDebug("开始初始化POC")
if Common.PocPath == "" {
Common.LogDebug("从内置目录加载POC")
// 从嵌入的POC目录加载
entries, err := Pocs.ReadDir("pocs")
if err != nil {
@ -78,9 +105,11 @@ func initpoc() {
if strings.HasSuffix(filename, ".yaml") || strings.HasSuffix(filename, ".yml") {
if poc, err := lib.LoadPoc(filename, Pocs); err == nil && poc != nil {
AllPocs = append(AllPocs, poc)
} else if err != nil {
}
}
}
Common.LogDebug(fmt.Sprintf("内置POC加载完成共加载 %d 个", len(AllPocs)))
} else {
// 从指定目录加载POC
Common.LogSuccess(fmt.Sprintf("从目录加载POC: %s", Common.PocPath))
@ -92,6 +121,7 @@ func initpoc() {
if !info.IsDir() && (strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")) {
if poc, err := lib.LoadPocbyPath(path); err == nil && poc != nil {
AllPocs = append(AllPocs, poc)
} else if err != nil {
}
}
return nil
@ -100,12 +130,16 @@ func initpoc() {
if err != nil {
Common.LogError(fmt.Sprintf("加载外部POC失败: %v", err))
}
Common.LogDebug(fmt.Sprintf("外部POC加载完成共加载 %d 个", len(AllPocs)))
}
}
// filterPoc 根据POC名称筛选
func filterPoc(pocname string) []*lib.Poc {
Common.LogDebug(fmt.Sprintf("开始筛选POC筛选条件: %s", pocname))
if pocname == "" {
Common.LogDebug(fmt.Sprintf("未指定POC名称返回所有POC: %d 个", len(AllPocs)))
return AllPocs
}
@ -115,5 +149,6 @@ func filterPoc(pocname string) []*lib.Poc {
matchedPocs = append(matchedPocs, poc)
}
}
Common.LogDebug(fmt.Sprintf("POC筛选完成匹配到 %d 个", len(matchedPocs)))
return matchedPocs
}