Skip to content

Commit

Permalink
Merge branch 'wikiZ-main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
0x7Fancy committed Oct 20, 2021
2 parents 828bacd + 3730a0b commit 6af982f
Show file tree
Hide file tree
Showing 14 changed files with 102 additions and 22 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v1.5] - 2021-10-20
### Added
- Optimized the HOSTS collision function
- Add HOSTS collision cross collision function
- Increase the output file path customization function

## [v1.4] - 2021-9-4
### Added
- Modify the POC storage directory of the pocsuite module
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ kunyu init --apikey <your zoomeye key> --seebug <your seebug key>
```
![](./images/setinfo.png)

You need to log in with ZoomEye credentials before using this tool for information collection.

Visit address: https://www.zoomeye.org/

The output file path can be customized by the following command

```
kunyu init --output C:\Users\风起\kunyu\output
```

![](./images/setoutput.png)

# 0x03 Tool instructions

## Detailed command
Expand Down Expand Up @@ -178,7 +190,9 @@ In versions after v1.3.1, you can use kunyu to link the console mode of pocsuite

Through the HOSTS collision, the hidden assets in the intranet can be effectively collided, and the intranet service can be accessed according to the ServerName domain name and IP configured in the middleware httpf.conf. This can be achieved by setting the local hosts file later, because the local hosts file takes precedence. The level is higher than DNS server resolution. Support reverse check through ZoomEye domain name library or read TXT file to get the list of domain names.

![](./images/searchcrash.png)
HOSTS cross collision

![](./images/searchcrashs.png)

**Data result**

Expand Down
8 changes: 7 additions & 1 deletion doc/README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ kunyu init --apikey <your zoomeye key> --seebug <your seebug key>
```
![](../images/setinfo.png)

需要通过ZoomEye登录凭证,才使用该工具进行信息收集。

访问地址:https://www.zoomeye.org/

# 0x03 工具使用

## 命令详解
Expand Down Expand Up @@ -177,7 +181,9 @@ ZoomEye:

通过HOSTS碰撞,可以有效的碰撞出内网中隐藏的资产,根据中间件httpf.conf中配置的ServerName域名和IP捆绑即可访问内网服务,后续通过设置本地hosts文件实现,因为本地hosts文件优先级高于DNS服务器解析。支持通过ZoomEye域名库反查或者读取TXT文件获取域名列表。

![](../images/searchcrash.png)
HOSTS交叉碰撞

![](../images/searchcrashs.png)

**数据结果**

Expand Down
Binary file modified images/infos.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed images/searchcrash.png
Binary file not shown.
Binary file added images/searchcrashs.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/setinfo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/setoutput.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions kunyu/config/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
__python_version__ = sys.version.split()[0]
__platform__ = platform.platform()
__url__ = "https://github.com/knownsec/Kunyu"
__version__ = '1.4.0'
__version__ = '1.5.0'
__author__ = '风起'
__Team__ = 'KnownSec 404 Team'
__author_email__ = 'onlyzaliks@gmail.com'
Expand Down Expand Up @@ -64,7 +64,8 @@
Usage:
kunyu -h
kunyu init -h
kunyu init --apikey "01234567-acbd-00000-1111-22222222222"
kunyu init --output /root/kunyu/output
kunyu init --apikey "xxxxx911-6D2A-12345-4e23-64exxxxx6fb"
kunyu init --username "404@knownsec.com" --password "P@ssword"
kunyu init --seebug "012345200157abcdef981bcc89a1452c34d62b8c"
kunyu init --apikey "01234567-acbd-0000" --seebug "a73503200157"(推荐)
Expand Down
15 changes: 15 additions & 0 deletions kunyu/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import configparser

from kunyu.utils import *
from kunyu.config import setting
from kunyu.config.__version__ import usage, init, __title__, __help__

parser = argparse.ArgumentParser(prog=__title__)
Expand All @@ -39,6 +40,7 @@
parser_init_console.add_argument("--username", help='ZoomEye Username')
parser_init_console.add_argument("--password", help='ZoomEye Password')
parser_init_console.add_argument("--seebug", help='ZoomEye Password')
parser_init_console.add_argument("--output", help='Set Output File Path')

