# $36k Google App Engine RCE

In non-production GAE environments, the internal stubby.Send and app_config_service APIs were reachable, letting the researcher invoke hidden Google RPCs and alter app config, rated RCE.

- Payout: $36,337
- Severity: critical
- Program: Google VRP
- Credited: Ezequiel Pereira
- Disclosed: 2019-08-31
- Write-up quality: 9/10
- Original: https://www.ezequiel.tech/p/36k-google-app-engine-rce.html
- Library page: https://bugbountylibrary.com/r/blog%3Aezequiel-tech-p-36k-google-app-engine-rce-html
- Tags: domain:cloud impact:internal-access impact:privesc kind:bounty platform:blog sev:critical tech:grpc technique:recon technique:reverse-engineering technique:source-review vuln:access-control vuln:misconfiguration

**Target:** GAE internal RPC endpoints (169.254.169.253:10001 HTTP and gRPC port 4) in staging/test environments
**Root cause:** Non-production GAE deployment environments exposed internal APIs (stubby.Send, app_config_service.ConfigApp/SetAdminConfig) to user apps without authorization checks, unlike production.
**Steps:**
1. Upload a manually-scaled GAE app to staging/test environment
2. Request www.appspot.com with Host: <project>.prom-qa.sandbox.google.com (or prom-nightly)
3. From the app, call gRPC apphosting.APIHost on 169.254.169.253:4 with JSON PB
4. Call `stubby.GetStubId` with `{"host":"<HOST>"}` to obtain stub_id
5. Call `stubby.Send` with `{"stubby_method":"/ServerStatus.GetServices","stubby_request":"","stub_id":"<STUB_ID>"}` to enumerate services
6. Call `app_config_service.SetAdminConfig` / `ConfigApp` with apphosting.SetAdminConfigRequest / GlobalConfig to set SuperApp, FILE_GOOGLE3_ACCESS, SA ID, quota bypass
**How found:** Noticed X-Cloud-Trace-Context header on appengine.google.com implying GAE; learned Java 8 runtime uses PB over http://169.254.169.253:10001/rpc_http; ran Nmap from inside GAE to find gRPC on port 4; extracted launcher argv via a C++ .init_array hook to reveal api_call_deadline_map listing stubby; probed method existence via incomplete-request vs not-exist error differential; found app_config_service methods in quotas.proto.
**Exploitation:** Researcher ran a C++ gRPC client inside a staging GAE app, used stubby.GetStubId/Send to invoke arbitrary internal Stubby methods, and app_config_service to modify internal app settings (email senders, service account, quota, SuperApp, Google3 file access). No weaponized end-to-end attack beyond demonstrating access; Google rated it RCE.
**Escalation:** Chained a prior bug granting staging/test environment access with hidden API discovery via launcher argv and PB definitions, escalating from stubby.Send to app_config_service config mutation.
**Insight:** Error-message differentials (incomplete request vs not-exist) reveal hidden RPC methods; hooking .init_array in a native lib to dump launcher argv exposes internal API names and deadlines not visible elsewhere.
