FOFAu代码解析

admin 2022年10月16日09:18:26评论14 views字数 7070阅读23分34秒阅读模式

0x01 设计思路

首先,写一个FOFA客户端,想到的第一件事就是需要调用哪些库,首当其冲,肯定是net库中的http库函数;其次,通过观察FOFA官网,我们应该知道有一个base64加密,所以,第二个需要用到的库是encoding库中的base64库函数;第三个,必备库函数,输入输出——fmt库和log库;第四个,要想做到命令行的参数输入效果,所使用的库——flag库。

到此,一个基本的设计思路已经确定,收到参数->http请求FOFA API->接受数据->处理数据->输出。

0x02 代码解析

首先我们需要做一个存储在FOFA注册过的email和FOFA KEY,那么我们需要创建一个文件。这里提一下fmt.Scanln和fmt.Scanf的区别——fmt.Scanln()是一行行的输入用enter结束,fmt.Scanf()是所有的输入,中间用空格隔开,enter结束。所以这里小CV使用的是fmt.Scanln()函数,而不是fmt.Scanf()函数。这里还使用了os库,具体使用可以参考Go中文API库:https://studygolang.com/pkgdoc,有兴趣的师傅可以去查看。

//创建文件func CreatFile(email string, key string) {  log.Println("存放FOFA KEY的文本文档fofa_key.txt不存在")  fmt.Print("请输入您在FOFA注册的email地址:")  fmt.Scanln(&email)  fmt.Print("请输入您在FOFA获取的API KEY:")  fmt.Scanln(&key)  fofa_key_data := fmt.Sprintf("FOFA_Email=%snFOFA_Key=%s", email, key)  file_creat, err := os.Create(fofa_file_key)  if err != nil {    log.Fatalln("创建文件失败,程序终止")  }  file_creat.WriteString(fofa_key_data)  log.Println("文件已创建并且FOFA KEY已写入")  file_creat.Close()}

这里解释一下这个fofa_file_key的由来——提前设置好的静态变量。

const fofa_file_key = "./fofa_key.txt"

创建文件之后肯定需要读取文件,所以我们需要写一个读取文件的函数。这里又用到了一个库——regexp库,主要作用用于正则表达式搜索。感兴趣是师傅可以点击上面推荐的链接查看函数库的使用。关于读取文件的写法,师傅们可以参考该条链接:https://blog.csdn.net/slphahaha/article/details/122995124。正则表达式,第一个FOFA_Email=w+@w+.com,其中w+@w+.com正则表达式的意思是获取email;第二个,FOFA_Key=w{0,32},其中w{0,32}正则表达式的意思是获取字母、数字、下划线,获取数量为0到32个(因为FOFA KEY是32位)。最后,if判断获取内容,若为不为空,则返回email和key的内容;若为空,则if不执行,执行最后语句,返回nil字符串。

//读取FOFA KEYfunc ReadFile() (email string, key string) {  file, err := os.Open(fofa_file_key)  if err != nil {    log.Fatalln("文件打开失败")  }    //创建一个大小为1024个字节变量数组  read_buf := make([]byte, 1024)  for {        //每次读取1024个字节    len, _ := file.Read(read_buf)    if len == 0 {      break    }  }  file.Close()  fofa_key_data := string(read_buf)  //使用正则获取关键段落  email_match := regexp.MustCompile(`FOFA_Email=w+@w+.com`)  key_match := regexp.MustCompile(`FOFA_Key=w{0,32}`)  email_value := email_match.FindStringSubmatch(fofa_key_data)  key_value := key_match.FindStringSubmatch(fofa_key_data)  if email_value != nil && key_value != nil {  //去除获取的标头    email = strings.Replace(email_value[0], "FOFA_Email=", "", -1)    key = strings.Replace(key_value[0], "FOFA_Key=", "", -1)    return email, key  }  return "nil", "nil"}

获取FOFA邮箱和FOFA KEY的功能函数已经完成,那么我们需要做的就是对FOFA API进行访问,获取数据。这里又使用了一个库——ioutil库,这个库的功能函数ioutil.ReadAll()作用是从res.Body读取数据直到EOF或遇到error,返回读取的数据和遇到的错误,成功的调用返回的err为nil而非EOF。

