【script】python調用shodan、fofa、zoomeye搜索引擎

環境說明

操作系統:windows10

python版本:python3

Shodan

安裝shodan模塊

pip install shodan

接口調用

self.SHODAN_API_KEY需要改爲自己的key

import shodan

class Shodan:
    '''搜索引擎 - Shodan'''
    def __init__(self):
        '''
        初始化
        '''
        # 指定API_KEY
        self.SHODAN_API_KEY = "your_key"
        self.api = shodan.Shodan(self.SHODAN_API_KEY)

    def condition_search(self, condition, limit=100):
        '''Shodan篩選參數及其內容(product:"mysql" +country:"cn" -city:"beijing"):
        {"ip":"ip", "port":"端口", "hostname":"域名", product":"使用的軟件或產品", "version":"版本",
        "vuln":"CVE漏洞編號", "http.title":"網頁標題", "http.html":"網頁內容", "http.status":"響應碼狀態",
        "service":"返回包中的服務", "os":"操作系統", "country":"國家", "city":"城市", "org":"組織機構"}
        '''
        try:
            Shodan.credit(self)
            # 調用接口
            results = self.api.search(condition, limit=limit)
            # 查詢內容總量
            print('\n[ Data for Shodan ]')
            print('[ Results found ]', results['total'])
            # 輸出ip、端口、域名、系統、國家、詳情數據
            print('ip_str\tport\tdomains\tsystem\tcountry_name\tdata')
            for result in results['matches']:
                print('%s\t%s\t%s\t%s\t%s\t%s' % (
                    result['ip_str'], result['port'], result['domains'], result['os'],
                    result['location']['country_name'], result['data']))
        except Exception as e:
            raise e
            sys.exit()

    def ip_search(self, ip):
        '''
        反查ip信息
        '''
        try:
            hosts = self.api.host(ip)
            # 輸出ip、廠家、系統
            print("IP: %s\r\nOrganization: %s\r\nOperating System: %s" % (
            hosts['ip_str'], hosts.get('org', 'n/a'), hosts.get('os', 'n/a')))
            # 遍歷輸出端口信息
            for host in hosts['data']:
                print("Port: %s\r\nBanner: %s" % (host['port'], host['data']))
        except shodan.exception.APIError as e:
            print('[ Info ]', e)
        except Exception as e:
            raise e
            sys.exit()

    def credit(self):
        '''
        查詢信用,不足則警示
        '''
        try:
            results = self.api.info()
            # 查詢信用不足5則發出警告
            if results['query_credits'] < 6: print('[ Info ] 查詢信用剩餘:%s' % str(results['query_credits']))
        except Exception as e:
            raise e
            sys.exit()
            
if __name__ == '__main__':
    s = Shodan()
    s.condition_search('port:"3306" country:"cn"', 5)

Fofa

安裝fofa模塊

手動創建(推薦)

1、在%Python38%\Lib\site-packages目錄下創建fofa.py

2、fofa.py內容如下:

# -*- coding: utf-8 -*-
import base64
import json
import urllib

class Fofa:
    def __init__(self,email,key):
        self.email = email
        self.key = key
        self.base_url = "https://fofa.so"
        self.search_api_url = "/api/v1/search/all"
        self.login_api_url = "/api/v1/info/my"
        self.get_userinfo() #check email and key

    def get_userinfo(self):
        api_full_url = "%s%s" % (self.base_url,self.login_api_url)
        param = {"email":self.email,"key":self.key}
        res = self.__http_get(api_full_url,param)
        return json.loads(res)

    def get_data(self,query_str,page=1,fields=""):
        res = self.get_json_data(query_str,page,fields)
        return json.loads(res)

    def get_json_data(self,query_str,page=1,fields=""):
        api_full_url = "%s%s" % (self.base_url,self.search_api_url)
        param = {"qbase64":base64.b64encode(query_str),"email":self.email,"key":self.key,"page":page,"fields":fields}
        res = self.__http_get(api_full_url,param)
        return res

    def __http_get(self,url,param):
        param = urllib.parse.urlencode(param)
        url = "%s?%s" % (url,param)
        try:
            req = urllib.request.Request(url)
            res = urllib.request.urlopen(req).read().decode()
            if "errmsg" in res:
                raise RuntimeError(res)
        except urllib.request.HTTPError as e:
            print("[ Error ]", e.read())
            raise e
            sys.exit()
        return res

