ehole简介
______ __ ______
/ ____/___/ /___ ____/_ __/__ ____ _____ ___
/ __/ / __ / __ `/ _ \/ / / _ \/ __ `/ __ `__ \
/ /___/ /_/ / /_/ / __/ / / __/ /_/ / / / / / /
/_____/\__,_/\__, /\___/_/ \___/\__,_/_/ /_/ /_/
/____/ https://forum.ywhack.com By:shihuang
EHole是一款对资产中重点系统指纹识别的工具,在红队作战中,信息收集是必不可少的环节,如何才能从大量的资产中提取有用的系统(如OA、VPN、Weblogic...)。EHole旨在帮助红队人员在信息收集期间能够快速从C段、大量杂乱的资产中精准定位到易被攻击的系统,从而实施进一步攻击。
抄自https://github.com/ShiHuang-ESec/EHole
ehole的fofaext功能粗略梳理
ehole3.0没找到源码,用的是上面git仓库里开源的2.0源码
ehole的fofa相关功能分fofaext导出模块和finger指纹识别模块两个部分,其中重要的api调用部分则是在finger之下的Fofaall_out语法查询函数实现,使用fofa_api函数组装api url,然后返回给fofahttp函数进行查询,Fafaips_out本地ip查询函数则是单独实现对本地文件中的ip的读取以及一些处理,然后交由Fofaall_out查询。fofaext使用finger中的相关函数来获取结果,然后储存到文件中。
开始改造
0zone api加装
直接用原来的fofa功能改造,fofa的api是用get方法来使用的,而0zone用的是post,所以要对fofa_api和fofahttp进行改造。先看关键部分请求的实现。
0zone api使用文档https://0.zone/applyParticulars?type=site
API请求部分
//请求api的json数据结构
type Zoneapi struct {
Title string `json:"title"`
Title_type string `json:"title_type"`
Page int `json:"page"`
Pagesize int `json:"pagesize"`
Zone_key_id string `json:"zone_key_id"`
}
//接受data的结构体,0zone api返回来的data需要用一个结构体接,直接用字符串数组接不到
type Zoneapi_data struct {
Ip string `json:"ip"`
Port string `json:"port"`
Url string `json:"url"`
Title string `json:"title"`
Cms string `json:"cms"`
Service string `json:"service"`
Banner string `json:"banner"`
Company string `json:"company"`
Html_banner string `json:"html_banner"`
}
//接受结果
type AutoGenerated_zone struct {
Code int `json:"code"`
Message string `json:"message"`
Page int `json:"page"`
Size int `json:"pagesize"`
Total string `json:"total"`
Data []Zoneapi_data `json:"data"`
}
//keyword是0zone的查询语句,这里的email其实是没用的,只是我懒得删,key是apitoken,这些参数由ext0zoneCmd方法传入
func zone_api(keyword string, email string, key string, page int, size int, timeout string) *AutoGenerated_zone {
data := Zoneapi{
Title: keyword,
Title_type: "site",
Page: page,
Pagesize: size,
Zone_key_id: key,
}
url := "https://0.zone/api/data/"
var itime, err = strconv.Atoi(timeout)
if err != nil {
log.Println("zone超时参数错误: ", err)
}
transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
client := &http.Client{
Timeout: time.Duration(itime) * time.Second,
Transport: transport,
}
payload, err := json.Marshal(&data)
if err != nil {
log.Fatal(err)
}
reader := bytes.NewReader(payload)
req, err := http.NewRequest("POST", url, reader)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Accept", "*/*;q=0.8")
req.Header.Set("Connection", "close")
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36")
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
res := &AutoGenerated_zone{}
json.Unmarshal(result, &res)
return res
}
//把结构体里面的数据塞进字符串数组
func Zone2string(Data []Zoneapi_data) (result [][]string) {
var tmp []string
for i := 0; i < len(Data); i++ {
tmp = append(tmp, Data[i].Ip)
tmp = append(tmp, Data[i].Url)
tmp = append(tmp, Data[i].Title)
tmp = append(tmp, Data[i].Port)
tmp = append(tmp, Data[i].Service)
tmp = append(tmp, Data[i].Cms)
tmp = append(tmp, Data[i].Company)
result = append(result, tmp)
tmp = nil
}
return result
}
func Zoneall_out(keyword string) (result [][]string) {
zone := GetConfig()
res := zone_api(keyword, zone.Email, zone.Zone_key, 1, 40, zone.Fofa_timeout)
it, _ := strconv.Atoi(res.Total)
rpage := math.Ceil(float64(it) / 40)
for i := 1; i <= int(rpage); i++ {
res = zone_api(keyword, zone.Email, zone.Zone_key, i, 40, zone.Fofa_timeout)
tmp := Zone2string(res.Data)
//res := zonehttp(url, zone.zone_timeout)
if len(res.Data) > 0 {
result = append(result, tmp...)
} else {
break
}
}
return
}
储存结果
func Ext0zone(msg [][]string, filename string) {
xlsx := excelize.NewFile()
xlsx.SetCellValue("Sheet1", "A1", "ip")
xlsx.SetCellValue("Sheet1", "B1", "url")
xlsx.SetCellValue("Sheet1", "C1", "title")
xlsx.SetCellValue("Sheet1", "D1", "port")
xlsx.SetCellValue("Sheet1", "E1", "service")
xlsx.SetCellValue("Sheet1", "F1", "cms")
xlsx.SetCellValue("Sheet1", "G1", "company")
for k, v := range msg {
xlsx.SetCellValue("Sheet1", "A"+strconv.Itoa(k+2), v[0])
xlsx.SetCellValue("Sheet1", "B"+strconv.Itoa(k+2), v[1])
xlsx.SetCellValue("Sheet1", "C"+strconv.Itoa(k+2), v[2])
xlsx.SetCellValue("Sheet1", "D"+strconv.Itoa(k+2), v[3])
xlsx.SetCellValue("Sheet1", "E"+strconv.Itoa(k+2), v[4])
xlsx.SetCellValue("Sheet1", "F"+strconv.Itoa(k+2), v[5])
xlsx.SetCellValue("Sheet1", "G"+strconv.Itoa(k+2), v[6])
}
err := xlsx.SaveAs(filename)
if err != nil {
fmt.Println(err)
}
}
整合到finger
先往finger上加参数
var fingerCmd = &cobra.Command{
Use: "finger",
Short: "ehole的指纹识别模块",
Long: `从fofa或者本地文件获取资产进行指纹识别,支持单条url识别。`,
Run: func(cmd *cobra.Command, args []string) {
color.RGBStyleFromString("105,187,92").Println("\n ______ __ ______ \n" +
" / ____/___/ /___ ____/_ __/__ ____ _____ ___ \n" +
" / __/ / __ / __ `/ _ \\/ / / _ \\/ __ `/ __ `__ \\\n" +
" / /___/ /_/ / /_/ / __/ / / __/ /_/ / / / / / /\n" +
" /_____/\\__,_/\\__, /\\___/_/ \\___/\\__,_/_/ /_/ /_/ \n" +
" /____/ https://forum.ywhack.com By:shihuang\n")
if localfile != "" {
urls := removeRepeatedElement(source.LocalFile(localfile))
s := finger.NewScan(urls, thread, output, proxy)
s.StartScan()
os.Exit(1)
}
if fofaip != "" {
urls := removeRepeatedElement(source.Fofaip(fofaip))
s := finger.NewScan(urls, thread, output, proxy)
s.StartScan()
os.Exit(1)
}
if zoneip != "" {
urls := removeRepeatedElement(source.Zoneip(zoneip))
s := finger.NewScan(urls, thread, output, proxy)
s.StartScan()
os.Exit(1)
}
if fofasearche != "" {
urls := removeRepeatedElement(source.Fafaall(fofasearche))
s := finger.NewScan(urls, thread, output, proxy)
s.StartScan()
os.Exit(1)
}
if zonesearche != "" {
urls := removeRepeatedElement(source.Zoneall(zonesearche))
s := finger.NewScan(urls, thread, output, proxy)
s.StartScan()
os.Exit(1)
}
if urla != "" {
s := finger.NewScan([]string{urla}, thread, output, proxy)
s.StartScan()
os.Exit(1)
}
},
}
var (
fofaip string
zoneip string
fofasearche string
zonesearche string
localfile string
urla string
thread int
output string
proxy string
)
func init() {
rootCmd.AddCommand(fingerCmd)
fingerCmd.Flags().StringVarP(&fofaip, "fip", "F", "", "从fofa提取资产,进行指纹识别,仅仅支持ip或者ip段,例如:192.168.1.1 | 192.168.1.0/24")
fingerCmd.Flags().StringVarP(&zoneip, "zip", "Z", "", "从0zone提取资产,进行指纹识别,仅仅支持ip或者ip段,例如:192.168.1.1 | 192.168.1.0/24")
fingerCmd.Flags().StringVarP(&fofasearche, "fofa", "s", "", "从fofa提取资产,进行指纹识别,支持fofa所有语法")
fingerCmd.Flags().StringVarP(&zonesearche, "zone", "S", "", "从0zone提取资产,进行指纹识别,支持0zone所有语法")
fingerCmd.Flags().StringVarP(&localfile, "localfofa", "l", "", "从本地文件读取资产,进行指纹识别,支持无协议,列如:192.168.1.1:9090 | http://192.168.1.1:9090")
fingerCmd.Flags().StringVarP(&output, "output", "o", "", "输出所有结果,当前仅支持json和xlsx后缀的文件。")
fingerCmd.Flags().IntVarP(&thread, "thread", "t", 100, "指纹识别线程大小。")
fingerCmd.Flags().StringVarP(&proxy, "proxy", "p", "", "指定访问目标时的代理,支持http代理和socks5,例如:http://127.0.0.1:8080、socks5://127.0.0.1:8080")
}
然后去zone里面实现,由于0zone返回的结果里面直接就有url,所以就很简单了
func Zoneip(ips string) (urls []string) {
color.RGBStyleFromString("244,211,49").Println("请耐心等待zone搜索......")
zone := GetConfig()
keyword := `ip=="` + ips + `"`
res := zone_api(keyword, zone.Email, zone.Zone_key, 1, 40, zone.Fofa_timeout)
it, _ := strconv.Atoi(res.Total)
rpage := math.Ceil(float64(it) / 40)
for i := 1; i <= int(rpage); i++ {
res = zone_api(keyword, zone.Email, zone.Zone_key, i, 40, zone.Fofa_timeout)
if len(res.Data) > 0 {
for _, data := range res.Data {
urls = append(urls, data.Url)
}
} else {
break
}
}
return
}
func Zoneall(keyword string) (urls []string) {
color.RGBStyleFromString("244,211,49").Println("请耐心等待zone搜索......")
zone := GetConfig()
res := zone_api(keyword, zone.Email, zone.Zone_key, 1, 40, zone.Fofa_timeout)
it, _ := strconv.Atoi(res.Total)
rpage := math.Ceil(float64(it) / 40)
for i := 1; i <= int(rpage); i++ {
res = zone_api(keyword, zone.Email, zone.Zone_key, i, 40, zone.Fofa_timeout)
if len(res.Data) > 0 {
for _, data := range res.Data {
urls = append(urls, data.Url)
}
} else {
break
}
}
return
}
go中结构体的tag
tag在结构体中起到类似注释和说明的作用,一些常见的tag选项可以用于一些函数
json - used by the encoding/json package, detailed at json.Marshal()
json - used by the encoding/json package, detailed at json.Marshal()
xml - used by the encoding/xml package, detailed at xml.Marshal()
bson - used by gobson, detailed at bson.Marshal()
protobuf - used by github.com/golang/protobuf/proto, detailed in the package doc
yaml - used by the gopkg.in/yaml.v2 package, detailed at yaml.Marshal()
db - used by the github.com/jmoiron/sqlx package; also used by github.com/go-gorp/gorp package
orm - used by the github.com/astaxie/beego/orm package, detailed at Models – Beego ORM
gorm - used by the github.com/jinzhu/gorm package, examples can be found in their doc: Models
valid - used by the github.com/asaskevich/govalidator package, examples can be found in the project page
datastore - used by appengine/datastore (Google App Engine platform, Datastore service), detailed at Properties
schema - used by github.com/gorilla/schema to fill a struct with HTML form values, detailed in the package doc
asn - used by the encoding/asn1 package, detailed at asn1.Marshal() and asn1.Unmarshal()