Skip to content

Commit

Permalink
version 1.7 enable Zoomeye API
Browse files Browse the repository at this point in the history
  • Loading branch information
Xyntax committed Jul 9, 2016
1 parent 7242dd8 commit 3ae1f82
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 14 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* 极简式模块编写
* 无交互运行,方便部署自动化
* 实用工具/PoC更新中
* 支持`Zoomeye api`

*(欢迎提交代码和改进建议)*

Expand Down Expand Up @@ -41,7 +42,7 @@
|模块|说明|
|:---|:---|
|`vote-example.py` |给基友开发的刷票脚本|
|`bingc.py`|基于Bing搜索引擎的C段/旁站扫描(支持API)|
|`bingc.py`|基于Bing搜索引擎的C段/旁站扫描(支持Bing-api)|
|`others`|等你开脑洞|


Expand All @@ -66,6 +67,7 @@
| lib | 项目代码 |
| docs | 文档及版权声明 |
| thirdparty | 第三方库 |
| api | 搜索引擎接口 |


## 模块编写
Expand Down Expand Up @@ -105,7 +107,7 @@ def poc(input_str):

#### 查看及使用模块
* `python POC-T.py --show` 查看全部模块名称
* 在命令行中使用 `-m poctest` 参数即可完成`poctest`模块的加载 (注意名称末尾不需要写`.py`)
* 在命令行中使用 `-m poctest` 参数即可完成`poctest`插件的加载 (注意名称末尾不需要写`.py`)

工具目录
----
Expand All @@ -118,6 +120,21 @@ def poc(input_str):
|useragent.py | User-Agent处理工具, 支持随机化UA以绕过防御规则|
|extracts.py | 正则提取工具,从采集到的杂乱文本中筛选IP地址|

第三方搜索引擎接口
---------
本工具拟支持主流空间搜索引擎的API,如Zoomeye/Shodan/Censys等(目前已支持Zoomeye).
从搜索引擎中直接获取目标,并结合本地插件进行扫描.

以下命令表示使用Zoomeye接口,搜索全网中开启8080号端口的服务,并使用`test.py`插件进行验证.
默认爬10页搜索结果,搜索结果将存入本地`/data/zoomeye`文件夹下.

`python POC-T.py -T -m test --api --dork "port:8080" --max-page 10`

如第一次使用接口,需按提示输入Zoomeye的帐号和密码.
Zoomeye现已开放注册,普通用户每月可以通过api下载5000页的搜索结果.