從github上下載(不推薦)

github地址:https://github.com/fofapro/fofa-py

1、將下載的fofa-py-master.zip文件解壓,並改名爲fofa,移動到%Python38%Lib\site-packages目錄下

2、進入fofa文件夾,運行如下命令即可:

python setup.py install

注:這種方法存在一個弊端,該github項目是使用python2寫的,如果使用python3來運行,中間可能會出現幾個跟client.py文件相關的報錯,需要自行修改。當然,如果使用的是python2運行,便不會有這些問題,本文環境統一使用python3

接口調用

接口的調用默認採用上述推薦模塊安裝方法

self.FOFA_API_MAIL、self.FOFA_API_KEY需要改爲自己的mail、key

import fofa

class Fofa:
    '''搜索引擎 - Fofa'''
    def __init__(self):
        '''
        初始化
        '''
        # 指定API_MAIL
        self.FOFA_API_MAIL = 'your_mail'
        # 指定API_KEY
        self.FOFA_API_KEY = 'your_key'
        self.api = fofa.Fofa(self.FOFA_API_MAIL, self.FOFA_API_KEY)

    def condition_search(self, condition, limit=100):
        '''Fofa篩選參數及其內容(app="mysql"&&country="cn"&&city!="beijing"):
        {"ip":"ip", "port":"端口", "domain":"域名", "host":"url", "app":"使用的軟件或產品",
         "title":"網頁標題", "body":"網頁內容", "header":"從http頭中搜索", "server":"返回包中的服務",
         "os":"操作系統", "country":"國家", "city":"城市", "org":"組織機構"}
         '''
        try:
            # 調用接口
            results = self.api
            # 輸出ip、端口、域名、國家、詳情數據
            print('\n[ Data for Fofa ]')
            print('ip\tport\tdomain\tcountry\tdata')
            # 輸出ip、端口、域名、國家
            for page in range(1, SFZ.count_pages('fofa', limit) + 2):
                for ip, port, domain, country, banner in results.get_data(condition, page, fields="ip, port, domain, country, banner")['results']:
                    print('[%s]\t[%s]\t[%s]\t[%s]\t[%s]' % (ip, port, domain, country, banner.replace('\n', '').replace('\r', '')))
        except Exception as e:
            raise e
            sys.exit()
            
if __name__ == '__main__':
    f = Fofa()
    f.condition_search('port="3306"&&country="cn"', 5)

ZoomEye

安裝zoomeye模塊

手動創建(推薦)

1、在%Python38%\Lib\site-packages目錄下創建zoomeye.py

2、zoomeye.py內容如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import requests
from requests import packages
import json
import sys

class Zoomeye(object):
    """docstring for zoomeye"""

    def __init__(self):
        self.access_token = ''
        self.search_type = ''
        self.query = ''
        self.page = 1
        self.facets = ''
        self.result = []

    def logIn(self, username, password):
        data = {
            'username' : username,
            'password' : password
        }
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
        }
        data_encoded = json.dumps(data)  # dumps 將 python 對象轉換成 json 字符串
        try:
            # 禁止SSL警告提示
            packages.urllib3.disable_warnings()
            resp = requests.post(url = 'https://api.zoomeye.org/user/login', headers=headers, data = data_encoded)
            resp_decode = json.loads(resp.content) # loads() 將 json 字符串轉換成 python 對象
            global access_token
            access_token = resp_decode['access_token']
            return access_token
        except json.decoder.JSONDecodeError as e:
            print('[ Error ] 請確保網絡通暢!')
            sys.exit()
        except Exception as e:
            raise e
            sys.exit()

    def getInfo(self):
        global access_token
        try:
            headers = {
                'Authorization' : 'JWT ' + access_token,
            }
            resp = requests.get(url='http://api.zoomeye.org/resources-info',headers=headers)

            try:
                user_type = json.loads(resp.content)['plan']
                host_search = json.loads(resp.content)['resources']['host-search']
                web_search = json.loads(resp.content)['resources']['web-search']
                info = 'Your account is %s, host search is %d and web search is %d' % (user_type,host_search,web_search)
                return info
            except Exception as e:
                print('[ Error ] Unauthorized, please login')
        except Exception as e:
            raise e
            sys.exit()

    def search(self, search_type='web', query='dedecms', page=1, facets='app,os'):
        search_page = page
        global access_token
        if search_type != 'web' and search_type != 'host':
            print('[ Error ] params is invalid')

        # 將 token 格式化並添加到 HTTP Header 中
        headers = {
            'Authorization' : 'JWT ' + access_token,
        }

        while(True):
            try:
                resp = requests.get(url = 'http://api.zoomeye.org/%s/search?query=%s&facet=%s&page=%d' % (str(search_type),str(query),str(facets),int(search_page)),headers = headers)
                resp_decode = json.loads(resp.content)
                self.result.append(resp_decode)
            except Exception as e:
                # 若搜索請求超過 API 允許的最大條目限制 或者 全部搜索結束,則終止請求
                if str(e.message) == 'matches':
                    print('[ info ] account was break, excceeding the max limitations')
                    break
                else:
                    print('[ info ]', str(e.message))
                    sys.exit()
            else:
                if search_page >= page:
                    break
                search_page += 1

        return self.result

