LOADING

加载过慢请开启缓存 浏览器默认开启

Z0SCAN在线使用手册

2025/6/13 z0scan book

Life is short. You need z0scan.

介绍#

z0scan(简称z0)是一款基于Python开发的Web漏洞扫描工具,兼具主动和被动扫描能力。
该项目摒弃了冗杂的组件及框架POC,专注于Web应用和常见服务漏洞检测,采用模块化设计,注重实际扫描效率与边缘类冷门漏洞的发现。

GitHub Repo stars

z0 的主要特点:

  1. 智能化的扫描策略

    • 集成WAF识别和指纹检测功能,可根据目标特征动态调整扫描策略
    • 通过优化请求频率和检测逻辑,尽可能减少对目标系统业务的干扰
  2. 全面的参数解析能力

    • 支持解析JSON、XML等结构化数据中的参数
    • 具备伪静态URL参数识别功能,扩展了漏洞检测范围
  3. 便捷的数据管理

    • 采用SQLite3数据库存储扫描记录,便于结果管理和去重处理
    • 数据默认存储在data/z0scan.db文件中
  4. 良好的跨平台支持

    • 基于Python3开发,支持主流操作系统
    • 可在Termux等移动端环境中运行

安装#

通用安装#

通过Pypi安装

pip install z0scan
z0 version

通过GitHub克隆安装

git clone https://github.com/JiuZero/z0scan
cd z0scan
pip install -r requirements.txt
python3 z0.py version

部分特殊环境#

Termux

apt install python-cryptography python-lxml
pip install z0scan
z0 version

iSH(未调试)

建议在M系芯片环境中运行

apk add py3-cryptography py3-lxml py3-certifi py3-six py3-idna py3-certifi py3-cffi
pip install z0scan
z0 version

使用#

参数#

version#

参数输入值示例说明
//显示版本信息

scan#

参数输入值示例说明
-h, –help-显示帮助信息
-s, –server-addr127.0.0.1:5920服务器地址格式(ip:port)
-u, –urlhttp://example.com目标URL
-f, –fileurls.txt批量扫描URL文件
-p, –proxyhttp@127.0.0.1:8080代理设置(支持http/https/socks5/socks4)
–timeout10连接超时时间(默认6秒)
–retry3超时重试次数(默认2次)
–random-agent-使用随机User-Agent
–html-输出HTML报告到默认目录
–jsonreport.json指定JSON报告输出路径
-t, –threads50最大并发请求数(默认31)
-l, –level2检测级别(0-3,默认2)
-r, –risk“0,1,2”风险等级检测范围(默认[0,1,2])
-c, –concise-简洁输出模式
-iw, –ignore-waf-忽略WAF检测
-sf, –skip-fingerprint-跳过指纹识别
-sc, –scan-cookie-启用Cookie扫描
–disable“xss,sqli”禁用指定扫描器
–able“rce,ssrf”仅启用指定扫描器
–debug2异常显示级别(1-3)

被动扫描#

默认配置下被动扫描(浏览器转发流量到5920端口)

z0 scan -s 127.0.0.1:5920

常用推荐

z0 scan -s 127.0.0.1:5920 --risk 0,1,2,3 --level 2 --disable cmdi,unauth

主动扫描#

默认配置下主动扫描

# 由Burp/Yakit发起请求流量的主动检测(推荐)
z0 scan -s 127.0.0.1:5920
# 直接检测
z0 scan -u https://example.com/?id=1
# 遍历URL列表检测
z0 scan -f urls.txt

报告#

报告展示#

插件#

插件列表#

  • PerFile
