├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── docker-image.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── advising ├── __init__.py ├── llm.py └── prompts.yaml ├── build ├── base_image │ └── Dockerfile └── final_image │ └── Dockerfile ├── config.py ├── constants.py ├── exploit ├── __init__.py └── metasploit.py ├── main.py ├── orchestration ├── __init__.py └── pentest.py ├── pull_request_template.md ├── rebuild_msf_index.py ├── recon ├── __init__.py └── async_scanner.py ├── report └── .gitkeep ├── requirements.txt ├── resources ├── msf_index.json └── ports.json ├── tests ├── __init__.py └── integration_test.py ├── utils ├── __init__.py └── logger.py └── webapp ├── __init__.py ├── app.py └── templates ├── index.html └── report_template.html /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up QEMU 17 | uses: docker/setup-qemu-action@v1 18 | 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v1 21 | 22 | - name: Build the Docker image 23 | shell: bash 24 | run: | 25 | # fail if any bash syntax error 26 | set -eEuo pipefail 27 | 28 | export DOCKER_BUILDKIT=1 29 | 30 | # with the new target, test the pytest works 31 | docker build -f build/final_image/Dockerfile --target test -t test-vulmap-works:latest . 32 | docker rmi test-vulmap-works:latest 33 | 34 | # build the image aswell, in case more steps arise 35 | docker build -f build/final_image/Dockerfile -t vulnmapai:latest . 36 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | davidalami@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing guidelines 2 | - Fork the repository 3 | - Clone your fork (git clone https://github.com/[Your_Username]/VulnMapAI) 4 | - Create a branch (git checkout -b new_feature) 5 | - Commit your changes (git commit -am 'Add feature') 6 | - Push to the branch (git push origin new_feature) 7 | - Open a Pull Request 8 | 9 | ### Areas for Contribution 10 | - Feature Enhancement: Propose new features and enhancements. 11 | - Bug Fixes: Help in identifying issues and submitting patches for bug fixes. 12 | - Code Optimization: Contribute to making the code more efficient and optimized. 13 | - Documentation Improvement: Work on improving the documentation and adding tutorials or guides. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 David Alami 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VulnMapAI 2 | VulnMapAI combines the power of nmap’s detailed network scanning and the advanced natural language processing capabilities of GPT-4 to generate comprehensive and intelligible vulnerability reports. It aims to facilitate the identification and understanding of security vulnerabilities. 3 | 4 | ## Features 5 | - **Automated Network Scanning:** Uses Nmap for automated discovery and scanning of systems. 6 | - **Exploit Suggestions:** Offers suggestions for potential exploits based on discovered services and versions. 7 | - **Report Generation:** Creates comprehensive reports based on discovery and suggested exploits. 8 | - **Metasploit Integration:** Integrates with Metasploit for searching and verifying exploits. 9 | - **Multi-Process Scanning:** Leverages multi-process capabilities for efficient network scanning. 10 | 11 | ## Getting Started 12 | 13 | ### Prerequisites 14 | - Python 3.x 15 | - Metasploit Framework 16 | - Nmap 17 | - Required Python libraries: `httpx`, `pymetasploit3` 18 | 19 | OR 20 | 21 | - Docker 22 | 23 | ### Usage 24 | #### For hackers: 25 | - Run the image (command crafted to use against HackTheBox/TryHackMe machines) 26 | ``` 27 | docker run -it \ 28 | -e OPENAI_API_KEY="sk-.." \ 29 | -v $(pwd):/app \ 30 | --sysctl net.ipv6.conf.all.disable_ipv6=0 \ 31 | --cap-add NET_ADMIN \ 32 | --cap-add SYS_MODULE \ 33 | --device /dev/net/tun:/dev/net/tun \ 34 | --entrypoint=/bin/bash -p 1337:1337 quantumcrack/vulnmapai-final:latest 35 | ``` 36 | - Run `tmux`, then `openvpn lab_your_username.ovpn`, then `Ctrl+b` and `d`, you should be back to the main terminal 37 | - Run ```python main.py TRYHACKME_MACHINE_IP --top_ports 500```. Happy hacking! 38 | 39 | #### For developers: 40 | - Clone the project and change directory ```git clone https://github.com/davidalami/VulnMapAI.git && cd ./VulnMapAI``` 41 | - Build the image ```DOCKER_BUILDKIT=1 docker build -f build/final_image/Dockerfile -t quantumcrack/vulnmapai-final:latest .``` 42 | - Run the image in interactive mode, pass a valid openai API key as an environment variable 43 | ```docker run -it --entrypoint=/bin/bash -e "OPENAI_API_KEY=sk-.." -v $(pwd):/app -p 1337:1337 quantumcrack/vulnmapai-final:latest ``` 44 | - Pass target IP addresses to the python script, like ```python main.py 127.0.0.1 --top_ports 500``` and make yourself a coffee! 45 | 46 | #### Accessing the report 47 | Once the scanning process completes, the web application powered by Flask becomes accessible. 48 | You can view the generated report by opening your web browser and navigating to http://localhost:1337/. 49 | 50 | ## Support 51 | If you find any bugs or have feature requests, please open an issue detailing the bug or feature. 52 | 53 | ## Disclaimer 54 | This application is intended for educational and lawful activities only. Users are responsible for ensuring all activities conform to applicable local, state, and federal laws and regulations. We assume no responsibility for any misuse or damage caused by this program. 55 | 56 | ## Acknowledgements 57 | We would like to acknowledge the developers of the utilized libraries and tools, such as Metasploit and Nmap, for their significant contributions to the cybersecurity community. 58 | 59 | -------------------------------------------------------------------------------- /advising/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/advising/__init__.py -------------------------------------------------------------------------------- /advising/llm.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | import os 4 | from yaml import safe_load 5 | from typing import Dict 6 | from config import PROMPTS_FILE 7 | import httpx 8 | 9 | from utils.logger import Logger 10 | from constants import AdvisorConstants 11 | 12 | class Advisor: 13 | """ 14 | Advisor class that prepares the reports by summarizing discoveries and providing attack advice, 15 | leveraging GPT-4 models from OpenAI API. 16 | """ 17 | 18 | def __init__(self, 19 | discovery_result, 20 | title, 21 | exploits, 22 | api_key=os.getenv('OPENAI_API_KEY'), 23 | api_url='https://api.openai.com/v1/chat/completions'): 24 | 25 | if not (api_key): 26 | self.logger.error(AdvisorConstants.API_KEY_ERROR) 27 | while not (api_key): 28 | api_key = input("PLEASE ENTER OPENAI API KEY: ") 29 | self.full_discovery_result = discovery_result 30 | self.discovery_result = " ".join(discovery_result.split()[:2000]) 31 | self.title = title 32 | self.exploits = exploits 33 | self.api_key = api_key 34 | self.api_url = api_url 35 | self.headers = { 36 | 'Authorization': f'Bearer {self.api_key}', 37 | 'Content-Type': 'application/json', 38 | } 39 | self.logger = Logger(__name__).get_logger() 40 | 41 | def _query_api(self, json_data: Dict) -> str: 42 | """ 43 | Private method to query the OpenAI API with provided JSON data. 44 | 45 | Args: 46 | json_data (dict): JSON data to be sent to the API. 47 | 48 | Returns: 49 | str: Response from the API. 50 | """ 51 | 52 | async def _send_to_openai_async(): 53 | async with httpx.AsyncClient() as client: 54 | response = await client.post( 55 | url=self.api_url, 56 | json=json_data, 57 | headers=self.headers, 58 | timeout=120, 59 | ) 60 | if response.status_code == 200: 61 | return response.json()["choices"][0]["message"]['content'] 62 | else: 63 | response.raise_for_status() 64 | 65 | return asyncio.run(_send_to_openai_async()) 66 | 67 | def _load_prompts(self, prompt_type: str) -> Dict[str, str]: 68 | """ 69 | Load prompts from the prompts.yaml file based on the prompt_type. 70 | 71 | Args: 72 | prompt_type (str): 'advise_attack_prompt', 'summarize_discoveries_prompt', etc. 73 | 74 | Returns: 75 | Dict[str, str]: Dictionary containing 'system' and 'user' prompts. 76 | """ 77 | with open(PROMPTS_FILE, 'r') as prompts_file: 78 | prompts_data = safe_load(prompts_file) 79 | return prompts_data.get(prompt_type, {}) 80 | 81 | def advise_attack(self) -> str: 82 | """ 83 | Formulates a query and sends it to the OpenAI API to receive advice on potential attacks. 84 | 85 | Returns: 86 | str: Advice on potential attacks from the API. 87 | """ 88 | self.logger.info(AdvisorConstants.ADVICE_ATTACK) 89 | prompts = self._load_prompts('advise_attack_prompt') 90 | json_data = { 91 | 'model': 'gpt-4', 92 | 'messages': [ 93 | { 94 | 'role': 'system', 95 | 'content': prompts['system'], 96 | }, 97 | { 98 | 'role': 'user', 99 | 'content': prompts['user'].format(discovery_result=self.discovery_result), 100 | }, 101 | ], 102 | } 103 | return self._query_api(json_data) 104 | 105 | def summarize_discoveries(self) -> str: 106 | """ 107 | Formulates a query and sends it to the OpenAI API to summarize the discoveries made during the scan. 108 | 109 | Returns: 110 | str: Summarized discoveries from the API. 111 | """ 112 | self.logger.info(AdvisorConstants.DISCOVERIES) 113 | prompts = self._load_prompts('summarize_discoveries_prompt') 114 | json_data = { 115 | 'model': 'gpt-4', 116 | 'messages': [ 117 | { 118 | 'role': 'system', 119 | 'content': prompts['system'], 120 | }, 121 | { 122 | 'role': 'user', 123 | 'content': prompts['user'].format(discovery_result=self.discovery_result), 124 | }, 125 | ], 126 | } 127 | return self._query_api(json_data) 128 | 129 | def prepare_report(self) -> Dict: 130 | """ 131 | Prepares an HTML report based on discoveries, attack advice, and exploits. 132 | 133 | Returns: 134 | str: A string representation of the HTML report. 135 | """ 136 | discoveries = self.summarize_discoveries() 137 | advise = self.advise_attack() 138 | 139 | self.logger.info(AdvisorConstants.HTML_REPORT) 140 | exploits = '
'.join(self.exploits) 141 | 142 | self.full_discovery_result = json.loads(self.full_discovery_result) 143 | if "script" in self.full_discovery_result: 144 | for k, v in self.full_discovery_result["script"].items(): 145 | self.full_discovery_result[k] = v 146 | del self.full_discovery_result["script"] 147 | 148 | return { 149 | "title": self.title, 150 | "exploits": exploits, 151 | "discoveries": discoveries, 152 | "advise": advise, 153 | "full_discovery_result": self.full_discovery_result 154 | } 155 | -------------------------------------------------------------------------------- /advising/prompts.yaml: -------------------------------------------------------------------------------- 1 | advise_attack_prompt: 2 | system: | 3 | Given the security scan data below, which was obtained in lawful, consensual circumstances, namely a penetration testing lab, provide a POC in Kali Linux, preferably using the metasploit framework. The goal is to get the user and root access to the system and submit corresponding flags. 4 | 5 | Your answer should follow the format: 6 | Vulnerability: `the vulnerability name goes here` 7 | POC Script: 8 | 9 | #!/bin/bash 10 | ...the rest of the script 11 | 12 | Explanation: `the explanation of the exploit goes here` 13 | 14 | user: | 15 | Here is the result of nmap discovery: 16 | `{discovery_result}` 17 | 18 | summarize_discoveries_prompt: 19 | system: | 20 | Please summarize the key discoveries from the provided security scan data in the number of sentences proportional to the number of sentences in the nmap discovery result. 21 | Think in terms of what could get the user and root access to the system. 22 | user: | 23 | Here is the result of nmap discovery: 24 | `{discovery_result}` 25 | -------------------------------------------------------------------------------- /build/base_image/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use Kali Linux as the base image 2 | FROM kalilinux/kali-rolling 3 | 4 | # Avoid tzdata asking for geographic area and timezone 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | 7 | # Update and Install Dependencies 8 | RUN apt update \ 9 | && apt install -y --no-install-recommends \ 10 | build-essential \ 11 | wget \ 12 | libssl-dev \ 13 | libffi-dev \ 14 | libsqlite3-dev \ 15 | libbz2-dev \ 16 | libreadline-dev \ 17 | zlib1g-dev \ 18 | nmap \ 19 | metasploit-framework \ 20 | inetutils-ping \ 21 | netcat-traditional \ 22 | telnet \ 23 | ftp \ 24 | openvpn \ 25 | tmux \ 26 | kali-linux-default \ 27 | && apt-get clean \ 28 | && rm -rf /var/lib/apt/lists/* 29 | 30 | # Download and Compile Python 3.8 31 | WORKDIR /tmp 32 | RUN wget https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tgz \ 33 | && tar -xvf Python-3.8.12.tgz \ 34 | && cd Python-3.8.12 \ 35 | && ./configure --enable-optimizations \ 36 | && make altinstall 37 | 38 | # Create a symbolic link for python3.8 39 | RUN ln -sf /usr/local/bin/python3.8 /usr/bin/python 40 | 41 | # Upgrade pip 42 | RUN python -m ensurepip --default-pip \ 43 | && python -m pip install --upgrade pip 44 | -------------------------------------------------------------------------------- /build/final_image/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use appropriate base image 2 | FROM quantumcrack/vulnmapai-base:0.1 as base 3 | 4 | # Copy scripts into the container 5 | WORKDIR /app 6 | COPY advising ./advising 7 | COPY exploit ./exploit 8 | COPY orchestration ./orchestration 9 | COPY recon ./recon 10 | COPY report ./report 11 | COPY resources ./resources 12 | COPY utils ./utils 13 | COPY webapp ./webapp 14 | COPY config.py . 15 | COPY constants.py . 16 | COPY main.py . 17 | COPY requirements.txt . 18 | 19 | # Install Python libraries 20 | RUN python -m pip install -r requirements.txt 21 | 22 | # Set the script as the entrypoint 23 | ENTRYPOINT ["python", "/app/main.py"] 24 | 25 | FROM base as test 26 | 27 | # copy the test files 28 | COPY tests ./tests 29 | 30 | # run pytest tests 31 | RUN pytest -svv 32 | 33 | # Go back to the base image 34 | FROM base as prod 35 | 36 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # configuration file for storing constants 2 | from pathlib import Path 3 | 4 | 5 | EXPLOITS_DIR = "/usr/share/metasploit-framework/modules/exploits" 6 | MSF_INDEX_FILE = '/app/resources/msf_index.json' 7 | COMMON_PORTS = "/app/resources/ports.json" 8 | REPORT_DIR = Path('./report') 9 | REPORT_TEMPLATE = '/app/webapp/templates/report_template.html' 10 | PROMPTS_FILE = "/app/advising/prompts.yaml" 11 | -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | class AdvisorConstants: 2 | API_KEY_ERROR = "OPENAI_API_KEY NOT SET AS AN ENVIRONMENT VARIABLE" 3 | ADVICE_ATTACK = "Advising attack based on the discovery result." 4 | DISCOVERIES = "Summarizing the discoveries." 5 | HTML_REPORT = "Preparing the HTML report." 6 | 7 | class ExploitManagerConstants: 8 | START_MSF = "Starting MSF..." 9 | MSF_SUCCESSFUL = "MSF successfully started." 10 | MSF_ERROR = "MSF failed to start!" 11 | MSF_TIMEDOUT = "Timed out waiting for MSF service to start!" 12 | 13 | class PenetrationTesterConstants: 14 | CLEAN_SCAN = "Cleaning results from previous scan..." 15 | START_SCAN = "Starting scan and report process." 16 | 17 | 18 | -------------------------------------------------------------------------------- /exploit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/exploit/__init__.py -------------------------------------------------------------------------------- /exploit/metasploit.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pty 3 | import re 4 | import socket 5 | import subprocess 6 | import time 7 | from typing import List, Dict 8 | 9 | from pymetasploit3.msfrpc import MsfRpcClient 10 | 11 | from config import MSF_INDEX_FILE 12 | from utils.logger import Logger 13 | from constants import ExploitManagerConstants 14 | 15 | 16 | class ExploitManager: 17 | """ 18 | Class for testing and executing exploits using Metasploit Framework. 19 | """ 20 | 21 | def __init__(self, host='127.0.0.1', password='msf', port=55552): 22 | self.logger = Logger(__name__).get_logger() 23 | self.client = self._start_msf(host, password, port) 24 | with open(MSF_INDEX_FILE, 'r') as f: 25 | self.msf_index = json.load(f) 26 | self.max_try_payloads = 3 27 | 28 | def _start_msf(self, host, password, port) -> MsfRpcClient: 29 | """ 30 | Starts the Metasploit Framework RPC server. 31 | 32 | Args: 33 | host (str): The hostname or IP address where the MSF RPC server will bind to. 34 | password (str): The password to connect to the MSF RPC server. 35 | port (int): The port number where the MSF RPC server will bind to. 36 | 37 | Returns: 38 | MsfRpcClient: An instance of the MsfRpcClient, which is connected to the started MSF RPC server. 39 | 40 | Raises: 41 | TimeoutError: If failed to start MSF and establish a connection within the timeout period. 42 | """ 43 | self.logger.info(ExploitManagerConstants.START_MSF) 44 | master, slave = pty.openpty() 45 | subprocess.Popen( 46 | ['msfconsole', '-qx', f'load msgrpc ServerHost={host} Pass={password}'], 47 | stdin=slave, stdout=slave, stderr=slave, universal_newlines=True 48 | ) 49 | if self._wait_msf_ready(host, port): 50 | self.logger.info(ExploitManagerConstants.MSF_SUCCESSFUL) 51 | return MsfRpcClient(password, port=port) 52 | else: 53 | self.logger.error(ExploitManagerConstants.MSF_ERROR) 54 | raise TimeoutError(ExploitManagerConstants.MSF_ERROR) 55 | 56 | def _wait_msf_ready(self, host: str, port: int, timeout=30) -> bool: 57 | """ 58 | Waits until the MSF RPC server is ready. 59 | 60 | Args: 61 | host (str): The host address of the MSF RPC server. 62 | port (int): Port number of MSF RPC server. 63 | timeout (int): Maximum time to wait for MSF RPC server to be ready. 64 | 65 | Returns: 66 | bool: True if the MSF RPC server is ready, False if timed out. 67 | """ 68 | start_time = time.time() 69 | while time.time() - start_time < timeout: 70 | try: 71 | with socket.create_connection((host, port), timeout=2): 72 | self.logger.info(f"MSF started in {time.time() - start_time} seconds") 73 | return True 74 | except (socket.timeout, ConnectionRefusedError): 75 | time.sleep(1) # Wait for a second before trying again 76 | self.logger.error(ExploitManagerConstants.MSF_TIMEDOUT) 77 | return False 78 | 79 | def find_exploit_modules(self, service: str, version: str) -> List[str]: 80 | """ 81 | Searches and returns exploit modules that match the provided service and version. 82 | 83 | Args: 84 | service (str): The name of the service. 85 | version (str): The version of the service. 86 | 87 | Returns: 88 | list: List of matching exploit module paths. 89 | """ 90 | self.logger.info(f"Searching for exploits of {service} with version {version}") 91 | service_pattern = re.compile(re.escape(service), re.IGNORECASE) 92 | version_pattern = re.compile(re.escape(str(version)), re.IGNORECASE) 93 | 94 | matching_modules = set() 95 | for module in self.msf_index: 96 | if service_pattern.search(module['name']) or service_pattern.search(module['description']): 97 | if version_pattern.search(module['name']) or version_pattern.search(module['description']): 98 | matching_modules.add(module['path']) 99 | 100 | return list(matching_modules) 101 | 102 | def apply_exploits(self, exploits: List, target: Dict) -> List[str]: 103 | """ 104 | Applies a list of exploit modules against a target. 105 | 106 | Args: 107 | exploits (list): List of exploit module names to be applied. 108 | target (dict): Target information, including host and port. 109 | 110 | Returns: 111 | list: List of successful exploit names. 112 | """ 113 | success_exploits = [] 114 | for exploit_name in exploits: 115 | self.logger.info(f"Trying exploit {exploit_name}") 116 | exploit = self.client.modules.use('exploit', exploit_name) 117 | if 'RHOSTS' in exploit.options: 118 | exploit['RHOSTS'] = target['host'] 119 | elif 'RHOST' in exploit.options: 120 | exploit['RHOST'] = target['host'] 121 | else: 122 | # for simplicity, try only exploits with defined host/port 123 | continue 124 | 125 | if 'RPORT' in exploit.options: 126 | exploit['RPORT'] = target['port'] 127 | 128 | for payload in self.client.call('module.compatible_payloads', [exploit_name])['payloads'][ 129 | :self.max_try_payloads]: 130 | self.logger.info(f"Trying payload {payload}") 131 | exploit.execute(payload=payload) 132 | if self._wait_for_success(): 133 | success_exploits.append(exploit_name) 134 | break 135 | return success_exploits 136 | 137 | def _wait_for_success(self, retries: int = 3, delay: int = 5) -> bool: 138 | """ 139 | Waits for an exploit to succeed. 140 | 141 | Args: 142 | retries (int): Number of times to check for successful exploitation. 143 | delay (int): Delay between each check in seconds. 144 | 145 | Returns: 146 | bool: True if successful, False otherwise. 147 | """ 148 | for _ in range(retries): 149 | time.sleep(delay) 150 | if self.client.sessions.list: 151 | return True 152 | return False 153 | 154 | def exploit_target(self, target: Dict, product: str, version: str) -> List[str]: 155 | """ 156 | Finds suitable exploit modules and applies them to a target. 157 | 158 | Args: 159 | target (dict): Target information, including host and port. 160 | product (str): Name of the product. 161 | version (str): Version of the product. 162 | 163 | Returns: 164 | list: List of response strings for successful exploits. 165 | """ 166 | exploits = self.find_exploit_modules(product, version) 167 | successful_exploits = self.apply_exploits(exploits, target) 168 | return self._prepare_responses(target, successful_exploits) 169 | 170 | @staticmethod 171 | def _prepare_responses(target: Dict, successful_exploits: List[str]) -> List[str]: 172 | """ 173 | Prepares response strings for successful exploits. 174 | 175 | Args: 176 | target (dict): Target information, including host and port. 177 | successful_exploits (list): List of successful exploit names. 178 | 179 | Returns: 180 | list: List of response strings for successful exploits. 181 | """ 182 | responses = [] 183 | for exploit in successful_exploits: 184 | msf_one_liner = ( 185 | f'msfconsole -q -x "use {exploit}; set RHOSTS {target["host"]}; ' 186 | f'set RPORT {target["port"]}; run"' 187 | ) 188 | response = f'Exploit {exploit} succeeded against {target["host"]}:{target["port"]}. ' \ 189 | f'MSF one-liner: {msf_one_liner}' 190 | responses.append(response) 191 | return responses 192 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from orchestration.pentest import PenetrationTester 4 | from webapp.app import app 5 | 6 | 7 | def main(targets, top_ports): 8 | pentester = PenetrationTester(targets, top_ports) 9 | pentester.scan_and_report() 10 | app.run(debug=False, host='0.0.0.0', port=1337) 11 | 12 | 13 | if __name__ == "__main__": 14 | parser = argparse.ArgumentParser(description='Run penetration tests on the given target subnets.') 15 | 16 | # Targets Argument 17 | parser.add_argument('targets', nargs='+', help='List of target subnets.') 18 | 19 | # Top Ports Argument 20 | parser.add_argument('-p', '--top_ports', type=int, default=300, help='Number of most common ports to scan.') 21 | 22 | # Parse the arguments 23 | args = parser.parse_args() 24 | 25 | # Validate top_ports 26 | if args.top_ports < 1: 27 | parser.error("top_ports must be a positive integer.") 28 | 29 | # Maximum limit of top_ports 30 | if args.top_ports > 8367: 31 | parser.error("top_ports must be less than or equal to 8367.") 32 | 33 | # Run the main function with parsed arguments 34 | main(args.targets, args.top_ports) 35 | -------------------------------------------------------------------------------- /orchestration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/orchestration/__init__.py -------------------------------------------------------------------------------- /orchestration/pentest.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from advising.llm import Advisor 4 | from config import REPORT_DIR 5 | from exploit.metasploit import ExploitManager 6 | from recon.async_scanner import NetworkScanner 7 | from utils.logger import Logger 8 | from constants import PenetrationTesterConstants 9 | 10 | 11 | class PenetrationTester: 12 | """ 13 | A class representing a Penetration Tester which combines the functionality of a NetworkScanner and an ExploitManager. 14 | 15 | Attributes: 16 | target_subnets (list): A list of subnets to be scanned. 17 | scanner (NetworkScanner): An instance of NetworkScanner to perform network scans on the target subnets. 18 | exploit_manager (ExploitManager): An instance of ExploitManager to find and manage exploits for discovered services. 19 | """ 20 | def __init__(self, target_subnets, top_ports): 21 | self.target_subnets = target_subnets 22 | self.scanner = NetworkScanner(target_subnets, top_ports) 23 | self.exploit_manager = ExploitManager() 24 | self.logger = Logger(__name__).get_logger() 25 | 26 | def scan_and_report(self): 27 | """ 28 | Performs scanning on target subnets, exploits the targets and prepares a report for each discovered service. 29 | """ 30 | 31 | self.logger.info(PenetrationTesterConstants.CLEAN_SCAN) 32 | 33 | # Ensure the directory exists before proceeding 34 | if REPORT_DIR.exists() and REPORT_DIR.is_dir(): 35 | # Iterate over all the HTML files in the directory 36 | for json_file in REPORT_DIR.glob('*.json'): 37 | # Delete the file 38 | json_file.unlink() 39 | 40 | self.logger.info(f"Old report files were removed from {str(REPORT_DIR.absolute())}") 41 | 42 | self.logger.info(PenetrationTesterConstants.START_SCAN) 43 | for result in self.scanner.execute_scan(): 44 | target = {'host': result['host'], 'port': result['port']} 45 | title = ' '.join([result['product'] or result['name'], result['version']]).strip() 46 | self.logger.info(f"Trying exploits for target: {result['host']}:{result['port']}, {title}") 47 | exploits = self.exploit_manager.exploit_target(target, result['product'] or result['name'], 48 | result['version']) 49 | 50 | advisor = Advisor( 51 | discovery_result=json.dumps(result), 52 | title=title, 53 | exploits=exploits 54 | ) 55 | 56 | report_file = REPORT_DIR / f"{title}.json" 57 | self.logger.info(f"Preparing report and saving to {report_file}") 58 | with report_file.open("w") as f: 59 | f.write(json.dumps(advisor.prepare_report())) 60 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Pull Request Description 2 | 3 | Please include a brief description of the changes this PR introduces and the issue it closes or the feature it adds. 4 | 5 | #### Fixes #(issue number) 6 | 7 | ### Type of Change 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | 12 | ### How Has This Been Tested? 13 | - [ ] New and existing unit tests pass locally with my changes 14 | 15 | 16 | ### Additional Context (if any) 17 | 18 | Provide any additional context or screenshots about the feature request or bug report here. 19 | -------------------------------------------------------------------------------- /rebuild_msf_index.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides functionality to build an index of exploits found in the specified directory. 3 | It will search for Ruby files, parse the exploit's name and description, and save the data to a JSON index file. 4 | """ 5 | 6 | import os 7 | import re 8 | import json 9 | 10 | from config import EXPLOITS_DIR, MSF_INDEX_FILE 11 | 12 | 13 | def build_index(exploits_dir=EXPLOITS_DIR) -> None: 14 | """ 15 | Parses and indexes the Name and Description from Ruby (.rb) exploit files located in the specified directory. 16 | Saves the indexed data as a JSON file (`MSF_INDEX_FILE`). 17 | The indexing will go through each Ruby file, extract the exploit's name and description, and add this information 18 | to the index, along with the relative path to the exploit. 19 | 20 | Args: 21 | exploits_dir (str): The path to the directory containing the Ruby exploit files. Defaults to `EXPLOITS_DIR` from config. 22 | Returns: 23 | None 24 | """ 25 | index = [] 26 | name_pattern = re.compile(r"'Name'\s*=>\s*'([^']*)'", re.IGNORECASE) 27 | description_pattern = re.compile(r"'Description'\s*=>\s*%q\{([^\}]*)\}", re.IGNORECASE | re.DOTALL) 28 | 29 | for foldername, _, filenames in os.walk(exploits_dir): 30 | for filename in filenames: 31 | if filename.endswith('.rb'): 32 | filepath = os.path.join(foldername, filename) 33 | try: 34 | with open(filepath, 'r', encoding='utf-8', errors='ignore') as file: 35 | content = file.read() 36 | name_match = name_pattern.search(content) 37 | description_match = description_pattern.search(content) 38 | if name_match and description_match: 39 | index.append({ 40 | 'name': name_match.group(1), 41 | 'description': description_match.group(1), 42 | 'path': os.path.relpath(filepath, exploits_dir).replace(os.path.sep, '/')[:-3] #remove extension 43 | }) 44 | except Exception as e: 45 | print(f"Error reading {filepath}: {e}") 46 | 47 | with open(MSF_INDEX_FILE, 'w') as f: 48 | json.dump(index, f) 49 | 50 | 51 | if __name__=='__main__': 52 | build_index() 53 | 54 | -------------------------------------------------------------------------------- /recon/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/recon/__init__.py -------------------------------------------------------------------------------- /recon/async_scanner.py: -------------------------------------------------------------------------------- 1 | import json 2 | import multiprocessing 3 | import time 4 | from typing import List, Dict 5 | from utils.logger import Logger 6 | import nmap 7 | 8 | from config import COMMON_PORTS 9 | 10 | 11 | class NetworkScanner: 12 | """ 13 | Сlass for performing network scans using nmap. 14 | """ 15 | 16 | def __init__(self, target_subnets: List[str], top_ports=300): 17 | self.target_subnets = target_subnets 18 | self.top_ports = top_ports 19 | self.ports = NetworkScanner._load_common_ports(COMMON_PORTS) 20 | self.results_queue = multiprocessing.Queue() 21 | self.logger = Logger(__name__).get_logger() 22 | 23 | @staticmethod 24 | def _load_common_ports(common_ports_file: str) -> List[str]: 25 | with open(common_ports_file) as f: 26 | return json.load(f) 27 | 28 | def extract_port_info(self, scanned_host: str, protocol_type: str, port_number: int, scan_data: Dict) -> Dict: 29 | """ 30 | Extracts information for a scanned port. 31 | 32 | Args: 33 | scanned_host (str): IP address of the scanned host. 34 | protocol_type (str): Protocol type, e.g., 'tcp'. 35 | port_number (int): Port number being scanned. 36 | scan_data (dict): Scan data returned from nmap scan. 37 | 38 | Returns: 39 | dict: A dictionary containing port information. 40 | """ 41 | self.logger.info(f"Extracting port info for {scanned_host}, {protocol_type}, {port_number}") 42 | protocol_data = scan_data[protocol_type][port_number] 43 | port_info = {'host': scanned_host, 'protocol': protocol_type, 'port': port_number} 44 | if protocol_data['state'] != 'open': 45 | return None 46 | for key in ['name', 'product', 'version', 'script']: 47 | if key in protocol_data: 48 | port_info[key] = protocol_data[key] 49 | 50 | return port_info 51 | 52 | def scan_range(self, subnet_address: str, start_port: int, end_port: int): 53 | """ 54 | Scans a range of ports on a subnet. 55 | 56 | Args: 57 | subnet_address (str): The address of the subnet to be scanned. 58 | start_port (int): The starting port number. 59 | end_port (int): The ending port number. 60 | """ 61 | self.logger.info(f"Scanning range {start_port}-{end_port} on subnet {subnet_address}") 62 | port_scanner = nmap.PortScanner() 63 | ports_enumeration = ",".join(self.ports[start_port:end_port + 1]) 64 | port_scanner.scan(hosts=subnet_address, arguments='-sV -T3 -sT --script=discovery -p' + ports_enumeration) 65 | 66 | for scanned_host in port_scanner.all_hosts(): 67 | for protocol_type in port_scanner[scanned_host].all_protocols(): 68 | if protocol_type != "tcp": 69 | continue 70 | 71 | for port_number in port_scanner[scanned_host][protocol_type].keys(): 72 | port_info = self.extract_port_info(scanned_host, protocol_type, port_number, 73 | port_scanner[scanned_host]) 74 | if port_info: 75 | self.results_queue.put(port_info) 76 | 77 | def _create_processes(self, subnet_address: str, cpu_count: int, port_range: int, last_port: int) -> List: 78 | """ 79 | Creates and starts a list of processes to scan a subnet. 80 | 81 | Args: 82 | subnet_address (str): The address of the subnet to be scanned. 83 | cpu_count (int): Number of CPU cores available. 84 | port_range (int): Range of ports to be scanned per process. 85 | last_port (int): The last port in the range to be scanned. 86 | 87 | Returns: 88 | list: List of created and started multiprocessing.Process objects. 89 | """ 90 | processes = [] 91 | for i in range(cpu_count): 92 | start_port = i * port_range 93 | end_port = (i + 1) * port_range if i < cpu_count - 1 else last_port 94 | p = multiprocessing.Process(target=self.scan_range, args=(subnet_address, start_port, end_port)) 95 | processes.append(p) 96 | p.start() 97 | return processes 98 | 99 | def _yield_results(self, processes: List) -> Dict: 100 | """ 101 | Yields results from the results_queue as they become available. 102 | 103 | Args: 104 | processes (list): List of multiprocessing.Process objects. 105 | 106 | Yields: 107 | dict: Port information dictionary from the results_queue. 108 | """ 109 | while processes: 110 | while not self.results_queue.empty(): 111 | yield self.results_queue.get() 112 | 113 | for p in processes[:]: 114 | if not p.is_alive(): 115 | processes.remove(p) 116 | while not self.results_queue.empty(): 117 | yield self.results_queue.get() 118 | 119 | time.sleep(1) 120 | 121 | def execute_scan(self) -> Dict: 122 | """ 123 | Executes the scanning, evently distributing the ports to be scanned among CPU cores. 124 | 125 | Yields: 126 | dict: Port information dictionary from the results_queue. 127 | """ 128 | cpu_count = multiprocessing.cpu_count() 129 | port_range = self.top_ports // cpu_count 130 | 131 | self.logger.info(f"Starting the scanning process with {cpu_count} processes for {self.top_ports} most common ports") 132 | 133 | for subnet_address in self.target_subnets: 134 | processes = self._create_processes(subnet_address, cpu_count, port_range, self.top_ports) 135 | yield from self._yield_results(processes) 136 | -------------------------------------------------------------------------------- /report/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/report/.gitkeep -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-nmap 2 | requests 3 | msgpack-python 4 | pymetasploit3 5 | openai 6 | httpx 7 | flask 8 | pytest 9 | PyYAML -------------------------------------------------------------------------------- /resources/ports.json: -------------------------------------------------------------------------------- 1 | ["80", "23", "443", "21", "22", "25", "3389", "110", "445", "139", "143", "53", "135", "3306", "8080", "1723", "111", "995", "993", "5900", "1025", "587", "8888", "199", "1720", "465", "548", "113", "81", "6001", "10000", "514", "5060", "179", "1026", "2000", "8443", "8000", "32768", "554", "26", "1433", "49152", "2001", "515", "8008", "49154", "1027", "5666", "646", "5000", "5631", "631", "49153", "8081", "2049", "88", "79", "5800", "106", "2121", "1110", "49155", "6000", "513", "990", "5357", "427", "49156", "543", "544", "5101", "144", "7", "389", "8009", "3128", "444", "9999", "5009", "7070", "5190", "3000", "5432", "1900", "3986", "13", "1029", "9", "5051", "6646", "49157", "1028", "873", "1755", "2717", "4899", "9100", "119", "37", "1000", "3001", "5001", "82", "10010", "1030", "9090", "2107", "1024", "2103", "6004", "1801", "5050", "19", "8031", "1041", "255", "1048", "1049", "1053", "1054", "1056", "1064", "1065", "2967", "3703", "17", "808", "3689", "1031", "1044", "1071", "5901", "100", "9102", "1039", "2869", "4001", "5120", "8010", "9000", "2105", "636", "1038", "2601", "1", "7000", "1066", "1069", "625", "311", "280", "254", "4000", "1761", "5003", "2002", "1998", "2005", "1032", "1050", "6112", "3690", "1521", "2161", "1080", "6002", "2401", "902", "4045", "787", "7937", "1058", "2383", "32771", "1033", "1040", "1059", "50000", "5555", "10001", "1494", "3", "593", "2301", "3268", "7938", "1022", "1234", "1035", "1036", "1037", "1074", "8002", "9001", "464", "497", "1935", "2003", "6666", "6543", "24", "1352", "3269", "1111", "407", "500", "20", "2006", "1034", "1218", "3260", "15000", "4444", "264", "33", "2004", "1042", "42510", "999", "3052", "1023", "222", "1068", "888", "7100", "563", "1717", "992", "2008", "32770", "7001", "32772", "2007", "8082", "5550", "512", "1043", "2009", "5801", "1700", "2701", "7019", "50001", "4662", "2065", "42", "2010", "161", "2602", "3333", "9535", "5100", "2604", "4002", "5002", "1047", "1051", "1052", "1055", "1060", "1062", "1311", "2702", "3283", "4443", "5225", "5226", "6059", "6789", "8089", "8192", "8193", "8194", "8651", "8652", "8701", "9415", "9593", "9594", "9595", "16992", "16993", "20828", "23502", "32769", "33354", "35500", "52869", "55555", "55600", "64623", "64680", "65000", "65389", "1067", "13782", "366", "5902", "9050", "85", "1002", "5500", "1863", "1864", "5431", "8085", "10243", "45100", "49999", "51103", "49", "90", "6667", "1503", "6881", "27000", "340", "1500", "8021", "2222", "5566", "8088", "8899", "9071", "1501", "5102", "6005", "9101", "9876", "32773", "32774", "163", "5679", "146", "648", "1666", "901", "83", "3476", "5004", "5214", "8001", "8083", "8084", "9207", "14238", "30", "912", "12345", "2030", "2605", "6", "541", "4", "1248", "3005", "8007", "306", "880", "2500", "1086", "1088", "1097", "2525", "4242", "8291", "9009", "52822", "900", "6101", "2809", "7200", "211", "800", "987", "1083", "12000", "32775", "705", "711", "20005", "6969", "13783", "1045", "1046", "1057", "1061", "1063", "1070", "1072", "1073", "1075", "1077", "1078", "1079", "1081", "1082", "1085", "1093", "1094", "1096", "1098", "1099", "1100", "1104", "1106", "1107", "1108", "1148", "1169", "1272", "1310", "1687", "1718", "1783", "1840", "1947", "2100", "2119", "2135", "2144", "2160", "2190", "2260", "2381", "2399", "2492", "2607", "2718", "2811", "2875", "3017", "3031", "3071", "3211", "3300", "3301", "3323", "3325", "3351", "3367", "3404", "3551", "3580", "3659", "3766", "3784", "3801", "3827", "3998", "4003", "4126", "4129", "4449", "5030", "5222", "5269", "5414", "5633", "5718", "5810", "5825", "5877", "5910", "5911", "5925", "5959", "5960", "5961", "5962", "5987", "5988", "5989", "6123", "6129", "6156", "6389", "6580", "6788", "6901", "7106", "7625", "7627", "7741", "7777", "7778", "7911", "8086", "8087", "8181", "8222", "8333", "8400", "8402", "8600", "8649", "8873", "8994", "9002", "9010", "9011", "9080", "9220", "9290", "9485", "9500", "9502", "9503", "9618", "9900", "9968", "10002", "10012", "10024", "10025", "10566", "10616", "10617", "10621", "10626", "10628", "10629", "11110", "11967", "13456", "14000", "14442", "15002", "15003", "15660", "16001", "16016", "16018", "17988", "19101", "19801", "19842", "20000", "20031", "20221", "20222", "21571", "22939", "24800", "25734", "27715", "28201", "30000", "30718", "31038", "32781", "32782", "33899", "34571", "34572", "34573", "40193", "48080", "49158", "49159", "49160", "50003", "50006", "50800", "57294", "58080", "60020", "63331", "65129", "89", "691", "212", "1001", "1999", "2020", "32776", "2998", "6003", "7002", "50002", "32", "898", "2033", "3372", "5510", "99", "425", "749", "5903", "43", "458", "5405", "6106", "6502", "7007", "13722", "1087", "1089", "1124", "1152", "1183", "1186", "1247", "1296", "1334", "1580", "1782", "2126", "2179", "2191", "2251", "2522", "3011", "3030", "3077", "3261", "3369", "3370", "3371", "3493", "3546", "3737", "3828", "3851", "3871", "3880", "3918", "3995", "4006", "4111", "4446", "5054", "5200", "5280", "5298", "5822", "5859", "5904", "5915", "5922", "5963", "7103", "7402", "7435", "7443", "7512", "8011", "8090", "8100", "8180", "8254", "8500", "8654", "9091", "9110", "9666", "9877", "9943", "9944", "9998", "10004", "10778", "15742", "16012", "18988", "19283", "19315", "19780", "24444", "27352", "27353", "27355", "32784", "49163", "49165", "49175", "50389", "50636", "51493", "55055", "56738", "61532", "61900", "62078", "1021", "9040", "32777", "32779", "616", "666", "700", "2021", "32778", "84", "545", "1112", "1524", "2040", "4321", "5802", "38292", "49400", "1084", "1600", "2048", "2111", "3006", "32780", "2638", "6547", "6699", "9111", "16080", "555", "667", "720", "801", "1443", "1533", "2034", "2106", "5560", "6007", "1090", "1091", "1114", "1117", "1119", "1122", "1131", "1138", "1151", "1175", "1199", "1201", "1271", "1862", "2323", "2393", "2394", "2608", "2725", "2909", "3003", "3168", "3221", "3322", "3324", "3390", "3517", "3527", "3800", "3809", "3814", "3826", "3869", "3878", "3889", "3905", "3914", "3920", "3945", "3971", "4004", "4005", "4279", "4445", "4550", "4567", "4848", "4900", "5033", "5061", "5080", "5087", "5221", "5440", "5544", "5678", "5730", "5811", "5815", "5850", "5862", "5906", "5907", "5950", "5952", "6025", "6100", "6510", "6565", "6566", "6567", "6689", "6692", "6779", "6792", "6839", "7025", "7496", "7676", "7800", "7920", "7921", "7999", "8022", "8042", "8045", "8093", "8099", "8200", "8290", "8292", "8300", "8383", "8800", "9003", "9081", "9099", "9200", "9418", "9575", "9878", "9898", "9917", "10003", "10009", "10180", "10215", "11111", "12174", "12265", "14441", "15004", "16000", "16113", "17877", "18040", "18101", "19350", "25735", "26214", "27356", "30951", "32783", "32785", "40911", "41511", "44176", "44501", "49161", "49167", "49176", "50300", "50500", "52673", "52848", "54045", "54328", "55056", "56737", "57797", "60443", "70", "417", "617", "714", "722", "777", "981", "1009", "2022", "4224", "4998", "6346", "301", "524", "668", "765", "1076", "2041", "5999", "10082", "259", "416", "1007", "1417", "1434", "1984", "2038", "2068", "4343", "6009", "7004", "44443", "109", "687", "726", "911", "1010", "1461", "2035", "2046", "4125", "6006", "7201", "9103", "125", "481", "683", "903", "1011", "1455", "2013", "2043", "2047", "6668", "6669", "256", "406", "783", "843", "2042", "2045", "5998", "9929", "31337", "44442", "1092", "1095", "1102", "1105", "1113", "1121", "1123", "1126", "1130", "1132", "1137", "1141", "1145", "1147", "1149", "1154", "1163", "1164", "1165", "1166", "1174", "1185", "1187", "1192", "1198", "1213", "1216", "1217", "1233", "1236", "1244", "1259", "1277", "1287", "1300", "1301", "1309", "1322", "1328", "1556", "1583", "1594", "1641", "1658", "1688", "1719", "1721", "1805", "1812", "1839", "1875", "1914", "1971", "1972", "1974", "2099", "2170", "2196", "2200", "2288", "2366", "2382", "2557", "2710", "2800", "2910", "2920", "2968", "3007", "3013", "3050", "3119", "3162", "3304", "3307", "3376", "3400", "3410", "3514", "3684", "3697", "3700", "3731", "3792", "3808", "3820", "3824", "3846", "3848", "3849", "3852", "3853", "3859", "3863", "3870", "3872", "3888", "3907", "3916", "3929", "3931", "3941", "3944", "3957", "3963", "3968", "3969", "3972", "3981", "3990", "3993", "3994", "4009", "4040", "4080", "4096", "4143", "4147", "4164", "4200", "4252", "4430", "4555", "4600", "4658", "4875", "4949", "5040", "5063", "5074", "5081", "5151", "5212", "5223", "5242", "5279", "5339", "5353", "5501", "5807", "5812", "5818", "5823", "5868", "5869", "5899", "5905", "5909", "5914", "5918", "5938", "5940", "5968", "5981", "6051", "6060", "6068", "6203", "6247", "6481", "6500", "6504", "6520", "6550", "6600", "6711", "6732", "6896", "7024", "7050", "7051", "7080", "7123", "7241", "7272", "7278", "7281", "7438", "7725", "7744", "7749", "7770", "7878", "7900", "7913", "7929", "8015", "8016", "8019", "8050", "8095", "8097", "8098", "8189", "8293", "8294", "8385", "8481", "8540", "8648", "8675", "8676", "8686", "8765", "8766", "8877", "8889", "8987", "8996", "9098", "9191", "9197", "9198", "9409", "9443", "9444", "9501", "9600", "9621", "9643", "9673", "9815", "9914", "9941", "9988", "10008", "10011", "10022", "10023", "10034", "10058", "10160", "10873", "12006", "12021", "12059", "12215", "12262", "12380", "12452", "13724", "15001", "15402", "16705", "16800", "16851", "17595", "18018", "18264", "19900", "20002", "21792", "22222", "23052", "23796", "26000", "26470", "27357", "28211", "29672", "29831", "30005", "30704", "31727", "32791", "32792", "32803", "32816", "32822", "32835", "33453", "33554", "35513", "37839", "38185", "38188", "39136", "39376", "39659", "40000", "40811", "41064", "41523", "44709", "46200", "46996", "47544", "49164", "49168", "49171", "49186", "49195", "49236", "49401", "50050", "51191", "51413", "52660", "52710", "52735", "52847", "52849", "52850", "52851", "52853", "53211", "53313", "53314", "53535", "55020", "55576", "57665", "58001", "58002", "58630", "58632", "58838", "59110", "59200", "59201", "59202", "60123", "60146", "60642", "61613", "65310", "502", "623", "639", "701", "710", "725", "780", "803", "913", "930", "1103", "1109", "1220", "1347", "2012", "2232", "2241", "2501", "4559", "5680", "6222", "10005", "27", "86", "102", "123", "250", "419", "441", "442", "447", "475", "540", "856", "874", "931", "953", "1158", "1222", "1270", "2044", "5010", "8118", "9992", "18000", "55", "57", "87", "210", "223", "251", "556", "709", "713", "829", "980", "1008", "1013", "1212", "1550", "2433", "2628", "3025", "3299", "5520", "5803", "6008", "6103", "7003", "9152", "10083", "77", "127", "157", "220", "333", "523", "557", "610", "657", "674", "684", "732", "748", "792", "825", "840", "904", "943", "1006", "1020", "1241", "1350", "1351", "1516", "1526", "1547", "2011", "2067", "4333", "7010", "225", "257", "600", "602", "655", "659", "660", "729", "730", "731", "757", "778", "782", "786", "790", "795", "822", "823", "839", "846", "862", "905", "969", "971", "996", "998", "1012", "1014", "1015", "1353", "1357", "1522", "2025", "2201", "2903", "3399", "3456", "3632", "6050", "6662", "59", "98", "252", "388", "411", "606", "621", "641", "669", "690", "715", "728", "740", "754", "758", "802", "805", "806", "859", "864", "878", "918", "921", "922", "924", "928", "1004", "1005", "1127", "1337", "1413", "1414", "1525", "2112", "2600", "3999", "5011", "6017", "6670", "12346", "38037", "44334", "1101", "1116", "1118", "1125", "1128", "1134", "1135", "1136", "1143", "1144", "1150", "1153", "1156", "1157", "1159", "1162", "1167", "1168", "1173", "1176", "1179", "1180", "1182", "1184", "1188", "1190", "1191", "1194", "1195", "1196", "1200", "1204", "1207", "1208", "1209", "1210", "1211", "1215", "1221", "1223", "1228", "1229", "1239", "1240", "1243", "1249", "1250", "1251", "1261", "1262", "1264", "1268", "1276", "1279", "1282", "1290", "1291", "1297", "1299", "1302", "1303", "1305", "1306", "1307", "1308", "1314", "1315", "1316", "1317", "1318", "1319", "1321", "1324", "1327", "1330", "1331", "1336", "1339", "1340", "1558", "1559", "1560", "1565", "1566", "1569", "1584", "1592", "1598", "1605", "1607", "1615", "1620", "1622", "1632", "1635", "1638", "1645", "1677", "1683", "1691", "1694", "1699", "1701", "1703", "1707", "1708", "1709", "1711", "1712", "1713", "1715", "1722", "1730", "1735", "1736", "1745", "1750", "1752", "1753", "1791", "1792", "1799", "1800", "1806", "1807", "1808", "1811", "1823", "1825", "1835", "1858", "1861", "1871", "1883", "1901", "1911", "1912", "1918", "1924", "1927", "1954", "1958", "1973", "1975", "1976", "1981", "2031", "2062", "2069", "2070", "2080", "2081", "2082", "2083", "2086", "2087", "2095", "2096", "2101", "2104", "2115", "2124", "2134", "2142", "2148", "2150", "2187", "2197", "2203", "2224", "2250", "2253", "2261", "2262", "2265", "2269", "2270", "2271", "2280", "2291", "2292", "2300", "2302", "2304", "2312", "2313", "2325", "2326", "2330", "2335", "2340", "2371", "2372", "2375", "2376", "2391", "2418", "2425", "2435", "2436", "2438", "2439", "2449", "2456", "2463", "2472", "2505", "2531", "2532", "2550", "2551", "2558", "2567", "2580", "2583", "2584", "2598", "2606", "2622", "2623", "2631", "2644", "2691", "2700", "2706", "2711", "2712", "2723", "2728", "2734", "2804", "2806", "2812", "2847", "2850", "2882", "2888", "2889", "2898", "2901", "2902", "2908", "2930", "2957", "2958", "2973", "2984", "2987", "2988", "2991", "2997", "3002", "3014", "3023", "3057", "3062", "3063", "3080", "3089", "3102", "3103", "3118", "3121", "3146", "3167", "3190", "3200", "3210", "3220", "3240", "3263", "3280", "3281", "3291", "3310", "3311", "3319", "3334", "3362", "3363", "3365", "3368", "3374", "3388", "3396", "3414", "3415", "3419", "3425", "3430", "3439", "3443", "3479", "3483", "3485", "3486", "3497", "3503", "3505", "3506", "3511", "3513", "3515", "3519", "3520", "3526", "3530", "3532", "3577", "3586", "3599", "3600", "3602", "3603", "3621", "3622", "3636", "3637", "3652", "3653", "3656", "3658", "3663", "3669", "3670", "3672", "3680", "3681", "3683", "3712", "3728", "3742", "3749", "3765", "3787", "3788", "3790", "3793", "3795", "3796", "3798", "3799", "3803", "3806", "3810", "3811", "3812", "3813", "3817", "3823", "3825", "3830", "3831", "3837", "3839", "3842", "3847", "3850", "3856", "3860", "3868", "3876", "3879", "3882", "3890", "3897", "3899", "3901", "3902", "3904", "3906", "3908", "3909", "3911", "3913", "3915", "3919", "3922", "3923", "3928", "3930", "3935", "3936", "3937", "3940", "3943", "3946", "3948", "3949", "3952", "3956", "3961", "3962", "3964", "3967", "3975", "3979", "3980", "3982", "3983", "3989", "3991", "3992", "3996", "3997", "4007", "4010", "4016", "4020", "4022", "4024", "4025", "4029", "4035", "4036", "4039", "4056", "4058", "4065", "4087", "4090", "4100", "4101", "4112", "4113", "4118", "4119", "4120", "4121", "4135", "4141", "4158", "4161", "4174", "4190", "4192", "4206", "4220", "4234", "4243", "4262", "4294", "4297", "4298", "4300", "4302", "4325", "4328", "4342", "4355", "4356", "4357", "4358", "4369", "4374", "4375", "4376", "4384", "4388", "4401", "4407", "4414", "4415", "4418", "4433", "4442", "4447", "4454", "4464", "4471", "4476", "4516", "4517", "4530", "4534", "4545", "4558", "4570", "4599", "4601", "4602", "4606", "4609", "4644", "4649", "4665", "4687", "4689", "4700", "4712", "4713", "4745", "4760", "4767", "4770", "4771", "4778", "4793", "4800", "4819", "4859", "4860", "4876", "4877", "4881", "4903", "4912", "4931", "4999", "5005", "5012", "5013", "5014", "5015", "5016", "5017", "5020", "5021", "5023", "5052", "5053", "5055", "5066", "5070", "5088", "5090", "5095", "5096", "5098", "5111", "5114", "5121", "5122", "5125", "5133", "5137", "5147", "5152", "5201", "5202", "5219", "5233", "5234", "5235", "5250", "5252", "5259", "5261", "5291", "5347", "5370", "5377", "5423", "5433", "5441", "5442", "5444", "5457", "5458", "5473", "5475", "5502", "5552", "5553", "5554", "5557", "5580", "5581", "5611", "5612", "5620", "5621", "5622", "5665", "5667", "5672", "5711", "5721", "5722", "5723", "5732", "5734", "5737", "5804", "5806", "5808", "5814", "5817", "5820", "5821", "5824", "5826", "5827", "5831", "5834", "5836", "5838", "5839", "5840", "5845", "5848", "5849", "5852", "5853", "5854", "5858", "5860", "5871", "5874", "5875", "5878", "5881", "5887", "5888", "5908", "5912", "5917", "5920", "5921", "5923", "5924", "5926", "5927", "5931", "5934", "5936", "5939", "5945", "5948", "5949", "5953", "5954", "5958", "5966", "5969", "5971", "5974", "5975", "5985", "5986", "6010", "6015", "6021", "6030", "6052", "6055", "6062", "6063", "6065", "6067", "6077", "6085", "6090", "6091", "6113", "6115", "6120", "6126", "6161", "6250", "6251", "6259", "6273", "6274", "6309", "6310", "6323", "6324", "6349", "6350", "6379", "6412", "6503", "6535", "6579", "6606", "6619", "6628", "6644", "6647", "6650", "6709", "6710", "6725", "6734", "6780", "6877", "6888", "6897", "6920", "6922", "6942", "6956", "6972", "6973", "7033", "7043", "7067", "7068", "7071", "7072", "7092", "7099", "7101", "7102", "7104", "7119", "7121", "7184", "7218", "7231", "7300", "7320", "7325", "7345", "7400", "7451", "7456", "7500", "7501", "7553", "7555", "7600", "7628", "7637", "7654", "7685", "7688", "7771", "7772", "7780", "7788", "7789", "7813", "7830", "7852", "7853", "7854", "7895", "7975", "7998", "8003", "8005", "8006", "8014", "8018", "8023", "8025", "8029", "8037", "8052", "8060", "8064", "8092", "8110", "8116", "8133", "8144", "8201", "8202", "8232", "8245", "8248", "8255", "8268", "8273", "8282", "8295", "8308", "8339", "8401", "8403", "8409", "8445", "8451", "8452", "8453", "8454", "8455", "8470", "8471", "8472", "8474", "8477", "8479", "8484", "8515", "8530", "8531", "8539", "8562", "8601", "8621", "8640", "8644", "8655", "8658", "8673", "8680", "8736", "8752", "8756", "8772", "8790", "8798", "8801", "8843", "8865", "8878", "8879", "8880", "8882", "8883", "8887", "8898", "8900", "8925", "8954", "8980", "8999", "9004", "9005", "9013", "9020", "9021", "9022", "9037", "9044", "9061", "9065", "9084", "9125", "9128", "9130", "9131", "9133", "9160", "9161", "9170", "9183", "9202", "9210", "9211", "9287", "9300", "9333", "9343", "9351", "9364", "9400", "9454", "9464", "9478", "9493", "9513", "9527", "9583", "9592", "9613", "9616", "9619", "9620", "9628", "9648", "9654", "9661", "9665", "9667", "9674", "9679", "9680", "9683", "9694", "9700", "9745", "9777", "9812", "9814", "9823", "9825", "9826", "9830", "9844", "9875", "9901", "9908", "9909", "9910", "9911", "9912", "9915", "9919", "9950", "9971", "9975", "9979", "9990", "9995", "10006", "10007", "10018", "10019", "10020", "10035", "10042", "10045", "10064", "10093", "10101", "10115", "10238", "10245", "10246", "10255", "10280", "10338", "10347", "10357", "10387", "10414", "10443", "10494", "10500", "10509", "10529", "10535", "10550", "10551", "10552", "10553", "10554", "10555", "10556", "10565", "10567", "10601", "10602", "10699", "10754", "10842", "10852", "10878", "10900", "11000", "11001", "11003", "11007", "11019", "11026", "11031", "11032", "11033", "11089", "11100", "11180", "11200", "11224", "11250", "11288", "11296", "11401", "11552", "11697", "11735", "11813", "11862", "11863", "11940", "12001", "12002", "12005", "12009", "12019", "12031", "12034", "12077", "12080", "12090", "12096", "12097", "12121", "12132", "12137", "12146", "12156", "12171", "12192", "12225", "12240", "12243", "12251", "12271", "12275", "12296", "12340", "12414", "12699", "12702", "12766", "12865", "12891", "12892", "12955", "12962", "13017", "13093", "13130", "13132", "13140", "13142", "13149", "13167", "13188", "13192", "13193", "13194", "13229", "13250", "13261", "13264", "13265", "13306", "13318", "13340", "13359", "13502", "13580", "13695", "13723", "13730", "13766", "13784", "13846", "13899", "14001", "14147", "14218", "14237", "14254", "14418", "14443", "14444", "14534", "14545", "14693", "14733", "14827", "14891", "14916", "15005", "15050", "15145", "15190", "15191", "15275", "15317", "15344", "15448", "15550", "15631", "15645", "15646", "15670", "15677", "15722", "15730", "15758", "15915", "16048", "16161", "16270", "16273", "16283", "16286", "16297", "16349", "16372", "16464", "16723", "16724", "16725", "16797", "16845", "16900", "16901", "17016", "17017", "17070", "17089", "17129", "17251", "17255", "17409", "17413", "17500", "17700", "17701", "17702", "17715", "17801", "17802", "17860", "17867", "17969", "17985", "17997", "18012", "18015", "18080", "18148", "18231", "18333", "18336", "18337", "18380", "18439", "18505", "18517", "18569", "18669", "18874", "18887", "18910", "18962", "19010", "19130", "19200", "19201", "19333", "19353", "19403", "19464", "19501", "19612", "19634", "19715", "19852", "19995", "19996", "20001", "20011", "20017", "20021", "20032", "20039", "20052", "20076", "20080", "20085", "20089", "20102", "20106", "20111", "20118", "20125", "20127", "20147", "20179", "20180", "20223", "20224", "20225", "20226", "20227", "20228", "20280", "20473", "20734", "20883", "20934", "20940", "20990", "21011", "21078", "21201", "21473", "21631", "21634", "21728", "21891", "21915", "22022", "22063", "22100", "22125", "22128", "22177", "22200", "22223", "22290", "22341", "22350", "22555", "22563", "22711", "22719", "22727", "22769", "22882", "22959", "22969", "23017", "23040", "23219", "23228", "23270", "23296", "23342", "23382", "23430", "23451", "23723", "23887", "23953", "24218", "24392", "24416", "24552", "24554", "24616", "24999", "25000", "25001", "25174", "25260", "25262", "25288", "25327", "25445", "25473", "25486", "25565", "25703", "25717", "25847", "26001", "26007", "26340", "26417", "26669", "26972", "27015", "27016", "27017", "27018", "27019", "27055", "27074", "27075", "27087", "27204", "27316", "27350", "27351", "27372", "27521", "27537", "27770", "28015", "28017", "28114", "28142", "28374", "28567", "28717", "28850", "28851", "28924", "28967", "29015", "29045", "29152", "29243", "29507", "29810", "30001", "30087", "30195", "30299", "30519", "30599", "30644", "30659", "30705", "30896", "31033", "31058", "31072", "31339", "31386", "31438", "31522", "31657", "31728", "32006", "32022", "32031", "32088", "32102", "32200", "32219", "32260", "32261", "32764", "32765", "32767", "32788", "32789", "32790", "32797", "32798", "32799", "32807", "32814", "32815", "32820", "32837", "32842", "32858", "32868", "32869", "32871", "32888", "32897", "32898", "32904", "32905", "32908", "32910", "32911", "32932", "32944", "32960", "32961", "32976", "33000", "33011", "33017", "33070", "33087", "33124", "33175", "33192", "33200", "33203", "33277", "33327", "33335", "33337", "33367", "33395", "33444", "33522", "33523", "33550", "33604", "33605", "33841", "33879", "33882", "33889", "33895", "34021", "34036", "34096", "34189", "34317", "34341", "34381", "34401", "34507", "34510", "34683", "34728", "34765", "34783", "34833", "34875", "35033", "35050", "35116", "35131", "35217", "35272", "35349", "35392", "35393", "35401", "35506", "35553", "35593", "35731", "35879", "35900", "35901", "35906", "35929", "35986", "36046", "36104", "36105", "36256", "36275", "36368", "36436", "36508", "36530", "36552", "36659", "36677", "36694", "36710", "36748", "36823", "36824", "36914", "36950", "36962", "36983", "37121", "37151", "37174", "37185", "37218", "37393", "37522", "37607", "37614", "37647", "37674", "37777", "37789", "37855", "38029", "38194", "38205", "38224", "38270", "38313", "38331", "38358", "38446", "38481", "38546", "38561", "38570", "38761", "38764", "38780", "38805", "38936", "39067", "39117", "39265", "39293", "39380", "39433", "39482", "39489", "39630", "39732", "39763", "39774", "39795", "39869", "39883", "39895", "39917", "40001", "40002", "40003", "40005", "40011", "40306", "40393", "40400", "40457", "40489", "40513", "40614", "40628", "40712", "40732", "40754", "40812", "40834", "40951", "41123", "41142", "41250", "41281", "41318", "41342", "41345", "41348", "41398", "41442", "41551", "41632", "41773", "41794", "41795", "41808", "42001", "42035", "42127", "42158", "42251", "42276", "42322", "42449", "42452", "42559", "42560", "42575", "42590", "42632", "42675", "42679", "42685", "42735", "42906", "42990", "43000", "43002", "43018", "43027", "43103", "43139", "43143", "43212", "43231", "43242", "43425", "43654", "43690", "43734", "43823", "43868", "44004", "44101", "44119", "44200", "44380", "44410", "44431", "44479", "44505", "44541", "44616", "44628", "44704", "44711", "44965", "44981", "45038", "45050", "45136", "45164", "45220", "45226", "45413", "45438", "45463", "45602", "45624", "45697", "45777", "45864", "45960", "46034", "46069", "46115", "46171", "46182", "46310", "46372", "46418", "46436", "46593", "46813", "46992", "47012", "47029", "47119", "47197", "47267", "47348", "47372", "47448", "47567", "47581", "47595", "47624", "47634", "47700", "47777", "47806", "47850", "47858", "47860", "47966", "47969", "48009", "48067", "48083", "48127", "48153", "48167", "48356", "48434", "48619", "48631", "48648", "48682", "48783", "48813", "48925", "48966", "48967", "48973", "49002", "49048", "49132", "49166", "49169", "49170", "49172", "49173", "49179", "49189", "49190", "49191", "49196", "49197", "49201", "49202", "49203", "49204", "49211", "49213", "49216", "49228", "49232", "49235", "49241", "49275", "49302", "49352", "49372", "49398", "49452", "49498", "49500", "49519", "49520", "49521", "49522", "49597", "49603", "49678", "49751", "49762", "49765", "49803", "49927", "50016", "50019", "50040", "50101", "50189", "50198", "50202", "50205", "50224", "50246", "50258", "50277", "50356", "50513", "50529", "50545", "50576", "50577", "50585", "50692", "50733", "50787", "50809", "50815", "50831", "50833", "50834", "50835", "50836", "50849", "50854", "50887", "50903", "50945", "50997", "51011", "51020", "51037", "51067", "51118", "51139", "51233", "51234", "51235", "51240", "51300", "51343", "51351", "51366", "51423", "51460", "51484", "51485", "51488", "51515", "51582", "51658", "51771", "51772", "51800", "51809", "51906", "51909", "51961", "51965", "52000", "52001", "52002", "52003", "52025", "52046", "52071", "52173", "52225", "52226", "52230", "52237", "52262", "52391", "52477", "52506", "52573", "52665", "52675", "52893", "52948", "53085", "53178", "53189", "53212", "53240", "53319", "53361", "53370", "53460", "53469", "53491", "53633", "53639", "53656", "53690", "53742", "53782", "53827", "53852", "53910", "53958", "54075", "54101", "54127", "54235", "54263", "54276", "54321", "54323", "54514", "54551", "54605", "54658", "54688", "54722", "54741", "54873", "54907", "54987", "54991", "55000", "55183", "55187", "55227", "55312", "55350", "55382", "55400", "55426", "55479", "55527", "55556", "55568", "55569", "55579", "55635", "55652", "55684", "55721", "55758", "55773", "55781", "55901", "55907", "55910", "55948", "56016", "56055", "56259", "56293", "56507", "56535", "56591", "56668", "56681", "56723", "56725", "56810", "56822", "56827", "56973", "56975", "57020", "57103", "57123", "57325", "57335", "57347", "57350", "57352", "57387", "57398", "57479", "57576", "57678", "57681", "57702", "57730", "57733", "57891", "57896", "57923", "57928", "57988", "57999", "58072", "58107", "58109", "58164", "58252", "58305", "58310", "58374", "58430", "58446", "58456", "58468", "58498", "58562", "58570", "58610", "58622", "58634", "58699", "58721", "58908", "58970", "58991", "59087", "59107", "59122", "59149", "59160", "59191", "59239", "59340", "59499", "59504", "59509", "59510", "59525", "59565", "59684", "59778", "59810", "59829", "59841", "59987", "60000", "60002", "60003", "60055", "60086", "60111", "60177", "60227", "60243", "60279", "60377", "60401", "60403", "60485", "60492", "60504", "60544", "60579", "60612", "60621", "60628", "60713", "60728", "60743", "60753", "60782", "60783", "60789", "60794", "60989", "61159", "61169", "61170", "61402", "61473", "61516", "61616", "61617", "61669", "61722", "61734", "61827", "61851", "61942", "62006", "62042", "62080", "62188", "62312", "62519", "62570", "62674", "62866", "63105", "63156", "63423", "63675", "63803", "64080", "64127", "64320", "64438", "64507", "64551", "64726", "64727", "64890", "65048", "65311", "65488", "65514", "11", "47", "66", "400", "446", "509", "578", "591", "635", "642", "678", "706", "743", "762", "769", "779", "789", "809", "811", "815", "817", "830", "834", "844", "858", "863", "914", "925", "926", "935", "942", "961", "965", "967", "973", "979", "983", "1019", "1441", "1444", "1492", "1495", "1507", "1989", "3985", "4008", "5308", "5632", "5977", "6105", "7005", "22273", "27001", "31416", "32786", "32787", "54320", "10", "12", "52", "68", "75", "76", "91", "101", "104", "122", "158", "219", "237", "440", "449", "501", "510", "525", "538", "577", "603", "615", "620", "629", "634", "649", "653", "664", "665", "682", "695", "707", "716", "727", "733", "750", "771", "798", "804", "807", "810", "814", "824", "828", "833", "835", "847", "860", "861", "889", "894", "897", "899", "916", "923", "946", "949", "952", "958", "974", "984", "985", "989", "1139", "1358", "1359", "1364", "1366", "1420", "1652", "1663", "1680", "3045", "3049", "3064", "3398", "4199", "6110", "9991", "27010", "16", "28", "45", "50", "92", "112", "221", "230", "249", "268", "300", "334", "343", "353", "448", "450", "456", "491", "492", "507", "605", "609", "627", "630", "632", "638", "640", "647", "651", "658", "661", "663", "673", "696", "702", "719", "724", "735", "736", "741", "745", "760", "761", "791", "813", "816", "819", "820", "826", "831", "836", "841", "850", "851", "855", "866", "870", "872", "875", "881", "883", "906", "919", "927", "929", "936", "938", "941", "945", "948", "950", "962", "988", "1016", "1018", "1178", "1214", "1346", "1349", "1356", "1370", "1384", "1385", "1388", "1389", "1399", "1400", "1402", "1404", "1445", "1465", "1474", "1484", "1517", "1523", "1551", "1552", "1651", "1988", "1991", "2014", "2018", "2024", "2120", "2307", "2430", "3086", "3900", "4557", "4660", "5145", "5191", "5232", "5300", "5400", "5978", "6347", "6401", "6665", "7273", "7597", "8076", "9104", "27374", "14", "15", "35", "40", "51", "60", "69", "97", "103", "137", "180", "182", "194", "201", "213", "214", "231", "253", "262", "402", "454", "505", "511", "553", "560", "561", "611", "622", "624", "628", "633", "637", "644", "654", "656", "670", "675", "676", "680", "681", "685", "689", "692", "694", "703", "704", "708", "718", "721", "723", "734", "751", "756", "770", "788", "794", "796", "797", "799", "812", "821", "832", "837", "854", "867", "868", "869", "879", "886", "891", "895", "909", "933", "939", "944", "947", "951", "959", "960", "963", "964", "968", "970", "975", "991", "994", "997", "1003", "1017", "1348", "1354", "1369", "1372", "1373", "1379", "1381", "1387", "1401", "1403", "1405", "1412", "1435", "1466", "1467", "1475", "1476", "1486", "1496", "1497", "1515", "1527", "1535", "1539", "1650", "1664", "1668", "1762", "1995", "1996", "1997", "2015", "2026", "2064", "3141", "3264", "3292", "3397", "4480", "4500", "5530", "6143", "6588", "6701", "7009", "8123", "8892", "9105", "9106", "9107", "11371", "13720", "14141", "18182", "18184", "27665", "47557", "29", "31", "34", "38", "44", "58", "71", "73", "74", "93", "94", "95", "114", "115", "118", "120", "129", "133", "136", "138", "176", "177", "181", "193", "196", "200", "202", "204", "206", "224", "233", "235", "236", "260", "261", "273", "276", "303", "308", "315", "325", "336", "350", "355", "362", "397", "399", "401", "403", "404", "410", "412", "415", "418", "422", "437", "453", "462", "466", "486", "493", "536", "568", "601", "604", "607", "608", "619", "626", "645", "672", "677", "686", "688", "697", "698", "699", "712", "717", "737", "738", "746", "747", "755", "759", "763", "764", "774", "776", "784", "785", "793", "818", "827", "838", "842", "848", "849", "852", "853", "857", "865", "871", "876", "877", "882", "884", "885", "887", "890", "892", "907", "908", "915", "920", "934", "956", "957", "966", "972", "978", "982", "1355", "1362", "1365", "1368", "1376", "1386", "1390", "1393", "1394", "1397", "1398", "1409", "1410", "1422", "1424", "1426", "1432", "1436", "1437", "1438", "1439", "1442", "1446", "1454", "1456", "1472", "1479", "1483", "1493", "1498", "1499", "1510", "1511", "1513", "1519", "1528", "1529", "1531", "1537", "1538", "1544", "1545", "1548", "1549", "1661", "1662", "1667", "1763", "1827", "1986", "1990", "1992", "1994", "2023", "2027", "2053", "2431", "2432", "2627", "3457", "3531", "4132", "4144", "5301", "5302", "5997", "6111", "6142", "6145", "6146", "6147", "6400", "6544", "6700", "7006", "7008", "7634", "8770", "9051", "13713", "16444", "18181", "18183", "26208", "65301", "2", "8", "48", "54", "56", "65", "67", "72", "96", "108", "116", "117", "124", "128", "130", "132", "141", "142", "148", "149", "150", "151", "162", "168", "173", "174", "184", "185", "189", "190", "191", "192", "205", "209", "216", "217", "226", "228", "229", "234", "238", "248", "258", "265", "267", "270", "271", "277", "284", "288", "289", "293", "294", "295", "305", "316", "322", "326", "329", "337", "346", "351", "352", "358", "360", "361", "364", "369", "370", "373", "380", "383", "391", "392", "408", "413", "414", "420", "423", "428", "432", "434", "435", "438", "439", "451", "452", "457", "460", "470", "472", "473", "479", "480", "485", "487", "496", "516", "518", "522", "526", "528", "530", "533", "535", "542", "552", "564", "569", "570", "571", "572", "582", "583", "596", "598", "599", "612", "613", "614", "618", "643", "652", "662", "739", "742", "744", "752", "753", "766", "767", "768", "773", "775", "781", "845", "893", "896", "910", "932", "937", "954", "955", "976", "977", "986", "1360", "1361", "1363", "1367", "1371", "1374", "1383", "1391", "1395", "1396", "1407", "1408", "1411", "1416", "1418", "1419", "1423", "1427", "1429", "1430", "1440", "1448", "1449", "1451", "1453", "1457", "1458", "1459", "1462", "1464", "1469", "1470", "1473", "1480", "1482", "1488", "1491", "1502", "1505", "1508", "1509", "1518", "1532", "1540", "1541", "1542", "1543", "1670", "1671", "1672", "1987", "1993", "2016", "2019", "2028", "2108", "2564", "2766", "3421", "3984", "4133", "4672", "4987", "5193", "5303", "5490", "5713", "5714", "5717", "6141", "6548", "7326", "7464", "13701", "13714", "13715", "13718", "13721", "15151", "17007", "17300", "18187", "19150", "27002", "27003", "27005", "27007", "27009", "43188", "5", "18", "39", "41", "46", "61", "62", "63", "64", "78", "105", "107", "121", "126", "131", "134", "140", "145", "147", "152", "153", "154", "155", "156", "159", "160", "164", "165", "166", "167", "169", "170", "171", "172", "175", "178", "183", "186", "187", "188", "195", "197", "198", "203", "207", "208", "215", "218", "242", "243", "244", "245", "246", "247", "263", "266", "269", "281", "282", "283", "286", "287", "309", "310", "312", "313", "314", "317", "318", "319", "320", "321", "323", "324", "344", "345", "347", "348", "349", "354", "356", "357", "359", "363", "365", "367", "368", "371", "372", "374", "375", "376", "377", "378", "379", "381", "382", "384", "385", "386", "387", "390", "393", "394", "395", "396", "398", "405", "409", "421", "424", "426", "429", "430", "431", "433", "436", "455", "459", "461", "463", "467", "468", "469", "471", "474", "476", "477", "478", "482", "483", "484", "488", "489", "490", "494", "495", "498", "499", "503", "504", "506", "508", "517", "519", "520", "521", "527", "529", "531", "532", "534", "537", "539", "546", "547", "549", "550", "551", "558", "559", "562", "565", "566", "567", "573", "574", "575", "576", "579", "580", "581", "584", "585", "586", "588", "589", "590", "592", "594", "595", "597", "650", "671", "679", "693", "772", "1115", "1120", "1129", "1133", "1140", "1142", "1146", "1155", "1160", "1161", "1170", "1171", "1172", "1177", "1181", "1189", "1193", "1197", "1202", "1203", "1205", "1206", "1219", "1224", "1225", "1226", "1227", "1230", "1231", "1232", "1235", "1237", "1238", "1242", "1245", "1246", "1252", "1253", "1254", "1255", "1256", "1257", "1258", "1260", "1263", "1265", "1266", "1267", "1269", "1273", "1274", "1275", "1278", "1280", "1281", "1283", "1284", "1285", "1286", "1288", "1289", "1292", "1293", "1294", "1295", "1298", "1304", "1312", "1313", "1320", "1323", "1325", "1326", "1329", "1332", "1333", "1335", "1338", "1341", "1342", "1343", "1344", "1345", "1375", "1377", "1378", "1380", "1382", "1392", "1406", "1415", "1421", "1425", "1428", "1431", "1447", "1450", "1452", "1460", "1463", "1468", "1471", "1477", "1478", "1481", "1485", "1487", "1489", "1490", "1504", "1506", "1512", "1514", "1520", "1530", "1534", "1536", "1546", "1553", "1554", "1555", "1557", "1561", "1562", "1563", "1564", "1567", "1568", "1570", "1571", "1572", "1573", "1574", "1575", "1576", "1577", "1578", "1579", "1581", "1582", "1585", "1586", "1587", "1588", "1589", "1590", "1591", "1593", "1595", "1596", "1597", "1599", "1601", "1602", "1603", "1604", "1606", "1608", "1609", "1610", "1611", "1612", "1613", "1614", "1616", "1617", "1618", "1619", "1621", "1623", "1624", "1625", "1626", "1627", "1628", "1629", "1630", "1631", "1633", "1634", "1636", "1637", "1639", "1640", "1642", "1643", "1644", "1646", "1647", "1648", "1649", "1653", "1654", "1655", "1656", "1657", "1659", "1660", "1665", "1669", "1673", "1674", "1675", "1676", "1678", "1679", "1681", "1682", "1684", "1685", "1686", "1689", "1690", "1692", "1693", "1695", "1696", "1697", "1698", "1702", "1704", "1705", "1706", "1710", "1714", "1716", "1724", "1725", "1726", "1727", "1728", "1729", "1731", "1732", "1733", "1734", "1737", "1738", "1739", "1740", "1741", "1742", "1743", "1744", "1746", "1747", "1748", "1749", "1751", "1754", "1756", "1757", "1758", "1759", "1760", "1764", "1765", "1766", "1767", "1768", "1769", "1770", "1771", "1772", "1773", "1774", "1775", "1776", "1777", "1778", "1779", "1780", "1781", "1784", "1785", "1786", "1787", "1788", "1789", "1790", "1793", "1794", "1795", "1796", "1797", "1798", "1802", "1803", "1804", "1809", "1810", "1813", "1814", "1815", "1816", "1817", "1818", "1819", "1820", "1821", "1822", "1824", "1826", "1828", "1829", "1830", "1831", "1832", "1833", "1834", "1836", "1837", "1838", "1841", "1842", "1843", "1844", "1845", "1846", "1847", "1848", "1849", "1850", "1851", "1852", "1853", "1854", "1855", "1856", "1857", "1859", "1860", "1865", "1866", "1867", "1868", "1869", "1870", "1872", "1873", "1874", "1876", "1877", "1878", "1879", "1880", "1881", "1882", "1884", "1885", "1886", "1887", "1888", "1889", "1890", "1891", "1892", "1893", "1894", "1896", "1897", "1898", "1899", "1902", "1903", "1904", "1905", "1906", "1907", "1908", "1909", "1910", "1913", "1915", "1916", "1917", "1919", "1920", "1921", "1922", "1923", "1925", "1926", "1928", "1929", "1930", "1931", "1932", "1933", "1934", "1936", "1937", "1938", "1939", "1940", "1941", "1942", "1943", "1944", "1945", "1946", "1948", "1949", "1950", "1951", "1952", "1953", "1955", "1956", "1957", "1959", "1960", "1961", "1962", "1963", "1964", "1965", "1966", "1967", "1968", "1969", "1970", "1977", "1978", "1979", "1980", "1982", "1983", "1985", "2017", "2029", "2032", "2036", "2037", "2039", "2050", "2051", "2052", "2054", "2055", "2056", "2057", "2058", "2059", "2060", "2061", "2063", "2066", "2071", "2072", "2073", "2074", "2075", "2076", "2077", "2078", "2079", "2084", "2085", "2088", "2089", "2090", "2091", "2092", "2093", "2094", "2097", "2098", "2102", "2109", "2110", "2113", "2114", "2116", "2117", "2118", "2122", "2123", "2125", "2127", "2128", "2129", "2130", "2131", "2132", "2133", "2136", "2137", "2138", "2139", "2140", "2141", "2143", "2145", "2146", "2147", "2149", "2151", "2152", "2153", "2154", "2155", "2156", "2157", "2158", "2159", "2162", "2163", "2164", "2165", "2166", "2167", "2168", "2169", "2171", "2172", "2173", "2174", "2175", "2176", "2177", "2178", "2180", "2181", "2182", "2183", "2184", "2185", "2186", "2188", "2189", "2192", "2193", "2198", "2199", "2202", "2204", "2205", "2206", "2207", "2208", "2209", "2210", "2211", "2212", "2213", "2214", "2215", "2216", "2217", "2218", "2219", "2220", "2221", "2223", "2225", "2226", "2227", "2228", "2229", "2230", "2231", "2233", "2234", "2235", "2236", "2237", "2238", "2239", "2240", "2242", "2243", "2244", "2245", "2246", "2247", "2248", "2249", "2252", "2254", "2255", "2256", "2257", "2258", "2259", "2263", "2264", "2266", "2267", "2268", "2272", "2273", "2274", "2275", "2276", "2277", "2278", "2279", "2281", "2282", "2283", "2284", "2285", "2286", "2287", "2289", "2290", "2293", "2294", "2295", "2296", "2297", "2298", "2299", "2303", "2305", "2306", "2308", "2309", "2310", "2311", "2314", "2315", "2316", "2317", "2318", "2319", "2320", "2321", "2322", "2324", "2327", "2328", "2329", "2331", "2332", "2333", "2334", "2336", "2337", "2338", "2339", "2341", "2342", "2343", "2344", "2345", "2346", "2347", "2348", "2349", "2350", "2351", "2352", "2353", "2354", "2355", "2356", "2357", "2358", "2359", "2360", "2361", "2362", "2363", "2364", "2365", "2367", "2368", "2370", "2373", "2374", "2377", "2378", "2379", "2380", "2384", "2385", "2386", "2387", "2388", "2389", "2390", "2392", "2395", "2396", "2397", "2398", "2400", "2402", "2403", "2404", "2405", "2406", "2407", "2408", "2409", "2410", "2411", "2412", "2413", "2414", "2415", "2416", "2417", "2419", "2420", "2421", "2422", "2423", "2424", "2426", "2427", "2428", "2429", "2434", "2437", "2440", "2441", "2442", "2443", "2444", "2445", "2446", "2447", "2448", "2450", "2451", "2452", "2453", "2454", "2455", "2457", "2458", "2459", "2460", "2461", "2462", "2464", "2465", "2466", "2467", "2468", "2469", "2470", "2471", "2473", "2474", "2475", "2476", "2477", "2478", "2479", "2480", "2481", "2482", "2483", "2484", "2485", "2486", "2487", "2488", "2489", "2490", "2491", "2493", "2494", "2495", "2496", "2497", "2498", "2499", "2502", "2503", "2504", "2506", "2507", "2508", "2509", "2510", "2511", "2512", "2513", "2514", "2515", "2516", "2517", "2518", "2519", "2520", "2521", "2523", "2524", "2526", "2527", "2528", "2529", "2530", "2533", "2534", "2535", "2536", "2537", "2538", "2539", "2540", "2541", "2542", "2543", "2544", "2545", "2546", "2547", "2548", "2549", "2552", "2553", "2554", "2555", "2556", "2559", "2560", "2561", "2562", "2563", "2565", "2566", "2568", "2569", "2570", "2571", "2572", "2573", "2574", "2575", "2576", "2577", "2578", "2579", "2581", "2582", "2585", "2586", "2587", "2588", "2589", "2590", "2591", "2592", "2593", "2594", "2595", "2596", "2597", "2599", "2603", "2609", "2610", "2611", "2612", "2613", "2614", "2615", "2616", "2617", "2618", "2619", "2620", "2621", "2624", "2625", "2626", "2629", "2630", "2632", "2633", "2634", "2635", "2636", "2637", "2639", "2640", "2641", "2642", "2643", "2645", "2646", "2647", "2648", "2649", "2650", "2651", "2652", "2653", "2654", "2655", "2656", "2657", "2658", "2659", "2660", "2661", "2662", "2663", "2664", "2665", "2666", "2667", "2668", "2669", "2670", "2671", "2672", "2673", "2674", "2675", "2676", "2677", "2678", "2679", "2680", "2681", "2683", "2684", "2685", "2686", "2687", "2688", "2689", "2690", "2692", "2694", "2695", "2696", "2697", "2698", "2699", "2703", "2704", "2705", "2707", "2708", "2709", "2713", "2714", "2715", "2716", "2719", "2720", "2721", "2722", "2724", "2726", "2727", "2729", "2730", "2731", "2732", "2733", "2735", "2736", "2737", "2738", "2739", "2740", "2741", "2742", "2743", "2744", "2745", "2746", "2747", "2748", "2749", "2750", "2751", "2752", "2753", "2754", "2755", "2756", "2757", "2758", "2759", "2760", "2761", "2762", "2763", "2764", "2765", "2767", "2768", "2769", "2770", "2771", "2772", "2773", "2774", "2775", "2776", "2777", "2778", "2779", "2780", "2781", "2782", "2783", "2784", "2785", "2786", "2787", "2788", "2789", "2790", "2791", "2792", "2793", "2795", "2796", "2797", "2798", "2799", "2801", "2802", "2803", "2805", "2807", "2808", "2810", "2813", "2814", "2815", "2816", "2817", "2818", "2819", "2820", "2821", "2822", "2823", "2824", "2826", "2827", "2828", "2829", "2830", "2831", "2832", "2833", "2834", "2835", "2836", "2837", "2838", "2839", "2840", "2841", "2842", "2843", "2844", "2845", "2846", "2848", "2849", "2851", "2852", "2853", "2854", "2855", "2856", "2857", "2858", "2859", "2860", "2861", "2862", "2863", "2864", "2865", "2866", "2867", "2868", "2870", "2871", "2872", "2873", "2874", "2876", "2877", "2878", "2879", "2880", "2881", "2883", "2884", "2885", "2886", "2887", "2890", "2891", "2892", "2893", "2894", "2895", "2896", "2897", "2899", "2900", "2904", "2905", "2906", "2907", "2911", "2912", "2913", "2914", "2915", "2916", "2917", "2918", "2919", "2921", "2922", "2923", "2924", "2926", "2927", "2928", "2929", "2931", "2932", "2933", "2934", "2935", "2936", "2937", "2938", "2939", "2940", "2941", "2942", "2943", "2944", "2945", "2946", "2947", "2948", "2949", "2950", "2951", "2952", "2953", "2954", "2955", "2956", "2959", "2960", "2961", "2962", "2963", "2964", "2965", "2966", "2969", "2970", "2971", "2972", "2974", "2975", "2976", "2977", "2978", "2979", "2980", "2981", "2982", "2983", "2985", "2986", "2989", "2990", "2992", "2993", "2994", "2995", "2996", "2999", "3004", "3008", "3009", "3010", "3012", "3015", "3016", "3018", "3019", "3020", "3021", "3022", "3024", "3026", "3027", "3028", "3029", "3032", "3033", "3034", "3035", "3036", "3037", "3038", "3039", "3040", "3041", "3042", "3043", "3044", "3046", "3047", "3048", "3051", "3053", "3054", "3055", "3056", "3058", "3059", "3060", "3061", "3065", "3066", "3067", "3068", "3069", "3070", "3072", "3073", "3074", "3075", "3076", "3078", "3079", "3081", "3082", "3083", "3084", "3085", "3087", "3088", "3090", "3091", "3093", "3094", "3095", "3096", "3098", "3099", "3100", "3101", "3104", "3105", "3106", "3107", "3108", "3109", "3110", "3111", "3112", "3113", "3114", "3115", "3116", "3117", "3120", "3122", "3123", "3124", "3125", "3127", "3129", "3130", "3131", "3132", "3133", "3134", "3135", "3136", "3137", "3138", "3139", "3140", "3142", "3143", "3144", "3145", "3147", "3148", "3149", "3150", "3151", "3152", "3153", "3154", "3155", "3156", "3157", "3158", "3159", "3160", "3161", "3163", "3164", "3165", "3166", "3169", "3170", "3171", "3172", "3173", "3174", "3175", "3176", "3177", "3178", "3179", "3180", "3181", "3182", "3183", "3184", "3185", "3186", "3187", "3188", "3189", "3191", "3192", "3193", "3194", "3195", "3196", "3197", "3198", "3199", "3201", "3202", "3203", "3204", "3205", "3206", "3207", "3208", "3209", "3212", "3213", "3214", "3215", "3216", "3217", "3218", "3219", "3222", "3223", "3224", "3225", "3226", "3227", "3228", "3229", "3230", "3231", "3232", "3233", "3234", "3235", "3236", "3237", "3238", "3239", "3241", "3242", "3243", "3244", "3245", "3246", "3247", "3248", "3249", "3250", "3251", "3252", "3253", "3254", "3255", "3256", "3257", "3258", "3259", "3262", "3265", "3266", "3267", "3270", "3271", "3272", "3273", "3274", "3275", "3276", "3277", "3278", "3279", "3282", "3284", "3285", "3286", "3287", "3288", "3289", "3290", "3293", "3294", "3295", "3296", "3297", "3298", "3302", "3303", "3305", "3308", "3309", "3312", "3313", "3314", "3315", "3316", "3317", "3318", "3320", "3321", "3326", "3327", "3328", "3329", "3330", "3331", "3332", "3335", "3336", "3337", "3338", "3339", "3340", "3341", "3342", "3343", "3344", "3345", "3346", "3347", "3348", "3349", "3350", "3352", "3353", "3354", "3355", "3356", "3357", "3358", "3359", "3360", "3361", "3364", "3366", "3373", "3375", "3377", "3378", "3379", "3380", "3381", "3382", "3383", "3384", "3385", "3386", "3387", "3391", "3392", "3393", "3394", "3395", "3401", "3402", "3405", "3406", "3407", "3408", "3409", "3411", "3412", "3413", "3416", "3417", "3418", "3420", "3422", "3423", "3424", "3426", "3427", "3428", "3429", "3431", "3432", "3433", "3434", "3435", "3436", "3437", "3438", "3440", "3441", "3442", "3444", "3445", "3446", "3447", "3448", "3449", "3450", "3451", "3452", "3453", "3454", "3455", "3458", "3459", "3460", "3461", "3462", "3463", "3464", "3465", "3466", "3467", "3468", "3469", "3470", "3471", "3472", "3473", "3474", "3475", "3477", "3478", "3480", "3481", "3482", "3484", "3487", "3488", "3489", "3490", "3491", "3492", "3494", "3495", "3496", "3498", "3499", "3500", "3501", "3502", "3504", "3507", "3508", "3509", "3510", "3512", "3516", "3518", "3521", "3522", "3523", "3524", "3525", "3528", "3529", "3533", "3534", "3535", "3536", "3537", "3538", "3539", "3540", "3541", "3542", "3543", "3544", "3545", "3547", "3548", "3549", "3550", "3552", "3553", "3554", "3555", "3556", "3557", "3558", "3559", "3560", "3561", "3562", "3563", "3564", "3565", "3566", "3567", "3568", "3569", "3570", "3571", "3572", "3573", "3574", "3575", "3576", "3578", "3579", "3581", "3582", "3583", "3584", "3585", "3587", "3588", "3589", "3590", "3591", "3592", "3593", "3594", "3595", "3596", "3597", "3598", "3601", "3604", "3605", "3606", "3607", "3608", "3609", "3610", "3611", "3612", "3613", "3614", "3615", "3616", "3617", "3618", "3619", "3620", "3623", "3624", "3625", "3626", "3627", "3628", "3629", "3630", "3631", "3633", "3634", "3635", "3638", "3639", "3640", "3641", "3642", "3643", "3644", "3645", "3646", "3647", "3648", "3649", "3650", "3651", "3654", "3655", "3657", "3660", "3661", "3662", "3664", "3665", "3666", "3667", "3668", "3671", "3673", "3674", "3675", "3676", "3677", "3678", "3679", "3682", "3685", "3686", "3687", "3688", "3691", "3692", "3693", "3695", "3696", "3698", "3699", "3701", "3702", "3704", "3705", "3706", "3707", "3708", "3709", "3710", "3711", "3713", "3714", "3715", "3716", "3717", "3718", "3719", "3720", "3721", "3722", "3723", "3724", "3725", "3726", "3727", "3729", "3730", "3732", "3733", "3734", "3735", "3736", "3738", "3739", "3740", "3741", "3743", "3744", "3745", "3746", "3747", "3748", "3750", "3751", "3752", "3753", "3754", "3755", "3756", "3757", "3758", "3759", "3760", "3761", "3762", "3763", "3764", "3767", "3768", "3769", "3770", "3771", "3772", "3773", "3774", "3775", "3776", "3777", "3778", "3779", "3780", "3781", "3782", "3783", "3785", "3786", "3789", "3791", "3794", "3797", "3802", "3804", "3805", "3807", "3815", "3816", "3818", "3819", "3821", "3822", "3829", "3832", "3833", "3834", "3835", "3836", "3838", "3840", "3841", "3843", "3844", "3845", "3854", "3855", "3857", "3858", "3861", "3862", "3864", "3865", "3866", "3867", "3873", "3874", "3875", "3877", "3881", "3883", "3884", "3885", "3886", "3887", "3891", "3892", "3893", "3894", "3895", "3896", "3898", "3903", "3910", "3912", "3917", "3921", "3924", "3925", "3926", "3927", "3932", "3933", "3934", "3938", "3939", "3942", "3947", "3950", "3951", "3953", "3954", "3955", "3958", "3959", "3960", "3965", "3966", "3970", "3973", "3974", "3976", "3977", "3978", "3987", "3988", "4011", "4012", "4013", "4014", "4015", "4017", "4018", "4019", "4021", "4023", "4026", "4027", "4028", "4030", "4031", "4032", "4033", "4034", "4037", "4038", "4041", "4042", "4043", "4044", "4046", "4047", "4049", "4050", "4051", "4052", "4053", "4054", "4055", "4057", "4059", "4060", "4061", "4062", "4063", "4064", "4066", "4067", "4068", "4069", "4070", "4071", "4072", "4073", "4074", "4075", "4076", "4077", "4078", "4079", "4081", "4082", "4083", "4084", "4085", "4086", "4088", "4089", "4091", "4092", "4093", "4094", "4095", "4097", "4098", "4099", "4102", "4103", "4104", "4105", "4106", "4107", "4108", "4109", "4110", "4114", "4115", "4116", "4117", "4122", "4123", "4124", "4127", "4128", "4130", "4131", "4134", "4136", "4137", "4138", "4139", "4140", "4142", "4145", "4146", "4148", "4149", "4150", "4151", "4152", "4153", "4154", "4155", "4156", "4157", "4159", "4160", "4162", "4163", "4165", "4166", "4167", "4168", "4169", "4170", "4171", "4172", "4173", "4175", "4176", "4177", "4178", "4179", "4180", "4181", "4182", "4183", "4184", "4185", "4186", "4187", "4188", "4189", "4191", "4193", "4194", "4195", "4197", "4201", "4202", "4203", "4204", "4205", "4207", "4208", "4209", "4210", "4211", "4212", "4213", "4214", "4215", "4216", "4217", "4218", "4219", "4221", "4222", "4223", "4225", "4226", "4227", "4228", "4229", "4230", "4231", "4232", "4233", "4235", "4236", "4237", "4238", "4239", "4240", "4241", "4244", "4245", "4246", "4247", "4248", "4249", "4250", "4251", "4253", "4254", "4255", "4256", "4257", "4258", "4259", "4260", "4261", "4263", "4264", "4265", "4266", "4267", "4268", "4269", "4270", "4271", "4272", "4273", "4274", "4275", "4276", "4277", "4278", "4280", "4281", "4282", "4283", "4284", "4285", "4286", "4287", "4288", "4289", "4290", "4291", "4292", "4293", "4295", "4296", "4299", "4301", "4303", "4304", "4305", "4306", "4307", "4308", "4309", "4310", "4311", "4312", "4313", "4314", "4316", "4317", "4319", "4320", "4322", "4323", "4324", "4326", "4327", "4329", "4330", "4331", "4332", "4334", "4335", "4336", "4340", "4341", "4344", "4345", "4346", "4347", "4348", "4349", "4350", "4351", "4352", "4353", "4354", "4359", "4360", "4361", "4362", "4366", "4368", "4370", "4371", "4372", "4373", "4377", "4378", "4379", "4389", "4390", "4391", "4392", "4393", "4394", "4395", "4396", "4400", "4402", "4403", "4404", "4405", "4406", "4408", "4409", "4410", "4411", "4412", "4413", "4416", "4417", "4419", "4420", "4421", "4422", "4423", "4425", "4426", "4427", "4428", "4429", "4431", "4432", "4441", "4448", "4450", "4451", "4452", "4453", "4455", "4456", "4457", "4458", "4460", "4484", "4485", "4486", "4487", "4488", "4502", "4535", "4536", "4537", "4538", "4546", "4547", "4548", "4549", "4551", "4552", "4553", "4554", "4556", "4563", "4566", "4568", "4569", "4573", "4590", "4591", "4592", "4593", "4594", "4595", "4596", "4597", "4598", "4603", "4604", "4605", "4621", "4646", "4659", "4661", "4663", "4664", "4666", "4667", "4668", "4669", "4670", "4671", "4673", "4674", "4675", "4676", "4677", "4678", "4679", "4680", "4681", "4682", "4683", "4684", "4685", "4686", "4688", "4690", "4691", "4692", "4701", "4702", "4703", "4704", "4711", "4725", "4726", "4727", "4728", "4729", "4730", "4731", "4732", "4733", "4737", "4738", "4739", "4740", "4741", "4742", "4743", "4744", "4746", "4747", "4749", "4750", "4751", "4752", "4753", "4754", "4755", "4756", "4774", "4784", "4785", "4786", "4787", "4788", "4789", "4790", "4791", "4792", "4801", "4802", "4803", "4804", "4827", "4837", "4838", "4839", "4840", "4841", "4842", "4843", "4844", "4845", "4846", "4847", "4849", "4850", "4851", "4867", "4868", "4869", "4870", "4871", "4878", "4879", "4880", "4882", "4883", "4884", "4885", "4888", "4889", "4894", "4901", "4902", "4913", "4914", "4915", "4936", "4937", "4940", "4941", "4942", "4950", "4951", "4952", "4953", "4969", "4970", "4971", "4980", "4984", "4985", "4986", "4988", "4989", "4990", "4991", "5006", "5007", "5008", "5022", "5024", "5025", "5026", "5027", "5028", "5029", "5031", "5032", "5034", "5042", "5043", "5044", "5045", "5046", "5047", "5048", "5049", "5056", "5057", "5058", "5059", "5062", "5064", "5065", "5067", "5068", "5069", "5071", "5072", "5073", "5075", "5078", "5079", "5082", "5083", "5084", "5085", "5086", "5092", "5093", "5094", "5099", "5103", "5104", "5105", "5106", "5107", "5112", "5115", "5116", "5117", "5134", "5135", "5136", "5146", "5150", "5153", "5154", "5155", "5156", "5157", "5161", "5162", "5163", "5164", "5165", "5166", "5167", "5168", "5172", "5192", "5194", "5195", "5196", "5197", "5203", "5209", "5215", "5224", "5227", "5228", "5229", "5230", "5231", "5236", "5237", "5243", "5245", "5246", "5247", "5248", "5249", "5251", "5253", "5254", "5264", "5265", "5270", "5271", "5272", "5281", "5282", "5299", "5304", "5305", "5306", "5307", "5309", "5310", "5312", "5313", "5314", "5315", "5316", "5317", "5318", "5320", "5321", "5343", "5344", "5349", "5350", "5351", "5352", "5354", "5355", "5356", "5358", "5359", "5360", "5361", "5362", "5363", "5364", "5397", "5398", "5399", "5401", "5402", "5403", "5404", "5406", "5407", "5408", "5409", "5410", "5411", "5412", "5413", "5415", "5416", "5417", "5418", "5419", "5420", "5421", "5422", "5424", "5425", "5426", "5427", "5428", "5429", "5430", "5434", "5435", "5436", "5437", "5443", "5445", "5450", "5453", "5454", "5455", "5456", "5461", "5462", "5463", "5464", "5465", "5470", "5471", "5472", "5474", "5503", "5504", "5505", "5506", "5507", "5540", "5556", "5565", "5567", "5568", "5569", "5573", "5574", "5575", "5579", "5582", "5583", "5584", "5585", "5586", "5597", "5598", "5599", "5600", "5601", "5602", "5603", "5604", "5605", "5618", "5627", "5628", "5629", "5630", "5634", "5635", "5636", "5637", "5638", "5639", "5646", "5670", "5671", "5673", "5674", "5675", "5676", "5677", "5681", "5682", "5683", "5684", "5687", "5688", "5689", "5693", "5696", "5700", "5705", "5715", "5716", "5719", "5720", "5724", "5725", "5726", "5727", "5728", "5729", "5741", "5742", "5743", "5744", "5745", "5746", "5747", "5748", "5750", "5755", "5757", "5766", "5767", "5768", "5769", "5770", "5771", "5777", "5780", "5781", "5782", "5783", "5784", "5785", "5786", "5787", "5793", "5794", "5798", "5813", "5841", "5842", "5863", "5883", "5913", "5979", "5984", "5990", "5991", "5992", "5993", "5994", "6011", "6012", "6013", "6014", "6016", "6018", "6019", "6020", "6022", "6023", "6024", "6026", "6027", "6028", "6029", "6031", "6032", "6033", "6034", "6035", "6036", "6037", "6038", "6039", "6040", "6041", "6042", "6043", "6044", "6045", "6046", "6047", "6048", "6049", "6053", "6054", "6056", "6057", "6058", "6061", "6064", "6066", "6069", "6070", "6071", "6072", "6073", "6074", "6075", "6076", "6080", "6081", "6082", "6083", "6084", "6086", "6087", "6088", "6099", "6102", "6104", "6107", "6108", "6109", "6114", "6116", "6117", "6118", "6121", "6122", "6124", "6130", "6133", "6140", "6144", "6148", "6149", "6159", "6160", "6162", "6163", "6200", "6201", "6209", "6241", "6242", "6243", "6244", "6252", "6253", "6267", "6268", "6269", "6300", "6301", "6306", "6315", "6316", "6317", "6320", "6321", "6322", "6325", "6326", "6343", "6344", "6355", "6360", "6363", "6370", "6382", "6390", "6402", "6403", "6404", "6405", "6406", "6407", "6408", "6409", "6410", "6417", "6418", "6419", "6420", "6421", "6432", "6440", "6442", "6443", "6444", "6445", "6446", "6455", "6456", "6464", "6471", "6480", "6482", "6483", "6484", "6485", "6486", "6487", "6488", "6489", "6501", "6505", "6506", "6507", "6508", "6509", "6511", "6513", "6514", "6515", "6549", "6551", "6556", "6558", "6568", "6581", "6582", "6583", "6601", "6602", "6620", "6621", "6622", "6623", "6624", "6625", "6626", "6627", "6629", "6632", "6633", "6634", "6635", "6636", "6640", "6653", "6655", "6656", "6657", "6671", "6672", "6673", "6678", "6679", "6687", "6688", "6690", "6696", "6697", "6702", "6703", "6714", "6715", "6716", "6767", "6768", "6769", "6770", "6771", "6777", "6778", "6784", "6785", "6786", "6787", "6790", "6791", "6801", "6817", "6831", "6841", "6842", "6850", "6868", "6900", "6924", "6935", "6936", "6946", "6951", "6961", "6962", "6963", "6964", "6965", "6966", "6970", "6980", "6997", "6998", "6999", "7011", "7012", "7013", "7014", "7015", "7016", "7017", "7018", "7020", "7021", "7022", "7023", "7026", "7030", "7031", "7040", "7073", "7088", "7095", "7107", "7117", "7128", "7129", "7161", "7162", "7163", "7164", "7165", "7166", "7167", "7168", "7169", "7170", "7171", "7172", "7173", "7174", "7181", "7202", "7215", "7216", "7227", "7228", "7229", "7234", "7235", "7236", "7237", "7244", "7262", "7274", "7275", "7276", "7277", "7279", "7280", "7282", "7283", "7301", "7302", "7303", "7304", "7305", "7306", "7307", "7308", "7309", "7310", "7311", "7312", "7313", "7314", "7315", "7316", "7317", "7318", "7319", "7321", "7322", "7323", "7324", "7327", "7328", "7329", "7330", "7331", "7332", "7333", "7334", "7335", "7336", "7337", "7338", "7339", "7340", "7341", "7342", "7343", "7344", "7346", "7347", "7348", "7349", "7350", "7351", "7352", "7353", "7354", "7355", "7356", "7357", "7358", "7359", "7365", "7391", "7392", "7393", "7394", "7395", "7397", "7401", "7410", "7411", "7420", "7421", "7426", "7427", "7428", "7429", "7430", "7431", "7437", "7471", "7473", "7474", "7478", "7491", "7508", "7509", "7510", "7511", "7542", "7543", "7544", "7545", "7546", "7547", "7548", "7549", "7550", "7551", "7560", "7563", "7566", "7569", "7570", "7574", "7588", "7606", "7624", "7626", "7629", "7630", "7631", "7633", "7648", "7663", "7672", "7673", "7674", "7675", "7677", "7680", "7683", "7687", "7689", "7690", "7697", "7700", "7701", "7707", "7708", "7720", "7724", "7726", "7727", "7728", "7734", "7738", "7742", "7743", "7747", "7775", "7779", "7781", "7784", "7786", "7787", "7794", "7797", "7798", "7799", "7801", "7802", "7810", "7845", "7846", "7847", "7869", "7870", "7871", "7872", "7880", "7887", "7901", "7902", "7903", "7932", "7933", "7962", "7967", "7979", "7980", "7981", "7982", "7997", "8004", "8017", "8020", "8026", "8027", "8032", "8033", "8034", "8040", "8041", "8043", "8044", "8051", "8053", "8054", "8055", "8056", "8057", "8058", "8059", "8066", "8067", "8070", "8074", "8077", "8091", "8101", "8102", "8111", "8115", "8117", "8121", "8122", "8128", "8129", "8130", "8131", "8132", "8140", "8148", "8149", "8153", "8160", "8161", "8162", "8182", "8183", "8184", "8190", "8191", "8195", "8199", "8204", "8205", "8206", "8207", "8208", "8211", "8230", "8231", "8243", "8266", "8270", "8276", "8280", "8301", "8313", "8320", "8321", "8322", "8351", "8376", "8377", "8378", "8379", "8380", "8384", "8404", "8405", "8415", "8416", "8417", "8423", "8432", "8442", "8444", "8450", "8457", "8473", "8501", "8502", "8503", "8554", "8555", "8567", "8609", "8610", "8611", "8612", "8613", "8614", "8615", "8665", "8666", "8688", "8699", "8710", "8711", "8732", "8733", "8750", "8763", "8764", "8767", "8768", "8769", "8778", "8786", "8787", "8793", "8804", "8805", "8807", "8808", "8809", "8834", "8881", "8890", "8891", "8893", "8894", "8901", "8908", "8910", "8911", "8912", "8913", "8937", "8953", "8981", "8989", "8990", "8991", "8997", "8998", "9007", "9008", "9023", "9024", "9025", "9026", "9060", "9083", "9085", "9086", "9087", "9088", "9089", "9092", "9093", "9119", "9122", "9123", "9162", "9163", "9164", "9201", "9203", "9204", "9205", "9206", "9208", "9209", "9212", "9213", "9214", "9215", "9216", "9217", "9222", "9255", "9277", "9278", "9279", "9280", "9281", "9282", "9283", "9284", "9285", "9286", "9292", "9293", "9294", "9295", "9306", "9310", "9312", "9318", "9321", "9339", "9340", "9344", "9345", "9346", "9374", "9380", "9387", "9388", "9389", "9390", "9396", "9397", "9401", "9402", "9445", "9450", "9522", "9536", "9555", "9559", "9596", "9597", "9598", "9599", "9612", "9614", "9617", "9629", "9630", "9631", "9632", "9640", "9668", "9695", "9747", "9750", "9753", "9762", "9800", "9801", "9802", "9888", "9889", "9899", "9903", "9925", "9951", "9952", "9953", "9954", "9955", "9956", "9966", "9978", "9981", "9987", "9993", "9994", "9996", "9997", "10050", "10051", "10055", "10080", "10081", "10100", "10102", "10103", "10104", "10107", "10110", "10111", "10113", "10114", "10116", "10117", "10125", "10128", "10129", "10161", "10162", "10200", "10201", "10252", "10253", "10260", "10261", "10288", "10321", "10439", "10540", "10541", "10542", "10543", "10544", "10548", "10631", "10800", "10805", "10809", "10810", "10860", "10880", "10933", "10990", "11095", "11103", "11104", "11105", "11106", "11108", "11109", "11112", "11161", "11162", "11163", "11164", "11165", "11171", "11172", "11173", "11174", "11175", "11201", "11202", "11208", "11211", "11235", "11319", "11320", "11321", "11367", "11430", "11489", "11600", "11623", "11720", "11723", "11751", "11796", "11876", "11877", "11971", "12003", "12004", "12007", "12008", "12010", "12012", "12013", "12109", "12168", "12172", "12300", "12302", "12321", "12322", "12753", "13160", "13216", "13217", "13218", "13223", "13224", "13400", "13702", "13705", "13706", "13708", "13709", "13710", "13711", "13712", "13716", "13717", "13785", "13786", "13818", "13819", "13820", "13821", "13822", "13823", "13882", "13894", "13929", "13930", "14002", "14033", "14034", "14142", "14143", "14145", "14149", "14150", "14154", "14250", "14414", "14500", "14936", "14937", "15118", "15126", "15345", "15363", "15555", "15740", "15998", "15999", "16002", "16003", "16020", "16021", "16162", "16309", "16310", "16311", "16360", "16361", "16367", "16368", "16384", "16385", "16619", "16665", "16666", "16789", "16950", "16959", "16991", "16994", "16995", "17010", "17184", "17185", "17219", "17220", "17221", "17222", "17223", "17224", "17225", "17234", "17235", "17555", "17729", "17754", "17755", "17756", "17777", "18104", "18136", "18185", "18186", "18241", "18242", "18243", "18262", "18463", "18516", "18634", "18635", "18668", "18769", "18881", "18888", "19000", "19007", "19020", "19191", "19194", "19220", "19398", "19410", "19411", "19412", "19539", "19540", "19541", "19788", "19790", "19998", "19999", "20003", "20012", "20013", "20014", "20034", "20046", "20048", "20049", "20057", "20167", "20202", "20480", "20670", "20810", "20999", "21000", "21010", "21212", "21213", "21221", "21553", "21554", "21590", "21800", "21801", "21845", "21846", "21847", "21848", "21849", "22000", "22001", "22002", "22003", "22004", "22005", "22289", "22305", "22321", "22333", "22335", "22343", "22347", "22351", "22370", "22537", "22763", "22800", "22951", "23000", "23001", "23002", "23003", "23004", "23005", "23053", "23272", "23294", "23333", "23400", "23401", "23402", "23456", "23457", "23546", "24000", "24001", "24002", "24003", "24004", "24005", "24006", "24242", "24249", "24321", "24322", "24323", "24386", "24465", "24577", "24666", "24676", "24677", "24678", "24680", "24754", "24850", "24922", "25002", "25003", "25004", "25005", "25006", "25007", "25008", "25009", "25471", "25576", "25604", "25793", "25900", "25901", "25902", "25903", "25954", "25955", "26133", "26257", "26260", "26261", "26262", "26263", "26486", "26487", "26489", "27004", "27006", "27008", "27345", "27442", "27504", "27782", "27876", "27999", "28000", "28001", "28010", "28080", "28119", "28200", "28240", "28589", "29000", "29167", "29999", "30002", "30003", "30004", "30100", "30260", "30400", "30832", "30999", "31016", "31020", "31029", "31400", "31457", "31620", "31685", "31765", "31948", "31949", "32034", "32249", "32400", "32483", "32635", "32636", "32801", "32811", "32896", "33060", "33123", "33331", "33333", "33334", "33434", "33435", "33656", "33890", "34249", "34378", "34379", "34567", "34962", "34963", "34964", "34980", "35000", "35001", "35002", "35003", "35004", "35005", "35006", "35100", "35354", "35355", "35356", "35357", "36001", "36411", "36423", "36424", "36443", "36444", "36462", "36524", "36602", "36700", "36865", "37472", "37475", "37483", "37601", "37654", "38000", "38001", "38002", "38201", "38202", "38203", "38412", "38422", "38462", "38472", "38638", "38800", "38865", "39063", "39681", "40023", "40404", "40841", "40842", "40843", "40853", "41111", "41121", "41230", "41796", "41797", "42508", "42509", "43189", "43190", "43191", "43210", "43438", "43439", "43440", "43441", "44123", "44321", "44322", "44323", "44444", "44445", "44544", "44553", "44600", "44818", "44900", "45000", "45001", "45002", "45045", "45054", "45514", "45678", "45824", "45825", "45966", "46336", "46998", "46999", "47000", "47001", "47100", "47808", "47809", "48000", "48001", "48002", "48003", "48004", "48005", "48048", "48049", "48050", "48128", "48129", "48556", "48653", "49000", "49001", "49150", "61439", "61440", "61441"] -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/tests/__init__.py -------------------------------------------------------------------------------- /tests/integration_test.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import socket 3 | import subprocess 4 | import time 5 | 6 | 7 | def is_port_open(host, port, timeout=1): 8 | """Check if a given port is open.""" 9 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 10 | s.settimeout(timeout) 11 | try: 12 | s.connect((host, port)) 13 | return True 14 | except socket.error: 15 | return False 16 | 17 | 18 | def test_server_start(): 19 | """ 20 | Test the startup of the server by running a Python script in the background. 21 | 22 | The test performs the following steps: 23 | 1. Initiates the server by running 'main.py' script with '127.0.0.1' as an argument. 24 | 2. Waits for an initial duration to account for any startup delay. 25 | 3. Checks for 60 seconds to see if the server starts and becomes accessible on '127.0.0.1:1337'. 26 | 4. If the server starts within the duration, the test is considered successful. 27 | 5. If the server doesn't start within 60 seconds, or if the Python script encounters an issue, 28 | the test is marked as failed. 29 | 30 | Returns: 31 | None. If the server doesn't start as expected, an assertion error is raised. 32 | """ 33 | # Run the python script in the background 34 | process = subprocess.Popen(["python", "main.py", "127.0.0.1"]) 35 | 36 | # Give an initial delay, assuming the server might not start instantly 37 | time.sleep(2) 38 | 39 | # Check if the server starts within 60 seconds 40 | for _ in range(60): 41 | if is_port_open("127.0.0.1", 1337): 42 | # If server started, end the test as successful 43 | process.terminate() 44 | return 45 | 46 | time.sleep(1) 47 | 48 | # If the server didn't start within 60 seconds, or if the python process had a problem 49 | process.terminate() 50 | assert False, "Server did not start within 60 seconds or had an error" 51 | 52 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/utils/__init__.py -------------------------------------------------------------------------------- /utils/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | class Logger: 4 | 5 | def __init__(self, name, log_level=logging.INFO): 6 | self.logger = logging.getLogger(name) 7 | self.logger.setLevel(log_level) 8 | 9 | # Create a file handler for writing log files 10 | log_file_path = 'runtime.log' 11 | file_handler = logging.FileHandler(log_file_path) 12 | 13 | # Create a console handler for printing logs to the console 14 | console_handler = logging.StreamHandler() 15 | 16 | # Create a formatter and set it for both handlers 17 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 18 | file_handler.setFormatter(formatter) 19 | console_handler.setFormatter(formatter) 20 | 21 | # Add both handlers to the logger 22 | self.logger.addHandler(file_handler) 23 | self.logger.addHandler(console_handler) 24 | 25 | def get_logger(self): 26 | return self.logger 27 | -------------------------------------------------------------------------------- /webapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidalami/VulnMapAI/25272f3aefa56545beb620409f64eadd8a8ee909/webapp/__init__.py -------------------------------------------------------------------------------- /webapp/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import openai 4 | 5 | from flask import ( 6 | Flask, 7 | render_template, 8 | send_from_directory, 9 | request, jsonify 10 | ) 11 | 12 | from config import REPORT_DIR 13 | 14 | app = Flask(__name__) 15 | 16 | app.config['REPORT_FOLDER'] = str(REPORT_DIR.absolute()) 17 | 18 | 19 | @app.route('/') 20 | def index(): 21 | """ 22 | Serve the main index page which displays a list of all HTML reports. 23 | 24 | The function scans the report folder as specified in the app's configuration 25 | for all HTML files and then renders them in the `index.html` template. 26 | 27 | Returns: 28 | Rendered HTML template with the list of HTML report files. 29 | """ 30 | files = [f for f in os.listdir(app.config['REPORT_FOLDER']) if f.endswith('.json')] 31 | return render_template("index.html", files=files) 32 | 33 | 34 | @app.route('/report/') 35 | def serve_report(filename): 36 | """ 37 | Serves a specific report file from the configured report directory. 38 | 39 | Returns: 40 | The requested report file. 41 | """ 42 | kwargs = json.load(open(os.path.join(app.config['REPORT_FOLDER'], filename))) 43 | return render_template("report_template.html", **kwargs) 44 | 45 | 46 | @app.route('/ask-chatgpt', methods=['POST']) 47 | def ask_chatgpt(): 48 | """ 49 | Handle POST requests to obtain responses from ChatGPT. 50 | 51 | This function is responsible for processing POST requests containing user queries 52 | and sending these queries to the ChatGPT API for responses. It then returns the 53 | generated response in a JSON format. 54 | 55 | Returns: 56 | JSON response containing the ChatGPT response to the user's query. 57 | """ 58 | # Extract the user's query from the POST request data 59 | user_query = request.form['query'] 60 | 61 | # Check if the 'query' parameter is provided in the request 62 | if not user_query: 63 | return jsonify({"error": "Please provide a 'query' parameter in the POST request."}), 400 64 | 65 | try: 66 | # Make a request to the ChatGPT API 67 | response = openai.Completion.create( 68 | engine="text-davinci-003", # You can choose a different engine if needed 69 | prompt=user_query, 70 | max_tokens=50, # Adjust the max tokens as per your requirements 71 | n=1 # Number of responses to generate 72 | ) 73 | 74 | # Extract the response text from the API response 75 | chatgpt_response = response.choices[0].text 76 | 77 | # Return the ChatGPT response as JSON 78 | return jsonify({"response": chatgpt_response}) 79 | 80 | except Exception as e: 81 | # Handle any exceptions that may occur during the API request 82 | return jsonify({"error": str(e)}), 500 83 | 84 | 85 | if __name__ == '__main__': 86 | app.run() 87 | -------------------------------------------------------------------------------- /webapp/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Report Index 6 | 7 | 8 | 9 |
10 |

Report Index

11 | 16 |
17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /webapp/templates/report_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | 7 | 8 | 9 | 10 |
11 |

{{ title }}

12 |
13 |

Discovery summary:

14 |
15 |
16 |

{{ exploits|join("
") }}

17 |

{{ discoveries }}

18 |
19 |
20 | 21 |
22 |

Attack Advice:

23 |
24 |

{{ advise }}

25 |
26 | 27 |
28 |

Full Discovery Result:

29 |
30 |

31 | {% for key, value in full_discovery_result.items() %} 32 | {{ key }}: {{ value }}
33 | {% endfor %} 34 |

35 |
36 |
37 | 38 | 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------