從github上下載(不推薦)

github地址:https://github.com/SEC08/ZoomEye-API-SDK

1、將下載的ZoomEye-API-SDK-master.zip文件解壓,並改名爲zoomeye,移動到%Python38%Lib\site-packages目錄下

2、進入zoomeye文件夾,運行如下命令即可:

python setup.py install

注:該方法存在的弊端和fofa的github項目是一樣的

接口調用

self.USERNAME、self.PASSWORD需要改爲自己的mail、password

import zoomeye

class Zoomeye:
    '''搜索引擎 - Zoomeye'''
    def __init__(self):
        '''
        初始化
        '''
        # 指定用戶名、密碼
        self.USERNAME = 'your_mail'
        self.PASSWORD = 'your_password'
        self.api = zoomeye.Zoomeye()
        # 登陸獲得token
        self.token = self.api.logIn(self.USERNAME, self.PASSWORD)

    def condition_search(self, condition, limit):
        '''Zoomeye篩選參數及其內容(app:"mysql" +country:"cn" -city:"beijing"):
        {"ip":"ip", "port":"端口", "hostname":"域名", "app":"使用的軟件或產品",
         "device":"設備類型", "ver":"版本", "title":"網頁標題", "desc":"網頁內容",
         "header":"從http頭中搜索", "keywords":"meta屬性關鍵詞", "os":"操作系統",
         "country":"國家", "city":"城市"}
         '''
        try:
            for page in range(1, SFZ.count_pages('zoomeye', limit) + 2):
                # 調用接口進行查詢
                results = self.api.search(query=condition, page=page)[0]
                if page == 1:
                    # 查詢內容總量
                    print('\n[ Data for Zoomeye ]')
                    print('[ Results found ]', results['total'])
                    # 輸出ip、域名、應用、系統、國家、返回包頭
                    print('ip\tdomains\twebapp\tsystem\tcountry\theader')
                for result in results['matches']:
                    # print(result)
                    # break
                    print('%s\t%s\t%s\t%s\t%s\t%s' % (
                        result['ip'], result['domains'], result['webapp'], result['system'],
                        result['geoinfo']['country']['names']['en'], result['headers'].replace('\n', ' ').replace('\r', ' ')))
        except Exception as e:
            raise e
            sys.exit()
          
if __name__ == '__main__':
    z = Zoomeye()
    z.condition_search('port:3306 +country:cn', 5)

三合一代碼(可同時調用三個搜索引擎)

'''
功能:搜索引擎API調用
引擎類型:shodan、roomeye、fofa
'''

import sys
import shodan
import fofa
import zoomeye

