refactor: 调整扫描逻辑

This commit is contained in:
ZacharyZcR 2025-01-04 14:04:41 +08:00
parent 75aeee5215
commit af06345aa5
7 changed files with 205 additions and 46 deletions

View File

@ -886,9 +886,6 @@ var (
Command string Command string
SkipFingerprint bool SkipFingerprint bool
// 本地扫描配置
LocalScan bool
// 文件配置 // 文件配置
HostsFile string // 原HostFile HostsFile string // 原HostFile
UsersFile string // 原Userfile UsersFile string // 原Userfile
@ -897,6 +894,8 @@ var (
PortsFile string // 原PortFile PortsFile string // 原PortFile
// Web配置 // Web配置
TargetURL string // 原URL
URLsFile string // 原UrlFile
URLs []string // 原Urls URLs []string // 原Urls
WebTimeout int64 = 5 WebTimeout int64 = 5
HttpProxy string // 原Proxy HttpProxy string // 原Proxy

View File

@ -200,9 +200,6 @@ func Flag(Info *HostInfo) {
flag.StringVar(&Command, "c", "", "指定要执行的系统命令(支持ssh和wmiexec)") flag.StringVar(&Command, "c", "", "指定要执行的系统命令(支持ssh和wmiexec)")
flag.BoolVar(&SkipFingerprint, "skip", false, "跳过端口指纹识别") flag.BoolVar(&SkipFingerprint, "skip", false, "跳过端口指纹识别")
// 本地扫描配置
flag.BoolVar(&LocalScan, "local", false, "启用本地扫描模式")
// 文件配置 // 文件配置
flag.StringVar(&HostsFile, "hf", "", "从文件中读取目标主机列表") flag.StringVar(&HostsFile, "hf", "", "从文件中读取目标主机列表")
flag.StringVar(&UsersFile, "userf", "", "从文件中读取用户名字典") flag.StringVar(&UsersFile, "userf", "", "从文件中读取用户名字典")
@ -211,6 +208,8 @@ func Flag(Info *HostInfo) {
flag.StringVar(&PortsFile, "portf", "", "从文件中读取端口列表") flag.StringVar(&PortsFile, "portf", "", "从文件中读取端口列表")
// Web配置 // Web配置
flag.StringVar(&TargetURL, "u", "", "指定目标URL")
flag.StringVar(&URLsFile, "uf", "", "从文件中读取URL列表")
flag.StringVar(&Cookie, "cookie", "", "设置HTTP请求Cookie") flag.StringVar(&Cookie, "cookie", "", "设置HTTP请求Cookie")
flag.Int64Var(&WebTimeout, "wt", 5, "设置Web请求超时时间(单位:秒)") flag.Int64Var(&WebTimeout, "wt", 5, "设置Web请求超时时间(单位:秒)")
flag.StringVar(&HttpProxy, "proxy", "", "设置HTTP代理服务器") flag.StringVar(&HttpProxy, "proxy", "", "设置HTTP代理服务器")

View File

@ -3,7 +3,6 @@ package Common
import ( import (
"bufio" "bufio"
"encoding/hex" "encoding/hex"
"flag"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
@ -114,6 +113,40 @@ func ParsePass(Info *HostInfo) error {
LogInfo(fmt.Sprintf("加载有效哈希值: %d 个", validCount)) 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 != "" { if PortsFile != "" {
ports, err := Readfile(PortsFile) ports, err := Readfile(PortsFile)
@ -171,16 +204,9 @@ func Readfile(filename string) ([]string, error) {
// ParseInput 解析和验证输入参数配置 // ParseInput 解析和验证输入参数配置
func ParseInput(Info *HostInfo) error { func ParseInput(Info *HostInfo) error {
// 检查必要的目标参数 // 所有目标参数为空时表示本地扫描模式
if Info.Host == "" && HostsFile == "" { if Info.Host == "" && HostsFile == "" && TargetURL == "" && URLsFile == "" {
LogError("未指定扫描目标") LogInfo("未指定扫描目标,将以本地模式运行")
flag.Usage()
return fmt.Errorf("必须指定扫描目标")
}
// 如果是本地扫描模式,输出提示
if LocalScan {
LogInfo("已启用本地扫描模式")
} }
// 配置基本参数 // 配置基本参数

View File

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

View File

@ -13,28 +13,74 @@ import (
"time" "time"
) )
// 定义在文件开头
var (
LocalScan bool // 本地扫描模式标识
WebScan bool // Web扫描模式标识
)
// Scan 执行扫描主流程 // Scan 执行扫描主流程
func Scan(info Common.HostInfo) { func Scan(info Common.HostInfo) {
Common.LogInfo("开始信息扫描") Common.LogInfo("开始信息扫描")
// 初始化HTTP客户端
lib.Inithttp()
// 处理特殊情况
if info.Host == "" && len(Common.URLs) == 0 {
// Host为空且没有URLs,设置Local模式
LocalScan = true
Common.ScanMode = Common.ModeLocal
Common.LogInfo("未检测到目标,自动切换为本地扫描模式")
} else if len(Common.URLs) > 0 {
// 存在URLs时设置为Web模式
WebScan = true
Common.ScanMode = Common.ModeWeb
Common.LogInfo("检测到URL列表,自动切换为Web扫描模式")
}
Common.ParseScanMode(Common.ScanMode) Common.ParseScanMode(Common.ScanMode)
ch := make(chan struct{}, Common.ThreadNum) ch := make(chan struct{}, Common.ThreadNum)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
// 本地信息收集模式 // 本地信息收集模式
if Common.LocalScan { if LocalScan {
executeScans([]Common.HostInfo{info}, &ch, &wg) executeScans([]Common.HostInfo{info}, &ch, &wg)
finishScan(&wg) finishScan(&wg)
return return
} }
// 初始化并解析目标 // Web模式直接处理URLs
hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) if WebScan {
if err != nil { var targetInfos []Common.HostInfo
Common.LogError(fmt.Sprintf("解析主机错误: %v", err)) for _, url := range Common.URLs {
urlInfo := info
// 确保URL包含协议前缀
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
url = "http://" + url
}
urlInfo.Url = url
targetInfos = append(targetInfos, urlInfo)
}
if len(targetInfos) > 0 {
Common.LogInfo("开始Web扫描")
executeScans(targetInfos, &ch, &wg)
finishScan(&wg)
}
return return
} }
lib.Inithttp()
// 常规模式:初始化并解析目标
var hosts []string
var err error
if info.Host != "" {
hosts, err = Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts)
if err != nil {
Common.LogError(fmt.Sprintf("解析主机错误: %v", err))
return
}
}
// 执行目标扫描 // 执行目标扫描
executeScan(hosts, info, &ch, &wg) executeScan(hosts, info, &ch, &wg)
@ -117,10 +163,8 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
} }
loadedPlugins := make([]string, 0) loadedPlugins := make([]string, 0)
// 先遍历一遍计算实际要执行的任务数
actualTasks := 0 actualTasks := 0
// 定义任务结构
type ScanTask struct { type ScanTask struct {
pluginName string pluginName string
target Common.HostInfo target Common.HostInfo
@ -137,7 +181,19 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
continue continue
} }
if Common.LocalScan { // Web模式特殊处理
if WebScan {
actualTasks++
loadedPlugins = append(loadedPlugins, pluginName)
tasks = append(tasks, ScanTask{
pluginName: pluginName,
target: target,
})
continue
}
// 本地扫描模式
if LocalScan {
if len(plugin.Ports) == 0 { if len(plugin.Ports) == 0 {
actualTasks++ actualTasks++
loadedPlugins = append(loadedPlugins, pluginName) loadedPlugins = append(loadedPlugins, pluginName)
@ -149,6 +205,7 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
continue continue
} }
// 单插件模式
if isSinglePlugin { if isSinglePlugin {
actualTasks++ actualTasks++
loadedPlugins = append(loadedPlugins, pluginName) loadedPlugins = append(loadedPlugins, pluginName)
@ -159,6 +216,7 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
continue continue
} }
// 常规模式
if len(plugin.Ports) > 0 { if len(plugin.Ports) > 0 {
if plugin.HasPort(targetPort) { if plugin.HasPort(targetPort) {
actualTasks++ actualTasks++
@ -193,7 +251,7 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
Common.LogInfo(fmt.Sprintf("加载的插件: %s", strings.Join(finalPlugins, ", "))) Common.LogInfo(fmt.Sprintf("加载的插件: %s", strings.Join(finalPlugins, ", ")))
// 初始化进度条的地方添加判断 // 初始化进度条
if !Common.NoProgress { if !Common.NoProgress {
Common.ProgressBar = progressbar.NewOptions(actualTasks, Common.ProgressBar = progressbar.NewOptions(actualTasks,
progressbar.OptionEnableColorCodes(true), progressbar.OptionEnableColorCodes(true),
@ -213,7 +271,7 @@ func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGro
) )
} }
// 开始执行收集所有任务 // 执行收集的任务
for _, task := range tasks { for _, task := range tasks {
AddScan(task.pluginName, task.target, ch, wg) AddScan(task.pluginName, task.target, ch, wg)
} }

View File

@ -20,13 +20,19 @@ import (
// WebTitle 获取Web标题和指纹信息 // WebTitle 获取Web标题和指纹信息
func WebTitle(info *Common.HostInfo) error { func WebTitle(info *Common.HostInfo) error {
Common.LogDebug(fmt.Sprintf("开始获取Web标题初始信息: %+v", info))
// 获取网站标题信息 // 获取网站标题信息
err, CheckData := GOWebTitle(info) err, CheckData := GOWebTitle(info)
Common.LogDebug(fmt.Sprintf("GOWebTitle执行完成 - 错误: %v, 检查数据长度: %d", err, len(CheckData)))
info.Infostr = WebScan.InfoCheck(info.Url, &CheckData) info.Infostr = WebScan.InfoCheck(info.Url, &CheckData)
Common.LogDebug(fmt.Sprintf("信息检查完成,获得信息: %v", info.Infostr))
// 检查是否为打印机,避免意外打印 // 检查是否为打印机,避免意外打印
for _, v := range info.Infostr { for _, v := range info.Infostr {
if v == "打印机" { if v == "打印机" {
Common.LogDebug("检测到打印机,停止扫描")
return nil return nil
} }
} }
@ -42,8 +48,11 @@ func WebTitle(info *Common.HostInfo) error {
// GOWebTitle 获取网站标题并处理URL // GOWebTitle 获取网站标题并处理URL
func GOWebTitle(info *Common.HostInfo) (err error, CheckData []WebScan.CheckDatas) { func GOWebTitle(info *Common.HostInfo) (err error, CheckData []WebScan.CheckDatas) {
Common.LogDebug(fmt.Sprintf("开始处理URL: %s", info.Url))
// 如果URL未指定根据端口生成URL // 如果URL未指定根据端口生成URL
if info.Url == "" { if info.Url == "" {
Common.LogDebug("URL为空根据端口生成URL")
switch info.Ports { switch info.Ports {
case "80": case "80":
info.Url = fmt.Sprintf("http://%s", info.Host) info.Url = fmt.Sprintf("http://%s", info.Host)
@ -51,28 +60,37 @@ func GOWebTitle(info *Common.HostInfo) (err error, CheckData []WebScan.CheckData
info.Url = fmt.Sprintf("https://%s", info.Host) info.Url = fmt.Sprintf("https://%s", info.Host)
default: default:
host := fmt.Sprintf("%s:%s", info.Host, info.Ports) host := fmt.Sprintf("%s:%s", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("正在检测主机协议: %s", host))
protocol := GetProtocol(host, Common.Timeout) protocol := GetProtocol(host, Common.Timeout)
Common.LogDebug(fmt.Sprintf("检测到协议: %s", protocol))
info.Url = fmt.Sprintf("%s://%s:%s", protocol, info.Host, info.Ports) info.Url = fmt.Sprintf("%s://%s:%s", protocol, info.Host, info.Ports)
} }
} else { } else {
// 处理未指定协议的URL // 处理未指定协议的URL
if !strings.Contains(info.Url, "://") { if !strings.Contains(info.Url, "://") {
Common.LogDebug("URL未包含协议开始检测")
host := strings.Split(info.Url, "/")[0] host := strings.Split(info.Url, "/")[0]
protocol := GetProtocol(host, Common.Timeout) protocol := GetProtocol(host, Common.Timeout)
Common.LogDebug(fmt.Sprintf("检测到协议: %s", protocol))
info.Url = fmt.Sprintf("%s://%s", protocol, info.Url) info.Url = fmt.Sprintf("%s://%s", protocol, info.Url)
} }
} }
Common.LogDebug(fmt.Sprintf("协议检测完成后的URL: %s", info.Url))
// 第一次获取URL // 第一次获取URL
Common.LogDebug("第一次尝试访问URL")
err, result, CheckData := geturl(info, 1, CheckData) err, result, CheckData := geturl(info, 1, CheckData)
Common.LogDebug(fmt.Sprintf("第一次访问结果 - 错误: %v, 返回信息: %s", err, result))
if err != nil && !strings.Contains(err.Error(), "EOF") { if err != nil && !strings.Contains(err.Error(), "EOF") {
return return
} }
// 处理URL跳转 // 处理URL跳转
if strings.Contains(result, "://") { if strings.Contains(result, "://") {
Common.LogDebug(fmt.Sprintf("检测到重定向到: %s", result))
info.Url = result info.Url = result
err, result, CheckData = geturl(info, 3, CheckData) err, result, CheckData = geturl(info, 3, CheckData)
Common.LogDebug(fmt.Sprintf("重定向请求结果 - 错误: %v, 返回信息: %s", err, result))
if err != nil { if err != nil {
return return
} }
@ -80,11 +98,14 @@ func GOWebTitle(info *Common.HostInfo) (err error, CheckData []WebScan.CheckData
// 处理HTTP到HTTPS的升级 // 处理HTTP到HTTPS的升级
if result == "https" && !strings.HasPrefix(info.Url, "https://") { if result == "https" && !strings.HasPrefix(info.Url, "https://") {
Common.LogDebug("正在升级到HTTPS")
info.Url = strings.Replace(info.Url, "http://", "https://", 1) info.Url = strings.Replace(info.Url, "http://", "https://", 1)
Common.LogDebug(fmt.Sprintf("升级后的URL: %s", info.Url))
err, result, CheckData = geturl(info, 1, CheckData) err, result, CheckData = geturl(info, 1, CheckData)
// 处理升级后的跳转 // 处理升级后的跳转
if strings.Contains(result, "://") { if strings.Contains(result, "://") {
Common.LogDebug(fmt.Sprintf("HTTPS升级后发现重定向到: %s", result))
info.Url = result info.Url = result
err, _, CheckData = geturl(info, 3, CheckData) err, _, CheckData = geturl(info, 3, CheckData)
if err != nil { if err != nil {
@ -93,38 +114,34 @@ func GOWebTitle(info *Common.HostInfo) (err error, CheckData []WebScan.CheckData
} }
} }
Common.LogDebug(fmt.Sprintf("GOWebTitle执行完成 - 错误: %v", err))
if err != nil { if err != nil {
return return
} }
return return
} }
// geturl 获取URL响应内容和信息
// 参数:
// - info: 主机配置信息
// - flag: 请求类型标志(1:首次尝试 2:获取favicon 3:处理302跳转 4:处理400转https)
// - CheckData: 检查数据数组
//
// 返回:
// - error: 错误信息
// - string: 重定向URL或协议
// - []WebScan.CheckDatas: 更新后的检查数据
func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) {
Common.LogDebug(fmt.Sprintf("geturl开始执行 - URL: %s, 标志位: %d", info.Url, flag))
// 处理目标URL // 处理目标URL
Url := info.Url Url := info.Url
if flag == 2 { if flag == 2 {
// 获取favicon.ico的URL Common.LogDebug("处理favicon.ico URL")
URL, err := url.Parse(Url) URL, err := url.Parse(Url)
if err == nil { if err == nil {
Url = fmt.Sprintf("%s://%s/favicon.ico", URL.Scheme, URL.Host) Url = fmt.Sprintf("%s://%s/favicon.ico", URL.Scheme, URL.Host)
} else { } else {
Url += "/favicon.ico" Url += "/favicon.ico"
} }
Common.LogDebug(fmt.Sprintf("favicon URL: %s", Url))
} }
// 创建HTTP请求 // 创建HTTP请求
Common.LogDebug("开始创建HTTP请求")
req, err := http.NewRequest("GET", Url, nil) req, err := http.NewRequest("GET", Url, nil)
if err != nil { if err != nil {
Common.LogDebug(fmt.Sprintf("创建HTTP请求失败: %v", err))
return err, "", CheckData return err, "", CheckData
} }
@ -136,36 +153,52 @@ func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er
req.Header.Set("Cookie", Common.Cookie) req.Header.Set("Cookie", Common.Cookie)
} }
req.Header.Set("Connection", "close") req.Header.Set("Connection", "close")
Common.LogDebug("已设置请求头")
// 选择HTTP客户端 // 选择HTTP客户端
var client *http.Client var client *http.Client
if flag == 1 { if flag == 1 {
client = lib.ClientNoRedirect // 不跟随重定向 client = lib.ClientNoRedirect
Common.LogDebug("使用不跟随重定向的客户端")
} else { } else {
client = lib.Client // 跟随重定向 client = lib.Client
Common.LogDebug("使用普通客户端")
}
// 检查客户端是否为空
if client == nil {
Common.LogDebug("错误: HTTP客户端为空")
return fmt.Errorf("HTTP客户端未初始化"), "", CheckData
} }
// 发送请求 // 发送请求
Common.LogDebug("开始发送HTTP请求")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
Common.LogDebug(fmt.Sprintf("HTTP请求失败: %v", err))
return err, "https", CheckData return err, "https", CheckData
} }
defer resp.Body.Close() defer resp.Body.Close()
Common.LogDebug(fmt.Sprintf("收到HTTP响应状态码: %d", resp.StatusCode))
// 读取响应内容 // 读取响应内容
body, err := getRespBody(resp) body, err := getRespBody(resp)
if err != nil { if err != nil {
Common.LogDebug(fmt.Sprintf("读取响应内容失败: %v", err))
return err, "https", CheckData return err, "https", CheckData
} }
Common.LogDebug(fmt.Sprintf("成功读取响应内容,长度: %d", len(body)))
// 保存检查数据 // 保存检查数据
CheckData = append(CheckData, WebScan.CheckDatas{body, fmt.Sprintf("%s", resp.Header)}) CheckData = append(CheckData, WebScan.CheckDatas{body, fmt.Sprintf("%s", resp.Header)})
Common.LogDebug("已保存检查数据")
// 处理非favicon请求 // 处理非favicon请求
var reurl string var reurl string
if flag != 2 { if flag != 2 {
// 处理编码 // 处理编码
if !utf8.Valid(body) { if !utf8.Valid(body) {
Common.LogDebug("检测到非UTF8编码尝试GBK解码")
body, _ = simplifiedchinese.GBK.NewDecoder().Bytes(body) body, _ = simplifiedchinese.GBK.NewDecoder().Bytes(body)
} }
@ -175,11 +208,13 @@ func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er
if length == "" { if length == "" {
length = fmt.Sprintf("%v", len(body)) length = fmt.Sprintf("%v", len(body))
} }
Common.LogDebug(fmt.Sprintf("提取的标题: %s, 内容长度: %s", title, length))
// 处理重定向 // 处理重定向
redirURL, err1 := resp.Location() redirURL, err1 := resp.Location()
if err1 == nil { if err1 == nil {
reurl = redirURL.String() reurl = redirURL.String()
Common.LogDebug(fmt.Sprintf("检测到重定向URL: %s", reurl))
} }
// 输出结果 // 输出结果
@ -193,22 +228,28 @@ func geturl(info *Common.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er
// 返回结果 // 返回结果
if reurl != "" { if reurl != "" {
Common.LogDebug(fmt.Sprintf("返回重定向URL: %s", reurl))
return nil, reurl, CheckData return nil, reurl, CheckData
} }
if resp.StatusCode == 400 && !strings.HasPrefix(info.Url, "https") { if resp.StatusCode == 400 && !strings.HasPrefix(info.Url, "https") {
Common.LogDebug("返回HTTPS升级标志")
return nil, "https", CheckData return nil, "https", CheckData
} }
Common.LogDebug("geturl执行完成无特殊返回")
return nil, "", CheckData return nil, "", CheckData
} }
// getRespBody 读取HTTP响应体内容 // getRespBody 读取HTTP响应体内容
func getRespBody(oResp *http.Response) ([]byte, error) { func getRespBody(oResp *http.Response) ([]byte, error) {
Common.LogDebug("开始读取响应体内容")
var body []byte var body []byte
// 处理gzip压缩的响应 // 处理gzip压缩的响应
if oResp.Header.Get("Content-Encoding") == "gzip" { if oResp.Header.Get("Content-Encoding") == "gzip" {
Common.LogDebug("检测到gzip压缩开始解压")
gr, err := gzip.NewReader(oResp.Body) gr, err := gzip.NewReader(oResp.Body)
if err != nil { if err != nil {
Common.LogDebug(fmt.Sprintf("创建gzip解压器失败: %v", err))
return nil, err return nil, err
} }
defer gr.Close() defer gr.Close()
@ -218,6 +259,7 @@ func getRespBody(oResp *http.Response) ([]byte, error) {
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, err := gr.Read(buf) n, err := gr.Read(buf)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
Common.LogDebug(fmt.Sprintf("读取压缩内容失败: %v", err))
return nil, err return nil, err
} }
if n == 0 { if n == 0 {
@ -225,25 +267,32 @@ func getRespBody(oResp *http.Response) ([]byte, error) {
} }
body = append(body, buf...) body = append(body, buf...)
} }
Common.LogDebug(fmt.Sprintf("gzip解压完成内容长度: %d", len(body)))
} else { } else {
// 直接读取未压缩的响应 // 直接读取未压缩的响应
Common.LogDebug("读取未压缩的响应内容")
raw, err := io.ReadAll(oResp.Body) raw, err := io.ReadAll(oResp.Body)
if err != nil { if err != nil {
Common.LogDebug(fmt.Sprintf("读取响应内容失败: %v", err))
return nil, err return nil, err
} }
body = raw body = raw
Common.LogDebug(fmt.Sprintf("读取完成,内容长度: %d", len(body)))
} }
return body, nil return body, nil
} }
// gettitle 从HTML内容中提取网页标题 // gettitle 从HTML内容中提取网页标题
func gettitle(body []byte) (title string) { func gettitle(body []byte) (title string) {
Common.LogDebug("开始提取网页标题")
// 使用正则表达式匹配title标签内容 // 使用正则表达式匹配title标签内容
re := regexp.MustCompile("(?ims)<title.*?>(.*?)</title>") re := regexp.MustCompile("(?ims)<title.*?>(.*?)</title>")
find := re.FindSubmatch(body) find := re.FindSubmatch(body)
if len(find) > 1 { if len(find) > 1 {
title = string(find[1]) title = string(find[1])
Common.LogDebug(fmt.Sprintf("找到原始标题: %s", title))
// 清理标题内容 // 清理标题内容
title = strings.TrimSpace(title) // 去除首尾空格 title = strings.TrimSpace(title) // 去除首尾空格
@ -253,38 +302,48 @@ func gettitle(body []byte) (title string) {
// 截断过长的标题 // 截断过长的标题
if len(title) > 100 { if len(title) > 100 {
Common.LogDebug("标题超过100字符进行截断")
title = title[:100] title = title[:100]
} }
// 处理空标题 // 处理空标题
if title == "" { if title == "" {
title = "\"\"" // 空标题显示为双引号 Common.LogDebug("标题为空,使用双引号代替")
title = "\"\""
} }
} else { } else {
title = "无标题" // 没有找到title标签 Common.LogDebug("未找到标题标签")
title = "无标题"
} }
Common.LogDebug(fmt.Sprintf("最终标题: %s", title))
return return
} }
// GetProtocol 检测目标主机的协议类型(HTTP/HTTPS) // GetProtocol 检测目标主机的协议类型(HTTP/HTTPS)
func GetProtocol(host string, Timeout int64) (protocol string) { func GetProtocol(host string, Timeout int64) (protocol string) {
Common.LogDebug(fmt.Sprintf("开始检测主机协议 - 主机: %s, 超时: %d秒", host, Timeout))
protocol = "http" protocol = "http"
// 根据标准端口快速判断协议 // 根据标准端口快速判断协议
if strings.HasSuffix(host, ":80") || !strings.Contains(host, ":") { if strings.HasSuffix(host, ":80") || !strings.Contains(host, ":") {
Common.LogDebug("检测到HTTP标准端口或无端口使用HTTP协议")
return return
} else if strings.HasSuffix(host, ":443") { } else if strings.HasSuffix(host, ":443") {
Common.LogDebug("检测到HTTPS标准端口使用HTTPS协议")
protocol = "https" protocol = "https"
return return
} }
// 尝试建立TCP连接 // 尝试建立TCP连接
Common.LogDebug("尝试建立TCP连接")
socksconn, err := Common.WrapperTcpWithTimeout("tcp", host, time.Duration(Timeout)*time.Second) socksconn, err := Common.WrapperTcpWithTimeout("tcp", host, time.Duration(Timeout)*time.Second)
if err != nil { if err != nil {
Common.LogDebug(fmt.Sprintf("TCP连接失败: %v", err))
return return
} }
// 尝试TLS握手 // 尝试TLS握手
Common.LogDebug("开始TLS握手")
conn := tls.Client(socksconn, &tls.Config{ conn := tls.Client(socksconn, &tls.Config{
MinVersion: tls.VersionTLS10, MinVersion: tls.VersionTLS10,
InsecureSkipVerify: true, InsecureSkipVerify: true,
@ -298,6 +357,7 @@ func GetProtocol(host string, Timeout int64) (protocol string) {
Common.LogError(fmt.Sprintf("连接关闭时发生错误: %v", err)) Common.LogError(fmt.Sprintf("连接关闭时发生错误: %v", err))
} }
}() }()
Common.LogDebug("关闭连接")
conn.Close() conn.Close()
} }
}() }()
@ -308,8 +368,12 @@ func GetProtocol(host string, Timeout int64) (protocol string) {
// 执行TLS握手 // 执行TLS握手
err = conn.Handshake() err = conn.Handshake()
if err == nil || strings.Contains(err.Error(), "handshake failure") { if err == nil || strings.Contains(err.Error(), "handshake failure") {
Common.LogDebug("TLS握手成功或握手失败但确认是HTTPS协议")
protocol = "https" protocol = "https"
} else {
Common.LogDebug(fmt.Sprintf("TLS握手失败: %v使用HTTP协议", err))
} }
Common.LogDebug(fmt.Sprintf("协议检测完成,使用: %s", protocol))
return protocol return protocol
} }

View File

@ -6,6 +6,7 @@ import (
"github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/WebScan/lib" "github.com/shadow1ng/fscan/WebScan/lib"
"net/http" "net/http"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -65,6 +66,18 @@ func WebScan(info *Common.HostInfo) {
func Execute(PocInfo Common.PocInfo) { func Execute(PocInfo Common.PocInfo) {
Common.LogDebug(fmt.Sprintf("开始执行POC检测目标: %s", PocInfo.Target)) Common.LogDebug(fmt.Sprintf("开始执行POC检测目标: %s", PocInfo.Target))
// 确保URL格式正确
if !strings.HasPrefix(PocInfo.Target, "http://") && !strings.HasPrefix(PocInfo.Target, "https://") {
PocInfo.Target = "http://" + PocInfo.Target
}
// 验证URL格式
_, err := url.Parse(PocInfo.Target)
if err != nil {
Common.LogError(fmt.Sprintf("无效的URL格式 %v: %v", PocInfo.Target, err))
return
}
// 创建基础HTTP请求 // 创建基础HTTP请求
req, err := http.NewRequest("GET", PocInfo.Target, nil) req, err := http.NewRequest("GET", PocInfo.Target, nil)
if err != nil { if err != nil {