操作系统:windows10
python版本:python3
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)
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地址: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)
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地址: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__]))