插件名称插件简述Risk
sqli-boolSQL布尔盲注检测2
sqli-timeSQL时间盲注检测2
sqli-errorSQL报错注入检测2
codei-aspAsp代码执行3
codei-phpPhp代码执行3
cmdi命令执行3
other-objectdese反序列参数分析3
sensi-jsJs敏感信息泄露0
sensi-jsonpJsonp敏感信息泄露1
sensi-php-realpathPhp真实目录发现0
redirect重定向1
sensi-webpackwebpack源码泄露1
other-webdav-passivewebdav服务被动发现1
xpathi-error基于报错的XPATH注入检测2
trave-path路径穿越2
sensi-backup_1基于文件的备份文件检测1
sensi-viewstate未加密的VIEWSTATE发现0
xss基于JS语义的XSS扫描1
crlf_1CRLF漏洞检测2
cors-passiveCORS漏洞检测(被动分析)2
unauth未授权访问漏洞2
leakpwd-page-passive后台登陆页弱口令2
sensi-editfile编辑器备份文件泄露1
sensi-sourcecode源码泄露1
captcha-bypass验证码绕过0
  • PerFolder
插件名称插件简述Risk
sensi-backup_2基于各级目录的备份文件扫描1
trave-list目录浏览2
sensi-repository仓库源码泄漏1
sensi-php-phpinfoPhpinfo文件发现0
upload-ossOSS储存桶任意文件上传2
  • PerServer
插件名称插件简述Risk
sensi-errorpage错误页敏感信息泄露0
xss-net.NET通杀XSS1
other-dns-zonetransferDNS域传送漏洞1
xss-flashFlash通杀XSS1
other-idea-parseIdea目录解析1
other-xstXST漏洞检测-1
other-webdav-activewebdav服务发现1
upload-put基于PUT请求的任意文件上传3
sensi-backup_3基于域名的备份文件检测1
cors-activeCORS漏洞检测(主动发现)2
crlf_3CRLF换行注入漏洞2
other-hostiHost头注入攻击检测1
other-oss-takeoverOSS储存桶接管漏洞3
sensi-iis-shortnameIIS短文件名漏洞0

插件示例&计划(部分)#

示例目标均为内网/外网公开靶场。

sqli-bool#

sqli-error#

sqli-time#

  • SQLite的延时检测支持

xss#

  • 部分情况下提供的Payload不可用
  • 部分情况下无法检测出漏洞

leakpwd-page-passive#

sensi-viewstate#

trave-path#

sensi-repository#

sensi-js#

captcha-bypass#

codei-php#

redirect#

trave-list#

crlf_1#

扫描插件编写#

命名#

插件名称按照以下命名规则命名:

漏洞类型(简写) + 指纹 + 简述

如PerFile/PerFolder/PerServer下存在同名插件,在单个插件名后按照PerFile:1,PerFolder:2,PerServer:3在名末下划线连接,如:

PerFile中的sensi-backup:sensi-backup_1

漏洞类型分类与简写形式见api/VulType部分

内置模块#

api#

名称描述使用必选/可选
generateResponse用于报告中响应体的生成generateResponse(r) # r=request.get(…)必选
random_num生成随机数/可选
random_str生成随机字符串/可选
VulType对漏洞类型的选定见api/VulType必选
Type对扫描类型的选定见api/Type必选
PluginBase被继承以获取关键数据class Z0SCAN(PluginBase)必选
conf储存一些命令行参数值主要使用level,见api/conf.level必选
PLACE对漏洞注入点(请求中的可控点)的选定见api/PLACE必选
Threads插件内置线程(针对参数的多线程)见api/Threads可选
  • api/VulType:
名称描述(插件命名)简写
CMD_INNJECTION命令注入漏洞cmdi
CODE_INJECTION代码注入漏洞codei
XSSXSS跨站脚本攻击xss
SQLISQL注入漏洞sqli
TRAVERSAL遍历漏洞trave
XXEXML外部实体注入xxe
SSRF服务器端请求伪造ssrf
CSRFCSRFcsrf
REDIRECT重定向漏洞redirect
WEAK_PASSWORD弱口令weakpwd
CRLF换行注入crlf
SENSITIVE敏感信息泄露漏洞sensi
SSTI服务器端模板注入ssti
UNAUTH未授权访问unauth
FILEUPLOAD文件上传upload
CORSCORS漏洞cors
OTHER其它漏洞other
  • api/Type :
