├── CNAME ├── fsociety ├── core │ ├── __init__.py │ ├── usernames.py │ ├── hosts.py │ ├── config.py │ ├── utilities.py │ ├── menu.py │ └── repo.py ├── __init__.py ├── __version__.py ├── networking │ ├── __init__.py │ ├── cli.py │ ├── bettercap.py │ └── nmap.py ├── passwords │ ├── __init__.py │ ├── cli.py │ ├── cupp.py │ ├── hash_buster.py │ ├── cr3dov3r.py │ └── changeme.py ├── web_apps │ ├── __init__.py │ ├── cli.py │ ├── photon.py │ └── xsstrike.py ├── obfuscation │ ├── __init__.py │ ├── cli.py │ └── cuteit.py ├── information_gathering │ ├── __init__.py │ ├── cli.py │ ├── striker.py │ ├── sublist3r.py │ ├── hydrarecon.py │ ├── sqlmap.py │ ├── s3scanner.py │ ├── sherlock.py │ └── gitgraber.py ├── console.py └── cli.py ├── requirements.txt ├── images ├── cli.png ├── logo.png └── fsociety.png ├── .github ├── FUNDING.yml ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── ---tool-request.md │ └── ---bug-report.md ├── dependabot.yml ├── workflows │ ├── pylint.yml │ └── python-publish.yml └── CODE_OF_CONDUCT.md ├── Dockerfile ├── _config.yml ├── LICENSE ├── PACKAGES.md ├── README.md ├── .gitignore ├── setup.py ├── CHANGELOG.md └── .pylintrc /CNAME: -------------------------------------------------------------------------------- 1 | fsociety.dev -------------------------------------------------------------------------------- /fsociety/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | rich>=2.0.0 2 | requests>=2.23.0 3 | GitPython>=3.1.3 -------------------------------------------------------------------------------- /fsociety/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import main as cli 2 | 3 | __all__ = ["cli"] 4 | -------------------------------------------------------------------------------- /images/cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agent00049/fsociety/master/images/cli.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agent00049/fsociety/master/images/logo.png -------------------------------------------------------------------------------- /fsociety/__version__.py: -------------------------------------------------------------------------------- 1 | VERSION = (3, 2, 3) 2 | 3 | __version__ = '.'.join(map(str, VERSION)) 4 | -------------------------------------------------------------------------------- /images/fsociety.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Agent00049/fsociety/master/images/fsociety.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: thehappydinoa 4 | -------------------------------------------------------------------------------- /fsociety/networking/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli, __tools__ 2 | 3 | __all__ = ["cli", "__tools__"] + [str(tool) for tool in __tools__] 4 | -------------------------------------------------------------------------------- /fsociety/passwords/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli, __tools__ 2 | 3 | __all__ = ["cli", "__tools__"] + [str(tool) for tool in __tools__] 4 | -------------------------------------------------------------------------------- /fsociety/web_apps/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli, __tools__ 2 | 3 | __all__ = ["cli", "__tools__"] + [str(tool) for tool in __tools__] 4 | -------------------------------------------------------------------------------- /fsociety/obfuscation/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli, __tools__ 2 | 3 | __all__ = ["cli", "__tools__"] + [str(tool) for tool in __tools__] 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | COPY . /fsociety 3 | WORKDIR /fsociety 4 | RUN apk --update add git nmap 5 | RUN pip install -e . 6 | CMD fsociety --info -------------------------------------------------------------------------------- /fsociety/information_gathering/__init__.py: -------------------------------------------------------------------------------- 1 | from .cli import cli, __tools__ 2 | 3 | __all__ = ["cli", "__tools__"] + [str(tool) for tool in __tools__] 4 | -------------------------------------------------------------------------------- /fsociety/obfuscation/cli.py: -------------------------------------------------------------------------------- 1 | # Core 2 | from fsociety.core.menu import tools_cli 3 | 4 | from .cuteit import cuteit 5 | 6 | __tools__ = [cuteit] 7 | 8 | 9 | def cli(): 10 | tools_cli(__name__, __tools__) 11 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | **Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) 2 | -------------------------------------------------------------------------------- /fsociety/networking/cli.py: -------------------------------------------------------------------------------- 1 | # Core 2 | from fsociety.core.menu import tools_cli 3 | 4 | from .nmap import nmap 5 | from .bettercap import bettercap 6 | 7 | __tools__ = [nmap, bettercap] 8 | 9 | 10 | def cli(): 11 | tools_cli(__name__, __tools__) 12 | -------------------------------------------------------------------------------- /fsociety/web_apps/cli.py: -------------------------------------------------------------------------------- 1 | # Core 2 | from fsociety.core.menu import tools_cli 3 | 4 | from .xsstrike import xsstrike 5 | from .photon import photon 6 | 7 | __tools__ = [xsstrike, photon] 8 | 9 | 10 | def cli(): 11 | tools_cli(__name__, __tools__) 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---tool-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "⛏️ Tool request" 3 | about: Suggest an tool for fsociety 4 | title: '' 5 | labels: tool 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Link to Tool** 11 | https://github.com/really_cool_project 12 | 13 | **Why?** 14 | _Why should we add this tool?_ 15 | -------------------------------------------------------------------------------- /fsociety/console.py: -------------------------------------------------------------------------------- 1 | from rich.console import Console 2 | from rich.theme import Theme 3 | from rich.traceback import install 4 | 5 | # Install Traceback 6 | install() 7 | 8 | # Console Setup 9 | fsociety_theme = Theme({ 10 | "command": "black on white", 11 | "warning": "bold yellow", 12 | }) 13 | console = Console(theme=fsociety_theme) 14 | -------------------------------------------------------------------------------- /fsociety/passwords/cli.py: -------------------------------------------------------------------------------- 1 | # Core 2 | from fsociety.core.menu import tools_cli 3 | 4 | from .cupp import cupp 5 | from .cr3dov3r import cr3dov3r 6 | from .hash_buster import hash_buster 7 | from .changeme import changeme 8 | 9 | __tools__ = [cupp, cr3dov3r, hash_buster, changeme] 10 | 11 | 12 | def cli(): 13 | tools_cli(__name__, __tools__) 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Which Tool?** 11 | A clear and concise description of what the bug is. 12 | 13 | **What is the error?** 14 | Stacktrace: 15 | 16 | ```bash 17 | ``` 18 | 19 | **fsociety info** 20 | Run `fsociety --info` in your terminal and copy the results here. 21 | 22 | ```bash 23 | ``` 24 | -------------------------------------------------------------------------------- /fsociety/passwords/cupp.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | 5 | 6 | class CuppRepo(GitHubRepo): 7 | def __init__(self): 8 | super().__init__(path="Mebus/cupp", 9 | install=None, 10 | description="Common User Passwords Profiler") 11 | 12 | def run(self): 13 | os.chdir(self.full_path) 14 | return os.system("python3 cupp.py -i") 15 | 16 | 17 | cupp = CuppRepo() 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /fsociety/information_gathering/cli.py: -------------------------------------------------------------------------------- 1 | # Core 2 | from fsociety.core.menu import tools_cli 3 | 4 | from .sqlmap import sqlmap 5 | from .striker import striker 6 | from .sublist3r import sublist3r 7 | from .sherlock import sherlock 8 | from .s3scanner import s3scanner 9 | from .gitgraber import gitgraber 10 | from .hydrarecon import hydrarecon 11 | 12 | __tools__ = [sqlmap, striker, sublist3r, 13 | sherlock, s3scanner, gitgraber, hydrarecon] 14 | 15 | 16 | def cli(): 17 | tools_cli(__name__, __tools__) 18 | -------------------------------------------------------------------------------- /fsociety/obfuscation/cuteit.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | 5 | 6 | class CuteitRepo(GitHubRepo): 7 | def __init__(self): 8 | super().__init__( 9 | path="D4Vinci/Cuteit", 10 | install=None, 11 | description="IP obfuscator made to make a malicious ip a bit cuter" 12 | ) 13 | 14 | def run(self): 15 | os.chdir(self.full_path) 16 | user_ip = input("\nEnter a ip: ").strip() 17 | return os.system(f"python3 Cuteit.py {user_ip}") 18 | 19 | 20 | cuteit = CuteitRepo() 21 | -------------------------------------------------------------------------------- /fsociety/passwords/hash_buster.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | 5 | 6 | class HashBusterRepo(GitHubRepo): 7 | def __init__(self): 8 | super().__init__( 9 | path="s0md3v/Hash-Buster", 10 | install=None, 11 | description="Why crack hashes when you can bust them?") 12 | 13 | def run(self): 14 | os.chdir(self.full_path) 15 | user_hash = input("\nEnter a hash: ").strip() 16 | return os.system(f"python3 hash.py -s {user_hash}") 17 | 18 | 19 | hash_buster = HashBusterRepo() 20 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: fsociety 2 | tagline: A Penetration Testing Framework 3 | author: fsociety-team 4 | url: https://fsociety.dev 5 | permalink: pretty 6 | 7 | remote_theme: pmarsceill/just-the-docs 8 | color_scheme: nil 9 | logo: "/images/logo.png" 10 | aux_links: 11 | "fsociety on GitHub": 12 | - "https://github.com/fsociety-team/fsociety" 13 | footer_content: "Copyright © 2020 fsociety-team. Distributed by an MIT license." 14 | 15 | ga_tracking: UA-111350566-3 16 | 17 | plugins: 18 | - jekyll-seo-tag 19 | -------------------------------------------------------------------------------- /fsociety/core/usernames.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from fsociety.core.config import INSTALL_DIR, get_config 4 | 5 | config = get_config() 6 | 7 | full_path = os.path.join(INSTALL_DIR, config.get("fsociety", "usernames_file")) 8 | 9 | 10 | def get_usernames(): 11 | try: 12 | with open(full_path, "r") as usernamefile: 13 | return [username.strip() for username in usernamefile] 14 | except FileNotFoundError: 15 | return list() 16 | 17 | 18 | def add_username(username): 19 | with open(full_path, "a") as usernamefile: 20 | usernamefile.write(f"\n{username}") 21 | -------------------------------------------------------------------------------- /fsociety/core/hosts.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | from fsociety.core.config import INSTALL_DIR, get_config 4 | 5 | config = get_config() 6 | 7 | full_path = os.path.join(INSTALL_DIR, config.get("fsociety", "host_file")) 8 | 9 | 10 | class InvalidHost(Exception): 11 | pass 12 | 13 | 14 | def get_hosts(): 15 | try: 16 | with open(full_path, "r") as hostfile: 17 | return [host.strip() for host in hostfile] 18 | except FileNotFoundError: 19 | return list() 20 | 21 | 22 | def add_host(host): 23 | if not host: 24 | raise ValueError 25 | with open(full_path, "a") as hostfile: 26 | hostfile.write(f"\n{host}") 27 | -------------------------------------------------------------------------------- /fsociety/passwords/cr3dov3r.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline 5 | 6 | 7 | class Cr3dov3rRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__( 10 | path="D4Vinci/Cr3dOv3r", 11 | install={"pip": "requirements.txt"}, 12 | description="Your best friend in credential reuse attacks") 13 | 14 | def run(self): 15 | os.chdir(self.full_path) 16 | set_readline([]) 17 | user_email = input("\nEnter a email: ").strip() 18 | return os.system(f"python3 Cr3d0v3r.py {user_email}") 19 | 20 | 21 | cr3dov3r = Cr3dov3rRepo() 22 | -------------------------------------------------------------------------------- /fsociety/information_gathering/striker.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline 5 | 6 | 7 | class StrikerRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__(path="s0md3v/Striker", 10 | install={"pip": "requirements.txt"}, 11 | description="Recon & Vulnerability Scanning Suite") 12 | 13 | def run(self): 14 | os.chdir(self.full_path) 15 | set_readline([]) 16 | user_domain = input("\nEnter a domain to scan: ").strip() 17 | return os.system(f"python3 striker.py {user_domain}") 18 | 19 | 20 | striker = StrikerRepo() 21 | -------------------------------------------------------------------------------- /fsociety/information_gathering/sublist3r.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline 5 | 6 | 7 | class Sublist3rRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__( 10 | path="aboul3la/Sublist3r", 11 | install={"pip": "requirements.txt"}, 12 | description="Fast subdomains enumeration tool for penetration testers") 13 | 14 | def run(self): 15 | os.chdir(self.full_path) 16 | set_readline([]) 17 | user_domain = input("\nEnter a domain to enumerate: ").strip() 18 | return os.system(f"python3 sublist3r.py -v -d {user_domain}") 19 | 20 | 21 | sublist3r = Sublist3rRepo() 22 | -------------------------------------------------------------------------------- /fsociety/information_gathering/hydrarecon.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline, confirm 5 | 6 | 7 | class HydrareconRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__(path="aufzayed/HydraRecon", 10 | install={"pip": "requirements.txt"}, 11 | description="Simple recon tool") 12 | 13 | def run(self): 14 | os.chdir(self.full_path) 15 | set_readline([]) 16 | user_domain = input("\nEnter a domain to scan: ").strip() 17 | arg = "--basic" 18 | if confirm("\nDo you want to crawl? [default=No]"): 19 | arg = "--crawl" 20 | return os.system(f"python3 hydrarecon.py {arg} -d {user_domain}") 21 | 22 | 23 | hydrarecon = HydrareconRepo() 24 | -------------------------------------------------------------------------------- /fsociety/information_gathering/sqlmap.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import confirm 5 | 6 | 7 | class SqlmapRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__( 10 | path="sqlmapproject/sqlmap", 11 | install=None, 12 | description="Automatic SQL injection and database takeover tool") 13 | 14 | def run(self): 15 | os.chdir(self.full_path) 16 | user_url = input("\nEnter a url to scan: ").strip() 17 | user_args = str() 18 | if confirm("\nDo you want to add any extra args?"): 19 | os.system("python3 sqlmap.py --help") 20 | user_args = input("\nEnter any extra args: ").strip() 21 | return os.system(f"python3 sqlmap.py -u {user_url} {user_args}") 22 | 23 | 24 | sqlmap = SqlmapRepo() 25 | -------------------------------------------------------------------------------- /fsociety/web_apps/photon.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import confirm 5 | 6 | 7 | class PhotonRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__( 10 | path="s0md3v/Photon", 11 | install={"pip": "requirements.txt"}, 12 | description="Incredibly fast crawler designed for OSINT") 13 | 14 | def run(self): 15 | os.chdir(self.full_path) 16 | user_url = input("\nEnter a url to scan: ").strip() 17 | args = list() 18 | if confirm("Do you want to clone the site?"): 19 | args.append("--clone") 20 | if confirm("Do you want to use wayback?"): 21 | args.append("--wayback") 22 | args_str = " ".join(args) 23 | return os.system(f"python3 photon.py --url {user_url} {args_str}") 24 | 25 | 26 | photon = PhotonRepo() 27 | -------------------------------------------------------------------------------- /fsociety/passwords/changeme.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline 5 | from fsociety.core.hosts import get_hosts, add_host, InvalidHost 6 | 7 | 8 | class ChangemeRepo(GitHubRepo): 9 | def __init__(self): 10 | super().__init__(path="ztgrace/changeme", 11 | install={"pip": "requirements.txt"}, 12 | description="A default credential scanner") 13 | 14 | def run(self): 15 | os.chdir(self.full_path) 16 | hosts = get_hosts() 17 | set_readline(hosts) 18 | user_host = input("\nEnter a host: ").strip() 19 | if not user_host: 20 | raise InvalidHost 21 | if user_host not in hosts: 22 | add_host(user_host) 23 | return os.system(f"python3 changeme.py {user_host}") 24 | 25 | 26 | changeme = ChangemeRepo() 27 | -------------------------------------------------------------------------------- /fsociety/web_apps/xsstrike.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import confirm 5 | 6 | 7 | class XsstrikeRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__(path="s0md3v/XSStrike", 10 | install={"pip": "requirements.txt"}, 11 | description="Advanced XSS Detection Suite") 12 | 13 | def run(self): 14 | os.chdir(self.full_path) 15 | user_url = input("\nEnter a url to scan: ").strip() 16 | args = list() 17 | if confirm("Do you want to crawl?"): 18 | args.append("--crawl") 19 | if confirm("Do you want to find hidden parameters?"): 20 | args.append("--params") 21 | args_str = " ".join(args) 22 | return os.system(f"python3 xsstrike.py --url {user_url} {args_str}") 23 | 24 | 25 | xsstrike = XsstrikeRepo() 26 | -------------------------------------------------------------------------------- /fsociety/information_gathering/s3scanner.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline 5 | from fsociety.core.config import INSTALL_DIR 6 | 7 | 8 | class S3scannerRepo(GitHubRepo): 9 | def __init__(self): 10 | super().__init__( 11 | path="sa7mon/S3Scanner", 12 | install={"pip": "requirements.txt"}, 13 | description="A tool to find open S3 buckets and dump their contents") 14 | 15 | def run(self): 16 | os.chdir(self.full_path) 17 | set_readline([]) 18 | user_domains = input("\nEnter one or more domains: ").strip() 19 | txt_path = os.path.join(INSTALL_DIR, "s3_domains.txt") 20 | with open(txt_path, "w") as domains_file: 21 | for domain in user_domains.split(): 22 | domains_file.write(domain) 23 | return os.system(f"python3 s3scanner.py {txt_path}") 24 | 25 | 26 | s3scanner = S3scannerRepo() 27 | -------------------------------------------------------------------------------- /fsociety/information_gathering/sherlock.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline 5 | from fsociety.core.usernames import get_usernames, add_username 6 | 7 | 8 | class SherlockRepo(GitHubRepo): 9 | def __init__(self): 10 | super().__init__( 11 | path="sherlock-project/sherlock", 12 | install={"pip": "requirements.txt"}, 13 | description="Hunt down social media accounts by username across social networks" 14 | ) 15 | 16 | def run(self): 17 | os.chdir(self.full_path) 18 | usernames = get_usernames() 19 | set_readline(usernames) 20 | user_usernames = input("\nEnter one or more usernames: ").strip() 21 | for username in user_usernames.split(): 22 | if not username in usernames: 23 | add_username(username) 24 | return os.system(f"python3 sherlock {user_usernames} -r") 25 | 26 | 27 | sherlock = SherlockRepo() 28 | -------------------------------------------------------------------------------- /fsociety/networking/bettercap.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | import os 3 | from shutil import which 4 | 5 | from fsociety.core.repo import GitHubRepo 6 | 7 | 8 | class BettercapRepo(GitHubRepo): 9 | def __init__(self): 10 | super().__init__( 11 | path="bettercap/bettercap", 12 | install={ 13 | "linux": 14 | "sudo apt install golang git build-essential libpcap-dev libusb-1.0-0-dev libnetfilter-queue-dev; go get -u github.com/bettercap/bettercap", 15 | "brew": "install bettercap" 16 | }, 17 | description="Swiss army knife for network attacks and monitoring") 18 | 19 | def installed(self): 20 | return which("bettercap") 21 | 22 | def install(self): 23 | super().install(clone=False) 24 | 25 | def run(self): 26 | print("Please note that bettercap must be run with sudo") 27 | return os.system("sudo bettercap") 28 | 29 | 30 | bettercap = BettercapRepo() 31 | -------------------------------------------------------------------------------- /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Pylint 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | # events but only for the master branch 7 | on: 8 | push: 9 | paths: 10 | - "*/**.py" 11 | pull_request: 12 | paths: 13 | - "*/**.py" 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | # This workflow contains a single job called "build" 18 | build: 19 | # The type of runner that the job will run on 20 | runs-on: ubuntu-latest 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | - name: Install apt 25 | run: sudo apt-get install libffi-dev 26 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 27 | - uses: actions/checkout@v2 28 | 29 | - name: GitHub Action for pylint 30 | uses: cclauss/GitHub-Action-for-pylint@0.7.0 31 | with: 32 | args: "pip install .;pip install .[dev]; pylint **/*.py" 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 fsociety 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 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | push: 8 | tags: 9 | - v* 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 3.7 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: 3.7 21 | - name: Test install 22 | run: | 23 | pip install -e . 24 | fsociety --info 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install setuptools wheel twine 29 | - name: Build and publish 30 | env: 31 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 32 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 33 | run: | 34 | python setup.py sdist bdist_wheel --universal 35 | twine upload dist/* 36 | -------------------------------------------------------------------------------- /fsociety/information_gathering/gitgraber.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from fsociety.core.repo import GitHubRepo 4 | from fsociety.core.menu import set_readline, confirm 5 | 6 | 7 | class GitgraberRepo(GitHubRepo): 8 | def __init__(self): 9 | super().__init__( 10 | path="hisxo/gitGraber", 11 | install={"pip": "requirements.txt"}, 12 | description="Search and find sensitive data in real time for GitHub" 13 | ) 14 | 15 | def run(self): 16 | os.chdir(self.full_path) 17 | wordlists_path = os.path.join(self.full_path, "wordlists") 18 | set_readline([]) 19 | user_query = input("\nEnter a search query: ").strip() 20 | set_readline([ 21 | f for f in os.listdir(os.path.join(self.full_path, "wordlists")) 22 | if os.path.isfile( 23 | os.path.join(os.path.join(self.full_path, "wordlists"), f)) 24 | ]) 25 | user_keywords = input( 26 | "\nEnter a keywords path [default=keywords.txt]: ").strip() 27 | if not user_keywords: 28 | user_keywords = "keywords.txt" 29 | keywords_path = os.path.join(wordlists_path, user_keywords) 30 | if confirm("\nDo you want to add a GitHub token?"): 31 | github_token = input("\nEnter a GitHub token: ").strip() 32 | os.environ["GITHUB_TOKENS"] = f"['{github_token}']" 33 | return os.system( 34 | f"python3 gitGraber.py -k {keywords_path} -q {user_query}") 35 | 36 | 37 | gitgraber = GitgraberRepo() 38 | -------------------------------------------------------------------------------- /PACKAGES.md: -------------------------------------------------------------------------------- 1 | # Packages 2 | 3 | ## Information Gathering 4 | 5 | - [sqlmap](https://github.com/sqlmapproject/sqlmap) 6 | Automatic SQL injection and database takeover tool 7 | - [Striker](https://github.com/s0md3v/Striker) 8 | Recon & Vulnerability Scanning Suite 9 | - [Sublist3r](https://github.com/aboul3la/Sublist3r) 10 | Fast subdomains enumeration tool for penetration testers 11 | - [sherlock](https://github.com/sherlock-project/sherlock) 12 | Hunt down social media accounts by username across social networks 13 | - [S3Scanner](https://github.com/sa7mon/S3Scanner) 14 | A tool to find open S3 buckets and dump their contents 15 | - [gitGraber](https://github.com/hisxo/gitGraber) 16 | Search and find sensitive data in real time for GitHub 17 | - [HydraRecon](https://github.com/aufzayed/HydraRecon) 18 | Simple recon tool 19 | 20 | ## Networking 21 | 22 | - [nmap](https://github.com/nmap/nmap) 23 | the Network Mapper 24 | - [bettercap](https://github.com/bettercap/bettercap) 25 | Swiss army knife for network attacks and monitoring 26 | 27 | ## Web Apps 28 | 29 | - [XSStrike](https://github.com/s0md3v/XSStrike) 30 | Advanced XSS Detection Suite 31 | - [Photon](https://github.com/s0md3v/Photon) 32 | Incredibly fast crawler designed for OSINT 33 | 34 | ## Passwords 35 | 36 | - [cupp](https://github.com/Mebus/cupp) 37 | Common User Passwords Profiler 38 | - [Cr3d0v3r](https://github.com/D4Vinci/Cr3d0v3r) 39 | Your best friend in credential reuse attacks 40 | - [Hash-Buster](https://github.com/s0md3v/Hash-Buster) 41 | Why crack hashes when you can bust them? 42 | - [changeme](https://github.com/ztgrace/changeme) 43 | A default credential scanner 44 | 45 | ## Obfuscation 46 | 47 | - [Cuteit](https://github.com/D4Vinci/Cuteit) 48 | IP obfuscator made to make a malicious ip a bit cuter 49 | -------------------------------------------------------------------------------- /fsociety/core/config.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | from sys import platform 3 | from pathlib import Path 4 | from configparser import RawConfigParser, NoOptionError 5 | 6 | from fsociety.__version__ import __version__ 7 | 8 | CURRENT_PLATFORM = platform 9 | if platform in ["win32", "cygwin"]: 10 | CURRENT_PLATFORM = "windows" 11 | elif platform.startswith("darwin"): 12 | CURRENT_PLATFORM = "macos" 13 | elif platform.startswith("linux") or platform.startswith("freebsd"): 14 | CURRENT_PLATFORM = "linux" 15 | 16 | INSTALL_DIR = os.path.join(str(Path.home()), ".fsociety") 17 | CONFIG_FILE = os.path.join(INSTALL_DIR, "fsociety.cfg") 18 | GITHUB_PATH = "fsociety-team/fsociety" 19 | 20 | DEFAULT_CONFIG = { 21 | "version": __version__, 22 | "agreement": "false", 23 | "ssh_clone": "false", 24 | "os": CURRENT_PLATFORM, 25 | "host_file": "hosts.txt", 26 | "usernames_file": "usernames.txt" 27 | } 28 | 29 | 30 | def get_config(): 31 | config = RawConfigParser() 32 | if not os.path.exists(INSTALL_DIR): 33 | os.mkdir(INSTALL_DIR) 34 | if not os.path.exists(CONFIG_FILE): 35 | config["fsociety"] = DEFAULT_CONFIG 36 | with open(CONFIG_FILE, "w") as configfile: 37 | config.write(configfile) 38 | config.read(CONFIG_FILE) 39 | check_config(config) 40 | if config.get("fsociety", "version") != __version__: 41 | config.set("fsociety", "version", __version__) 42 | write_config(config) 43 | return config 44 | 45 | 46 | def write_config(config): 47 | with open(CONFIG_FILE, "w") as configfile: 48 | config.write(configfile) 49 | 50 | 51 | def check_config(config): 52 | for key in DEFAULT_CONFIG: 53 | try: 54 | config.get("fsociety", key) 55 | except NoOptionError: 56 | config.set("fsociety", key, DEFAULT_CONFIG.get(key)) 57 | write_config(config) 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fsociety [![Python Version](https://img.shields.io/pypi/pyversions/fsociety?color=orange&style=flat-square)](https://www.python.org/downloads/) [![PyPi](https://img.shields.io/pypi/v/fsociety?style=flat-square)](https://pypi.org/project/fsociety/) ![GitHub repo size](https://img.shields.io/github/languages/code-size/fsociety-team/fsociety?style=flat-square) ![PyPI - Downloads](https://img.shields.io/pypi/dm/fsociety?style=flat-square) [![Docker Cloud build](https://img.shields.io/docker/cloud/build/fsocietyteam/fsociety?style=flat-square)](https://hub.docker.com/r/fsocietyteam/fsociety) 2 | 3 | A Modular Penetration Testing Framework 4 | 5 | [![Packages](https://img.shields.io/badge/PACKAGES.md-red?style=flat-square)](https://github.com/fsociety-team/fsociety/blob/master/PACKAGES.md) [![Changelog](https://img.shields.io/badge/CHANGELOG.md-red?style=flat-square)](https://github.com/fsociety-team/fsociety/blob/master/CHANGELOG.md) 6 | 7 | [![fsociety](https://raw.githubusercontent.com/fsociety-team/fsociety/master/images/fsociety.png)](https://github.com/fsociety-team/fsociety) 8 | 9 | ![cli](https://raw.githubusercontent.com/fsociety-team/fsociety/master/images/cli.png) 10 | 11 | ## Install 12 | 13 | ```bash 14 | pip install fsociety 15 | ``` 16 | 17 | ## Update 18 | 19 | ```bash 20 | pip install --upgrade fsociety 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```bash 26 | usage: fsociety [-h] [-i] [-s] 27 | 28 | A Penetration Testing Framework 29 | 30 | optional arguments: 31 | -h, --help show this help message and exit 32 | -i, --info gets fsociety info 33 | -s, --suggest suggest a tool 34 | ``` 35 | 36 | ## Develop 37 | 38 | ```bash 39 | git clone https://github.com/fsociety-team/fsociety.git 40 | pip install -e ".[dev]" 41 | ``` 42 | 43 | ## Docker 44 | 45 | ```bash 46 | docker pull fsocietyteam/fsociety 47 | docker run -it fsocietyteam/fsociety fsociety 48 | ``` 49 | 50 | ## License 51 | 52 | [![License](https://img.shields.io/pypi/l/fsociety?style=flat-square)](https://github.com/fsociety-team/fsociety/blob/master/LICENSE) 53 | 54 | [![Twitter Follow](https://img.shields.io/badge/fuck%20it-ship%20it-blue?style=flat-square)](https://twitter.com/fsociety_team) [![Twitter Follow](https://img.shields.io/twitter/follow/fsociety_team?color=blue&style=flat-square)](https://twitter.com/fsociety_team) 55 | -------------------------------------------------------------------------------- /fsociety/networking/nmap.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | import os 3 | from shutil import which 4 | 5 | from fsociety.core.repo import GitHubRepo 6 | from fsociety.core.menu import set_readline 7 | from fsociety.core.hosts import get_hosts, add_host, InvalidHost 8 | 9 | premade_args = { 10 | "simple": 11 | "{host}", 12 | "common_ports": 13 | "-F {host}", 14 | "all_ports": 15 | "-p- {host}", 16 | "detect_os": 17 | "-A {host}", 18 | "tcp_syn_scan": 19 | "-sS {host}", 20 | "tcp_connect": 21 | "-sT {host}", 22 | "nse_standard": 23 | "-sV -sC {host}", 24 | "vuln_scan": 25 | "-Pn --script vuln {host}", 26 | "google_malware": 27 | "-p80 --script http-google-malware {host}", 28 | "argressive_scan": 29 | "-A -T4 {host}", 30 | "detect_web_app": 31 | "--script=http-enum {host}", 32 | "subdomain_enumaration": 33 | "-sn --script hostmap-crtsh {host}", 34 | "heartbleed_test": 35 | "-sV -p 443 --script=ssl-heartbleed {host}", 36 | "slowloris": 37 | "-max-parallelism 800 -Pn --script http-slowloris --script-args http-slowloris.runforever=true {host}" 38 | } 39 | 40 | 41 | class NmapRepo(GitHubRepo): 42 | def __init__(self): 43 | super().__init__(path="nmap/nmap", 44 | install={ 45 | "linux": "sudo apt-get install nmap", 46 | "brew": "install nmap" 47 | }, 48 | description="the Network Mapper") 49 | 50 | def installed(self): 51 | return which("nmap") 52 | 53 | def install(self): 54 | super().install(clone=False) 55 | 56 | def run(self): 57 | hosts = get_hosts() 58 | set_readline(hosts) 59 | host = input("\nEnter a host: ").strip() 60 | if not host: 61 | raise InvalidHost 62 | if host not in hosts: 63 | add_host(host) 64 | longest_key = max([len(key) for key in premade_args]) + 2 65 | print("\nName".ljust(longest_key) + " | Args") 66 | for name, args in premade_args.items(): 67 | print(f"{name.ljust(longest_key)}: {args.format(host=host)}") 68 | set_readline(premade_args.keys()) 69 | selected = input("\nMake a selection: ") 70 | if selected and selected in premade_args.keys(): 71 | args = premade_args.get(selected).format(host=host) 72 | return os.system(f"nmap {args}") 73 | return self.run() 74 | 75 | 76 | nmap = NmapRepo() 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # VSCord 132 | .vscode/ 133 | 134 | # macOS 135 | .DS_Store 136 | -------------------------------------------------------------------------------- /fsociety/core/utilities.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=invalid-name,line-too-long 2 | import os 3 | from base64 import b64decode 4 | from socket import gethostbyname 5 | from webbrowser import open_new_tab 6 | from abc import ABCMeta, abstractmethod 7 | 8 | from requests import get 9 | 10 | from fsociety.console import console 11 | 12 | from .menu import set_readline, tools_cli 13 | from .config import INSTALL_DIR, GITHUB_PATH 14 | from .hosts import get_hosts, add_host 15 | 16 | 17 | class Utility(metaclass=ABCMeta): 18 | def __init__(self, description=None): 19 | self.description = description 20 | 21 | def __str__(self): 22 | return self.__class__.__name__ 23 | 24 | @abstractmethod 25 | def run(self): 26 | pass 27 | 28 | 29 | class host2ip(Utility): 30 | def __init__(self): 31 | super().__init__(description="Gets IP from host") 32 | 33 | def run(self): 34 | hosts = get_hosts() 35 | set_readline(hosts) 36 | user_host = input("\nEnter a host: ").strip() 37 | if not user_host in hosts: 38 | add_host(user_host) 39 | ip = gethostbyname(user_host) 40 | console.print(f"\n{user_host} has the IP of {ip}") 41 | 42 | 43 | class base64_decode(Utility): 44 | def __init__(self): 45 | super().__init__(description="Decodes base64") 46 | 47 | def run(self): 48 | user_base64 = input("\nEnter base64: ").strip() 49 | text = b64decode(user_base64) 50 | console.print(f"\nDecoded that is: {text}") 51 | 52 | 53 | class spawn_shell(Utility): 54 | def __init__(self): 55 | super().__init__(description="Spawns a local shell") 56 | 57 | def run(self): 58 | console.print("Enter `exit` to return to fsociety") 59 | shell = os.getenv("SHELL", "/bin/bash") 60 | os.chdir(INSTALL_DIR) 61 | os.system(shell) 62 | 63 | 64 | class suggest_tool(Utility): 65 | def __init__(self): 66 | super().__init__(description="Suggest a tool or utility") 67 | 68 | def run(self): 69 | open_new_tab( 70 | f"https://github.com/{GITHUB_PATH}/issues/new?assignees=&labels=tool&template=---tool-request.md&title=" 71 | ) 72 | 73 | 74 | class print_contributors(Utility): 75 | def __init__(self): 76 | super().__init__(description="Prints the usernames of our devs") 77 | 78 | def run(self): 79 | console.print(""" 80 | 8888b. 888888 Yb dP .dP"Y8 81 | 8I Yb 88__ Yb dP `Ybo." 82 | 8I dY 88"" YbdP o.`Y8b 83 | 8888Y" 888888 YP 8bodP' 84 | """, style="bold yellow", highlight=False) 85 | response = get( 86 | f"https://api.github.com/repos/{GITHUB_PATH}/contributors") 87 | contributors = response.json() 88 | for contributor in sorted(contributors, 89 | key=lambda c: c['contributions'], 90 | reverse=True): 91 | username = contributor.get("login") 92 | console.print(f" {username} ".center(30, "-")) 93 | 94 | 95 | __tools__ = [ 96 | tool() for tool in 97 | [host2ip, base64_decode, spawn_shell, suggest_tool, print_contributors] 98 | ] 99 | 100 | 101 | def cli(): 102 | tools_cli(__name__, __tools__, links=False) 103 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@fsociety.dev. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | 77 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import io 5 | import os 6 | import sys 7 | 8 | from setuptools import find_packages, setup, Command 9 | 10 | # Package meta-data. 11 | NAME = 'fsociety' 12 | DESCRIPTION = 'A Modular Penetration Testing Framework' 13 | URL = 'https://fsociety.dev/' 14 | PROJECT_URLS = { 15 | 'Packages': 'https://github.com/fsociety-team/fsociety/blob/master/PACKAGES.md', 16 | 'Changelog': 'https://github.com/fsociety-team/fsociety/blob/master/CHANGELOG.md', 17 | 'Funding': 'https://github.com/sponsors/thehappydinoa', 18 | 'Tracker': 'https://github.com/fsociety-team/fsociety/issues', 19 | 'Source': 'https://github.com/fsociety-team/fsociety', 20 | } 21 | EMAIL = 'contact@fsociety.dev' 22 | AUTHOR = 'fsociety-team' 23 | REQUIRES_PYTHON = '>=3.7.0' 24 | VERSION = None 25 | 26 | # Required Packages 27 | REQUIRED = ['gitpython', 'rich', 'requests'] 28 | 29 | # Optional Packages 30 | EXTRAS = { 31 | 'dev': [ 32 | 'wheel', 33 | 'pylint', 34 | 'autopep8', 35 | 'twine', 36 | ] 37 | } 38 | 39 | here = os.path.abspath(os.path.dirname(__file__)) 40 | 41 | try: 42 | with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f: 43 | long_description = '\n' + f.read() 44 | except FileNotFoundError: 45 | long_description = DESCRIPTION 46 | 47 | about = {} 48 | if not VERSION: 49 | project_slug = NAME.lower().replace("-", "_").replace(" ", "_") 50 | with open(os.path.join(here, project_slug, '__version__.py')) as f: 51 | exec(f.read(), about) 52 | else: 53 | about['__version__'] = VERSION 54 | 55 | 56 | class TagCommand(Command): 57 | """Support setup.py push_tag.""" 58 | 59 | description = 'Push latest version as tag.' 60 | user_options = [] 61 | 62 | @staticmethod 63 | def status(s): 64 | print('\033[1m{0}\033[0m'.format(s)) 65 | 66 | def initialize_options(self): 67 | pass 68 | 69 | def finalize_options(self): 70 | pass 71 | 72 | def run(self): 73 | self.status('Pushing git tags…') 74 | os.system('git tag v{0}'.format(about['__version__'])) 75 | os.system('git push --tags') 76 | 77 | sys.exit() 78 | 79 | 80 | setup( 81 | name=NAME, 82 | version=about['__version__'], 83 | description=DESCRIPTION, 84 | long_description=long_description, 85 | long_description_content_type='text/markdown', 86 | author=AUTHOR, 87 | author_email=EMAIL, 88 | python_requires=REQUIRES_PYTHON, 89 | url=URL, 90 | project_urls=PROJECT_URLS, 91 | packages=find_packages( 92 | exclude=["tests", "*.tests", "*.tests.*", "tests.*"]), 93 | entry_points={ 94 | 'console_scripts': ['fsociety=fsociety:cli'], 95 | }, 96 | install_requires=REQUIRED, 97 | extras_require=EXTRAS, 98 | include_package_data=True, 99 | license='MIT', 100 | classifiers=[ 101 | # Trove classifiers 102 | # Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers 103 | 'Topic :: Security', 104 | 'License :: OSI Approved :: MIT License', 105 | 'Programming Language :: Python', 106 | 'Programming Language :: Python :: 3 :: Only', 107 | 'Programming Language :: Python :: 3', 108 | 'Programming Language :: Python :: 3.7', 109 | 'Programming Language :: Python :: 3.8', 110 | 'Programming Language :: Python :: Implementation :: PyPy' 111 | ], 112 | # python setup.py upload 113 | cmdclass={ 114 | 'push_tag': TagCommand, 115 | }, 116 | ) 117 | -------------------------------------------------------------------------------- /fsociety/core/menu.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=unused-import,broad-except,inconsistent-return-statements 2 | import os 3 | import shutil 4 | 5 | from rich.text import Text 6 | from rich.table import Table 7 | from rich.style import Style 8 | from rich import box 9 | 10 | from fsociety.console import console 11 | from fsociety.core.config import INSTALL_DIR 12 | 13 | BACK_COMMANDS = ["back", "return"] 14 | 15 | 16 | class CommandCompleter(): 17 | def __init__(self, options): 18 | self.options = sorted(options) 19 | self.matches = list() 20 | 21 | def complete(self, text, state): 22 | response = None 23 | if state == 0: 24 | if text: 25 | self.matches = [ 26 | s for s in self.options if s and s.startswith(text) 27 | ] 28 | else: 29 | self.matches = self.options[:] 30 | try: 31 | response = self.matches[state] 32 | except IndexError: 33 | response = None 34 | return response 35 | 36 | 37 | def set_readline(items): 38 | try: 39 | import readline 40 | except ImportError: 41 | pass 42 | else: 43 | import rlcompleter 44 | if isinstance(items, list): 45 | readline.set_completer(CommandCompleter(items).complete) 46 | elif isinstance(items, dict): 47 | readline.set_completer(CommandCompleter(items.keys()).complete) 48 | else: 49 | readline.set_completer(CommandCompleter(list(items)).complete) 50 | readline.parse_and_bind("tab: complete") 51 | 52 | 53 | def clear_screen(): 54 | os.system('cls' if os.name == 'nt' else 'clear') 55 | 56 | 57 | def format_tools(tools): 58 | cutoff = 5 59 | etc = False 60 | if len(tools) > cutoff: 61 | tools = tools[:cutoff] 62 | etc = True 63 | res = "".join([f"\n{str(tool)}" for tool in tools]) 64 | if etc: 65 | res += "\n..." 66 | return res 67 | 68 | 69 | def module_name(module): 70 | return module.__name__.split(".")[-1] 71 | 72 | 73 | def prompt(path="", base_path="~"): 74 | encoded_path = os.path.join(base_path, path, "") 75 | return f"\nfsociety {encoded_path}# " 76 | 77 | 78 | def input_wait(): 79 | input("\nPress [ENTER] to continue... ") 80 | 81 | 82 | def tools_cli(name, tools, links=True): 83 | table = Table(box=box.HEAVY_HEAD) 84 | table.add_column("Name", style="red", no_wrap=True) 85 | table.add_column("Description", style="magenta") 86 | if links: 87 | table.add_column("Link", no_wrap=True) 88 | 89 | tools_dict = dict() 90 | for tool in tools: 91 | tools_dict[str(tool)] = tool 92 | args = [str(tool), tool.description] 93 | if links: 94 | text_link = Text(f"{tool.path}") 95 | text_link.stylize_all( 96 | Style(link=f"https://github.com/{tool.path}")) 97 | args.append(text_link) 98 | table.add_row(*args) 99 | 100 | console.print(table) 101 | console.print("back", style="command") 102 | set_readline(list(tools_dict.keys()) + BACK_COMMANDS) 103 | selected_tool = input(prompt(name.split(".")[-2])).strip() 104 | if not selected_tool in tools_dict.keys(): 105 | if selected_tool in BACK_COMMANDS: 106 | return 107 | console.print("Invalid Command", style="bold yellow") 108 | return tools_cli(name, tools, links) 109 | tool = tools_dict.get(selected_tool) 110 | if hasattr(tool, "install") and not tool.installed(): 111 | tool.install() 112 | try: 113 | response = tool.run() 114 | if response and response > 0 and response != 256: 115 | console.print( 116 | f"{selected_tool} returned a non-zero exit code", style="bold red") 117 | if hasattr(tool, "install") and confirm("Do you want to reinstall?"): 118 | os.chdir(INSTALL_DIR) 119 | shutil.rmtree(tool.full_path) 120 | tool.install() 121 | except KeyboardInterrupt: 122 | return 123 | 124 | return input_wait() 125 | 126 | 127 | def confirm(message="Do you want to?"): 128 | response = input(f"{message} (y/n): ").lower() 129 | if response: 130 | return response[0] == "y" 131 | return False 132 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | - Any unreleased changes 11 | 12 | ## [3.2.3] - 06-09-2020 13 | 14 | - Update Docs 15 | - Misc fixes 16 | 17 | ## [3.2.2] - 06-08-2020 18 | 19 | - Update Docs 20 | - Misc fixes 21 | - Replaced `colorama` with `rich` 22 | - Added `GitPython` package 23 | - Python Dependency graph 24 | 25 | ## [3.2.1] - 06-02-2020 26 | 27 | - Update Docs 28 | - Misc fixes 29 | 30 | ## [3.1.9] - 05-30-2020 31 | 32 | - Implement Pylint 33 | - Refactor 34 | - Update Docs 35 | - Random Banner 36 | 37 | ## [3.1.8] - 05-28-2020 38 | 39 | - Fixed config 40 | - Cleaned up workflow 41 | - Added [Striker](https://github.com/s0md3v/Striker) 42 | - Added [HydraRecon](https://github.com/aufzayed/HydraRecon) 43 | 44 | ## [3.1.5] - 05-28-2020 45 | 46 | - Added Networking category 47 | - Added [bettercap](https://github.com/bettercap/bettercap) 48 | - Moved [nmap](https://github.com/nmap/nmap) to Networking 49 | - Added [Photon](https://github.com/s0md3v/Photon) 50 | - Added [gitGraber](https://github.com/hisxo/gitGraber) 51 | 52 | ## [3.1.4] - 05-28-2020 53 | 54 | - Added `--suggest` argument 55 | 56 | ## [3.1.3] - 05-23-2020 57 | 58 | - Added GitHub Templates 59 | - Added Docker Support 60 | - New Icon 61 | - Changed ASCII art 62 | - Added `--info` argument 63 | - Added `base64_decode` to utilities 64 | 65 | ## [3.1.2] - 05-20-2020 66 | 67 | - Added [changeme](https://github.com/ztgrace/changeme) 68 | - Added nmap scripts 69 | 70 | ## [3.1.1] - 05-19-2020 71 | 72 | - Added [sqlmap](https://github.com/sqlmapproject/sqlmap) 73 | - Added `utility` 74 | 75 | ## [3.1.0] - 05-19-2020 76 | 77 | - Fixed `packages` in `setup.py` 78 | 79 | ## [3.0.9] - 05-19-2020 80 | 81 | - Fixed `setup.py` 82 | 83 | ## [3.0.8] - 05-18-2020 84 | 85 | - Added [Hash-Buster](https://github.com/s0md3v/Hash-Buster) 86 | - Added [Sublist3r](https://github.com/aboul3la/Sublist3r) 87 | - Added [S3Scanner](https://github.com/sa7mon/S3Scanner) 88 | - Added [Cr3d0v3r](https://github.com/D4Vinci/Cr3d0v3r) 89 | - Added [Cuteit](https://github.com/D4Vinci/Cuteit) 90 | 91 | ## [3.0.7] - 05-18-2020 92 | 93 | - Created [Docs](https://fsociety.dev/) with `pmarsceill/just-the-docs` 94 | 95 | ## [3.0.6] - 05-17-2020 96 | 97 | - Added [XSStrike](https://github.com/s0md3v/XSStrike) 98 | 99 | ## [3.0.5] - 05-17-2020 100 | 101 | - Changed minimum Python version to 3.7 102 | - Added PyPi Badge to `README.md` 103 | 104 | ## [3.0.4] - 05-17-2020 105 | 106 | - Added hosts file at `~/.fsociety/hosts.txt` 107 | - Added usernames file at `~/.fsociety/usernames.txt` 108 | - Added [nmap](https://github.com/nmap/nmap) 109 | - Added [sherlock](https://github.com/sherlock-project/sherlock) 110 | 111 | ## [3.0.3] - 05-17-2020 112 | 113 | - Added `argparse` 114 | 115 | ## [3.0.2] - 05-17-2020 116 | 117 | - Published to `pypi` 118 | - Added upload script to `setup.py` 119 | - Added config file at `~/.fsociety/fsociety.cfg` 120 | - Added `colorama` package 121 | - Added `GitHubRepo` class 122 | - Added [cupp](https://github.com/Mebus/cupp) 123 | 124 | ## [3.0.0] - 05-16-2020 125 | 126 | - Initial Release 127 | 128 | [unreleased]: https://github.com/fsociety-team/fsociety/compare/v3.2.3...HEAD 129 | [3.2.3]: https://github.com/fsociety-team/fsociety/compare/v3.2.2...v3.2.3 130 | [3.2.2]: https://github.com/fsociety-team/fsociety/compare/v3.2.1...v3.2.2 131 | [3.2.1]: https://github.com/fsociety-team/fsociety/compare/v3.1.9...v3.2.1 132 | [3.1.9]: https://github.com/fsociety-team/fsociety/compare/v3.1.8...v3.1.9 133 | [3.1.8]: https://github.com/fsociety-team/fsociety/compare/v3.1.5...v3.1.8 134 | [3.1.5]: https://github.com/fsociety-team/fsociety/compare/v3.1.4...v3.1.5 135 | [3.1.4]: https://github.com/fsociety-team/fsociety/compare/v3.1.3...v3.1.4 136 | [3.1.3]: https://github.com/fsociety-team/fsociety/compare/v3.1.2...v3.1.3 137 | [3.1.2]: https://github.com/fsociety-team/fsociety/compare/v3.1.1...v3.1.2 138 | [3.1.1]: https://github.com/fsociety-team/fsociety/compare/v3.1.0...v3.1.1 139 | [3.1.0]: https://github.com/fsociety-team/fsociety/compare/v3.0.9...v3.1.0 140 | [3.0.9]: https://github.com/fsociety-team/fsociety/compare/v3.0.8...v3.0.9 141 | [3.0.8]: https://github.com/fsociety-team/fsociety/compare/v3.0.7...v3.0.8 142 | [3.0.7]: https://github.com/fsociety-team/fsociety/compare/v3.0.6...v3.0.7 143 | [3.0.6]: https://github.com/fsociety-team/fsociety/compare/v3.0.5...v3.0.6 144 | [3.0.5]: https://github.com/fsociety-team/fsociety/compare/v3.0.4...v3.0.5 145 | [3.0.4]: https://github.com/fsociety-team/fsociety/compare/v3.0.3...v3.0.4 146 | [3.0.3]: https://github.com/fsociety-team/fsociety/compare/v3.0.2...v3.0.3 147 | [3.0.2]: https://github.com/fsociety-team/fsociety/compare/v3.0.0...v3.0.2 148 | [3.0.0]: https://github.com/fsociety-team/fsociety/releases/tag/v3.0.0 149 | -------------------------------------------------------------------------------- /fsociety/core/repo.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=too-many-branches,line-too-long 2 | import os 3 | from shutil import rmtree, which 4 | from abc import ABCMeta, abstractmethod 5 | from collections.abc import Iterable 6 | 7 | from git import Repo, RemoteProgress 8 | from rich.progress import BarColumn, Progress 9 | from rich.table import Table 10 | 11 | from fsociety.core.config import INSTALL_DIR, get_config 12 | from fsociety.core.menu import confirm 13 | from fsociety.console import console 14 | 15 | config = get_config() 16 | 17 | 18 | def print_pip_deps(packages): 19 | requirements = list() 20 | if isinstance(packages, str) and os.path.exists(packages): 21 | with open(packages, "r") as requirements_file: 22 | for line in requirements_file: 23 | if line.strip(): 24 | requirements.append(line) 25 | elif isinstance(packages, Iterable): 26 | requirements = packages 27 | else: 28 | raise ValueError 29 | table = Table("Packages", title="Pip Dependencies") 30 | for req in requirements: 31 | table.add_row(req) 32 | console.print() 33 | console.print(table) 34 | 35 | 36 | class InstallError(Exception): 37 | pass 38 | 39 | 40 | class CloneError(Exception): 41 | pass 42 | 43 | 44 | class GitProgress(RemoteProgress): 45 | def __init__(self): 46 | super().__init__() 47 | self.progress = Progress( 48 | "[progress.description]{task.description}", 49 | BarColumn(None), 50 | "[progress.percentage]{task.percentage:>3.0f}%", 51 | "[progress.filesize]{task.fields[msg]}", 52 | ) 53 | self.current_opcode = None 54 | self.task = None 55 | 56 | def update(self, opcode, count, max_value, msg=None): 57 | opcode_strs = { 58 | self.COUNTING: 'Counting', 59 | self.COMPRESSING: 'Compressing', 60 | self.WRITING: 'Writing', 61 | self.RECEIVING: 'Receiving', 62 | self.RESOLVING: 'Resolving', 63 | self.FINDING_SOURCES: 'Finding sources', 64 | self.CHECKING_OUT: 'Checking out', 65 | } 66 | stage, real_opcode = opcode & self.STAGE_MASK, opcode & self.OP_MASK 67 | 68 | try: 69 | count = int(count) 70 | max_value = int(max_value) 71 | except ValueError: 72 | return 73 | 74 | if self.current_opcode != real_opcode: 75 | if self.task: 76 | self.progress.update(self.task, total=1, completed=1, msg='') 77 | self.current_opcode = real_opcode 78 | self.task = self.progress.add_task( 79 | opcode_strs[real_opcode].ljust(15), msg='') 80 | 81 | if stage & self.BEGIN: 82 | self.progress.start() 83 | if stage & self.END: 84 | self.progress.stop() 85 | self.progress.update(self.task, msg=msg or '', 86 | total=max_value, completed=count) 87 | 88 | 89 | class GitHubRepo(metaclass=ABCMeta): 90 | def __init__(self, 91 | path="fsociety-team/fsociety", 92 | install="pip install -e .", 93 | description=None): 94 | self.path = path 95 | self.name = self.path.split("/")[-1] 96 | self.install_options = install 97 | self.full_path = os.path.join(INSTALL_DIR, self.name) 98 | self.description = description 99 | 100 | def __str__(self): 101 | return self.name.lower().replace("-", "_") 102 | 103 | def clone(self, overwrite=False): 104 | if os.path.exists(self.full_path): 105 | if not overwrite: 106 | repo = Repo(self.full_path) 107 | repo.remotes.origin.pull() 108 | return self.full_path 109 | rmtree(self.full_path) 110 | url = f"https://github.com/{self.path}" 111 | if config.getboolean("fsociety", "ssh_clone"): 112 | url = f"git@github.com:{self.path}.git" 113 | Repo.clone_from(url, self.full_path, progress=GitProgress()) 114 | if not os.path.exists(self.full_path): 115 | raise CloneError(f"{self.full_path} not found") 116 | return self.full_path 117 | 118 | def install(self, no_confirm=False, clone=True): 119 | if no_confirm or not confirm( 120 | f"\nDo you want to install https://github.com/{self.path}?"): 121 | print("Cancelled") 122 | return 123 | if clone: 124 | self.clone() 125 | if self.install_options: 126 | if clone: 127 | os.chdir(self.full_path) 128 | else: 129 | os.chdir(INSTALL_DIR) 130 | install = self.install_options 131 | 132 | if isinstance(install, dict): 133 | if "pip" in install.keys(): 134 | packages = install.get("pip") 135 | if isinstance(packages, list): 136 | message = "Do you want to install these packages?" 137 | packages_str = " ".join(packages) 138 | command = f"pip install {packages_str}" 139 | elif isinstance(packages, str): 140 | requirements_txt = os.path.join( 141 | self.full_path, "requirements.txt") 142 | message = f"Do you want to install these packages from {requirements_txt}?" 143 | command = f"pip install -r {requirements_txt}" 144 | 145 | print_pip_deps(packages) 146 | if not confirm(message): 147 | raise InstallError("User Cancelled") 148 | elif config.get("fsociety", "os") == "macos" and "brew" in install.keys() and which("brew"): 149 | brew_opts = install.get("brew") 150 | command = f"brew {brew_opts}" 151 | elif "linux" in install.keys() or "windows" in install.keys() or "macs" in install.keys(): 152 | command = install.get(config.get( 153 | "fsociety", "os"), install.get("linux")) 154 | else: 155 | command = install 156 | 157 | os.system(command) 158 | 159 | def installed(self): 160 | return os.path.exists(self.full_path) 161 | 162 | @abstractmethod 163 | def run(self): 164 | pass 165 | -------------------------------------------------------------------------------- /fsociety/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # pylint: disable=exec-used,broad-except,inconsistent-return-statements,invalid-name,trailing-whitespace 4 | 5 | import argparse 6 | import platform 7 | from random import choice 8 | 9 | from rich.text import Text 10 | from rich.columns import Columns 11 | 12 | # Core 13 | from fsociety.console import console 14 | from fsociety.core.menu import (set_readline, format_tools, 15 | module_name, prompt, clear_screen) 16 | from fsociety.core.config import get_config, write_config, CONFIG_FILE 17 | import fsociety.core.utilities 18 | import fsociety.information_gathering 19 | import fsociety.passwords 20 | import fsociety.web_apps 21 | import fsociety.obfuscation 22 | import fsociety.networking 23 | 24 | # Config 25 | config = get_config() 26 | 27 | # Menu 28 | TERMS = """ 29 | I shall not use fsociety to: 30 | (i) upload or otherwise transmit, display or distribute any 31 | content that infringes any trademark, trade secret, copyright 32 | or other proprietary or intellectual property rights of any 33 | person; (ii) upload or otherwise transmit any material that contains 34 | software viruses or any other computer code, files or programs 35 | designed to interrupt, destroy or limit the functionality of any 36 | computer software or hardware or telecommunications equipment; 37 | """ 38 | BANNER1 = ( 39 | """ 40 | ____ _ __ 41 | / __/________ _____(_)__ / /___ __ 42 | / /_/ ___/ __ \/ ___/ / _ \/ __/ / / / 43 | / __(__ ) /_/ / /__/ / __/ /_/ /_/ / 44 | /_/ /____/\____/\___/_/\___/\__/\__, / 45 | /____/ 46 | """) 47 | BANNER2 = ( 48 | """ 49 | __ _ _ 50 | / _|___ ___ ___(_) ___| |_ _ _ 51 | | |_/ __|/ _ \ / __| |/ _ \ __| | | | 52 | | _\__ \ (_) | (__| | __/ |_| |_| | 53 | |_| |___/\___/ \___|_|\___|\__|\__, | 54 | |___/ 55 | """) 56 | BANNER3 = ( 57 | """ 58 | .-. . 59 | | o _|_ 60 | -|-.--. .-. .-. . .-. | . . 61 | | `--.( )( | (.-' | | | 62 | ' `--' `-' `-'-' `-`--'`-'`--| 63 | ; 64 | `-' 65 | """) 66 | BANNER4 = ( 67 | """ 68 | ,__ . 69 | / ` ____ __. ___ ` ___ _/_ , . 70 | |__ ( .' \ .' ` | .' ` | | ` 71 | | `--. | | | | |----' | | | 72 | | \___.' `._.' `._.' / `.___, \__/ `---|. 73 | / \___/ 74 | """) 75 | BANNERS = [BANNER1, BANNER2, BANNER3, BANNER4] 76 | MENU_ITEMS = [ 77 | fsociety.information_gathering, fsociety.networking, fsociety.web_apps, 78 | fsociety.passwords, fsociety.obfuscation, fsociety.core.utilities 79 | ] 80 | BUILTIN_FUNCTIONS = { 81 | "exit": lambda: exec('raise KeyboardInterrupt'), 82 | } 83 | items = dict() 84 | 85 | 86 | def print_menu_items(): 87 | cols = list() 88 | for value in MENU_ITEMS: 89 | name = module_name(value) 90 | tools = format_tools(value.__tools__) 91 | tools_str = Text() 92 | tools_str.append("\n") 93 | tools_str.append(name, style="command") 94 | tools_str.append(tools) 95 | cols.append(tools_str) 96 | 97 | console.print(Columns(cols, equal=True, expand=True)) 98 | 99 | for key in BUILTIN_FUNCTIONS: 100 | print() 101 | console.print(key, style="command") 102 | 103 | 104 | def agreement(): 105 | while not config.getboolean("fsociety", "agreement"): 106 | clear_screen() 107 | console.print(TERMS, style="bold yellow") 108 | agree = input( 109 | "You must agree to our terms and conditions first (Y/n) ") 110 | if agree.lower()[0] == "y": 111 | config.set('fsociety', 'agreement', 'true') 112 | 113 | 114 | for item in MENU_ITEMS: 115 | items[module_name(item)] = item 116 | 117 | commands = list(items.keys()) + list(BUILTIN_FUNCTIONS.keys()) 118 | 119 | 120 | def mainloop(): 121 | agreement() 122 | console.print(choice(BANNERS), style="red", highlight=False) 123 | print_menu_items() 124 | selected_command = input(prompt()).strip() 125 | if not selected_command or (not selected_command in commands): 126 | console.print("Invalid Command", style="bold yellow") 127 | return 128 | if selected_command in BUILTIN_FUNCTIONS.keys(): 129 | func = BUILTIN_FUNCTIONS.get(selected_command) 130 | return func() 131 | try: 132 | func = items[selected_command].cli 133 | return func() 134 | except Exception as error: 135 | console.print(str(error)) 136 | console.print_exception() 137 | return 138 | 139 | 140 | def info(): 141 | data = dict() 142 | # Config File 143 | with open(CONFIG_FILE) as file: 144 | data["Config File"] = file.read().strip() 145 | data["Python Version"] = platform.python_version() 146 | data["Platform"] = platform.platform() 147 | os = config.get("fsociety", "os") 148 | if os == "macos": 149 | data["macOS"] = platform.mac_ver()[0] 150 | elif os == "windows": 151 | data["Windows"] = platform.win32_ver()[0] 152 | 153 | for key, value in data.items(): 154 | console.print(f"# {key}") 155 | console.print(value, end="\n\n") 156 | 157 | 158 | def interactive(): 159 | 160 | try: 161 | while True: 162 | set_readline(commands) 163 | mainloop() 164 | except KeyboardInterrupt: 165 | console.print("\nExitting...") 166 | write_config(config) 167 | exit(0) 168 | 169 | 170 | def main(): 171 | parser = argparse.ArgumentParser( 172 | description='A Penetration Testing Framework') 173 | parser.add_argument('-i', 174 | '--info', 175 | action='store_true', 176 | help='gets fsociety info') 177 | parser.add_argument('-s', 178 | '--suggest', 179 | action='store_true', 180 | help='suggest a tool') 181 | 182 | args = parser.parse_args() 183 | 184 | if args.info: 185 | info() 186 | elif args.suggest: 187 | fsociety.core.utilities.suggest_tool() 188 | else: 189 | interactive() 190 | 191 | 192 | if __name__ == "__main__": 193 | main() 194 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code. 6 | extension-pkg-whitelist= 7 | 8 | # Specify a score threshold to be exceeded before program exits with error. 9 | fail-under=10 10 | 11 | # Add files or directories to the blacklist. They should be base names, not 12 | # paths. 13 | ignore=CVS 14 | 15 | # Add files or directories matching the regex patterns to the blacklist. The 16 | # regex matches against base names, not paths. 17 | ignore-patterns= 18 | 19 | # Python code to execute, usually for sys.path manipulation such as 20 | # pygtk.require(). 21 | #init-hook= 22 | 23 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 24 | # number of processors available to use. 25 | jobs=1 26 | 27 | # Control the amount of potential inferred values when inferring a single 28 | # object. This can help the performance when dealing with large functions or 29 | # complex, nested conditions. 30 | limit-inference-results=100 31 | 32 | # List of plugins (as comma separated values of python module names) to load, 33 | # usually to register additional checkers. 34 | load-plugins= 35 | 36 | # Pickle collected data for later comparisons. 37 | persistent=yes 38 | 39 | # When enabled, pylint would attempt to guess common misconfiguration and emit 40 | # user-friendly hints instead of false-positive error messages. 41 | suggestion-mode=yes 42 | 43 | # Allow loading of arbitrary C extensions. Extensions are imported into the 44 | # active Python interpreter and may run arbitrary code. 45 | unsafe-load-any-extension=no 46 | 47 | 48 | [MESSAGES CONTROL] 49 | 50 | # Only show warnings with the listed confidence levels. Leave empty to show 51 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 52 | confidence= 53 | 54 | # Disable the message, report, category or checker with the given id(s). You 55 | # can either give multiple identifiers separated by comma (,) or put this 56 | # option multiple times (only on the command line, not in the configuration 57 | # file where it should appear only once). You can also use "--disable=all" to 58 | # disable everything first and then reenable specific checks. For example, if 59 | # you want to run only the similarities checker, you can use "--disable=all 60 | # --enable=similarities". If you want to run only the classes checker, but have 61 | # no Warning level messages displayed, use "--disable=all --enable=classes 62 | # --disable=W". 63 | disable=print-statement, 64 | parameter-unpacking, 65 | unpacking-in-except, 66 | old-raise-syntax, 67 | backtick, 68 | long-suffix, 69 | old-ne-operator, 70 | old-octal-literal, 71 | import-star-module-level, 72 | non-ascii-bytes-literal, 73 | raw-checker-failed, 74 | bad-inline-option, 75 | locally-disabled, 76 | file-ignored, 77 | suppressed-message, 78 | useless-suppression, 79 | deprecated-pragma, 80 | use-symbolic-message-instead, 81 | apply-builtin, 82 | basestring-builtin, 83 | buffer-builtin, 84 | cmp-builtin, 85 | coerce-builtin, 86 | execfile-builtin, 87 | file-builtin, 88 | long-builtin, 89 | raw_input-builtin, 90 | reduce-builtin, 91 | standarderror-builtin, 92 | unicode-builtin, 93 | xrange-builtin, 94 | coerce-method, 95 | delslice-method, 96 | getslice-method, 97 | setslice-method, 98 | no-absolute-import, 99 | old-division, 100 | dict-iter-method, 101 | dict-view-method, 102 | next-method-called, 103 | metaclass-assignment, 104 | indexing-exception, 105 | raising-string, 106 | reload-builtin, 107 | oct-method, 108 | hex-method, 109 | nonzero-method, 110 | cmp-method, 111 | input-builtin, 112 | round-builtin, 113 | intern-builtin, 114 | unichr-builtin, 115 | map-builtin-not-iterating, 116 | zip-builtin-not-iterating, 117 | range-builtin-not-iterating, 118 | filter-builtin-not-iterating, 119 | using-cmp-argument, 120 | eq-without-hash, 121 | div-method, 122 | idiv-method, 123 | rdiv-method, 124 | exception-message-attribute, 125 | invalid-str-codec, 126 | sys-max-int, 127 | bad-python3-import, 128 | deprecated-string-function, 129 | deprecated-str-translate-call, 130 | deprecated-itertools-function, 131 | deprecated-types-field, 132 | next-method-defined, 133 | dict-items-not-iterating, 134 | dict-keys-not-iterating, 135 | dict-values-not-iterating, 136 | deprecated-operator-function, 137 | deprecated-urllib-function, 138 | xreadlines-attribute, 139 | deprecated-sys-function, 140 | exception-escape, 141 | comprehension-escape, 142 | missing-module-docstring, 143 | missing-class-docstring, 144 | missing-function-docstring, 145 | anomalous-backslash-in-string, 146 | consider-using-sys-exit, 147 | too-few-public-methods, 148 | arguments-differ, 149 | import-outside-toplevel 150 | 151 | # Enable the message, report, category or checker with the given id(s). You can 152 | # either give multiple identifier separated by comma (,) or put this option 153 | # multiple time (only on the command line, not in the configuration file where 154 | # it should appear only once). See also the "--disable" option for examples. 155 | enable=c-extension-no-member 156 | 157 | 158 | [REPORTS] 159 | 160 | # Python expression which should return a score less than or equal to 10. You 161 | # have access to the variables 'error', 'warning', 'refactor', and 'convention' 162 | # which contain the number of messages in each category, as well as 'statement' 163 | # which is the total number of statements analyzed. This score is used by the 164 | # global evaluation report (RP0004). 165 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 166 | 167 | # Template used to display messages. This is a python new-style format string 168 | # used to format the message information. See doc for all details. 169 | #msg-template= 170 | 171 | # Set the output format. Available formats are text, parseable, colorized, json 172 | # and msvs (visual studio). You can also give a reporter class, e.g. 173 | # mypackage.mymodule.MyReporterClass. 174 | output-format=text 175 | 176 | # Tells whether to display a full report or only the messages. 177 | reports=no 178 | 179 | # Activate the evaluation score. 180 | score=yes 181 | 182 | 183 | [REFACTORING] 184 | 185 | # Maximum number of nested blocks for function / method body 186 | max-nested-blocks=5 187 | 188 | # Complete name of functions that never returns. When checking for 189 | # inconsistent-return-statements if a never returning function is called then 190 | # it will be considered as an explicit return statement and no message will be 191 | # printed. 192 | never-returning-functions=sys.exit 193 | 194 | 195 | [LOGGING] 196 | 197 | # The type of string formatting that logging methods do. `old` means using % 198 | # formatting, `new` is for `{}` formatting. 199 | logging-format-style=old 200 | 201 | # Logging modules to check that the string format arguments are in logging 202 | # function parameter format. 203 | logging-modules=logging 204 | 205 | 206 | [SPELLING] 207 | 208 | # Limits count of emitted suggestions for spelling mistakes. 209 | max-spelling-suggestions=4 210 | 211 | # Spelling dictionary name. Available dictionaries: none. To make it work, 212 | # install the python-enchant package. 213 | spelling-dict= 214 | 215 | # List of comma separated words that should not be checked. 216 | spelling-ignore-words= 217 | 218 | # A path to a file that contains the private dictionary; one word per line. 219 | spelling-private-dict-file= 220 | 221 | # Tells whether to store unknown words to the private dictionary (see the 222 | # --spelling-private-dict-file option) instead of raising a message. 223 | spelling-store-unknown-words=no 224 | 225 | 226 | [MISCELLANEOUS] 227 | 228 | # List of note tags to take in consideration, separated by a comma. 229 | notes=FIXME, 230 | XXX, 231 | TODO 232 | 233 | # Regular expression of note tags to take in consideration. 234 | #notes-rgx= 235 | 236 | 237 | [TYPECHECK] 238 | 239 | # List of decorators that produce context managers, such as 240 | # contextlib.contextmanager. Add to this list to register other decorators that 241 | # produce valid context managers. 242 | contextmanager-decorators=contextlib.contextmanager 243 | 244 | # List of members which are set dynamically and missed by pylint inference 245 | # system, and so shouldn't trigger E1101 when accessed. Python regular 246 | # expressions are accepted. 247 | generated-members= 248 | 249 | # Tells whether missing members accessed in mixin class should be ignored. A 250 | # mixin class is detected if its name ends with "mixin" (case insensitive). 251 | ignore-mixin-members=yes 252 | 253 | # Tells whether to warn about missing members when the owner of the attribute 254 | # is inferred to be None. 255 | ignore-none=yes 256 | 257 | # This flag controls whether pylint should warn about no-member and similar 258 | # checks whenever an opaque object is returned when inferring. The inference 259 | # can return multiple potential results while evaluating a Python object, but 260 | # some branches might not be evaluated, which results in partial inference. In 261 | # that case, it might be useful to still emit no-member and other checks for 262 | # the rest of the inferred objects. 263 | ignore-on-opaque-inference=yes 264 | 265 | # List of class names for which member attributes should not be checked (useful 266 | # for classes with dynamically set attributes). This supports the use of 267 | # qualified names. 268 | ignored-classes=optparse.Values,thread._local,_thread._local 269 | 270 | # List of module names for which member attributes should not be checked 271 | # (useful for modules/projects where namespaces are manipulated during runtime 272 | # and thus existing member attributes cannot be deduced by static analysis). It 273 | # supports qualified module names, as well as Unix pattern matching. 274 | ignored-modules= 275 | 276 | # Show a hint with possible names when a member name was not found. The aspect 277 | # of finding the hint is based on edit distance. 278 | missing-member-hint=yes 279 | 280 | # The minimum edit distance a name should have in order to be considered a 281 | # similar match for a missing member name. 282 | missing-member-hint-distance=1 283 | 284 | # The total number of similar names that should be taken in consideration when 285 | # showing a hint for a missing member. 286 | missing-member-max-choices=1 287 | 288 | # List of decorators that change the signature of a decorated function. 289 | signature-mutators= 290 | 291 | 292 | [VARIABLES] 293 | 294 | # List of additional names supposed to be defined in builtins. Remember that 295 | # you should avoid defining new builtins when possible. 296 | additional-builtins= 297 | 298 | # Tells whether unused global variables should be treated as a violation. 299 | allow-global-unused-variables=yes 300 | 301 | # List of strings which can identify a callback function by name. A callback 302 | # name must start or end with one of those strings. 303 | callbacks=cb_, 304 | _cb 305 | 306 | # A regular expression matching the name of dummy variables (i.e. expected to 307 | # not be used). 308 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 309 | 310 | # Argument names that match this expression will be ignored. Default to name 311 | # with leading underscore. 312 | ignored-argument-names=_.*|^ignored_|^unused_ 313 | 314 | # Tells whether we should check for unused import in __init__ files. 315 | init-import=no 316 | 317 | # List of qualified module names which can have objects that can redefine 318 | # builtins. 319 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 320 | 321 | 322 | [FORMAT] 323 | 324 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 325 | expected-line-ending-format= 326 | 327 | # Regexp for a line that is allowed to be longer than the limit. 328 | ignore-long-lines=^\s*(# )??$ 329 | 330 | # Number of spaces of indent required inside a hanging or continued line. 331 | indent-after-paren=4 332 | 333 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 334 | # tab). 335 | indent-string=' ' 336 | 337 | # Maximum number of characters on a single line. 338 | max-line-length=100 339 | 340 | # Maximum number of lines in a module. 341 | max-module-lines=1000 342 | 343 | # List of optional constructs for which whitespace checking is disabled. `dict- 344 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 345 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 346 | # `empty-line` allows space-only lines. 347 | no-space-check=trailing-comma, 348 | dict-separator 349 | 350 | # Allow the body of a class to be on the same line as the declaration if body 351 | # contains single statement. 352 | single-line-class-stmt=no 353 | 354 | # Allow the body of an if to be on the same line as the test if there is no 355 | # else. 356 | single-line-if-stmt=no 357 | 358 | 359 | [SIMILARITIES] 360 | 361 | # Ignore comments when computing similarities. 362 | ignore-comments=yes 363 | 364 | # Ignore docstrings when computing similarities. 365 | ignore-docstrings=yes 366 | 367 | # Ignore imports when computing similarities. 368 | ignore-imports=no 369 | 370 | # Minimum lines number of a similarity. 371 | min-similarity-lines=4 372 | 373 | 374 | [BASIC] 375 | 376 | # Naming style matching correct argument names. 377 | argument-naming-style=snake_case 378 | 379 | # Regular expression matching correct argument names. Overrides argument- 380 | # naming-style. 381 | #argument-rgx= 382 | 383 | # Naming style matching correct attribute names. 384 | attr-naming-style=snake_case 385 | 386 | # Regular expression matching correct attribute names. Overrides attr-naming- 387 | # style. 388 | #attr-rgx= 389 | 390 | # Bad variable names which should always be refused, separated by a comma. 391 | bad-names=foo, 392 | bar, 393 | baz, 394 | toto, 395 | tutu, 396 | tata 397 | 398 | # Bad variable names regexes, separated by a comma. If names match any regex, 399 | # they will always be refused 400 | bad-names-rgxs= 401 | 402 | # Naming style matching correct class attribute names. 403 | class-attribute-naming-style=any 404 | 405 | # Regular expression matching correct class attribute names. Overrides class- 406 | # attribute-naming-style. 407 | #class-attribute-rgx= 408 | 409 | # Naming style matching correct class names. 410 | class-naming-style=PascalCase 411 | 412 | # Regular expression matching correct class names. Overrides class-naming- 413 | # style. 414 | #class-rgx= 415 | 416 | # Naming style matching correct constant names. 417 | const-naming-style=UPPER_CASE 418 | 419 | # Regular expression matching correct constant names. Overrides const-naming- 420 | # style. 421 | #const-rgx= 422 | 423 | # Minimum line length for functions/classes that require docstrings, shorter 424 | # ones are exempt. 425 | docstring-min-length=-1 426 | 427 | # Naming style matching correct function names. 428 | function-naming-style=snake_case 429 | 430 | # Regular expression matching correct function names. Overrides function- 431 | # naming-style. 432 | #function-rgx= 433 | 434 | # Good variable names which should always be accepted, separated by a comma. 435 | good-names=i, 436 | j, 437 | k, 438 | ex, 439 | Run, 440 | _ 441 | 442 | # Good variable names regexes, separated by a comma. If names match any regex, 443 | # they will always be accepted 444 | good-names-rgxs= 445 | 446 | # Include a hint for the correct naming format with invalid-name. 447 | include-naming-hint=no 448 | 449 | # Naming style matching correct inline iteration names. 450 | inlinevar-naming-style=any 451 | 452 | # Regular expression matching correct inline iteration names. Overrides 453 | # inlinevar-naming-style. 454 | #inlinevar-rgx= 455 | 456 | # Naming style matching correct method names. 457 | method-naming-style=snake_case 458 | 459 | # Regular expression matching correct method names. Overrides method-naming- 460 | # style. 461 | #method-rgx= 462 | 463 | # Naming style matching correct module names. 464 | module-naming-style=snake_case 465 | 466 | # Regular expression matching correct module names. Overrides module-naming- 467 | # style. 468 | #module-rgx= 469 | 470 | # Colon-delimited sets of names that determine each other's naming style when 471 | # the name regexes allow several styles. 472 | name-group= 473 | 474 | # Regular expression which should only match function or class names that do 475 | # not require a docstring. 476 | no-docstring-rgx=^_ 477 | 478 | # List of decorators that produce properties, such as abc.abstractproperty. Add 479 | # to this list to register other decorators that produce valid properties. 480 | # These decorators are taken in consideration only for invalid-name. 481 | property-classes=abc.abstractproperty 482 | 483 | # Naming style matching correct variable names. 484 | variable-naming-style=snake_case 485 | 486 | # Regular expression matching correct variable names. Overrides variable- 487 | # naming-style. 488 | #variable-rgx= 489 | 490 | 491 | [STRING] 492 | 493 | # This flag controls whether inconsistent-quotes generates a warning when the 494 | # character used as a quote delimiter is used inconsistently within a module. 495 | check-quote-consistency=no 496 | 497 | # This flag controls whether the implicit-str-concat should generate a warning 498 | # on implicit string concatenation in sequences defined over several lines. 499 | check-str-concat-over-line-jumps=no 500 | 501 | 502 | [IMPORTS] 503 | 504 | # List of modules that can be imported at any level, not just the top level 505 | # one. 506 | allow-any-import-level= 507 | 508 | # Allow wildcard imports from modules that define __all__. 509 | allow-wildcard-with-all=no 510 | 511 | # Analyse import fallback blocks. This can be used to support both Python 2 and 512 | # 3 compatible code, which means that the block might have code that exists 513 | # only in one or another interpreter, leading to false positives when analysed. 514 | analyse-fallback-blocks=no 515 | 516 | # Deprecated modules which should not be used, separated by a comma. 517 | deprecated-modules=optparse,tkinter.tix 518 | 519 | # Create a graph of external dependencies in the given file (report RP0402 must 520 | # not be disabled). 521 | ext-import-graph= 522 | 523 | # Create a graph of every (i.e. internal and external) dependencies in the 524 | # given file (report RP0402 must not be disabled). 525 | import-graph= 526 | 527 | # Create a graph of internal dependencies in the given file (report RP0402 must 528 | # not be disabled). 529 | int-import-graph= 530 | 531 | # Force import order to recognize a module as part of the standard 532 | # compatibility libraries. 533 | known-standard-library= 534 | 535 | # Force import order to recognize a module as part of a third party library. 536 | known-third-party=enchant 537 | 538 | # Couples of modules and preferred modules, separated by a comma. 539 | preferred-modules= 540 | 541 | 542 | [CLASSES] 543 | 544 | # List of method names used to declare (i.e. assign) instance attributes. 545 | defining-attr-methods=__init__, 546 | __new__, 547 | setUp, 548 | __post_init__ 549 | 550 | # List of member names, which should be excluded from the protected access 551 | # warning. 552 | exclude-protected=_asdict, 553 | _fields, 554 | _replace, 555 | _source, 556 | _make 557 | 558 | # List of valid names for the first argument in a class method. 559 | valid-classmethod-first-arg=cls 560 | 561 | # List of valid names for the first argument in a metaclass class method. 562 | valid-metaclass-classmethod-first-arg=cls 563 | 564 | 565 | [DESIGN] 566 | 567 | # Maximum number of arguments for function / method. 568 | max-args=5 569 | 570 | # Maximum number of attributes for a class (see R0902). 571 | max-attributes=7 572 | 573 | # Maximum number of boolean expressions in an if statement (see R0916). 574 | max-bool-expr=5 575 | 576 | # Maximum number of branch for function / method body. 577 | max-branches=12 578 | 579 | # Maximum number of locals for function / method body. 580 | max-locals=15 581 | 582 | # Maximum number of parents for a class (see R0901). 583 | max-parents=7 584 | 585 | # Maximum number of public methods for a class (see R0904). 586 | max-public-methods=20 587 | 588 | # Maximum number of return / yield for function / method body. 589 | max-returns=6 590 | 591 | # Maximum number of statements in function / method body. 592 | max-statements=50 593 | 594 | # Minimum number of public methods for a class (see R0903). 595 | min-public-methods=2 596 | 597 | 598 | [EXCEPTIONS] 599 | 600 | # Exceptions that will emit a warning when being caught. Defaults to 601 | # "BaseException, Exception". 602 | overgeneral-exceptions=BaseException, 603 | Exception 604 | --------------------------------------------------------------------------------