環境說明
操作系統: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__]))