Skip to content

itres-labs/glpi-plugin-surface-mapper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

glpi-plugin-surface-mapper

Source-derived GLPI plugin surface mapping.

glpi-plugin-surface-mapper helps identify reachable GLPI plugin code paths by combining two steps:

  1. Build canonical path wordlists from open-source GLPI plugin archives.
  2. 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.

Why

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.

Tools

The repository contains two scripts.

glpi_plugin_wordlist_builder.py

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.

glpi_plugin_scanner.py

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.

Installation

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.txt

If no requirements.txt is used, install the only external runtime dependency manually:

pip install requests

glpi_plugin_wordlist_builder.py only uses the Python standard library.

Basic workflow

1. Build the plugin path corpus

python3 glpi_plugin_wordlist_builder.py \
  --out-dir glpi-plugin-corpus \
  --php-extensions .php,.phtml,.inc

This 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

2. Scan a GLPI target

python3 glpi_plugin_scanner.py \
  https://glpi.example.local \
  glpi-plugin-corpus/glpi_plugin_paths_scan_candidates.txt \
  -o scan_results.csv

By default, the scanner tests both:

/plugins/<canonical_path>
/marketplace/<canonical_path>

3. Use baseline calibration

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.json

4. Review signals

The 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.

Example output

[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}

Builder usage

python3 glpi_plugin_wordlist_builder.py --help

Common 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-only

Scanner usage

python3 glpi_plugin_scanner.py --help

Common 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.csv

Example: 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.csv

Example: 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.csv

Output files

Recommended 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.json

scan_results.csv

All 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

scan_signals.csv

Only paths that produced useful non-baseline behavior.

scan_signal_urls.txt

Signal URLs, one per line.

scan_summary.json

Summary metadata, status counts, verdict counts, signal counts, and configuration.

Interpreting results

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:

  1. Review scan_signals.csv.
  2. Group by plugin_key.
  3. Prioritize front/, ajax/, and root PHP candidates.
  4. Map the path back to the extracted source tree.
  5. Review authentication, authorization, input handling, and database/file/system sinks.

Responsible use

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/'

Related research

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.

Limitations

  • 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.

Project status

Initial public release.

The tool is being released as research tooling. Interfaces and output fields may change while the project stabilizes.

License

See LICENSE.

About

Source-derived GLPI plugin surface mapper for building canonical plugin path wordlists and probing reachable code paths under /plugins/ and /marketplace/.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages