From 22c7aa2eed3401ab760dfd20e26142cec8fb5999 Mon Sep 17 00:00:00 2001 From: qgw Date: Fri, 29 Jan 2021 17:03:21 +0800 Subject: [PATCH 01/22] add dockerfile --- docker/Dockerfile | 60 +++++++++++++++++++++++++++++++++++++ docker/docker-compose.yaml | 41 +++++++++++++++++++++++++ docker/docker-entrypoint.sh | 16 ++++++++++ docker/readme.md | 12 ++++++++ docker/requirement.txt | 9 ++++++ docker/xray.sh | 10 +++++++ 6 files changed, 148 insertions(+) create mode 100644 docker/Dockerfile create mode 100644 docker/docker-compose.yaml create mode 100644 docker/docker-entrypoint.sh create mode 100644 docker/readme.md create mode 100644 docker/requirement.txt create mode 100644 docker/xray.sh diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..de80c6f --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,60 @@ +#FROM ubuntu:16.04 + +FROM python:3.7.9-alpine3.12 + +Label author "qboy0000<2006qgw@163.com>" + +#COPY dist/floodlight /root/floodlight + +ENV XARY 1.7.0 +ENV PYTHON3 3.7.9 +ENV CHROMEDRIVER_VERSION 89.0.4389.23 + +# RUN sed -i 's/http:\/\/archive.ubuntu.com/http:\/\/mirrors.huaweicloud.com/g' /etc/apt/sources.list && \ +# sed -i 's/http:\/\/security.ubuntu.com/http:\/\/mirrors.huaweicloud.com/g' /etc/apt/sources.list && \ +# apt-get update && \ + +COPY ./requirement.txt /tmp/ + +RUN set -x && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ + apk update && \ +# GM+8 +# set time zone + apk add --no-cache tzdata && \ + echo "Asia/Shanghai" > /etc/timezone && \ + cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + # dpkg-reconfigure -f noninteractive tzdata && \ + apk add --no-cache musl-dev linux-headers git wget curl unzip mysql-client mariadb-dev make gcc && \ + +# update python3 to 3.7 + # wget https://www.python.org/ftp/python/$PYTHON3/Python-$PYTHON3.tar.xz -O /tmp/Python-$PYTHON3.tar.xz && \ + # cd /tmp && tar -xvf Python-$PYTHON3.tar.xz && cd Python-$PYTHON3 && ./configure && make && make install && \ + + git clone --depth 1 https://github.com/knownsec/LSpider.git /opt/LSpider && \ + + cd /opt/LSpider/ && \ + pip3 install -r /tmp/requirement.txt -i https://mirrors.aliyun.com/pypi/simple && \ + + wget https://download.xray.cool/xray/$XARY/xray_linux_amd64.zip -O /tmp/xray_linux_amd64.zip && \ + mkdir -p /opt/xray && \ + unzip /tmp/xray_linux_amd64.zip -d /opt/xray && \ + + wget http://npm.taobao.org/mirrors/chromedriver/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip -O /tmp/chromedriver_linux64.zip && \ + mkdir /opt/LSpider/bin && \ + unzip /tmp/chromedriver_linux64.zip -d /opt/LSpider/bin && \ + mv /opt/LSpider/bin/chromedriver /opt/LSpider/bin/chromedriver_linux64 && \ + + rm -rf /tmp/* && \ + apk del make gcc git + +WORKDIR /opt/LSpider/ +COPY ./docker-entrypoint.sh /opt/LSpider/docker-entrypoint.sh +COPY ./settings.py /opt/LSpider/LSpider/settings.py +COPY ./xray.sh /opt/LSpider/xray.sh + +RUN chmod a+x /opt/LSpider/docker-entrypoint.sh && \ + chmod a+x /opt/LSpider/*.sh + +EXPOSE 2062 + +CMD /opt/LSpider/docker-entrypoint.sh \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000..a20ff9c --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,41 @@ +version: "2.0" + +services: + mysql: + image: mysql:5.7 + container_name: mysql + hostname: mysql + restart: always + environment: + - MYSQL_ROOT_PASSWORD=mysql@lspider + rabbitmq: + image: rabbitmq:3 + container_name: rabbitmq + hostname: rabbitmq + restart: always + environment: + - RABBITMQ_DEFAULT_USER=user + - RABBITMQ_DEFAULT_PASS=rabbitmq@lspider + - RABBITMQ_DEFAULT_VHOST=lspider_vhost + lspider: + build: . + container_name: lspider + hostname: lspider + restart: always + environment: + - RABBITMQ_IP=rabbitmq + - RABBITMQ_PORT=5672 + - RABBITMQ_USERNAME=user + - RABBITMQ_PASSWORD=rabbitmq@lspider + - RABBITMQ_VHOST=lspider_vhost + - MYSQL_USER=root + - MYSQL_PASSWORD=mysql@lspider + - MYSQL_HOST=mysql + - MYSQL_PORT=3306 + - MYSQL_DBName=LSpider + ports: + - 2062:2062 + links: + - mysql + - rabbitmq + depends_on: ['mysql','rabbitmq'] \ No newline at end of file diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh new file mode 100644 index 0000000..2ab816c --- /dev/null +++ b/docker/docker-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +#wait mysql statup +sleep 10 + +mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -h$MYSQL_HOST -P $MYSQL_PORT -e "CREATE DATABASE $MYSQL_DBName;" + +python3 manage.py makemigrations +python3 manage.py migrate + +sed -i 's/\/bin\/bash/\/bin\/sh/g' lspider_webhook.sh +sed -i 's/\/bin\/bash/\/bin\/sh/g' lspider_start.sh + +./lspider_webhook.sh +./lspider_start.sh +./xray.sh \ No newline at end of file diff --git a/docker/readme.md b/docker/readme.md new file mode 100644 index 0000000..d5dc79e --- /dev/null +++ b/docker/readme.md @@ -0,0 +1,12 @@ +# LSpider的docker环境 +> 为了简化构建环境,将该环境部署为一个docker + +## docker运行如下 +``` +docker-compose up -d +``` + +## docker 访问 +``` +http://127.0.0.1:2602 +``` \ No newline at end of file diff --git a/docker/requirement.txt b/docker/requirement.txt new file mode 100644 index 0000000..c2be690 --- /dev/null +++ b/docker/requirement.txt @@ -0,0 +1,9 @@ +django==3.1.1 +mysqlclient==2.0.1 +pika==1.1.0 +bs4 +selenium==3.141.0 +requests==2.24.0 +wechatpy==1.8.14 +colorlog +pycrypto \ No newline at end of file diff --git a/docker/xray.sh b/docker/xray.sh new file mode 100644 index 0000000..7e60ee7 --- /dev/null +++ b/docker/xray.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +while : +do + if [ $(ps aux | grep xray_linux_amd64|grep -v grep|wc -l) -eq 0 ];then + echo "start" + /opt/xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output /opt/xray/r__datetime__.html + fi + sleep 10 +done From 887e8755813772621e3f7148f02584372aebb535 Mon Sep 17 00:00:00 2001 From: qgw Date: Fri, 29 Jan 2021 17:43:03 +0800 Subject: [PATCH 02/22] update chromeheadless --- core/chromeheadless.py | 1 + docker/Dockerfile | 13 +- docker/chromeheadless.py | 600 ++++++++++++++++++++++++++++++++++++ docker/docker-entrypoint.sh | 8 +- 4 files changed, 612 insertions(+), 10 deletions(-) create mode 100644 docker/chromeheadless.py diff --git a/core/chromeheadless.py b/core/chromeheadless.py index 1f903ab..8c6c9a5 100644 --- a/core/chromeheadless.py +++ b/core/chromeheadless.py @@ -115,6 +115,7 @@ def init_object(self): self.chrome_options.add_argument( 'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36') + print("====>chromedriver_path:{}".format(self.chromedriver_path)) self.driver = webdriver.Chrome(chrome_options=self.chrome_options, executable_path=self.chromedriver_path, desired_capabilities=desired_capabilities) diff --git a/docker/Dockerfile b/docker/Dockerfile index de80c6f..947d1ab 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,7 +8,7 @@ Label author "qboy0000<2006qgw@163.com>" ENV XARY 1.7.0 ENV PYTHON3 3.7.9 -ENV CHROMEDRIVER_VERSION 89.0.4389.23 +ENV CHROMEDRIVER_VERSION 88.0.4324.96 # RUN sed -i 's/http:\/\/archive.ubuntu.com/http:\/\/mirrors.huaweicloud.com/g' /etc/apt/sources.list && \ # sed -i 's/http:\/\/security.ubuntu.com/http:\/\/mirrors.huaweicloud.com/g' /etc/apt/sources.list && \ @@ -24,7 +24,7 @@ RUN set -x && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/re echo "Asia/Shanghai" > /etc/timezone && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ # dpkg-reconfigure -f noninteractive tzdata && \ - apk add --no-cache musl-dev linux-headers git wget curl unzip mysql-client mariadb-dev make gcc && \ + apk add --no-cache musl-dev linux-headers git wget curl unzip mysql-client mariadb-dev make gcc chromium chromium-chromedriver && \ # update python3 to 3.7 # wget https://www.python.org/ftp/python/$PYTHON3/Python-$PYTHON3.tar.xz -O /tmp/Python-$PYTHON3.tar.xz && \ @@ -39,10 +39,10 @@ RUN set -x && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/re mkdir -p /opt/xray && \ unzip /tmp/xray_linux_amd64.zip -d /opt/xray && \ - wget http://npm.taobao.org/mirrors/chromedriver/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip -O /tmp/chromedriver_linux64.zip && \ - mkdir /opt/LSpider/bin && \ - unzip /tmp/chromedriver_linux64.zip -d /opt/LSpider/bin && \ - mv /opt/LSpider/bin/chromedriver /opt/LSpider/bin/chromedriver_linux64 && \ + # wget http://npm.taobao.org/mirrors/chromedriver/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip -O /tmp/chromedriver_linux64.zip && \ + # mkdir /opt/LSpider/bin && \ + # unzip /tmp/chromedriver_linux64.zip -d /usr/bin && \ + # mv /usr/bin/chromedriver /usr/bin/chromedriver_linux64 && \ rm -rf /tmp/* && \ apk del make gcc git @@ -51,6 +51,7 @@ WORKDIR /opt/LSpider/ COPY ./docker-entrypoint.sh /opt/LSpider/docker-entrypoint.sh COPY ./settings.py /opt/LSpider/LSpider/settings.py COPY ./xray.sh /opt/LSpider/xray.sh +COPY ./chromeheadless.py /opt/LSpider/core/ RUN chmod a+x /opt/LSpider/docker-entrypoint.sh && \ chmod a+x /opt/LSpider/*.sh diff --git a/docker/chromeheadless.py b/docker/chromeheadless.py new file mode 100644 index 0000000..f7506bc --- /dev/null +++ b/docker/chromeheadless.py @@ -0,0 +1,600 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +@author: LoRexxar +@contact: lorexxar@gmail.com +@file: chromeheadless.py.py +@time: 2020/3/17 15:17 +@desc: +''' + +import time + +import selenium +from selenium import webdriver +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +from selenium.webdriver.common.keys import Keys +from selenium.common.exceptions import WebDriverException +from selenium.webdriver.common.action_chains import ActionChains + +import os +import traceback +import random +from urllib.parse import urlparse + +from LSpider.settings import CHROME_WEBDRIVER_PATH, CHROME_PROXY, IS_OPEN_CHROME_PROXY +from LSpider.settings import CHROME_DOWNLOAD_PATH, IS_TEST_ENVIRONMENT +from utils.log import logger +from utils.base import random_string + + +class ChromeDriver: + def __init__(self): + self.chromedriver_path = CHROME_WEBDRIVER_PATH + self.checkos() + + try: + self.init_object() + + except selenium.common.exceptions.SessionNotCreatedException: + logger.error("[Chrome Headless] ChromeDriver version wrong error.") + exit(0) + + except selenium.common.exceptions.WebDriverException: + logger.error("[Chrome Headless] ChromeDriver load error.") + exit(0) + + self.origin_url = "" + + def checkos(self): + + if os.name == 'nt': + self.chromedriver_path = os.path.join(self.chromedriver_path, "chromedriver_win32.exe") + elif os.name == 'posix': + self.chromedriver_path = os.path.join(self.chromedriver_path, "chromedriver") + else: + self.chromedriver_path = os.path.join(self.chromedriver_path, "chromedriver_mac64") + + def init_object(self): + + self.chrome_options = webdriver.ChromeOptions() + if not IS_TEST_ENVIRONMENT: + self.chrome_options.add_argument('--headless') + self.chrome_options.add_argument('--disable-gpu') + self.chrome_options.add_argument('--no-sandbox') + self.chrome_options.add_argument('--disable-images') + self.chrome_options.add_argument('--ignore-certificate-errors') + self.chrome_options.add_argument('--allow-running-insecure-content') + self.chrome_options.add_argument('blink-settings=imagesEnabled=false') + self.chrome_options.add_argument('--omnibox-popup-count="5"') + self.chrome_options.add_argument("--disable-popup-blocking") + self.chrome_options.add_argument("--disable-web-security") + self.chrome_options.add_argument("--disk-cache-size=1000") + + # for download path + # try: + # if os.path.exists(CHROME_DOWNLOAD_PATH): + # os.mkdir(CHROME_DOWNLOAD_PATH) + # + # chrome_downloadfile_path = CHROME_DOWNLOAD_PATH + # except: + # chrome_downloadfile_path = "./tmp" + + if os.name == 'nt': + chrome_downloadfile_path = "./tmp" + else: + chrome_downloadfile_path = '/dev/null' + + prefs = { + 'download.prompt_for_download': True, + 'profile.default_content_settings.popups': 0, + 'download.default_directory': chrome_downloadfile_path + } + + self.chrome_options.add_experimental_option('prefs', prefs) + + # proxy + desired_capabilities = self.chrome_options.to_capabilities() + if IS_OPEN_CHROME_PROXY: + logger.info("[Chrome Headless] Proxy {} init".format(CHROME_PROXY)) + + desired_capabilities['acceptSslCerts'] = True + desired_capabilities['acceptInsecureCerts'] = True + desired_capabilities['proxy'] = { + "httpProxy": CHROME_PROXY, + "ftpProxy": CHROME_PROXY, + "sslProxy": CHROME_PROXY, + "noProxy": None, + "proxyType": "MANUAL", + "class": "org.openqa.selenium.Proxy", + "autodetect": False, + } + # self.chrome_options.add_argument('--proxy-server={}'.format(CHROME_PROXY)) + + self.chrome_options.add_argument( + 'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36') + + print("====>chromedriver_path:{}".format(self.chromedriver_path)) + self.driver = webdriver.Chrome(chrome_options=self.chrome_options, executable_path=self.chromedriver_path, + desired_capabilities=desired_capabilities) + + self.driver.set_page_load_timeout(15) + self.driver.set_script_timeout(5) + + def get_resp(self, url, cookies=None, times=0, isclick=True): + + try: + response_code = 1 + + self.origin_url = url + self.driver.implicitly_wait(5) + self.driver.get(url) + + if cookies: + self.add_cookie(cookies) + self.driver.implicitly_wait(10) + self.driver.get(url) + + # else: + # 检查是否是登录界面 + if self.check_login(): + logger.info("[ChromeHeadless] Page {} need login.".format(url)) + response_code = 2 + # return 2, True, "" + + time.sleep(3) + + if isclick: + if not self.click_page(): + self.driver.implicitly_wait(10) + self.driver.get(url) + + response_source = self.driver.page_source + response_title = self.driver.title + + # return 1, self.driver.page_source, self.driver.title + return response_code, response_source, response_title + + except selenium.common.exceptions.InvalidSessionIdException: + logger.warning("[ChromeHeadless]Chrome Headless quit unexpectedly..") + + self.init_object() + + logger.warning("[ChromeHeadless]retry once..{}".format(url)) + self.get_resp(url, cookies, times + 1, isclick) + return -1, False, "" + + except selenium.common.exceptions.TimeoutException: + logger.warning("[ChromeHeadless]Chrome Headless request timeout..{}".format(url)) + if times > 0: + return -1, False, "" + + logger.warning("[ChromeHeadless]retry once..{}".format(url)) + self.get_resp(url, cookies, times + 1, isclick) + return -1, False, "" + + except selenium.common.exceptions.InvalidCookieDomainException: + logger.warning("[ChromeHeadless]Chrome Headless request with cookie error..{}".format(url)) + + logger.warning("[ChromeHeadless]retry once..{}".format(url)) + self.get_resp(url, None, times + 1, isclick) + return -1, False, "" + + except selenium.common.exceptions.InvalidArgumentException: + logger.warning("[ChromeHeadless]Request error...{}".format(url)) + logger.warning("[ChromeHeadless]{}".format(traceback.format_exc())) + return -1, False, "" + + def add_cookie(self, cookies): + + for cookie in cookies.split(';'): + key = cookie.split('=')[0].strip() + value = cookie.split('=')[1].strip() + + if key and value: + try: + self.driver.add_cookie({'name': key, 'value': value}) + + except selenium.common.exceptions.UnableToSetCookieException: + logger.warning("[ChromeHeadless] Wrong Cookie {} set..".format(key)) + continue + + def click_page(self): + + # self.click_link() + # 先把格子和表单填了 + self.click_button() + + # 链接要处理一下 + self.click_link() + + # onclick + self.click_onlick() + + def check_back(self): + if self.check_host(): + new_url = self.driver.current_url + # self.driver.back() + self.driver.implicitly_wait(5) + self.driver.get(self.origin_url) + + return True + return False + + def click_link(self): + """ + 遇到一个问题,如果页面变化,那么获取到的标签hook会丢失,这里我们尝试用计数器来做 + """ + + links = self.driver.find_elements_by_xpath('//a') + links_len = len(links) + + for i in range(links_len): + + try: + links = self.driver.find_elements_by_xpath('//a') + link = links[i] + + href = link.get_attribute('href') + self.driver.execute_script( + "atags = document.getElementsByTagName('a');for(i=0;i<=atags.length;i++) { if(atags[i]){atags[i].setAttribute('target', '')}}") + + if link.is_displayed() and link.is_enabled(): + link.click() + + self.check_back() + + except selenium.common.exceptions.ElementNotInteractableException as e: + logger.warning("[ChromeHeadless][Click Page] error interact. {}".format(e)) + + self.check_back() + continue + + except selenium.common.exceptions.StaleElementReferenceException: + logger.warning("[ChromeHeadless][Click Page] page reload or wrong back redirect") + + self.check_back() + return + + except IndexError: + logger.warning("[ChromeHeadless][Click Page] wrong index for link") + continue + + except selenium.common.exceptions.NoSuchElementException: + logger.warning("[ChromeHeadless][Click Page] No Such Element") + return + + def click_onlick(self): + """ + 点包含onlick的按钮 + :return: + """ + divs = self.driver.find_elements_by_xpath('//*[@onclick]') + divs_len = len(divs) + + for i in range(divs_len): + + try: + divs = self.driver.find_elements_by_xpath('//*[@onclick]') + div = divs[i] + + # href = div.get_attribute('href') + + div.click() + + self.check_back() + + except selenium.common.exceptions.ElementNotInteractableException as e: + logger.warning("[ChromeHeadless][Click Page] error interact. {}".format(e)) + + self.check_back() + continue + + except selenium.common.exceptions.StaleElementReferenceException: + logger.warning("[ChromeHeadless][Click Page] page reload or wrong back redirect") + + self.check_back() + return + + except IndexError: + logger.warning("[ChromeHeadless][Click Page] wrong index for link") + continue + + except selenium.common.exceptions.NoSuchElementException: + logger.warning("[ChromeHeadless][Click Page] No Such Element") + return + + def smart_input(self, input): + """ + 简单的智能表单填充 + :param input: + :return: + """ + + # user + for key in ['user', '用户名', 'name']: + if key in input.get_attribute('outerHTML'): + input.send_keys('admin') + return + + # pass + for key in ['pass', 'pwd', '密码']: + if key in input.get_attribute('outerHTML'): + input.send_keys('123456') + return + + # email + for key in ['email']: + if key in input.get_attribute('outerHTML'): + input.send_keys('{}@{}.com'.format(random_string(4), random_string(4))) + return + + # phone + for key in ['phone']: + if key in input.get_attribute('outerHTML'): + input.send_keys('{}'.format(random.randint(13000000000, 14000000000))) + return + + # address + for key in ['address', 'street']: + if key in input.get_attribute('outerHTML'): + input.send_keys('4492 Garfield Road') + return + + # checkbox + if input.get_attribute('type') == 'checkbox': + input.click() + + if input.get_attribute('type') == 'radio': + input.click() + + input.send_keys(random_string()) + + return + + def finish_form(self): + """ + 填充表单 + :return: + """ + inputs = self.driver.find_elements_by_xpath("//input") + self.driver.execute_script( + "itags = document.getElementsByTagName('input');for(i=0;i<=itags.length;i++) { if(itags[i]){itags[i].removeAttribute('style')}}") + + input_lens = len(inputs) + + if not inputs: + return + + for i in range(input_lens): + try: + input = inputs[i] + + # 移动鼠标 + # 如果标签没有隐藏,那么移动鼠标 + if input.is_enabled() and input.is_displayed(): + + action = ActionChains(self.driver) + action.move_to_element(input).perform() + + self.smart_input(input) + else: + tag_id = input.get_attribute('id') + + if tag_id: + self.driver.execute_script( + "document.getElementById('{}').setAttribute('value', '{}')".format(tag_id, + random_string())) + + except selenium.common.exceptions.ElementNotInteractableException as e: + logger.warning("[ChromeHeadless][Click button] error interact...{}".format(e)) + tag_id = input.get_attribute('id') + + if tag_id: + self.driver.execute_script( + "document.getElementById('{}').setAttribute('value', '{}')".format(tag_id, random_string())) + + continue + + except selenium.common.exceptions.JavascriptException: + tag_id = input.get_attribute('id') + + if tag_id: + self.driver.execute_script( + "document.getElementById('{}').setAttribute('value', '{}')".format(tag_id, random_string())) + + continue + + except selenium.common.exceptions.StaleElementReferenceException: + logger.warning("[ChromeHeadless][Click button] page reload or wrong back redirect") + + return + + except IndexError: + logger.warning("[ChromeHeadless][Click button] wrong index for button") + continue + + def click_button(self): + + try: + submit_buttons = self.driver.find_element_by_xpath("//input[@type='submit']") + + submit_buttons_len = len(submit_buttons) + + for i in range(submit_buttons_len): + + try: + submit_buttons = self.driver.find_elements_by_xpath("//input[@type='submit']") + submit_button = submit_buttons[i] + + # 完成表单 + self.finish_form() + + # 移动鼠标 + if submit_button.is_displayed() and submit_button.is_enabled(): + action = ActionChains(self.driver) + action.move_to_element(submit_button).perform() + + submit_button.click() + + self.check_back() + except selenium.common.exceptions.ElementNotInteractableException: + logger.warning("[ChromeHeadless][Click button] error interact") + + self.check_back() + continue + + except selenium.common.exceptions.StaleElementReferenceException: + logger.warning("[ChromeHeadless][Click button] page reload or wrong back redirect") + + return + + except IndexError: + logger.warning("[ChromeHeadless][Click button] wrong index for button") + continue + except selenium.common.exceptions.NoSuchElementException as e: + logger.warning("[ChromeHeadless][Click button] No Such Element.{}".format(e)) + + try: + buttons = self.driver.find_elements_by_tag_name('button') + buttons_len = len(buttons) + + for i in range(buttons_len): + + try: + buttons = self.driver.find_elements_by_tag_name('button') + button = buttons[i] + + # 完成表单 + self.finish_form() + + if button.is_enabled() and button.is_displayed(): + action = ActionChains(self.driver) + action.move_to_element(button).perform() + button.click() + + self.check_back() + + except selenium.common.exceptions.ElementNotInteractableException: + logger.warning("[ChromeHeadless][Click button] error interact") + + self.check_back() + continue + + except selenium.common.exceptions.StaleElementReferenceException: + logger.warning("[ChromeHeadless][Click button] page reload or wrong back redirect") + + return + + except IndexError: + logger.warning("[ChromeHeadless][Click button] wrong index for button") + continue + + except selenium.common.exceptions.NoSuchElementException: + logger.warning("[ChromeHeadless][Click button] No Such Element.{}".format(traceback.format_exc())) + return + + def check_login(self): + """ + 检查当前页面是否有登录框 + :return: + """ + try: + is_has_login_form = False + is_has_login_button = False + is_has_login_input = False + is_has_login_a = False + + forms = self.driver.find_elements_by_tag_name('form') + forms_len = len(forms) + + if not forms: + is_has_login_form = False + + for i in range(forms_len): + form = forms[i] + + for key in ['login', '登录', 'sign', '用户名', 'user', 'pass', '用户名', 'pwd', 'phone', '注册']: + if key in form.text: + is_has_login_form = True + + buttons = self.driver.find_elements_by_tag_name('button') + buttons_len = len(buttons) + + if not buttons: + is_has_login_button = False + + for i in range(buttons_len): + button = buttons[i] + + if button.is_enabled() and button.is_displayed(): + + for key in ['login', 'sign', 'user', 'pass']: + if key in button.get_attribute('outerHTML'): + is_has_login_button = True + inputs = self.driver.find_elements_by_tag_name('input') + inputs_len = len(inputs) + + if not inputs: + is_has_login_input = False + + for i in range(inputs_len): + input = inputs[i] + + if input.is_enabled() and input.is_displayed(): + + for key in ['login', 'sign', 'user', 'pass', 'account', 'phone', '手机']: + if key in input.get_attribute('outerHTML'): + is_has_login_input = True + + atags = self.driver.find_elements_by_tag_name('a') + atags_len = len(atags) + + for i in range(atags_len): + atag = atags[i] + + if atag.is_enabled() and atag.is_displayed(): + + for key in ['login', 'sign', '登录', '登入']: + if key in atag.text: + is_has_login_a = True + + if is_has_login_button or is_has_login_form or is_has_login_input or is_has_login_a: + return True + else: + return False + + except selenium.common.exceptions.NoSuchElementException: + logger.warning("[ChromeHeadless][Click Page] No Such Element") + return + + except: + logger.error("[ChromeHeadless] Bad check...{}".format(traceback.format_exc())) + return False + + def check_host(self): + origin = urlparse(self.origin_url) + now = urlparse(self.driver.current_url) + + if (origin.netloc != now.netloc) or (origin.path.replace('/', '') != now.path.replace('/', '')) or ( + origin.params != now.params) or (origin.query != now.query): + return now.geturl() + + return False + + def close_driver(self): + self.driver.quit() + # self.driver.close() + time.sleep(1) + + def __del__(self): + self.close_driver() + + +if __name__ == "__main__": + Req = ChromeDriver() + + Req.get_resp("http://baidu.com", isclick=False) + + # print(Req.get_resp("https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js")) diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 2ab816c..6d66972 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh -#wait mysql statup -sleep 10 +#wait mysql & rabbitmq statup +sleep 20 mysql -u$MYSQL_USER -p$MYSQL_PASSWORD -h$MYSQL_HOST -P $MYSQL_PORT -e "CREATE DATABASE $MYSQL_DBName;" @@ -11,6 +11,6 @@ python3 manage.py migrate sed -i 's/\/bin\/bash/\/bin\/sh/g' lspider_webhook.sh sed -i 's/\/bin\/bash/\/bin\/sh/g' lspider_start.sh -./lspider_webhook.sh -./lspider_start.sh +nohup ./lspider_webhook.sh & +nohup ./lspider_start.sh & ./xray.sh \ No newline at end of file From 52298ce1b9a83050370544ff9e0ba5f4727621c8 Mon Sep 17 00:00:00 2001 From: qgw Date: Fri, 29 Jan 2021 19:00:15 +0800 Subject: [PATCH 03/22] =?UTF-8?q?=E6=B7=BB=E5=8A=A0phpmyadmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index a20ff9c..3d941b0 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -8,6 +8,21 @@ services: restart: always environment: - MYSQL_ROOT_PASSWORD=mysql@lspider + phpmyadmin: + image: phpmyadmin:5.0.4-apache + container_name: myadmin + hostname: myadmin + restart: always + environment: + - MYSQL_ROOT_PASSWORD=mysql@lspider + - PMA_HOST=mysql + - MYSQL_USER=root + - MYSQL_PASSWORD=phpmyadmin@123 + ports: + - 8080:80 + links: + - mysql + depends_on: ['mysql'] rabbitmq: image: rabbitmq:3 container_name: rabbitmq From d2f36cb486433a1888e289ab09c1818954370c7e Mon Sep 17 00:00:00 2001 From: qgw Date: Fri, 29 Jan 2021 19:02:41 +0800 Subject: [PATCH 04/22] remove debug info --- core/chromeheadless.py | 1 - docker/chromeheadless.py | 1 - 2 files changed, 2 deletions(-) diff --git a/core/chromeheadless.py b/core/chromeheadless.py index 8c6c9a5..1f903ab 100644 --- a/core/chromeheadless.py +++ b/core/chromeheadless.py @@ -115,7 +115,6 @@ def init_object(self): self.chrome_options.add_argument( 'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36') - print("====>chromedriver_path:{}".format(self.chromedriver_path)) self.driver = webdriver.Chrome(chrome_options=self.chrome_options, executable_path=self.chromedriver_path, desired_capabilities=desired_capabilities) diff --git a/docker/chromeheadless.py b/docker/chromeheadless.py index f7506bc..97ba9fc 100644 --- a/docker/chromeheadless.py +++ b/docker/chromeheadless.py @@ -115,7 +115,6 @@ def init_object(self): self.chrome_options.add_argument( 'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36') - print("====>chromedriver_path:{}".format(self.chromedriver_path)) self.driver = webdriver.Chrome(chrome_options=self.chrome_options, executable_path=self.chromedriver_path, desired_capabilities=desired_capabilities) From 59de725b774e3cd288d18b035ee475eecaa987ac Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 14:37:57 +0800 Subject: [PATCH 05/22] fixd #13 --- core/chromeheadless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/chromeheadless.py b/core/chromeheadless.py index 1f903ab..d255b89 100644 --- a/core/chromeheadless.py +++ b/core/chromeheadless.py @@ -417,7 +417,7 @@ def finish_form(self): def click_button(self): try: - submit_buttons = self.driver.find_element_by_xpath("//input[@type='submit']") + submit_buttons = self.driver.find_elements_by_xpath("//input[@type='submit']") submit_buttons_len = len(submit_buttons) From 06e87114684861a14f90a05b2cae1ab36b6c1955 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 15:45:06 +0800 Subject: [PATCH 06/22] add webview for scan result --- LSpider/settings.py.bak | 6 ++++++ templates/Vullist.html | 9 +++++++++ web/index/urls.py | 1 + web/index/views.py | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 templates/Vullist.html diff --git a/LSpider/settings.py.bak b/LSpider/settings.py.bak index 0c6e79e..a5f28bd 100644 --- a/LSpider/settings.py.bak +++ b/LSpider/settings.py.bak @@ -213,5 +213,11 @@ WECHAT_NOTICE_DEBUG = { 'agent_id': ' ', } +# for xray result +VUL_LIST_PATH = os.path.join(BASE_DIR, 'vuls/') + +if os.path.isdir(VUL_LIST_PATH) is not True: + os.mkdir(VUL_LIST_PATH) + # for test IS_TEST_ENVIRONMENT = False diff --git a/templates/Vullist.html b/templates/Vullist.html new file mode 100644 index 0000000..86d073d --- /dev/null +++ b/templates/Vullist.html @@ -0,0 +1,9 @@ +{% block title %}Vulfile list{% endblock %} + +{% block body %} + + {% for file in filelist %} + ={{ file }} + {% endfor %} + +{% endblock %} \ No newline at end of file diff --git a/web/index/urls.py b/web/index/urls.py index 79e705f..b76562b 100644 --- a/web/index/urls.py +++ b/web/index/urls.py @@ -21,5 +21,6 @@ urlpatterns = [ path("", views.index), + path("vuls/", views.VulFileListView.as_view(), name="vullist"), path("webhook", csrf_exempt(views.WebhookView.as_view()), name="webhook"), ] diff --git a/web/index/views.py b/web/index/views.py index 516fe8a..5bdcaed 100644 --- a/web/index/views.py +++ b/web/index/views.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import os import json import time @@ -10,11 +11,48 @@ from utils.wechathandler import ReMess +from LSpider.settings import VUL_LIST_PATH + def index(req): return HttpResponse("Hello Lspider.") +class VulFileListView(View): + """ + 扫描器结果展示列表 + """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def get(self, request, path=""): + + if path and ('./' in path or '..' in path): + return HttpResponse("Go back. Hacker~") + + now_vul_path = os.path.join(VUL_LIST_PATH, path) + + if os.path.isfile(now_vul_path): + return render(request, now_vul_path) + + if not os.path.isdir(now_vul_path): + return HttpResponse("Bad Request. VUL_LIST_PATH needs to be configured or current path Error.") + + self.file_list = [] + + for filename in os.listdir(now_vul_path): + if os.path.isdir(os.path.join(now_vul_path, filename)): + self.file_list.append("{}/".format(filename)) + + else: + self.file_list.append(filename) + + data = {'filelist': self.file_list} + + return render(request, 'Vullist.html', data) + + class WebhookView(View): """ 授权模块 From 7a83bcf3ea0e861ca0b96dae3a701aa29b45db29 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 15:59:42 +0800 Subject: [PATCH 07/22] add webview for scan result --- templates/Vullist.html | 2 +- web/index/urls.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/Vullist.html b/templates/Vullist.html index 86d073d..a181a71 100644 --- a/templates/Vullist.html +++ b/templates/Vullist.html @@ -3,7 +3,7 @@ {% block body %} {% for file in filelist %} - ={{ file }} + ={{ file }} {% endfor %} {% endblock %} \ No newline at end of file diff --git a/web/index/urls.py b/web/index/urls.py index b76562b..5ff0272 100644 --- a/web/index/urls.py +++ b/web/index/urls.py @@ -21,6 +21,7 @@ urlpatterns = [ path("", views.index), - path("vuls/", views.VulFileListView.as_view(), name="vullist"), + path("vuls/", views.VulFileListView.as_view(), name="vullist"), + path("vuls/", views.VulFileListView.as_view(), name="vulpath"), path("webhook", csrf_exempt(views.WebhookView.as_view()), name="webhook"), ] From 3be275025a18984d5338fed9860ee8dc1fd52820 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:03:31 +0800 Subject: [PATCH 08/22] add webview for scan result --- templates/Vullist.html | 2 +- web/index/views.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/Vullist.html b/templates/Vullist.html index a181a71..b1e20dd 100644 --- a/templates/Vullist.html +++ b/templates/Vullist.html @@ -3,7 +3,7 @@ {% block body %} {% for file in filelist %} - ={{ file }} + {{ file }}
{% endfor %} {% endblock %} \ No newline at end of file diff --git a/web/index/views.py b/web/index/views.py index 5bdcaed..4c5804b 100644 --- a/web/index/views.py +++ b/web/index/views.py @@ -26,12 +26,12 @@ class VulFileListView(View): def __init__(self, **kwargs): super().__init__(**kwargs) - def get(self, request, path=""): + def get(self, request, filepath=""): - if path and ('./' in path or '..' in path): + if filepath and ('./' in filepath or '..' in filepath): return HttpResponse("Go back. Hacker~") - now_vul_path = os.path.join(VUL_LIST_PATH, path) + now_vul_path = os.path.join(VUL_LIST_PATH, filepath) if os.path.isfile(now_vul_path): return render(request, now_vul_path) From 6f6c720429e74bc5df692aa6e00fcf003a1af804 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:13:46 +0800 Subject: [PATCH 09/22] add webview for scan result --- web/index/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/index/views.py b/web/index/views.py index 4c5804b..f29605b 100644 --- a/web/index/views.py +++ b/web/index/views.py @@ -4,6 +4,7 @@ import os import json import time +import codecs from django.views import View from django.shortcuts import render @@ -34,7 +35,10 @@ def get(self, request, filepath=""): now_vul_path = os.path.join(VUL_LIST_PATH, filepath) if os.path.isfile(now_vul_path): - return render(request, now_vul_path) + os.chmod(now_vul_path, 0o644) + + content = codecs.open(now_vul_path, 'r+', encoding='utf-8', errors='ignore') + return HttpResponse(content) if not os.path.isdir(now_vul_path): return HttpResponse("Bad Request. VUL_LIST_PATH needs to be configured or current path Error.") From f145c20f61329024bf1d419cb09379f436cb736d Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:14:50 +0800 Subject: [PATCH 10/22] add webview for scan result --- web/index/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/index/views.py b/web/index/views.py index f29605b..aa7cfbe 100644 --- a/web/index/views.py +++ b/web/index/views.py @@ -35,7 +35,6 @@ def get(self, request, filepath=""): now_vul_path = os.path.join(VUL_LIST_PATH, filepath) if os.path.isfile(now_vul_path): - os.chmod(now_vul_path, 0o644) content = codecs.open(now_vul_path, 'r+', encoding='utf-8', errors='ignore') return HttpResponse(content) From ca1ca31e4d122922a293ea7b8d762f9419a7100e Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:36:47 +0800 Subject: [PATCH 11/22] add webview for scan result --- web/index/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/web/index/views.py b/web/index/views.py index aa7cfbe..fdd40f8 100644 --- a/web/index/views.py +++ b/web/index/views.py @@ -51,6 +51,7 @@ def get(self, request, filepath=""): else: self.file_list.append(filename) + self.file_list.sort(reverse=True) data = {'filelist': self.file_list} return render(request, 'Vullist.html', data) From 723ced9e2db7ebcbbf6f4d24a9eb1283decb2bbe Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:38:22 +0800 Subject: [PATCH 12/22] add webview for scan result --- templates/Vullist.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/Vullist.html b/templates/Vullist.html index b1e20dd..b421b59 100644 --- a/templates/Vullist.html +++ b/templates/Vullist.html @@ -1,4 +1,4 @@ -{% block title %}Vulfile list{% endblock %} +{% block title %}Vulfile list:
{% endblock %} {% block body %} From 20a4ad3b8135ccb8c04eaf3837858c4d08634aca Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:40:40 +0800 Subject: [PATCH 13/22] update shell script --- lspider_start.sh | 1 + lspider_stop.sh | 3 --- xray.sh | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lspider_start.sh b/lspider_start.sh index d72359f..16fd573 100644 --- a/lspider_start.sh +++ b/lspider_start.sh @@ -4,6 +4,7 @@ while : do if [ $(ps aux | grep SpiderCoreBackendStart|grep -v grep|wc -l) -eq 0 ];then echo "start" + chmod 644 $(cd "$(dirname "$0")";pwd)/vuls/* python3 $(cd "$(dirname "$0")";pwd)/manage.py SpiderCoreBackendStart fi sleep 100 diff --git a/lspider_stop.sh b/lspider_stop.sh index 496f85e..33f5d11 100644 --- a/lspider_stop.sh +++ b/lspider_stop.sh @@ -4,6 +4,3 @@ kill -2 $(ps aux | grep SpiderCoreBackendStart|grep -v grep|awk '{print $2}') sleep 3 kill -9 $(ps aux | grep SpiderCoreBackendStart|grep -v grep|awk '{print $2}') kill -9 $(ps aux | grep chrome|grep -v grep|awk '{print $2}') - - -chown www:www /home/wwwroot/default/xray/r* \ No newline at end of file diff --git a/xray.sh b/xray.sh index 69efd43..2137c1f 100644 --- a/xray.sh +++ b/xray.sh @@ -4,7 +4,7 @@ while : do if [ $(ps aux | grep xray_linux_amd64|grep -v grep|wc -l) -eq 0 ];then echo "start" - /home/ubuntu/lorexxar/xray/xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output /home/wwwroot/default/xray/r__datetime__.html + ..//xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output $(cd "$(dirname "$0")";pwd)/vuls/r__datetime__.html fi sleep 10 done From 7a6eaa51e4fef55505e395e90c2a0252bf963a65 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:41:46 +0800 Subject: [PATCH 14/22] update shell script --- xray.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xray.sh b/xray.sh index 2137c1f..1aac02e 100644 --- a/xray.sh +++ b/xray.sh @@ -4,7 +4,7 @@ while : do if [ $(ps aux | grep xray_linux_amd64|grep -v grep|wc -l) -eq 0 ];then echo "start" - ..//xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output $(cd "$(dirname "$0")";pwd)/vuls/r__datetime__.html + ../xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output $(cd "$(dirname "$0")";pwd)/vuls/r__datetime__.html fi sleep 10 done From 0337f3cbf3fd119c4fd5215bc6775103d5d9c385 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:56:22 +0800 Subject: [PATCH 15/22] update docs --- README.md | 6 +++++- docs/6.png | Bin 0 -> 76710 bytes docs/changelog.md | 7 +++++++ docs/config.md | 10 ++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 docs/6.png create mode 100644 docs/changelog.md diff --git a/README.md b/README.md index c0a45f2..940cf7a 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ python3 manage.py SpiderCoreBackendStart --test **值得注意的是,以下脚本可能会涉及到项目路径影响,使用前请修改相应的配置** -启动LSpider webhook(默认端口2062) +启动LSpider webhook 与漏洞展示页面(默认端口2062) ``` ./lspider_webhook.sh @@ -93,6 +93,10 @@ python3 manage.py SpiderCoreBackendStart --test [如何配置扫描任务 以及 其他的配置相关](./docs/manage.md) +扫描器结果输出到配置文件相同目录(默认为vuls/),则可以通过web界面访问。 + +![](./docs/6.png) + # 使用内置的hackerone、bugcrowd爬虫获取目标 使用hackerone爬虫,你需要首先配置好hackerone账号 diff --git a/docs/6.png b/docs/6.png new file mode 100644 index 0000000000000000000000000000000000000000..be58ff3e4361b5d420ce1212da15815768dc7b6a GIT binary patch literal 76710 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 U0p974n=Q!RQMjEW`Ox=&0W;-%<^TWy literal 0 HcmV?d00001 diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 0000000..b772c1a --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,7 @@ +## 更新日志 +- 2021-1-26 + - LSpider v1.0.0正式发布 + - 更新大量相关文档 +- 2021-2-2 + - LSpider v1.0.1 + - 添加Web模式用来适配被动扫描器输出 \ No newline at end of file diff --git a/docs/config.md b/docs/config.md index e4ef1cb..c37778c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -80,6 +80,16 @@ WECHAT_NOTICE_DEBUG = { } ``` +这个配置是关于被动扫描器的输出位置,这里默认为当前目录的vuls,其中对应的web界面也为相同路径。 +``` +# for xray result +VUL_LIST_PATH = os.path.join(BASE_DIR, 'vuls/') + +if os.path.isdir(VUL_LIST_PATH) is not True: + os.mkdir(VUL_LIST_PATH) +``` + + 如果开启这个配置,Chrome webdriver 会以非headless的模式启动,便于调试环境 ``` # for test From cf56d330a94e221230e1752863ac0d28091cd365 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Tue, 2 Feb 2021 16:59:15 +0800 Subject: [PATCH 16/22] update docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 940cf7a..5be9d8f 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ python3 manage.py SpiderCoreBackendStart --test **值得注意的是,以下脚本可能会涉及到项目路径影响,使用前请修改相应的配置** +建议配合screen来挂起进程 + 启动LSpider webhook 与漏洞展示页面(默认端口2062) ``` From b1c11c5d9ef553ff0cb42b07789da4c36398d523 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Mon, 22 Feb 2021 15:28:28 +0800 Subject: [PATCH 17/22] fix xray profile --- docker/readme.md | 5 +++++ docker/xray.sh | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/readme.md b/docker/readme.md index d5dc79e..b2e9eee 100644 --- a/docker/readme.md +++ b/docker/readme.md @@ -6,6 +6,11 @@ docker-compose up -d ``` +## 结果访问 +``` +http://127.0.0.1:2602/vuls/ +``` + ## docker 访问 ``` http://127.0.0.1:2602 diff --git a/docker/xray.sh b/docker/xray.sh index 7e60ee7..450c9d0 100644 --- a/docker/xray.sh +++ b/docker/xray.sh @@ -4,7 +4,7 @@ while : do if [ $(ps aux | grep xray_linux_amd64|grep -v grep|wc -l) -eq 0 ];then echo "start" - /opt/xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output /opt/xray/r__datetime__.html + /opt/xray/xray_linux_amd64 webscan --listen 127.0.0.1:7777 --html-output /opt/LSpider/vuls/r__datetime__.html fi sleep 10 done From 7631e3b96ee42560b9d1d3cae7dd000505ad57a4 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Mon, 22 Feb 2021 15:44:59 +0800 Subject: [PATCH 18/22] add settings file --- docker/Dockerfile | 5 +- docker/settings.py.docker.bak | 223 ++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 docker/settings.py.docker.bak diff --git a/docker/Dockerfile b/docker/Dockerfile index 947d1ab..39814b7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -49,12 +49,11 @@ RUN set -x && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/re WORKDIR /opt/LSpider/ COPY ./docker-entrypoint.sh /opt/LSpider/docker-entrypoint.sh -COPY ./settings.py /opt/LSpider/LSpider/settings.py +COPY ./settings.py.docker.bak /opt/LSpider/LSpider/settings.py COPY ./xray.sh /opt/LSpider/xray.sh COPY ./chromeheadless.py /opt/LSpider/core/ -RUN chmod a+x /opt/LSpider/docker-entrypoint.sh && \ - chmod a+x /opt/LSpider/*.sh +RUN chmod a+x /opt/LSpider/*.sh EXPOSE 2062 diff --git a/docker/settings.py.docker.bak b/docker/settings.py.docker.bak new file mode 100644 index 0000000..9e3a872 --- /dev/null +++ b/docker/settings.py.docker.bak @@ -0,0 +1,223 @@ +""" +Django settings for LSpider project. + +Generated by 'django-admin startproject' using Django 1.11.29. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'this_is_a_secret_key' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'web.index', + 'web.spider', + 'web.vultargetspider', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'LSpider.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')] + , + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'LSpider.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.11/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'LSpider', + 'USER': 'root', + 'PASSWORD': 'mysql@lspider', + 'HOST': 'mysql', + 'PORT': '3306', + 'OPTIONS': { + 'init_command': 'SET default_storage_engine=INNODB;SET NAMES utf8mb4', + 'charset': 'utf8mb4', + }, + 'TEST': { + 'CHARSET': 'utf8', + 'COLLATION': 'utf8_general_ci', + }, + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# cors +CORS_ALLOW_CREDENTIALS = True +CORS_ORIGIN_ALLOW_ALL = True +CORS_ORIGIN_WHITELIST = [ + '*' +] + +CORS_ALLOW_METHODS = [ + 'DELETE', + 'GET', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', + 'VIEW', +] + +CORS_ALLOW_HEADERS = [ + 'XMLHttpRequest', + 'X_FILENAME', + 'accept-encoding', + 'authorization', + 'content-type', + 'dnt', + 'origin', + 'user-agent', + 'x-csrftoken', + 'x-requested-with', + 'Pragma', +] + +CSRF_COOKIE_SAMESITE = None +SESSION_COOKIE_SAMESITE = None + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.11/howto/static-files/ + +STATIC_URL = os.path.join(BASE_DIR, '/static/') + +CHROME_DOWNLOAD_PATH = '/tmp/lspider' + +# Chrome webdriver +CHROME_WEBDRIVER_PATH = os.path.join(BASE_DIR, 'bin/') + +# setting for spider + +LIMIT_DEEP = 2 +THREADPOOL_MAX_THREAD_NUM = 5 + +# rabbitmq +RABBITMQ_IP = "rabbitmq" +RABBITMQ_PORT = "5672" +RABBITMQ_USERNAME = "user" +RABBITMQ_PASSWORD = "rabbitmq@lspider" +RABBITMQ_VHOST = "lspider_vhost" + +# wheather open rabbitmq +IS_OPEN_RABBITMQ = True + +# proxy for chrome headless +IS_OPEN_CHROME_PROXY = True +CHROME_PROXY = '127.0.0.1:7777' + +# for hackerone +HACKERONE_USERNAME = "" +HACKERONE_PASSWORD = "" + +# loghander +LOGHANDER_IS_OPEN_WEIXIN = False + +# for weixin +WECHAT_NOTICE = { + 'corp_id': ' ', + 'secret': ' ', + 'agent_id': ' ', +} + +WECHAT_NOTICE_DEBUG = { + 'corp_id': ' ', + 'secret': ' ', + 'agent_id': ' ', +} + +# for xray result +VUL_LIST_PATH = os.path.join(BASE_DIR, 'vuls/') + +if os.path.isdir(VUL_LIST_PATH) is not True: + os.mkdir(VUL_LIST_PATH) + +# for test +IS_TEST_ENVIRONMENT = False From aff415c55be70508350b5c8b09d24bbf07d6b039 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Mon, 22 Feb 2021 16:25:14 +0800 Subject: [PATCH 19/22] modify port for phpmyadmin --- docker/docker-compose.yaml | 2 +- docker/readme.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 3d941b0..a399530 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -19,7 +19,7 @@ services: - MYSQL_USER=root - MYSQL_PASSWORD=phpmyadmin@123 ports: - - 8080:80 + - 2063:80 links: - mysql depends_on: ['mysql'] diff --git a/docker/readme.md b/docker/readme.md index b2e9eee..76eafa9 100644 --- a/docker/readme.md +++ b/docker/readme.md @@ -8,10 +8,10 @@ docker-compose up -d ## 结果访问 ``` -http://127.0.0.1:2602/vuls/ +http://127.0.0.1:2062/vuls/ ``` -## docker 访问 +## phpmyadmin访问 ``` -http://127.0.0.1:2602 +http://127.0.0.1:2063 ``` \ No newline at end of file From 8269ada2d34a03b055d4062a1302e657191925f5 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Mon, 22 Feb 2021 17:03:52 +0800 Subject: [PATCH 20/22] add allowed hosts --- docker/settings.py.docker.bak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/settings.py.docker.bak b/docker/settings.py.docker.bak index 9e3a872..acc4165 100644 --- a/docker/settings.py.docker.bak +++ b/docker/settings.py.docker.bak @@ -25,7 +25,7 @@ SECRET_KEY = 'this_is_a_secret_key' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['*'] # Application definition From e2c96901345604f3a7f405cc6780b16778772450 Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Mon, 22 Feb 2021 17:09:16 +0800 Subject: [PATCH 21/22] update dockerfile --- docker/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 39814b7..c15596a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -44,8 +44,7 @@ RUN set -x && sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/re # unzip /tmp/chromedriver_linux64.zip -d /usr/bin && \ # mv /usr/bin/chromedriver /usr/bin/chromedriver_linux64 && \ - rm -rf /tmp/* && \ - apk del make gcc git + rm -rf /tmp/* WORKDIR /opt/LSpider/ COPY ./docker-entrypoint.sh /opt/LSpider/docker-entrypoint.sh From 951b743dba6f5be5c024b008802401c872de7aae Mon Sep 17 00:00:00 2001 From: LoRexxar Date: Mon, 22 Feb 2021 17:18:09 +0800 Subject: [PATCH 22/22] update to 1.0.2 --- README.md | 17 +++++++++++++++++ docs/changelog.md | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5be9d8f..66d2055 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,17 @@ LSpider从设计之初是为了配合像xray这种被动扫描器而诞生的, python3 manage.py SpiderCoreBackendStart --test ``` +通过dockerfile安装(不推荐的安装模式) +``` +cd ./docker + +docker-compose up -d +``` + +[dockerfile 安装&使用](./docker/readme.md) + +**使用dockerfile安装,推荐修改其中必要的配置信息以避免安全漏洞诞生。** + **值得注意的是,以下脚本可能会涉及到项目路径影响,使用前请修改相应的配置** 建议配合screen来挂起进程 @@ -114,6 +125,12 @@ python3 manage.py SpiderCoreBackendStart --test ![](./docs/5.png) +# Contributors + +感谢如下贡献者对本工具发展过程中的贡献: + +- [QGW](https://github.com/qboy0000) + # 404StarLink ![](https://github.com/knownsec/404StarLink-Project/raw/master/logo.png) diff --git a/docs/changelog.md b/docs/changelog.md index b772c1a..fdaf301 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,4 +4,7 @@ - 更新大量相关文档 - 2021-2-2 - LSpider v1.0.1 - - 添加Web模式用来适配被动扫描器输出 \ No newline at end of file + - 添加Web模式用来适配被动扫描器输出 +- 2021-2-22 + - LSpider v1.0.2 + - 添加docker环境以便于快速搭建环境。感谢@QGW \ No newline at end of file