args = parser.parse_args()

Expand All @@ -52,6 +54,10 @@
conf.read(__path)

def initial_config():
"""
Determine whether the parameters in the configuration file exist
If it does not exist, create a parameter and set the initial value to None
"""
if not conf.has_section("zoomeye") and not conf.has_section("login"):
conf.add_section('zoomeye')
conf.set("zoomeye", "apikey", "None")
Expand All @@ -62,6 +68,11 @@ def initial_config():
conf.add_section('seebug')
conf.set("seebug", "apikey", "None")

# The path of the output file
if not conf.has_section("path"):
conf.add_section("path")
conf.set("path", "output", setting.OUTPUT_PATH)

def _get_login():
param = '{{"username": "{}", "password": "{}"}}'.format(args.username, args.password)
resp = requests.post(
Expand Down Expand Up @@ -89,6 +100,10 @@ def _get_login():
if args.seebug:
conf.set("seebug", "apikey", args.seebug)

# set output file path
if args.output:
conf.set("path", "output", args.output)

except requests.HTTPError as err:
print("\033[31;1m{}\033[0m".format(err))
print(__help__.format(datil=init))
Expand Down
57 changes: 45 additions & 12 deletions kunyu/core/crash.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import re
import json
import random
import sys

import grequests
import requests
Expand All @@ -17,8 +18,8 @@
from rich.console import Console
from kunyu.utils.log import logger
from kunyu.utils.convert import convert
from kunyu.lib.batchfile import get_domain_file
from kunyu.config.setting import UA, DOMAIN_SEARCH_API, DOMAIN_CHECK_REGEX
from kunyu.lib.batchfile import get_domain_file, get_file
from kunyu.config.setting import UA, DOMAIN_SEARCH_API, DOMAIN_CHECK_REGEX, IP_ADDRESS_REGEX

console = Console(color_system="auto", record=True)
ZOOMEYE_KEY = conf.get("zoomeye", "apikey")
Expand All @@ -31,6 +32,7 @@ def __init__(self):
"User-Agent": random.choice(UA)
}
self.params = {"type": 1}
self.IP_STATUS = 0
self.__get_login()

# Check whether the HTTP request returns an error
Expand Down Expand Up @@ -67,7 +69,6 @@ def request_get():
# Dynamically calculate the number of pages that need to be queried to obtain all result sets
count = int(result["total"] / 30)
page = count if (result["total"] % 30) == 0 else count + 1
console.log("Host Header Scan Domain Total: ", result["total"], style="green")
# Get ZoomEye Domain result
for i in range(page):
self.params["page"] = str(i + 1)
Expand All @@ -76,6 +77,9 @@ def request_get():
data = convert(result["list"][num])
domain_list.append(data.name)

# Remove duplicate domain names
domain_list = list(set(domain_list))
console.log("Host Header Scan Domain Total: ", len(domain_list), style="green")
return domain_list

except requests.HTTPError as err:
Expand All @@ -90,6 +94,15 @@ def __is_valid_domain(self, search):
pattern = re.compile(DOMAIN_CHECK_REGEX)
return True if pattern.match(search) else False

def __is_valid_ip(self, ip):
"""
Return whether or not given value is a valid ip address.
If the value is valid ip address this function returns ``True``, otherwise False
:param ip: ip string to validate
"""
pattern = re.compile(IP_ADDRESS_REGEX)
return True if pattern.match(ip) else False