class SFZ:
    '''同時調用Shodan、Fofa、Zoomeye三個搜索引擎'''
    def condition_search(condition, limit=100):
        '''同時調用三個引擎(不推薦)篩選參數及其內容(app="mysql"&country="cn"&city!="beijing"):
        {"ip":"ip", "port":"端口", "domain":"域名", "app":"使用的軟件或產品", "title":"網頁標題",
        "body":"網頁內容", "os":"操作系統", "country":"國家", "city":"城市"}
        '''
        s = Shodan()
        s.condition_search(SFZ.replace_conditon('shodan', condition), limit)
        f = Fofa()
        f.condition_search(SFZ.replace_conditon('fofa', condition), limit)
        z = Zoomeye()
        z.condition_search(SFZ.replace_conditon('zoomeye', condition), limit)

    def count_pages(sz ,limit):
        '''計算查詢頁數'''
        try:
            # Fofa一頁爲100個,故分母爲101
            if sz == 'fofa': d = 101
            # Zoomeye一頁爲20個,故分母爲21
            else: d = 21
            limit = int(limit)
            if limit / d > 0:
                pages = int(limit / d)
            else:
                pages = 1
            return pages
        except Exception as e:
            raise e
            sys.exit()

    def replace_conditon(sz, conditiion):
        '''
        替換傳過來的篩選語句,使之能與搜索引擎兼容
        三引擎同時調用的篩選參數與規則幾乎和fofa的定義無異
        '''
        try:
            # 將篩選條件轉化爲字典,方便後面替換運算
            doubles_list = conditiion.split('&')
            double_dict = {}
            for double in doubles_list:
                double_dict[double.split('=')[0]] = double.split('=')[1]

            # 對Shodan或Zoomeye進行替換
            if sz in ['shodan', 'zoomeye']:
                # 判斷是哪個搜索引擎需要進行替換,並擬定替換的參數名稱
                if sz == 'shodan': instead_dict = {'domain': 'hostname', 'app': 'product', 'tittle': 'http.tittle', 'body': 'http.html'}
                if sz == 'zoomeye': instead_dict = {'domain': 'hostname', 'body': 'desc'}

                for key_1 in double_dict.keys():
                    # 替換"!"符號爲"-"符號
                    if key_1[-1] == "!": conditiion = conditiion.replace(key_1, "-" + key_1[:-1])
                    # 在所有非去除參數前加上"+"符號
                    else: conditiion = conditiion.replace(key_1, "+" + key_1)
                    
                    # 替換參數名稱
                    for key_2 in instead_dict.keys():
                        if key_2 in key_1:
                            conditiion = conditiion.replace(key_2, instead_dict[key_2])
                
                # 替換"&"符號爲空格
                conditiion = conditiion.replace('&', " ")
                # 替換"="符號爲”:“
                conditiion = conditiion.replace('=', ":")

            # Fofa只需替換"&"符號爲"&&"符號
            if sz == 'fofa':
                conditiion = conditiion.replace('&', "&&")
            return conditiion
        except Exception as e:
            raise e
            sys.exit()

class Shodan:
    '''搜索引擎 - Shodan'''
    def __init__(self):
        '''
        初始化
        '''
        # 指定API_KEY
        self.SHODAN_API_KEY = "your_key"
        self.api = shodan.Shodan(self.SHODAN_API_KEY)

    def condition_search(self, condition, limit=100):
        '''Shodan篩選參數及其內容(product:"mysql" +country:"cn" -city:"beijing"):
        {"ip":"ip", "port":"端口", "hostname":"域名", product":"使用的軟件或產品", "version":"版本",
        "vuln":"CVE漏洞編號", "http.title":"網頁標題", "http.html":"網頁內容", "http.status":"響應碼狀態",
        "service":"返回包中的服務", "os":"操作系統", "country":"國家", "city":"城市", "org":"組織機構"}
        '''
        try:
            Shodan.credit(self)
            # 調用接口
            results = self.api.search(condition, limit=limit)
            # 查詢內容總量
            print('\n[ Data for Shodan ]')
            print('[ Results found ]', results['total'])
            # 輸出ip、端口、域名、系統、國家、詳情數據
            print('ip_str\tport\tdomains\tsystem\tcountry_name\tdata')
            for result in results['matches']:
                print('%s\t%s\t%s\t%s\t%s\t%s' % (
                    result['ip_str'], result['port'], result['domains'], result['os'],
                    result['location']['country_name'], result['data']))
        except Exception as e:
            raise e
            sys.exit()

    def ip_search(self, ip):
        '''
        反查ip信息
        '''
        try:
            hosts = self.api.host(ip)
            # 輸出ip、廠家、系統
            print("IP: %s\r\nOrganization: %s\r\nOperating System: %s" % (
            hosts['ip_str'], hosts.get('org', 'n/a'), hosts.get('os', 'n/a')))
            # 遍歷輸出端口信息
            for host in hosts['data']:
                print("Port: %s\r\nBanner: %s" % (host['port'], host['data']))
        except shodan.exception.APIError as e:
            print('[ Info ]', e)
        except Exception as e:
            raise e
            sys.exit()

    def credit(self):
        '''
        查詢信用,不足則警示
        '''
        try:
            results = self.api.info()
            # 查詢信用不足5則發出警告
            if results['query_credits'] < 6: print('[ Info ] 查詢信用剩餘:%s' % str(results['query_credits']))
        except Exception as e:
            raise e
            sys.exit()