//向FOFA发出请求func get_fofa_contents(url string) (result string) {  log.Print("正在访问链接", url)  res, err := http.Get(url)  if err != nil {    log.Fatal("请求出错,请查看填写的email和FOFA Key,错误参数为:", err)  }  fofa_data, err := ioutil.ReadAll(res.Body)  if err != nil {    log.Fatal("收到的FOFA数据类型错误")  }  return string(fofa_data)}

有写过FOFA客户端的师傅应该知道,从FOFA的API中获取的数据是JSON格式的数据,所以我们下一步应该是写一个JSON格式转换的功能函数。

这里提一下FOFA API返回的JSON格式

{    "error": false, // 是否出现错误    "size": 查询总数量,    "page": 当前页码,     "mode": "extended",    "query": "查询语句",     "results": [      [      数据1],      [      数据2],      [      ......]    ]}

在此功能函数中用到了json库,json.Unmarshl()库的作用是将JSON字符串转换成定义类型的切片,比如,小CV这里创建了一个map类型,用于接受JSON字符串。值得一提的是动态创建二维数组的方法,妙用make函数var arr = make([][]string, len(results_1)),刚学Go的师傅们可以借鉴。有的师傅肯定疑惑,这里为什么要创建二维数组,不是已经将results字段里面的内容提取出来了吗?这里小CV解释一下,提取了results字段,现在里面还是一个整体的字符串,而我们需要一个二维数组将里面的数据分离出来,所以小CV这里创建了一个二维数组,而为什么采用动态创建二维数组,主要是考虑师傅们使用的时候,获取的参数数据自由度,比如,默认是获取ip,host,port这三个字段,那么二维数组长度就是一级长度就是3。也就是这里为什么是len(results_1)的缘故。

//转换JSON数据func Convert_json(json_result string) (results [][]string) {  data_convert := make(map[string]interface{})  if err := json.Unmarshal([]byte(json_result), &data_convert); err != nil {    log.Fatal("JSON数据解析失败")  }  //动态创建二维数组  results_1 := data_convert["results"].([]interface{})  var arr = make([][]string, len(results_1))  i := 0  for _, result_1 := range results_1 {    result_2 := result_1.([]interface{})    arr[i] = make([]string, len(result_2))    j := 0    for _, result_3 := range result_2 {      arr[i][j] = fmt.Sprintf("%v", result_3)      j++    }    i++  }  return arr}

在此,JSON数据转换就完成了,小CV也是初学Go语言,所以对JSON数据的处理比较笨,大师傅们轻点喷。

有了结果数据,那么我们肯定就是输出结果数据,所以下面就是写一个输出功能函数。

//输出查询数据func out_result(results [][]string, columns []string, dict map[string]string) {  log.Println("FOFA查询结果如下:")  for i := 0; i < len(results); i++ {    for j := 0; j < len(columns); j++ {      fmt.Printf("%s:%sn", dict[columns[j]], results[i][j])    }  }}

        这里使用了一个map类型的变量,主要作用是翻译字段(FOFA官网的API文档里面有写)。

dict := map[string]string{"ip": "ip地址", "port": "端口", "protocol": "协议名", "country": "国家代码", "country_name": "国家名", "region": "区域", "city": "城市", "longitude": "地理位置-经度", "latitude": "地理位置-纬度", "as_number": "asn编号", "as_organization": "asn组织", "host": "主机名", "domain": "域名", "os": "操作系统", "server": "网站server", "icp": "icp备案号", "title": "网站标题", "jarm": "jarm 指纹", "header": "网站header", "banner": "协议 banner", "cert": "证书", "body": "网站正文内容", "fid": "fid", "structinfo": "结构化信息"}

写到这里基本上FOFA客户端的功能已经实现了,当然,小CV这里还写了一个保存数据生成csv文件的功能函数,让使用的师傅体验感更好。所以,用到了encoding库中的csv功能函数。师傅们可以参考https://blog.csdn.net/weixin_44676081/article/details/108029175,用于查看csv功能函数的使用方法。

//保存结果func ResultSave(results [][]string, columns []string, dict map[string]string, save_path string) {  file, err := os.Create(save_path)  if err != nil {    log.Fatal("创建保存数据文件失败,失败原因:", err)  }  defer file.Close()  writer := csv.NewWriter(file)    //写字段名  var column_name = make([]string, len(columns))  for i, column := range columns {    column_name[i] = dict[column]  }  writer.Write(column_name)    //写内容数据  var column_value = make([]string, len(results[0]))  for _, result_1 := range results {    for j, result_2 := range result_1 {      column_value[j] = result_2    }    writer.Write(column_value)  }  writer.Flush()  log.Printf("文件已保存,保存路径:%s", save_path)}

最后使用main主函数进行调用。提一下log的使用,log.Fatal()函数是输出之后结束整个进程的使用,log.SetPrefix()函数设置输出头,比如,[CV].......这种格式,小CV这里主要是用于署名,hhhhh

func main() {  var email string  var key string  fmt.Println(flg)  log.SetPrefix("[CV]")  //判断文件是否存在  _, err := os.Stat(fofa_file_key)  if os.IsNotExist(err) {    CreatFile(email, key)    log.Fatal("使用本工具请输入参数-h 查看帮助信息")  } else {    for {      email, key = ReadFile()      if email == "nil" || key == "nil" {        log.Println("未获取到Email或FOFA KEY")        os.Remove(fofa_file_key)        CreatFile(email, key)      } else {        break      }    }  }  //设置工具参数  content_point := flag.String("q", ``, "参数q:搜索内容,默认为空(必填项)")  size := flag.String("s", "100", "参数s:获取搜索的数量,默认为100条,最大为10000条")  field := flag.String("f", "ip,host,port", "  参数f:获取指定队列名,默认ip,host,port")  save := flag.String("sv", "up", "参数sv:搜索结果保存,默认开启")  save_path := flag.String("p", "_"+time.Now().Format("2006_01_02_15_04_05"), "参数p:结果保存位置以及文件名,默认本文件夹,如:./xxxx")  flag.Parse()  //判断q参数是否为空  if *content_point == `` {    log.Fatal("搜索参数为空,请填写q参数")  }  //生成url  dict := map[string]string{"ip": "ip地址", "port": "端口", "protocol": "协议名", "country": "国家代码", "country_name": "国家名", "region": "区域", "city": "城市", "longitude": "地理位置-经度", "latitude": "地理位置-纬度", "as_number": "asn编号", "as_organization": "asn组织", "host": "主机名", "domain": "域名", "os": "操作系统", "server": "网站server", "icp": "icp备案号", "title": "网站标题", "jarm": "jarm 指纹", "header": "网站header", "banner": "协议 banner", "cert": "证书", "body": "网站正文内容", "fid": "fid", "structinfo": "结构化信息"}  *save_path = "./" + *content_point + *save_path + ".csv"  //生成文件名拼接  columns := strings.Split(*field, ",")  content := base64.StdEncoding.EncodeToString([]byte(*content_point))  content = strings.Replace(content, "==", "", -1)  url := fmt.Sprintf("%semail=%s&key=%s&qbase64=%s&size=%s&field=%s", fofaurl, email, key, content, *size, *field)  //开始对FOFA的数据进行收集  result_json := get_fofa_contents(url)  log.Println("搜索内容为:", *content_point)  results := Convert_json(result_json)  out_result(results, columns, dict)  if *save == "up" {    ResultSave(results, columns, dict, *save_path)  }  if *save != "off" && *save != "up" {    log.Fatal("sv参数错误,只能选择up或者off")  }
}

0x03 申明

此工具仅供学习网络安全交流,不负有相关法律责任。请大家维护好网络环境,请勿进行非法测试。

以上内容属于原创,转发请附上链接。

工具已经开源,可以关注公众号:乌托邦安全团队,在后台回复:FOFAu,可获取GitHub链接。


原文始发于微信公众号(乌托邦安全团队):FOFAu代码解析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月16日09:18:26
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   FOFAu代码解析http://cn-sec.com/archives/1352168.html

发表评论

匿名网友 填写信息