diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index ccc4e98..53201dd 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -19,28 +19,39 @@ import ( "golang.org/x/text/encoding/simplifiedchinese" ) +// WebTitle 获取Web标题并执行扫描 func WebTitle(info *Config.HostInfo) error { + // 如果是webpoc扫描模式,直接执行WebScan if Common.Scantype == "webpoc" { WebScan.WebScan(info) return nil } + + // 获取网站标题信息 err, CheckData := GOWebTitle(info) info.Infostr = WebScan.InfoCheck(info.Url, &CheckData) - //不扫描打印机,避免打纸 + + // 检查是否为打印机,避免意外打印 for _, v := range info.Infostr { if v == "打印机" { return nil } } + + // 根据配置决定是否执行漏洞扫描 if !Common.NoPoc && err == nil { WebScan.WebScan(info) } else { errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) Common.LogError(errlog) } + return err } + +// GOWebTitle 获取网站标题并处理URL func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckDatas) { + // 如果URL未指定,根据端口生成URL if info.Url == "" { switch info.Ports { case "80": @@ -53,6 +64,7 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData info.Url = fmt.Sprintf("%s://%s:%s", protocol, info.Host, info.Ports) } } else { + // 处理未指定协议的URL if !strings.Contains(info.Url, "://") { host := strings.Split(info.Url, "/")[0] protocol := GetProtocol(host, Common.Timeout) @@ -60,12 +72,13 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData } } + // 第一次获取URL err, result, CheckData := geturl(info, 1, CheckData) if err != nil && !strings.Contains(err.Error(), "EOF") { return } - //有跳转 + // 处理URL跳转 if strings.Contains(result, "://") { info.Url = result err, result, CheckData = geturl(info, 3, CheckData) @@ -74,10 +87,12 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData } } + // 处理HTTP到HTTPS的升级 if result == "https" && !strings.HasPrefix(info.Url, "https://") { info.Url = strings.Replace(info.Url, "http://", "https://", 1) err, result, CheckData = geturl(info, 1, CheckData) - //有跳转 + + // 处理升级后的跳转 if strings.Contains(result, "://") { info.Url = result err, _, CheckData = geturl(info, 3, CheckData) @@ -86,22 +101,28 @@ func GOWebTitle(info *Config.HostInfo) (err error, CheckData []WebScan.CheckData } } } - //是否访问图标 - //err, _, CheckData = geturl(info, 2, CheckData) + if err != nil { return } return } +// geturl 获取URL响应内容和信息 +// 参数: +// - info: 主机配置信息 +// - flag: 请求类型标志(1:首次尝试 2:获取favicon 3:处理302跳转 4:处理400转https) +// - CheckData: 检查数据数组 +// +// 返回: +// - error: 错误信息 +// - string: 重定向URL或协议 +// - []WebScan.CheckDatas: 更新后的检查数据 func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (error, string, []WebScan.CheckDatas) { - //flag 1 first try - //flag 2 /favicon.ico - //flag 3 302 - //flag 4 400 -> https - + // 处理目标URL Url := info.Url if flag == 2 { + // 获取favicon.ico的URL URL, err := url.Parse(Url) if err == nil { Url = fmt.Sprintf("%s://%s/favicon.ico", URL.Scheme, URL.Host) @@ -109,61 +130,77 @@ func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er Url += "/favicon.ico" } } + + // 创建HTTP请求 req, err := http.NewRequest("GET", Url, nil) if err != nil { return err, "", CheckData } + + // 设置请求头 req.Header.Set("User-agent", Common.UserAgent) req.Header.Set("Accept", Common.Accept) req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9") if Common.Cookie != "" { req.Header.Set("Cookie", Common.Cookie) } - //if Common.Pocinfo.Cookie != "" { - // req.Header.Set("Cookie", "rememberMe=1;"+Common.Pocinfo.Cookie) - //} else { - // req.Header.Set("Cookie", "rememberMe=1") - //} req.Header.Set("Connection", "close") + + // 选择HTTP客户端 var client *http.Client if flag == 1 { - client = lib.ClientNoRedirect + client = lib.ClientNoRedirect // 不跟随重定向 } else { - client = lib.Client + client = lib.Client // 跟随重定向 } + // 发送请求 resp, err := client.Do(req) if err != nil { return err, "https", CheckData } - defer resp.Body.Close() - var title string + + // 读取响应内容 body, err := getRespBody(resp) if err != nil { return err, "https", CheckData } + + // 保存检查数据 CheckData = append(CheckData, WebScan.CheckDatas{body, fmt.Sprintf("%s", resp.Header)}) + + // 处理非favicon请求 var reurl string if flag != 2 { + // 处理编码 if !utf8.Valid(body) { body, _ = simplifiedchinese.GBK.NewDecoder().Bytes(body) } - title = gettitle(body) + + // 获取页面信息 + title := gettitle(body) length := resp.Header.Get("Content-Length") if length == "" { length = fmt.Sprintf("%v", len(body)) } + + // 处理重定向 redirURL, err1 := resp.Location() if err1 == nil { reurl = redirURL.String() } - result := fmt.Sprintf("[*] WebTitle %-25v code:%-3v len:%-6v title:%v", resp.Request.URL, resp.StatusCode, length, title) + + // 输出结果 + result := fmt.Sprintf("[*] 网站标题 %-25v 状态码:%-3v 长度:%-6v 标题:%v", + resp.Request.URL, resp.StatusCode, length, title) if reurl != "" { - result += fmt.Sprintf(" 跳转url: %s", reurl) + result += fmt.Sprintf(" 重定向地址: %s", reurl) } Common.LogSuccess(result) } + + // 返回结果 if reurl != "" { return nil, reurl, CheckData } @@ -173,14 +210,19 @@ func geturl(info *Config.HostInfo, flag int, CheckData []WebScan.CheckDatas) (er return nil, "", CheckData } +// getRespBody 读取HTTP响应体内容 func getRespBody(oResp *http.Response) ([]byte, error) { var body []byte + + // 处理gzip压缩的响应 if oResp.Header.Get("Content-Encoding") == "gzip" { gr, err := gzip.NewReader(oResp.Body) if err != nil { return nil, err } defer gr.Close() + + // 循环读取解压内容 for { buf := make([]byte, 1024) n, err := gr.Read(buf) @@ -193,6 +235,7 @@ func getRespBody(oResp *http.Response) ([]byte, error) { body = append(body, buf...) } } else { + // 直接读取未压缩的响应 raw, err := io.ReadAll(oResp.Body) if err != nil { return nil, err @@ -202,30 +245,41 @@ func getRespBody(oResp *http.Response) ([]byte, error) { return body, nil } +// gettitle 从HTML内容中提取网页标题 func gettitle(body []byte) (title string) { + // 使用正则表达式匹配title标签内容 re := regexp.MustCompile("(?ims)