class Fofa:
    '''搜索引擎 - Fofa'''
    def __init__(self):
        '''
        初始化
        '''
        # 指定API_MAIL
        self.FOFA_API_MAIL = 'your_mail'
        # 指定API_KEY
        self.FOFA_API_KEY = 'your_key'
        self.api = fofa.Fofa(self.FOFA_API_MAIL, self.FOFA_API_KEY)

    def condition_search(self, condition, limit=100):
        '''Fofa篩選參數及其內容(app="mysql"&&country="cn"&&city!="beijing"):
        {"ip":"ip", "port":"端口", "domain":"域名", "host":"url", "app":"使用的軟件或產品",
         "title":"網頁標題", "body":"網頁內容", "header":"從http頭中搜索", "server":"返回包中的服務",
         "os":"操作系統", "country":"國家", "city":"城市", "org":"組織機構"}
         '''
        try:
            # 調用接口
            results = self.api
            # 輸出ip、端口、域名、國家、詳情數據
            print('\n[ Data for Fofa ]')
            print('ip\tport\tdomain\tcountry\tdata')
            # 輸出ip、端口、域名、國家
            for page in range(1, SFZ.count_pages('fofa', limit) + 2):
                for ip, port, domain, country, banner in results.get_data(condition, page, fields="ip, port, domain, country, banner")['results']:
                    print('[%s]\t[%s]\t[%s]\t[%s]\t[%s]' % (ip, port, domain, country, banner.replace('\n', '').replace('\r', '')))
        except Exception as e:
            raise e
            sys.exit()

class Zoomeye:
    '''搜索引擎 - Zoomeye'''
    def __init__(self):
        '''
        初始化
        '''
        # 指定用戶名、密碼
        self.USERNAME = 'your_mail'
        self.PASSWORD = 'your_password'
        self.api = zoomeye.Zoomeye()
        # 登陸獲得token
        self.token = self.api.logIn(self.USERNAME, self.PASSWORD)

    def condition_search(self, condition, limit):
        '''Zoomeye篩選參數及其內容(app:"mysql" +country:"cn" -city:"beijing"):
        {"ip":"ip", "port":"端口", "hostname":"域名", "app":"使用的軟件或產品",
         "device":"設備類型", "ver":"版本", "title":"網頁標題", "desc":"網頁內容",
         "header":"從http頭中搜索", "keywords":"meta屬性關鍵詞", "os":"操作系統",
         "country":"國家", "city":"城市"}
         '''
        try:
            for page in range(1, SFZ.count_pages('zoomeye', limit) + 2):
                # 調用接口進行查詢
                results = self.api.search(query=condition, page=page)[0]
                if page == 1:
                    # 查詢內容總量
                    print('\n[ Data for Zoomeye ]')
                    print('[ Results found ]', results['total'])
                    # 輸出ip、域名、應用、系統、國家、返回包頭
                    print('ip\tdomains\twebapp\tsystem\tcountry\theader')
                for result in results['matches']:
                    # print(result)
                    # break
                    print('%s\t%s\t%s\t%s\t%s\t%s' % (
                        result['ip'], result['domains'], result['webapp'], result['system'],
                        result['geoinfo']['country']['names']['en'], result['headers'].replace('\n', ' ').replace('\r', ' ')))
        except Exception as e:
            raise e
            sys.exit()

if __name__ == '__main__':
    if len(sys.argv) == 3:
        # 所有搜索引擎同時調用
        SFZ.condition_search(sys.argv[1], sys.argv[2])
    elif len(sys.argv) == 4 and sys.argv[1] in ['shodan', 'fofa', 'zoomeye']:
        # 調用其中一個搜索引擎
        sz = locals()[sys.argv[1].capitalize()]()
        sz.condition_search(sys.argv[2], sys.argv[3])
    else:
        print('[ Usage ] python3 SearchEngine.py [空|shodan|fofa|zoomeye] str limit')
        list(map(print, [SFZ.condition_search.__doc__, Shodan.condition_search.__doc__, Fofa.condition_search.__doc__, Zoomeye.condition_search.__doc__]))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章