[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n*.pyc\n__pycache__/\n"
  },
  {
    "path": "DISCLAIMER.md",
    "content": "# DISCLAIMER.MD\n\nThis tool has been developed as part of my work at BSSI consulting and cybersecurity.\n\nFor any further information, please contact us at contact@bssi.fr\n"
  },
  {
    "path": "LICENCE.txt",
    "content": "MIT License\n\nCopyright (c) 2017 Nitrax - nitrax@lokisec.fr\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# whichCDN\n\nWhichCDN allows to detect if a given website is protected by a Content Delivery Network\n\n## Requirements\n\nInstall the necessary python packages.\n\n```\npip install -r requirements.txt\n```\n\n## Usage\n\nTo scan a website, run whichCDN followed by the target URL or its domain name\n\n```\nwhichCDN http://www.example.com | example.com\n```\n\n## CDN supported\n\n* Cloudflare\n* Incapsula\n* Cloudfront\n* Akamai\n* Airee\n* CacheFly\n* EdgeCast\n* MaxCDN\n* Beluga\n* Limelight\n* Fastly\n* Myracloud\n* Microsft Azure\n\n## Todo\n\n* Azion\n* ArvanCloud\n* Beluga\n* DN77\n* CDNetwork\n* CDNsun\n* CDNvideo\n* ChinaCache\n* ChinaNetCenter\n* Highwinds\n* KeyCDN\n* Level3\n* NGENIX\n* Quantil\n* SkyparkCDN\n* Verizon Digital Media services\n* Turbobyte\n\n## More docs\n\nHelp is available by running ```whichCDN -help```\n\n## Contribution\n\nPull requests for new features, bug fixes, and suggestions are welcome !\n"
  },
  {
    "path": "plugins/DNSDetection/__init__.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nDNSDetection plugin performs CDN detection through nslookup results.\n\"\"\"\n\nfrom plugins.DNSDetection.behaviors import detect\n"
  },
  {
    "path": "plugins/DNSDetection/behaviors.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport sys\nimport re\nfrom utils import CDNEngine\n\nif sys.version_info >= (3, 0):\n    import subprocess as commands\n    import urllib.parse as urlparse\nelse:\n    import commands\n    import urlparse\n\ndef detect(hostname):\n    \"\"\"\n    Performs CDN detection through the DNS, using the nslookup command.\n\n    Parameters\n    ----------\n    hostname : str\n        Hostname to assess\n    \"\"\"\n\n    print('[+] DNS detection\\n')\n\n    hostname = urlparse.urlparse(hostname).netloc\n    regexp = re.compile('\\\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\\\b')\n\n    out = commands.getoutput(\"host \" + hostname)\n    addresses = regexp.finditer(out)\n\n    for addr in addresses:\n        CDNEngine.find(commands.getoutput('nslookup ' + addr.group()))\n"
  },
  {
    "path": "plugins/ErrorServerDetection/__init__.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nErrorServerDetection plugin performs CDN detection when attempts to access\nthe web server via its IP address fail and disclose information about\nthe CDN in place.\n\"\"\"\n\nfrom plugins.ErrorServerDetection.behaviors import detect\n"
  },
  {
    "path": "plugins/ErrorServerDetection/behaviors.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport sys\nimport re\nfrom utils import CDNEngine\nfrom utils import request\n\nif sys.version_info >= (3, 0):\n    import subprocess as commands\n    import urllib.parse as urlparse\nelse:\n    import commands\n    import urlparse\n\ndef detect(hostname):\n    \"\"\"\n    Performs CDN detection thanks to information disclosure from server error.\n\n    Parameters\n    ----------\n    hostname : str\n        Hostname to assess\n    \"\"\"\n\n    print('[+] Error server detection\\n')\n\n    hostname = urlparse.urlparse(hostname).netloc\n    regexp = re.compile('\\\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\\\b')\n\n    out = commands.getoutput(\"host \" + hostname)\n    addresses = regexp.finditer(out)\n\n    for addr in addresses:\n        res = request.do('http://' + addr.group())\n        if res is not None and res.status_code == 500:\n            CDNEngine.find(res.text.lower())\n"
  },
  {
    "path": "plugins/HTTPHeaderDetection/__init__.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nHTTPHeaderDetection plugin performs CDN detection by assessing HTTP headers.\n\"\"\"\n\nfrom plugins.HTTPHeaderDetection.behaviors import detect\n"
  },
  {
    "path": "plugins/HTTPHeaderDetection/behaviors.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport sys\nfrom utils import CDNEngine\nfrom utils import request\n\nif sys.version_info >= (3, 0):\n    import urllib.parse as urlparse\nelse:\n    import urlparse\n\ndef detect(hostname):\n    \"\"\"\n    Performs CDN detection thanks to HTTP headers.\n\n    Parameters\n    ----------\n    hostname : str\n        Hostname to assess\n    \"\"\"\n\n    print('[+] HTTP header detection\\n')\n\n    hostname = urlparse.urlparse(hostname).scheme + '://' + urlparse.urlparse(hostname).netloc\n\n    fields = {\n        'Server': True,\n        'X-CDN': True,\n        'x-cache': True,\n        'X-CDN-Forward': True,\n        'Fastly-Debug-Digest': False\n    }\n\n    res = request.do(hostname)\n\n    if res is None:\n        return\n\n    for field, state in fields.items():\n        value = res.headers.get(field)\n        if state and value is not None:\n            CDNEngine.find(value.lower())\n        elif not state and value is not None:\n            CDNEngine.find(field.lower())\n"
  },
  {
    "path": "plugins/SubdomainDetection/__init__.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nCDN are, sometimes, deployed on a specific subdomain. SubdomainDetection\nplugin performs CDN detection by trying to access this specific subdomain and\nby analyzing its DNS.\n\"\"\"\n\nfrom plugins.SubdomainDetection.behaviors import detect\n"
  },
  {
    "path": "plugins/SubdomainDetection/behaviors.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport sys\nfrom utils import CDNEngine\n\nif sys.version_info >= (3, 0):\n    import subprocess as commands\n    import urllib.parse as urlparse\nelse:\n    import commands\n    import urlparse\n\ndef detect(hostname):\n    \"\"\"\n    Performs CDN detection by trying to access the cdn subdomain of the\n    specified hostname.\n\n    Parameters\n    ----------\n    hostname : str\n        Hostname to assess\n    \"\"\"\n\n    print('[+] CDN subdomain detection\\n')\n\n    hostname = \"cdn.\" + urlparse.urlparse(hostname).netloc\n\n    out = commands.getoutput(\"host -a \" + hostname)\n\n    CDNEngine.find(out.lower())\n"
  },
  {
    "path": "plugins/WhoisDetection/__init__.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nWhois Detection plugin performs CDN detection by analyzing data returned by\nwhois command's.\n\"\"\"\n\nfrom plugins.WhoisDetection.behaviors import detect\n"
  },
  {
    "path": "plugins/WhoisDetection/behaviors.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport sys\nfrom utils import CDNEngine\n\nif sys.version_info >= (3, 0):\n    import subprocess as commands\n    import urllib.parse as urlparse\nelse:\n    import commands\n    import urlparse\n\ndef detect(hostname):\n    \"\"\"\n    Performs CDN detection through whois command's.\n\n    Parameters\n    ----------\n    hostname : str\n        Hostname to assess\n    \"\"\"\n\n    print('[+] Whois detection\\n')\n\n    hostname = urlparse.urlparse(hostname).netloc\n\n    out = commands.getoutput(\"whois \" + hostname)\n\n    CDNEngine.find(out.lower())\n"
  },
  {
    "path": "plugins/__init__.py",
    "content": "#!/usr/bin/env python\n\nfrom . import * # import all plugins\n"
  },
  {
    "path": "requirements.txt",
    "content": "requests\n"
  },
  {
    "path": "setup.py",
    "content": "#!/usr/bin/env python\n\nimport os\nfrom setuptools import setup, find_packages\nfrom setuptools.command.install import install\n\nsetup(\n    name='whichCDN',\n    version='1.0',\n    description='WhichCDN allows to detect if a given website is protected by a Content Delivery Network',\n    author_email='nitrax@lokisec.fr',\n    author='Nitrax',\n    license='MIT',\n    packages=find_packages(),\n    url='https://github.com/Nitr4x/whichCDN',\n    scripts=['whichCDN'],\n    classifiers=[\n        'Development Status :: 5 - Production/Stable',\n        'License :: MIT',\n        'Operating system :: Unix',\n        'Operating system :: Windows',\n        'Operating system :: MacOS',\n        'Programming Language :: Python :: 2.7',\n        'Programming Language :: Python :: 3'\n    ],\n    install_requires=[\n        'requests'\n    ]\n)\n"
  },
  {
    "path": "utils/CDNEngine.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport sys\n\nCDN = {\n    'Cloudflare': 'Cloudflare - https://www.cloudflare.com',\n    'Incapsula': 'Incapsula - https://www.incapsula.com/',\n    'Cloudfront': 'Cloudfront - https://aws.amazon.com/cloudfront/',\n    'Akamai': 'Akamai - https://akamai.com',\n    'Airee': 'Airee - https://airee.international',\n    'CacheFly': 'CacheFly - https://www.cachefly.com/',\n    'EdgeCast': 'EdgeCast - https://verizondigitalmedia.com',\n    'MaxCDN': 'MaxCDN - https://www.maxcdn.com/',\n    'Beluga': 'BelugaCDN - https://belugacdn.com',\n    'Limelight': 'Limelight -  https://www.limelight.com',\n    'Fastly': 'Fastly - https://www.fastly.com/',\n    'Myracloud': 'Myra - https://myracloud.com',\n    'msecnd.ne': 'Microsoft Azure - https://azure.microsoft.com/en-us/services/cdn/',\n    'Clever-cloud': 'Clever Cloud - https://www.clever-cloud.com/'\n}\n\ndef find(data):\n    \"\"\"\n    Compares the provided data to the CDN supported.\n\n    Parameters\n    ----------\n    data : str\n        Data to analyze\n    \"\"\"\n\n    for keyword, description in CDN.items():\n        if data.find(keyword.lower()) != -1:\n            print('\\033[1;32mCDN found: ' + description + '\\033[1;m\\n')\n            sys.exit(0)\n"
  },
  {
    "path": "utils/__init__.py",
    "content": "#!/usr/bin/env python2\n"
  },
  {
    "path": "utils/loader.py",
    "content": "#!/usr/bin/env python\n\nimport imp\nimport os\n\nPluginFolder = \"./plugins\"\nMainModule = \"__init__\"\n\ndef getPlugins():\n    \"\"\"\n    List the plugins located in the plugins folder.\n    \"\"\"\n\n    plugins = []\n    pluginList = os.listdir(PluginFolder)\n    for pluginName in pluginList:\n        location = os.path.join(PluginFolder, pluginName)\n        if not os.path.isdir(location) or not MainModule + \".py\" in os.listdir(location):\n            continue\n        info = imp.find_module(MainModule, [location])\n        plugins.append({\"name\": pluginName, \"info\": info})\n    return plugins\n\ndef loadPlugin(plugin):\n    \"\"\"\n    Loads the specified plugin.\n\n    Parameters\n    ----------\n    plugin : Plugin\n        Plugin to load\n\n    Return\n    ------\n    The plugin loaded\n    \"\"\"\n\n    return imp.load_module(MainModule, *plugin[\"info\"])\n"
  },
  {
    "path": "utils/request.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport signal\nimport requests\n\nclass TimeoutException(Exception):\n    \"\"\"\n    Exception called on timeouts.\n    \"\"\"\n    pass\n\ndef requestTimeout(signum, frame):\n    \"\"\"\n    Request timeout.\n    \"\"\"\n\n    raise TimeoutException()\n\ndef do(hostname):\n    \"\"\"\n    Performs a GET request.\n\n    Parameters\n    ----------\n    hostname : str\n        Target request\n\n    Return\n    ------\n    The request results\n    \"\"\"\n\n    try:\n        return requests.get(hostname, timeout=10)\n\n    except TimeoutException:\n        print(\"\\033[1;31mRequest timeout: test aborted\\n\\033[1;m\")\n        return None\n\n    except requests.ConnectionError:\n        print(\"\\033[1;31mServer not found: test aborted\\n\\033[1;m\")\n        return None\n\n    finally:\n        signal.alarm(0)\n"
  },
  {
    "path": "whichCDN",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport argparse\nimport signal\nimport sys\n\nfrom utils import loader\nfrom utils import request\n\nif sys.version_info >= (3, 0):\n    import urllib.parse as urlparse\nelse:\n    import urlparse\n\ndef gracefulExit(signal, frame):\n    sys.exit(0)\n\ndef parser():\n    \"\"\"\n    Parse arguments.\n    \"\"\"\n\n    parser = argparse.ArgumentParser(description=\"\"\"\\\n        WhichCDN allows to detect if a given website is protected by a Content\n        Delivery Network.\\r Fell free to contact the maintainer for any further\n        questions or improvement vectors.\\r Maintained by Nitrax\n        <nitrax@lokisec.fr>\n    \"\"\")\n\n    parser.add_argument('target', type=str, help='hostname to scan')\n\n    parser.parse_args()\n\ndef sanitizeURL(hostname):\n    \"\"\"\n    Sanitizes the hostname by adding the http protocol if it has not been\n    provided.\n\n    Parameters\n    ----------\n    hostname : str\n        Hostname to assess\n\n    Return\n    ------\n    The hostname sanitized\n    \"\"\"\n\n    components = urlparse.urlparse(hostname)\n\n    hostname = \"http://\" + hostname if components.scheme == '' else hostname\n    return hostname\n\n\nif __name__ == \"__main__\":\n\n    print(\"\"\"\n     __      __.__    .__       .__    _________ ________    _______\n    /  \\    /  \\  |__ |__| ____ |  |__ \\_   ___ \\\\\\\\______ \\   \\      \\\\\n    \\   \\/\\/   /  |  \\|  |/ ___\\|  |  \\/    \\  \\/ |    |  \\  /   |   \\\\\n     \\        /|   Y  \\  \\  \\___|   Y  \\     \\____|    `   \\/    |    \\\\\n      \\__/\\  / |___|  /__|\\___  >___|  /\\______  /_______  /\\____|__  /\n           \\/       \\/        \\/     \\/        \\/        \\/         \\/\n    \"\"\")\n\n    parser()\n\n    signal.signal(signal.SIGALRM, request.requestTimeout)\n    signal.signal(signal.SIGINT, gracefulExit)\n    signal.alarm(5)\n\n    hostname = sanitizeURL(sys.argv[1])\n\n    for module in loader.getPlugins():\n        plugin = loader.loadPlugin(module)\n        plugin.detect(hostname)\n\n    print('\\033[1;31mNo CDN found\\033[1;m')\n"
  }
]