[Zoomeye-api官方文档](https://www.zoomeye.org/api/doc)

相关链接
----
* [感谢列表](./docs/THANKS.md)
Expand Down
6 changes: 5 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@

1.6
---
* 2016.06.14 - 重构controller,对PoC结果提供重新扫描功能,支持自定义结果输出
* 2016.06.14 - 重构controller,对PoC结果提供重新扫描功能,支持自定义结果输出

1.7
---
* 2016.07.08 - 添加`--api`,支持Zoomeye api
Binary file modified docs/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/usage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions lib/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# !/usr/bin/env python
# -*- coding: utf-8 -*-
3 changes: 3 additions & 0 deletions lib/api/zoomeye/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# !/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'xy'
20 changes: 20 additions & 0 deletions lib/api/zoomeye/rcGen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Copyright (c) 2014-2016 pocsuite developers (https://seebug.org)
See the file 'docs/COPYING' for copying permission
"""
import os


# TODO 这里不改成统一的path调用?
def initial():
currentUserHomePath = os.path.expanduser('~')
_ = """[zoomeye]\nusername = Your ZoomEye Username\npassword = Your ZoomEye Password\n\n[token]\nseebug = Your Seebug Token"""
if not os.path.isfile(currentUserHomePath + '/.rc'):
with open(os.path.join(currentUserHomePath, '.rc'), 'w') as fp:
fp.write(_)


initial()
109 changes: 109 additions & 0 deletions lib/api/zoomeye/x.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Copyright (c) 2014-2016 pocsuite developers (https://seebug.org)
See the file 'docs/COPYING' for copying permission
"""
import ast
import json
import urllib
import requests
import ConfigParser
from .rcGen import initial


class ZoomEye():
def __init__(self, confPath=None):
self.plan = self.token = None
self.headers = self.username = self.password = None
self.resources = {}

if confPath:
self.confPath = confPath
self.parser = ConfigParser.ConfigParser()
self.parser.read(self.confPath)

self.username = self.parser.get('zoomeye', 'Username')
self.password = self.parser.get('zoomeye', 'Password')

def newToken(self):
data = '{{"username": "{}", "password": "{}"}}'.format(self.username, self.password)
req = requests.post('https://api.zoomeye.org/user/login', data=data, )
content = json.loads(req.content)
if req.status_code != 401 and "access_token" in content:
self.token = content['access_token']
self.headers = {'Authorization': 'JWT %s' % self.token}
return True
return False

def resourceInfo(self):
req = requests.get('https://api.zoomeye.org/resources-info', headers=self.headers, )
content = json.loads(req.content)
if 'plan' in content:
self.plan = content['plan']
self.resources['web-search'] = content['resources']['web-search']
self.resources['host-search'] = content['resources']['host-search']
return True
return False

def search(self, dork, page=1, resource='web'):
req = requests.get(
'https://api.zoomeye.org/{}/search?query="{}"&page={}&facet=app,os'.format(resource, urllib.quote(dork), page + 1),
headers=self.headers
)
content = json.loads(req.content)
if 'matches' in content:
return [match['ip'] for match in content['matches']]
else:
return []

def write_conf(self):
if not self.parser.has_section("zoomeye"):
self.parser.add_section("zoomeye")

username = raw_input("ZoomEye Email:")
password = raw_input("ZoomEye Password:")
self.parser.set("zoomeye", "Username", username)
self.parser.set("zoomeye", "Password", password)
self.username = username
self.password = password
self.parser.write(open(self.confPath, "w"))


class Seebug():
def __init__(self, confPath=None):
self.token = None

if confPath:
self.confPath = confPath
self.parser = ConfigParser.ConfigParser()
self.parser.read(self.confPath)
self.token = self.parser.get('token', 'seebug')

self.headers = {'Authorization': 'Token %s' % self.token}

def static(self):
req = requests.get('https://www.seebug.org/api/user/poc_list', headers=self.headers, )
self.stats = ast.literal_eval(req.content)
if 'detail' in self.stats:
return False
return 'According to record total %s PoC purchased' % len(self.stats)

def seek(self, keyword):
req = requests.get('https://www.seebug.org/api/user/poc_list?q=%s' % keyword, headers=self.headers, )
self.pocs = ast.literal_eval(req.content)
return '%s purchased poc related to keyword "%s"' % (len(self.pocs), keyword)

def retrieve(self, ID):
req = requests.get('https://www.seebug.org/api/user/poc_detail?id=%s' % ID, headers=self.headers, )
return ast.literal_eval(req.content)

def write_conf(self):
if not self.parser.has_section("token"):
self.parser.add_section("token")

token = raw_input("Seebug Token:")
self.parser.set("token", "seebug", token)
self.token = token
self.parser.write(open(self.confPath, "w"))
89 changes: 89 additions & 0 deletions lib/api/zoomeye/zoomeye.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Copyright (c) 2014-2016 pocsuite developers (https://seebug.org)
See the file 'docs/COPYING' for copying permission
"""
from pocsuite.thirdparty import requests
from pocsuite.lib.core.data import logger
from pocsuite.lib.core.enums import CUSTOM_LOGGING


class Zoomeye(object):

def __init__(self, token, host="api.zoomeye.org"):
self._base_uri = "http://%s" % host
self._headers = {"Authorization": "JWT %s" % token, "Content-Type": "application/json"}

def _response_for(self, path):
uri = "/".join([self._base_uri, path])
response = requests.get(uri, headers=self._headers)
if response.status_code == 200:
body = self._handle_success(response, uri)
return body
else:
self._handle_error(response, uri)

def _handle_success(self, response, uri):
try:
return response.json()
except ValueError as ex:
logger.log(CUSTOM_LOGGING.ERROR, ex)

def _handle_error(self, response, uri):
status = response.status_code

if 400 <= status < 500:
self._handle_4xx_status(response, status, uri)
elif 500 <= status < 600:
self._handle_5xx_status(status, uri)
else:
self._handle_non_200_status(status, uri)

def _handle_non_200_status(self, status, uri):
errMsg = "Received a very surprising HTTP status %i for %s" % (status, uri)
logger.log(CUSTOM_LOGGING.ERROR, errMsg)

def _handle_5xx_status(self, status, uri):
errMsg = "Received a server error %i for %s" % (status, uri)
logger.log(CUSTOM_LOGGING.ERROR, errMsg)

def _handle_4xx_status(self, response, status, uri):
if not response.content:
errMsg = "Received a %i error for %s with no body." % (status, uri)
logger.log(CUSTOM_LOGGING.ERROR, errMsg)
elif response.headers["Content-Type"].find("json") == -1:
errMsg = "Received a %i for %s with the following body: %s" % (status, uri, response.content)
logger.log(CUSTOM_LOGGING.ERROR, errMsg)

try:
body = response.json()
except ValueError:
errMsg = "Received a %i error for %s but it did not include the expected JSON body" % (status, uri)
logger.log(CUSTOM_LOGGING.ERROR, errMsg)
else:
if "error" in body:
self._handle_web_service_error(body.get("error"), status, uri)
else:
errMsg = "Response contains JSON but it does not specify code or error keys"
logger.log(CUSTOM_LOGGING.ERROR, errMsg)

def _handle_web_service_error(self, message, status, uri):
if message == "unauthorized":
errMsg = "AuthenticationError, please check your Zoomeye Token"
logger.log(CUSTOM_LOGGING.ERROR, errMsg)
else:
logger.log(CUSTOM_LOGGING.ERROR, message)

def resource_info(self):
return self._response_for("resources-info")

def search(self, keyword, page=1, searchtype="web"):
path = '%s/search?query="%s"&page=%s&fact=app,os' % (searchtype, keyword, page)
return self._response_for(path)


if __name__ == "__main__":
z = Zoomeye("RSjz3c")
print z.search("port:80")
53 changes: 53 additions & 0 deletions lib/controller/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# !/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
import time
from lib.api.zoomeye.x import ZoomEye
from lib.core.data import conf, paths, logger
from lib.core.log import CUSTOM_LOGGING


def runZoomeyeApi(args):
if args['dork']:
z = ZoomEye(paths.RC_PATH)
if z.newToken():
logger.log(CUSTOM_LOGGING.SUCCESS, 'ZoomEye API authorization success.')
z.resourceInfo()
else:
logger.log(CUSTOM_LOGGING.SUCCESS,
'ZoomEye API authorization failed,Please input ZoomEye Email and Password for use ZoomEye API!')
z.write_conf()
if z.newToken():
logger.log(CUSTOM_LOGGING.SUCCESS, 'ZoomEye API authorization success.')
z.resourceInfo()
else:
sys.exit(logger.log(CUSTOM_LOGGING.ERROR,
'ZoomEye API authorization failed, make sure correct credentials provided in "~/.pocsuiterc".'))

info = z.resources
logger.log(
CUSTOM_LOGGING.SYSINFO,
'Available ZoomEye search, web-search:{}, host-search:{}'.format(info['web-search'], info['host-search'])
)

tmpIpFile = os.path.join(conf.ZOOMEYE_OUTPUT_PATH, '%s_%s.txt' % (
args['dork'].replace(':', '-').replace(' ', '-').strip(), time.strftime('%Y_%m_%d_%H_%M_%S')))
with open(tmpIpFile, 'w') as fp:
search_types = args.get('search_type', 'web')
if 'host' not in search_types and 'web' not in search_types:
search_types = 'web'
for page in range(args.get('max_page', 1)):
for search_type in search_types.split(','):
if search_type in ['web', 'host']:
for ip in z.search(args['dork'], page, search_type):
if type(ip) == list:
fp.write('%s\n' % ip[0])
else:
fp.write('%s\n' % ip)
return tmpIpFile


def setApi():
# TODO 判断使用哪家的api
return runZoomeyeApi(conf)
Loading

0 comments on commit 3ae1f82

Please sign in to comment.