Source-derived GLPI plugin surface mapping.
glpi-plugin-surface-mapper helps identify reachable GLPI plugin code paths by combining two steps:
- Build canonical path wordlists from open-source GLPI plugin archives.
- Probe those paths against a live GLPI instance under
/plugins/,/marketplace/, or custom prefixes.
The goal is to reduce the search space before source-code manual or ai-assisted review.
Traditional directory probing can be noisy on hardened GLPI deployments.
For example, a reverse proxy, WAF, or managed SaaS perimeter may return uniform 403, 302, or login responses for many plugin directories. In that situation, probing only plugin roots such as:
/plugins/<plugin>/
/marketplace/<plugin>/
can produce misleading results. So, this project derive concrete file paths from real plugin source archives, keep the plugin context in the path, and then look for differential application behavior on the target.
Example canonical paths:
fields/front/container.php
fields/ajax/dropdownFields.php
glpiinventory/ajax/taskjob.php
These paths are then expanded against GLPI web roots such as:
/plugins/fields/front/container.php
/marketplace/fields/front/container.php
Useful responses can then be mapped back to local source code for review.
The repository contains two scripts.
Builds a local GLPI plugin source corpus and generates canonical path wordlists.
It can:
- fetch plugin metadata from the GLPI Plugin Directory API;
- download plugin version archives;
- safely extract ZIP/TAR archives;
- index PHP-like files;
- normalize paths into
<plugin_key>/<path_inside_plugin>; - classify paths for review and scanning;
- generate TXT, CSV, and JSON outputs.
Scans canonical plugin paths against a GLPI base URL.
It can:
- test
/plugins/and/marketplace/by default; - support additional custom prefixes;
- read TXT wordlists or CSV output from the builder;
- classify responses into useful verdicts;
- calibrate baseline responses to reduce false positives;
- export all results, signal-only results, URLs, and a JSON summary.
Requires Python 3.10+.
git clone https://github.com/itres-labs/glpi-plugin-surface-mapper.git
cd glpi-plugin-surface-mapper
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtIf no requirements.txt is used, install the only external runtime dependency manually:
pip install requestsglpi_plugin_wordlist_builder.py only uses the Python standard library.
python3 glpi_plugin_wordlist_builder.py \
--out-dir glpi-plugin-corpus \
--php-extensions .php,.phtml,.incThis creates a local corpus under glpi-plugin-corpus/ and generates several outputs, including:
php_unique_canonical_paths.txt
php_unique_scan_candidate_paths.txt
glpi_plugin_paths_all.txt
glpi_plugin_paths_scan_candidates.txt
php_occurrences.csv
plugin_summary.csv
manifest_versions.csv
run_metadata.json
For most scans, start with:
glpi_plugin_paths_scan_candidates.txt
or:
php_unique_scan_candidate_paths.txt
python3 glpi_plugin_scanner.py \
https://glpi.example.local \
glpi-plugin-corpus/glpi_plugin_paths_scan_candidates.txt \
-o scan_results.csvBy default, the scanner tests both:
/plugins/<canonical_path>
/marketplace/<canonical_path>
On hardened deployments, baseline responses are often uniform. Enable baseline calibration to reduce obvious false positives:
python3 glpi_plugin_scanner.py \
https://glpi.example.local \
glpi-plugin-corpus/glpi_plugin_paths_scan_candidates.txt \
--calibrate-baseline \
-o scan_results.csv \
--signals-output scan_signals.csv \
--signal-urls-output scan_signal_urls.txt \
--summary-output scan_summary.jsonThe scanner separates noisy responses from more interesting application-level signals.
Typical useful signals include:
200 OK
302 Found
401 Unauthorized
405 Method Not Allowed
500 Internal Server Error
A signal does not mean a vulnerability exists. It means the target behaved differently for a specific plugin path, which may justify source code review.
[INFO] Base URL: https://glpi.example.local
[INFO] Prefixes: /plugins, /marketplace
[INFO] Paths loaded: 10618
[INFO] Attempts: 21236
[INFO] Signals: 123
[INFO] Errors: 0
[INFO] Status counts: {'200': 32, '302': 88, '403': 17004, '404': 4109, '405': 2, '500': 1}
[INFO] Verdict counts: {'signal': 123, 'baseline_like': 17004, 'not_found': 4109}
python3 glpi_plugin_wordlist_builder.py --helpCommon options:
--out-dir DIR Output directory
--php-extensions LIST Extensions to index, default: .php,.phtml,.inc
--latest-only Keep only the latest version per plugin
--compatibility-regex REGEX Filter versions by compatibility string
--plugin-include-regex REGEX Include only matching plugin keys/names
--plugin-exclude-regex REGEX Exclude matching plugin keys/names
--plan-only Fetch metadata and write the plan without downloading
--download-only Download archives without extracting/indexing
--force-download Re-download existing archives
--force-extract Re-extract existing archives
--verbose More logging
--quiet Less logging
Example: build only GLPI 10-compatible plugin paths.
python3 glpi_plugin_wordlist_builder.py \
--out-dir glpi-plugin-corpus \
--compatibility-regex '10\.'Example: keep only latest versions.
python3 glpi_plugin_wordlist_builder.py \
--out-dir glpi-plugin-corpus \
--latest-onlypython3 glpi_plugin_scanner.py --helpCommon options:
--input-column COLUMN Read paths from a CSV column
--prefix PREFIX Add a custom HTTP prefix
--include-root Also test <base_url>/<path>
--no-default-prefixes Do not test /plugins and /marketplace
--scan-all-prefixes Do not stop after first signal
--workers N Concurrent workers
--timeout SECONDS HTTP timeout
--method GET|HEAD|AUTO HTTP method
--follow-redirects Follow redirects
--insecure Disable TLS verification
--proxy URL Use HTTP/HTTPS proxy
--header 'Name: value' Add HTTP header
--positive-statuses LIST Statuses considered direct positives
--signal-statuses LIST Statuses considered useful application signals
--calibrate-baseline Probe random missing paths per prefix
--include-regex REGEX Scan only matching paths
--exclude-regex REGEX Exclude matching paths
Example: scan from the builder CSV using only scan candidates.
python3 glpi_plugin_scanner.py \
https://glpi.example.local \
glpi-plugin-corpus/php_occurrences.csv \
--input-column canonical_relative_path \
--scan-candidates-only \
--calibrate-baseline \
-o scan_results.csvExample: add a custom plugin root.
python3 glpi_plugin_scanner.py \
https://glpi.example.local \
glpi-plugin-corpus/glpi_plugin_paths_scan_candidates.txt \
--prefix custom_plugins \
--scan-all-prefixes \
-o scan_results.csvExample: authenticated scan.
python3 glpi_plugin_scanner.py \
https://glpi.example.local \
glpi-plugin-corpus/glpi_plugin_paths_scan_candidates.txt \
--header 'Cookie: glpi_session=REDACTED' \
--calibrate-baseline \
-o scan_results.csvRecommended scanner outputs:
python3 glpi_plugin_scanner.py \
https://glpi.example.local \
glpi-plugin-corpus/glpi_plugin_paths_scan_candidates.txt \
-o scan_results.csv \
--signals-output scan_signals.csv \
--signal-urls-output scan_signal_urls.txt \
--summary-output scan_summary.jsonAll attempts.
Useful columns include:
plugin_key
path_inside_plugin
prefix
url
method
status
verdict
signal
baseline_like
final_url
location
content_type
content_length
sample_sha256
elapsed_ms
error
Only paths that produced useful non-baseline behavior.
Signal URLs, one per line.
Summary metadata, status counts, verdict counts, signal counts, and configuration.
A result marked as a signal means the target responded differently for that specific path.
It does not automatically mean:
- the plugin is vulnerable;
- the endpoint is exploitable;
- the response is reachable with the same privileges in all contexts;
- the detected path belongs to the latest plugin version.
Recommended triage:
- Review
scan_signals.csv. - Group by
plugin_key. - Prioritize
front/,ajax/, and root PHP candidates. - Map the path back to the extracted source tree.
- Review authentication, authorization, input handling, and database/file/system sinks.
This tool is intended for authorized security assessments, internal reviews, and defensive research.
Do not use it against systems you do not own or do not have permission to test.
The scanner performs HTTP probing and may generate a large number of requests depending on the wordlist and prefixes used. Tune concurrency, delay, timeout, and filters according to the target and authorization scope.
Examples:
--workers 4
--delay 0.05
--timeout 10
--include-regex '^fields/'
--exclude-regex '/vendor/'This tool was released alongside the ITRES Labs article:
Source-Driven Recon: GLPI Plugin Mapping via Functional Paths
The article explains the assessment context and the reasoning behind source-derived plugin path mapping.
- The tool maps reachable paths. It does not detect vulnerabilities.
- Results depend on target routing, authentication state, reverse proxies, WAFs, and GLPI deployment layout.
- A positive or signal response may still require manual validation.
- A missing response does not prove the plugin is absent.
- Closed-source or commercial plugins cannot be reviewed locally unless their source is available.
- Plugin archives may contain historical paths that no longer exist in newer deployments.
Initial public release.
The tool is being released as research tooling. Interfaces and output fields may change while the project stabilizes.
See LICENSE.