Go语言实现指纹识别

指纹识别

指纹识别是一种用于识别网络设备、操作系统、应用程序或用户的技术。它通过收集和分析目标系统的特征信息,如网络协议、端口号、操作系统版本、应用程序版本等,来确定系统的身份或特征。指纹识别可以用于多种用途,包括网络侦察、入侵检测、安全审计等。

常用的指纹识别方法:

  1. 特定文件的MD5值:一些CMS的特定的静态资源:图片favicon.ico logo.ico之类的、js文件、css文件一般是不会修改的。通过爬虫对这些文件进行抓取并比对 md5 值,如果和规则库中的 Md5 一致则说明是同一 CMS。这种方式速度比较快,误报率相对低一些,但也不排除有些二次开发的 CMS 会修改这些文件。
  2. 正常页面或错误页面中包含的关键字:先访问首页或特定页面如 robots.txt 等,通过正则的方式去匹配某些关键字,如 Powered by Discuz、dedecms 等。或者可以构造错误页面,根据报错信息来判断使用的 CMS 或者中间件信息,比较常见的如 tomcat 的报错页面。
  3. 请求头关键字信息的匹配:根据网站 response 返回头信息进行关键字匹配,whatweb 和 Wappalyzer 就是通过 banner 信息来快速识别指纹,之前 fofa 的 web 指纹库很多都是使用的这种方法,效率非常高,基本请求一次就可以,但搜集这些规则可能会耗时很长。而且这些 banner 信息有些很容易被改掉。

根据 response header 一般有以下几种识别方式:

  • 查看 http 响应报头的 X-Powered-By 字段来识别;
  • 根据 Cookies 来进行判断,比如一些 waf 会在返回头中包含一些信息,如 360wzws、Safedog、yunsuo 等;
  • 根据 header 中的 Server 信息来判断,如 DVRDVS-Webs、yunjiasu-nginx、Mod_Security、nginx-wallarm 等;
  • 根据 WWW-Authenticate 进行判断,一些路由交换设备可能存在这个字段,如 NETCORE、huawei、h3c 等设备。
  1. 部分URL中包含的关键字:比如 wp-includes、dede 等 URL 关键特征。通过规则库去探测是否有相应目录,或者根据爬虫结果对链接 url 进行分析,或者对 robots.txt 文件中目录进行检测等等方式,通过 url 地址来判别是否使用了某 CMS,比如 wordpress 默认存在 wp-includes 和 wp-admin 目录,织梦默认管理后台为 dede 目录,solr 平台可能使用 /solr 目录,weblogic 可能使用 wls-wsat 目录等。
  2. 开发语言的识别:php?jsp?aspx?asp?
    主要方式:
  • 通过爬虫获取动态链接直接判断
  • 通过 X-Powered-By :常见的有 X-Powered-By: ASP.NET 或者 X-Powered-By: PHP/7.1.8
  • 通过 Set-Cookie 进行识别:Set-Cookie 中包含 PHPSSIONID 说明是 php、包含 JSESSIONID 说明是 java、包含 ASP.NET_SessionId 说明是 aspx 等

Goland,启动!

参考:https://github.com/newbe3three/gotoscan

选定指纹识别方式

对比文件md5值和页面中关键字

解析json文件 parsecms.go

首先分析json文件

"gowinsoft_jw": [{
		"path": "/web/web/web/images/4bt1.jpg",
		"option": "md5",
		"content": "ef1ee9c8708cde1bd25a90054de85690"
	}, {
	...
	}],
	"maticsoftsns": [{
		"path": "/msgbox/images/gb_tip_layer.png",
		"option": "md5",
		"content": "c8cb16e8b61bc549ebd339858e66fa5c"
	}, {
	...
	}],
	.....

可以看见cms对应特征有三个内容:path、option、content,我们需要将其解析成Go结构体。通常将json数据解析成map[][],然后借助encoding/json 包的方法实现对json文件的解析。因此定义:

//对应CMS特征的内容
type CmsFeature struct {
	Path    string `json:"path"`
	Option  string `json:"option"`
	Content string `json:"content"`
}
//通过map来对应json形式的数据,k就是cms名,v就是对应的特征切片。
map[string][]CmsFeature

发起请求 request.go

先对服务器发起head请求,状态码为200再发起get请求。先发起head请求主要是由于其响应头和get是完全一样的,但是服务器不会返回请求的实体数据,避免了传输请求、响应体的数据浪费。head请求时非常快的。

首先会定义一个代理池,然后随机选择一个代理头。文件中除此之外就是两个处理url的函数

func HeadReq(url string) (resp *http.Response, err error) {
	// 创建了自定义的http.Transport 结构体
	tranCfg := &http.Transport{
		// 设置了 TLSClientConfig 字段,将 InsecureSkipVerify 设置为 true
		// 这表示跳过对服务器证书的验证,用于处理不安全的 HTTPS 连接
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	// 创建http.Client结构体,设置超时时间为10s
	client := &http.Client{
		Timeout:   10 * time.Second,
		Transport: tranCfg,
	}
	req, err := http.NewRequest("HEAD", url, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("User_Agent", get_random_ua())
	//resp, err2 :=
	// 返回响应和可能的错误
	return client.Do(req)
}

func GetReq(url string) (content []byte, err error) {
	tranCfg := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}

	client := &http.Client{
		Timeout:   10 * time.Second,
		Transport: tranCfg,
	}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	// 读取http响应体标准用法
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return bytes, nil
}

作者这儿的处理还是蛮简单的

实现测试 scancms.go

扫描部分重要的是并发————同时扫描多台主机;同时扫描多个cms
Go语言的并发是易得的,只需要再你想要的并发的函数前面加上关键字go就实现了简单的并发操作。

//对多个host的并发操作
func HostWorker(hosts []string, cmslist map[string][]CmsFeature, sortList CmsSortList) []string {
    // 创建通道
	hostsChan := make(chan string)
	resultChan := make(chan string)
	var resultList []string
    // 根据获取到的目标数量开启并发
	for i := 0; i < len(hosts); i++ {
		go cmsWorker(hostsChan, cmslist, sortList, resultChan)
	}
	for _, host := range hosts {
		hostsChan <- host
	}
    // 使用通道获取线程
	for i := 0; i < len(hosts); i++ {
        // 程序会在这里阻塞直到有数据传入通道
		result := <-resultChan
		resultList = append(resultList, result)
	}

	close(hostsChan)
	close(resultChan)
	return resultList
}
//对多个cms的并发操作
func cmsWorker(hosts chan string, cmslist map[string][]CmsFeature, sortList CmsSortList, resultChan chan string) {
	for host := range hosts {
		var scanStatus bool = false
		cmsListChan := make(chan map[string][]CmsFeature, 10)

		var wg sync.WaitGroup// 用来控制线程
		for i := 0; i < cap(cmsListChan); i++ {
			go featureWorker(host, cmsListChan, &wg, &scanStatus, resultChan)
		}

        // 如果匹配到了直接return,没匹配到计数器+1
		for _, data := range sortList {
			if !scanStatus {
				wg.Add(1)
				cmsListChan <- map[string][]CmsFeature{data.Name: cmslist[data.Name]}
			} else {
				//wg1.Done()
				return
			}

		}

		wg.Wait()
        // 扫描完所有特征都没匹配到,向结果通道添加一条数据,也就是所谓发起和接收要一样。
        // 每个host都应该有一个扫描结果
		resultChan <- fmt.Sprintf("The host: %s has no matching results", host)
		close(cmsListChan)

	}

}

TODO

开发一个自己的指纹识别插件

参考文章

https://www.cnblogs.com/newbe3three/p/15767076.html

posted @ 2024-04-14 18:45  smile_2233  阅读(140)  评论(0编辑  收藏  举报