名称描述
ANALYZE被动分析发现
REQUEST主动请求发现
  • api/conf.level :

扫描深度(反映请求量)

描述
0纯被动分析模式,不做任何请求
1最低请求量的扫描,最低的业务影响
2中等请求量的扫描,Payload多为通用Top5
3大量请求扫描,Payload覆盖面更广
  • api/conf.risk :

需要扫描的漏洞危害程度

描述
-1难以利用的极低危常见漏洞
0可能产生1~3级危害的辅助性信息
1低危漏洞
2中危漏洞
3高危漏洞
  • api/PLACE :
名称描述
PARAMURL后参数部分
DATA在BODY中传递的参数
COOKIECOOKIE中传递的参数
URL伪静态参数
NORMAL_DATA常规POST传参格式中的参数
JSON_DATAJSON格式中的参数
XML_DATAXML格式中的参数
MULTIPART_DATAMULTIPART格式中的参数
ARRAY_LIKE_DATAARRAY_LIKE格式中的参数
SOAP_DATASOAP_DATA格式中的参数
  • api/Threads :
z0thread = Threads(name="sqli-error") # name : 插件名
z0thread.submit(task_func, task_data, args, kwargs)
# task_func: 要执行的任务函数
# task_data: 任务数据迭代器,每个元素会作为task_func的第一个参数
# args: 传递给task_func的额外位置参数
# kwargs: 传递给task_func的额外关键字参数 (可选)

解析数据#

注:继承PluginBase后读取

  • self.fingerprints :
名称类型描述
osdictOS系统指纹
programingdict项目类型
wafstrWAF名称(未检测到WAF时为None)

对于os、webserver、programing

>> print(self.fingerprints.programing)
{"PHP" : "1.9.6"} # 名称 : 版本信息
  • self.requests :
名称类型描述示例
urlstr完整的URL(包含GET参数)https://www.example.com:443/a/file.php?id=1
suffixstr文件后缀.php
schemestr请求协议http
portint服务端口8888
hoststr域名(不包括端口)www.myscantest.com
netlocstr包含协议与端口信息的域名https://www.example.com:443
rawstr原始的请求包/
methodstr请求方法GET
headersdict请求头字典/
cookiesdictCOOKIE/
paramsdict在URL中包含的参数{‘id’: ‘1’}
post_hintstrPOST请求包类型/
datasdictPOST数据/
datastr原始请求头/

注: 仅常规解析,datas、params均允许直接requests构造请求

如需解析伪静态及xml等格式请求中的参数,见self.generateItemdatas()

  • self.response :
名称类型描述示例
status_codeint返回状态码200
contentbyte返回字节类型/
headersdict请求头/
rawstr原始的返回包/
textstr返回的文本/
  • self.generateItemdatas() :

generateItemdatas()会将参数名、参数值及其所处的可控点整理后返回

注意: 它会额外地解析出伪静态及xml等包含的参数,
并按照用户要求不将cookie参数作为解析对象,
以达到不对cookie扫描检测的目的。

>> iterdatas = self.generateItemdatas()
>> print(iterdatas)
[
    ["id", "1", "URL"],
    ["username", "admin", "DATA"],
]
  • self.insertPayload({“key”: k, “value”: v, “position”: position, “payload”: _payload}) :

令参数名为key,参数值为value(value为可选值),并向参数值后添加payload

最终返回其对应可控点修改后的数据

建议配合self.generateItemdatas()使用

>> datas = self.insertPayload({"key": "username", "value": "admin", "position": "DATA", "payload": "'--+"})
>> print(datas)
{"username": "admin'--+", "passwd": "admin"}
>> r = request.get(url, data=datas)
  • self.req(position, payload) :

payload为对应可控点修改后的整体数据,可以为self.insertPayload的返回

需配合self.generateItemdatas()使用