def _get_file(self, search):
"""
Get the array of domain names required for HOST collision
Expand All @@ -108,23 +121,43 @@ def _get_file(self, search):
console.log("Host Header Scan Domain Total: ", len(domain_list), style="green")
return domain_list

def _get_ip_file(self, ip):
"""
Get the array of ip address required for HOST collision
If it is IP, directly HOST collision
Otherwise, read the file to obtain the IP address
:param ip: Enter the main ip address or ip address file path
"""
ip_list = []
if self.__is_valid_ip(ip):
ip_list.append(ip)
else:
for ip_address in get_file(ip):
ip_list.append(ip_address)
return ip_list

def host_scan(self, search, ip):
"""
Obtain hidden assets through HOST collision
:param search: Ways to obtain domain names
:param ip: IP address to be collided
"""
resp = []
crash_list = []
protocol = ['http://{}/', 'https://{}/']
self.params["q"] = search
domain_list = self._get_file(search)
url = "http://{}/".format(ip)
resp = []
for domain in domain_list:
headers = {'Host': domain.strip(),
'User-Agent': random.choice(UA)
}
# Concurrent requests through the encapsulated coroutine module
resp.append(grequests.get(url, headers=headers, timeout=1))
url = self._get_ip_file(ip)
for server in protocol:
for ip_address in url:
urls = server.format(ip_address)
for domain in domain_list:
headers = {'Host': domain.strip(),
'User-Agent': random.choice(UA),
'ip': urls
}
# Concurrent requests through the encapsulated coroutine module
resp.append(grequests.get(urls, headers=headers, timeout=2))
res_list = grequests.map(resp)
for res in res_list:
try:
Expand All @@ -133,7 +166,7 @@ def host_scan(self, search, ip):
res.encoding = 'gbk2312'
# Get the title of the returned result
title = re.findall('<title>(.+)</title>', res.text)
crash_list.append([ip, res.request.headers['Host'], title[0]])
crash_list.append([res.request.headers['ip'], res.request.headers['Host'], title[0]])

except Exception:
continue
Expand Down
8 changes: 5 additions & 3 deletions kunyu/core/zoomeye.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def __init__(self, method):
self.page = 1
self.method = method
self.headers = {
"User-Agent": random.choice(UA)
"User-Agent": random.choice(UA),
"author": "ZoomEye Kunyu"
}

def __call__(self, func):
Expand Down Expand Up @@ -82,14 +83,14 @@ def __request(self, login_url, data=None, headers=None):
login_url,
data=data,
headers=headers,
timeout=20
timeout=30
)
else:
resp = requests.post(
login_url,
data=data,
headers=headers,
timeout=20
timeout=30
)
self.check_status(resp)
self.check_error(resp.json())
Expand Down Expand Up @@ -326,6 +327,7 @@ def command_searchbatch(cls, filename):
# Use ZooEye batch query mode,Search: "ip:1.1.1.1 ip:2.2.2.2 ip:3.3.3.3"
for ip in get_file(filename):
search += "ip:{} ".format(ip)
# Determine the type of interface used
if cls.btype == "host":
return cls.command_searchhost(search)

Expand Down
5 changes: 3 additions & 2 deletions kunyu/lib/batchfile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# encoding: utf-8
'''
@author: 风起
Expand All @@ -17,7 +18,7 @@


def getresult(func):
__ip_list = []


@wraps(func)
def getfile(file):
Expand All @@ -26,6 +27,7 @@ def getfile(file):
Determine whether it is a txt file
:param file: The path of the file to be read
"""
__ip_list = []
try:
# Check File Type
if file.endswith(".txt"):
Expand All @@ -34,7 +36,6 @@ def getfile(file):
logger.warning("Only input TXT type files are allowed")
raise Exception

nonlocal __ip_list
with open(file, "r", encoding='utf-8') as ip_text:
for line in ip_text:
__ip_list.append(line.strip())
Expand Down
4 changes: 3 additions & 1 deletion kunyu/lib/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import csv
import xlwt

from kunyu.core import conf
from kunyu.utils.log import logger
from kunyu.config import setting

Expand All @@ -24,11 +25,12 @@
Security researchers can also modify code files as needed.
"""

OUTPUT_PATH = conf.get("path", "output")

def createdir():
# Create the results output directory.
__dirnamae = datetime.datetime.now().strftime("%Y%m%d%H%M")
path = os.path.expanduser(setting.OUTPUT_PATH)
path = os.path.expanduser(OUTPUT_PATH)
__path = os.path.join(path, __dirnamae)
setting.OUTPUT_PATH = __path
if os.path.exists(__path):
Expand Down

0 comments on commit 6af982f

Please sign in to comment.