') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - moloch54/CMScan: CMS scanner (Wordpress, Joomla, Drupal...) · GitHub
Skip to content

Repository files navigation

CMScan

No API key, no limitation

CMScan screenshot

CMScan is a unified security scanner for WordPress, Drupal, Joomla, PrestaShop, Shopify, Magento, TYPO3, and OpenCart websites.

It detects CMS versions, enumerates users, checks for known vulnerabilities, and exports the results to CSV.

Features

  • ✅ Vulnerability checking through:
  • ✅ Detection of sensitive files and paths (wp-config, .git, .env, etc.)
  • ✅ Security header auditing (HSTS, CSP, X-Frame-Options, etc.)
  • ✅ Comprehensive CSV export
  • --host mode support for shared hosting environments
  • ✅ Automatic 403 bypass using User-Agent rotation

Installation

Using the installer (recommended)

git clone https://github.com/moloch54/CMScan
cd CMScan
chmod +x install.sh
./install.sh

Usage

python3 CMScan.py -L target.com 

📖 CMScan Documentation

CMScan is a multi-CMS security scanner designed to detect CMS installations, versions, themes, plugins/modules, known vulnerabilities, and exposed sensitive paths.


Command-line Options

OptionDescription
-L <TARGET>Scan a single URL or a text file containing multiple URLs.
-v, --verboseVerbose mode. Display every HTTP request and detection.
--forceForce scanning of all supported CMSs, even if WordPress is detected first.
--stealthDisable exposed-path checks to reduce the number of HTTP requests and make scans more discreet.
-o, --outputOutput CSV filename (default: automatically generated).
--updateUpdate vulnerability databases (WordPress and FriendsOfPHP).

Usage Examples

Scan a single website

python3 CMScan.py -L https://example.com

Verbose mode

python3 CMScan.py -L https://example.com -v

Scan multiple targets

python3 CMScan.py -L targets.txt

Stealth mode

python3 CMScan.py -L https://example.com --stealth

Force detection of every supported CMS

python3 CMScan.py -L https://example.com --force

Combine multiple options

python3 CMScan.py -L https://example.com -v --stealth --force

Update vulnerability databases

python3 CMScan.py --update

Version Detection Priority

CMScan attempts to determine the CMS version using the following priority order:

  1. readme.txt / README.txt
  2. Translation files (*.pot, Project-Id-Version)
  3. ES module imports (e.g. workbox-v7.3.0)
  4. HTML meta tags and comments (passive detection)
  5. ?ver= parameters in asset URLs (fallback)

Automatic Updates

CMScan can automatically check GitHub for new releases.

When a newer version is available, it downloads the update and restarts automatically.


Generated Files

results/
└── cmscan_*.csv

The CSV report includes:

  • CMS detection
  • Version
  • Themes
  • Plugins / Modules
  • Known vulnerabilities
  • Exposed paths
  • Authors
  • Additional security findings

Stealth Mode (--stealth)

Stealth mode disables exposed-path enumeration to reduce the number of HTTP requests and lower the scan footprint.

Typical skipped paths include:

  • /wp-config.php.*
  • /.git/
  • /.env
  • /wp-content/debug.log
  • /wp-content/uploads/
  • /xmlrpc.php
  • /wp-admin/
  • /wp-login.php
  • /wp-cron.php
  • /wp-content/plugins/
  • /wp-content/themes/
  • /readme.html
  • /license.txt
  • and many others.

Supported CMS

CMSFeatures
✅ WordPressCore version, themes, plugins, vulnerabilities
✅ DrupalVersion, modules, exposed paths
✅ JoomlaVersion, extensions, exposed paths
✅ PrestaShopVersion, modules, exposed paths
✅ MagentoVersion, extensions, exposed paths
✅ ShopifyTheme detection
✅ TYPO3Version, extensions
✅ OpenCartVersion, extensions

Legal Notice

CMScan should only be used against systems that you own or for which you have explicit written authorization.

Unauthorized security testing may violate applicable laws.


License

CMScan is released under the MIT License.

You are free to use, modify, and redistribute it in accordance with the license terms.