>> datas = {"username": "admin'--+", "passwd": "admin"}
>> r = self.req("DATA", datas)
>> print(r)
… # r为request的返回

示范#

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# w8ay 2020/5/10
# JiuZero 2025/3/13

from data.rule.sqli_error import rules
from api import generateResponse, random_num, random_str, VulType, Type, PluginBase, conf, logger, Thread
from lib.helper.helper_sensitive import sensitive_page_error_message_check
import re

class Z0SCAN(PluginBase):
    name = "sqli-error" # 插件名
    desc = 'SQL Error Finder' # 描述
    version = "2025.3.13" # 版本(最后更新日期)
    risk = 2 # 危害等级
        
    def audit(self):
        if not self.fingerprints.waf and 2 in conf.risk and conf.level != 0:
            _payloads = [
                ## 宽字节
                r'鎈\'"\(',
                ## 通用报错
                r';)\\\'\\"',
                r'\' oRdeR bY 500 ',
                r';`)',
                r'\\', 
                r"%%2727", 
                r"%25%27", 
                r"%60", 
                r"%5C",
            ]
            if conf.level == 3: 
                _payloads += [
                ## 强制报错
                # MySQL
                r'\' AND 0xG1#',
                # PostgreSQL  
                r"' AND 'a' ~ 'b\[' -- ",
                # MSSQL
                r"; RAISERROR('Error generated', 16, 1) -- ", 
                # Oracle
                r"' UNION SELECT XMLType('<invalid><xml>') FROM dual -- ",  
                # SQLite
                r"' UNION SELECT SUBSTR('o', -1, 1) -- ",
                ]
    
            iterdatas = self.generateItemdatas()
            # 内置的线程并发
            z0thread = Threads(name="sqli-error")
            z0thread.submit(self.process, iterdatas, _payloads)
    
    def Get_sql_errors(self):
        sql_errors = []
        for database, re_strings in rules.items():
            for re_string in re_strings:
                sql_errors.append((re.compile(re_string, re.IGNORECASE), database))
        return sql_errors
    
    def process(self, _, _payloads):
        k, v, position = _
        for _payload in _payloads:
            payload = self.insertPayload({
                "key": k, 
                "value": v, 
                "position": position, 
                "payload": _payload
                })
            r = self.req(position, payload)
            if not r:
                continue
            html = r.text
            for sql_regex, dbms_type in self.Get_sql_errors():
                match = sql_regex.search(html)
                if match:
                    # 生成报告
                    result = self.generate_result()
                    result.main({
                        "type": Type.REQUEST, # 扫描类型
                        "url": self.requests.url, # 漏洞URL
                        "vultype": VulType.SQLI, # 漏洞类型
                        "show": { # 你希望向命令行展示的信息
                            "Position": f"{position} > {k}", # 建议键名首字母大写
                            "Payload": payload, 
                            "Msg": "DBMS_TYPE Maybe {}; Match {}".format(dbms_type, match.group())
                            }
                        })
                    # 验证步骤(可以添加多个过程,如二次验证)
                    result.step("Request1", {
                        "request": r.reqinfo, 
                        "response": generateResponse(r), 
                        "desc": "Dbms Maybe {}; Match {}".format(dbms_type, match.group())
                        })
                    self.success(result)
                    return True
            message_lists = sensitive_page_error_message_check(html)
            if message_lists:
                result = self.generate_result()
                result.main({
                    "type": Type.REQUEST, 
                    "url": self.requests.url, 
                    "vultype": VulType.SQLI, 
                    "show": {
                        "Position": f"{position} > {k}", 
                        "Payload": payload, 
                        "Msg": "Receive Error Msg {}".format(repr(message_lists))
                        }
                    })
                result.step("Request1", { # 步骤标题
                    "request": r.reqinfo, # 请求体
                    "response": generateResponse(r), # 响应体(由generateResponse生成)
                    "desc": "Receive Error Msg {}".format(repr(message_lists)) # 步骤的关键信息
                    })
                self.success(result)
                break