【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__]))

你可能感兴趣的:(python编程)