Case Study: Interspire and PayPal Express 2 Case: Interspire and PayPal Express • Interspire is an eCommerce merchant software • Can be integrated with PayPal Express to collect the payments • Need to maintain state of a transaction between 3 parties: the Attacker, the Merchant, and PayPal • Logic flaw allows the attacker to purchase an order at a reduced price • Refer to Section III. B 1) in How to Shop For Free Online for the complete description of this case 3 The Flaw • Attacker can use a session with a successful payment status on a different order than it was intended for • Allows attacker to complete an expensive order using the payment intended for a cheaper order 4 Discovering the Flaw • Presented by Wang et al. in How to Shop for Free Online: Security Analysis of Cashier-as-aService Based Web Stores • Used a manual code review method – Identify all API parameters the attacker can access – Test how parameters affect the internal state 5 Identify the API Parameters • API parameters (method arguments) can be signed or unsigned • Unsigned arguments: – Sent in cleartext • Signed arguments: – Sender uses a digital signature to sign the argument – Provides data integrity 6 Manipulating the API Parameters • Attackers can manipulate data sent from the client side • Test manipulating the arguments one at a time, then trace through the rest of the checkout process – See how each individual arguments affects the internal state of the transaction, and the end result 7 Modifying Unsigned Arguments • Change the value manually – store.com/checkout?price=5 • Leave the argument value empty – store.com/checkout?price= • Remove the argument completely – store.com/checkout 8 Modifying Signed Arguments • Replace a signed argument from one session with the same signed argument from a second session – Session 1: • store.com/updateOrder?orderID=(SIGNED1)&sessionID=1 – Session 2: • store.com/updateOrder?orderID=(SIGNED2)&sessionID=2 – Replace orderID in session 1 with orderID from session 2: • store.com/updateOrder?orderID=(SIGNED2)&sessionID=1 9 Modifying Signed Arguments • Leave the signed argument value empty – store.com/updateOrder?orderID=&sessionID=1 • Remove the signed argument completely – store.com/updateOrder?sessionID=1 • Reuse a signed argument from a completed transaction in a new transaction – Replay attack 10 Modifying the Workflow • Try skipping steps of the transaction – Skip one step, then run the rest of the transaction – Skip multiple steps, then run the rest of the transaction • Try running the steps out of order • Do these manipulations with expected argument values • Then, do these workflow manipulations with manipulated unsigned and signed arguments Interspire and PayPal Express API Parameters • • • • • • • • Price (Total cost of items in the cart) (ORDERID) (Signed Order object) SessionID (Temporary session number) PayerID (Like a userID) PayPalExpress (Cashier being used) Result (sent from PayPal to Interspire) StoreID (sent to PayPal from Interspire) Token (sent from PayPal to Interspire) 11 12 Notation Used in the Animation • If a parameter is unsigned, it is represented with lower case words. For example: – price – sessionID • If a parameter is signed, it is represented with all uppercase words inside parenthesis. For example: – (ORDERID) • Parameter values are not shown – price – instead of price=5 13 The Attacker • Parameters sent by the Attacker, or ones the Attacker could modify, are in red text • API Methods accessible by the attacker are in black text 14 The Merchant • Parameters sent by the Merchant are in blue text • Code run by the Merchant is in blue text • Public API Methods (attacker accessible): – Store_checkout(Price,PayPalExpress) – Store_ finishOrder(TokenID, PayerID, SessionID); – Store_updateOrderStatus(OrderID, SessionID); 15 PayPal Express • Parameters sent by PayPal Express is in green text • Code run by PayPal Express is in green text • Public API Methods (attacker accessible): – PayPal_pay(token, payerID); Interspire and PayPal Checkout Transaction Overview • • • • • Step 1: Attacker adds items to their cart Step 2: Attacker clicks on ‘Checkout’ Step 2a: The Store contacts PayPal and gets a token Step 3: The Attacker is redirected PayPal for payment Step 4: The Attacker is redirected to the Store to finish the order • Step 4a: The Store contacts PayPal to check if payment was made • Step 5: The Attacker is redirected to another Store page to complete the order 16 Checkout Transaction Overview 1. Attacker adds cheap items to cart 2. store.com/checkout?price&PayPalExpress 2a. PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID 3. PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID 4. store.com/finishOrder?token&payerID&sessionID 4a. PayPal.com/doExpressPayment?StoreID&payerID result Redirect to: store.com/updateOrderStatus?(ORDERID)&sessionID 5. store.com/updateOrderStatus?(ORDERID)&sessionID Transaction Completed 17 Translation between Case Study and How to Shop for Free 18 Case Study Figure 7 in How to Shop for Free article 2. store.com/checkout?price&PayPalExpress RT1.a: TStore.com/placeOrder 2a. PayPal.com/setExpressCheckout?StoreID RT1.a.a: CaaS.com/SetExpCheckout?identityT&… token RT1.a.b: tokenC Redirect to : PayPal.com/pay?token&payerID RT1.b: redir to CaaS.com/pay?tokenC 3. PayPal.com/pay?token&payerID RT2.a: CaaS.com/pay?tokenA Redirect to: store.com/finishOrder?token&payerID&sessionID RT2.b: redir to TStore.com/finishOrder?tokenC&payerIDC 4. store.com/finishOrder?token&payerID&sessionID RT3.a: TStore.com/finishOrder?tokenA&payerIDA 4a. PayPal.com/doExpressPayment?StoreID&payerID RT3.a.a: CaaS.com/DoExpPay?identityT&tokenC&grossT result RT3.a.b: resultC Redirect to: store.com/updateOrderStatus?(ORDERID)&sessionID RT3.b: redir to TStore.com/updateOrderStatus?orderIDT* 5. store.com/updateOrderStatus?(ORDERID)&sessionID RT4.a: Store.com/updateOrderStatus?orderIDT* Transaction Completed RT4.b: Purchase done 19 Interspire and PayPal Express Flaw • The following slides show how the logic flaw when Interspire used PayPal Express to collect payment can be exploited • The HTTP interactions will be shown, followed by the manual code review • A table tracks the internal state of the variables through each transaction step 20 The Animation Each step of the transaction includes: 1. Description of the step 2. Sequence diagram to show which two parties are communicating, and includes: – The API method called – The last party who could modified unsigned parameters 3. The backend code for that API method – Pseudocode is based on C 21 1. Create Cheap Order (First Session) • On the merchant website, the Attacker adds cheap items to their cart 1. Create Cheap Order (First Session) Attacker adds cheap items to cart 22 1. Create Cheap Order 23 myCustomer = Attacker; myPrice = 5; mySessionID = 0; Program Variables Session 1 (Cheap Order) carts[ Session 2 (Expensive Order) ]= carts[ ] = price = tokens[ payments[ price = ]= tokens[ ] = recipientID = payerID = recipientID = payerID = orders[ orders[ ]= ]= payerID = recipientID = paymentAmount = paymentType = orderID = ]= orderID = price = status = paymentType = orderID = price = status = paymentType = ExpResults[ ExpResults[ ]= Payment and other Variables ]= currentOrderID = 0 currentPaymentID = 0 currentSessionID = 0 currentTokenID = 0 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = (ORDERID) = 24 2. Checkout Cheap Order • On the merchant website, the Attacker clicks the checkout button • On the merchants’ server side, a cart object is created – Is linked to the sessionID 2. Checkout Cheap Order Attacker adds cheap items to cart store.com/checkout?price&PayPalExpress 25 2. Checkout Cheap Order 26 Store_checkout(myPrice,PayPalExpress) createCart(myPrice) carts[currentSessionID].price = myPrice; ExpResults[currentSessionID] = Pay_NotHappened; currentSessionID++; Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ price = 5 price = tokens[ ]= tokens[ ]= recipientID = payerID = orders[ orders[ ] = ]= payments[ ]= payerID = recipientID = paymentAmount = paymentType = orderID = ]= recipientID = payerID = Payment and other Variables orderID = price = status = paymentType = orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_NotHappened ExpResults[ ]= currentOrderID = 0 currentPaymentID = 0 currentSessionID = 01 currentTokenID = 0 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = (ORDERID) = 2.a During Checkout: Store Gets PayPal Token • On the merchants’ server side, a call is made to PayPal Express API method to request a token for the transaction • A token is returned to the merchants’ server • The Attacker does not have access to this communication; it is directly between the merchant and PayPal Express 27 2.a During Checkout: Store Gets PayPal Token Attacker adds cheap items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token 28 2.a During Checkout: Store Gets PayPal Token PayPal_setExpressCheckout(StoreID); tokens[currentTokenID].recipientID = StoreID; tokens[currentTokenID].payer = Not Set; currentTokenID++; //payer not valid yet Program Variables Session 1 Session 2 carts[ 0 ] = carts[ price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Not Set recipientID = payerID = orders[ orders[ ] = ]= ]= Payment and other Variables payments[ ]= payerID = recipientID = paymentAmount = paymentType = orderID = ]= orderID = price = status = paymentType = orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_NotHappened ExpResults[ ]= currentOrderID = 0 currentPaymentID = 0 currentSessionID = 1 currentTokenID = 0 1 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = (ORDERID) = 29 3. Store Redirects to PayPal. Attacker Makes Payment • The merchant redirects the Attacker to PayPal Express to make a payment – Attacker does pay for the order • The token received from PayPal Express in step 2.a is included as a parameter in this new page request 30 31 3. Store Redirects to PayPal. Attacker Makes Payment Attacker adds cheap items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID 3. Attacker Makes Payment on PayPal PayPal_Pay(myTokenID, myCustomer); if (myTokenID < 0 || myTokenID >= currentTokenID) return; tokens[myTokenID].payerID = myCustomer; //if invalid tokenID Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker Not_Set recipientID = payerID = orders[ orders[ ] = ]= ]= Payment and other Variables payments[ ]= payerID = recipientID = paymentAmount = paymentType = orderID = ]= orderID = price = status = paymentType = orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_NotHappened ExpResults[ ]= currentOrderID = 0 currentPaymentID = 0 currentSessionID = 1 currentTokenID = 1 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 32 (ORDERID) = 4. PayPal Redirects to Store. Store Creates Order • After payment is made, PayPal Express redirects the Attacker back to the merchant to continue the checkout process • The merchant creates an order object on the server – The payment status of the order is set to pending – Price is retrieved from cart stored on the server, not from an API parameter – Is not linked to the sessionID 33 34 4. PayPal Redirects to Store. Store Creates Order Attacker adds cheap items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect: PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID 4. Store Creates Order 35 Store_finishOrder(myTokenID, myCustomer, mySessionID); if (mySessionID < 0 || mySessionID >= currentSessionID) return; //if invalid sessionID orderID = createOrder( carts[mySessionID].price, PENDING, PayPalExpress); orders[currentOrderID].orderID = currentOrderID; orders[currentOrderID].price = carts[mySessionID].price; orders[currentOrderID].status = PENDING; orders[currentOrderID].paymentType = PayPalExpress currentOrderID++; return currentOrderID-1; Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ ]= price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker recipientID = payerID = orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_NotHappened ExpResults[ Payment and other Variables payments[ ]= payerID = recipientID = paymentAmount = paymentType = orderID = ]= ]= currentOrderID = 01 currentPaymentID = 0 currentSessionID = 1 currentTokenID = 1 customer = Attacker orderID = 0 Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) = 36 4.a Store Checks if Payment was Made • The merchant asks PayPal Express if payment was made • PayPal Express records the payment on their server • PayPal Express returns the result of payment – Payment Suceeded – Is linked to the sessionID • The merchant digitally signs an orderID with information about the order, whether or not payment suceeded 37 4.a Store Checks if Payment was Made Attacker adds cheap items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID PayPal.com/doExpressPayment?StoreID&payerID result 4.a Store Checks if Payment was Made 38 ExpResults[mySessionID] = PayPal_doExpressPayment(myCustomer, StoreID, myTokenID, orderID, carts[mySessionID].price); if (myTokenID < 0 || myTokenID >= currentTokenID) return Pay_Failed; //if tokenID invalid //if payer not confirmed if (tokens[myTokenID].payerID == Not_Set) return Pay_Failed; if (tokens[myTokenID].payerID != myCustomer) return Pay_Failed; //if payer not the customer //if recipient not the store if (tokens[myTokenID].recipientID != StoreID) return Pay_Failed; recordPayment(myCustomer, StoreID, PayPalExpress, carts[mySessionID].price, orderID); Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ ]= price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker recipientID = payerID = orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_NotHappened ExpResults[ Payment and other Variables payments[ ]= payerID = recipientID = paymentAmount = paymentType = orderID = ]= ]= currentOrderID = 1 currentPaymentID = 0 currentSessionID = 0 currentTokenID = 1 customer = Attacker orderID = 0 Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) = 4.a Store Checks if Payment was Made 39 recordPayment(myCustomer, StoreID, PayPalExpress, carts[mySessionID].price, orderID); payments[currentPaymentID].payerID = myCustomer; payments[currentPaymentID].recipientID = StoreID; payments[currentPaymentID].paymentAmount = carts[mySessionID].price; payments[currentPaymentID].paymentType = PayPalExpress; payments[currentPaymentID].orderID = orderID; currentPaymentID++; return Pay_Succeeded; sign(orderID); Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ ]= price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker recipientID = payerID = orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_NotHappened Pay_Succeeded ExpResults[ Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 ]= ]= currentOrderID = 1 currentPaymentID = 01 currentSessionID = 1 currentTokenID = 1 customer = Attacker orderID = 0 Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) = 5. Store Redirects to Update Order Status • After checking payment status with PayPal Express, the merchant redirects the Attacker to a final merchant page to complete the transaction – Signed (ORDERID) is included as a parameter • Attacker stops this page from loading 40 41 5. Store Redirects to Update Order Status Attacker adds cheap items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID PayPal.com/doExpressPayment?StoreID&payerID result Redirect to: store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker stops the redirect from loading (can intercept with a proxy to do this) Attacker now has session from cheap order stored with successful payment results 5. Store Redirects to Update Order Status Store_UpdateOrderStatus((ORDERID), mySessionID); Attacker stops the redirect from loading (can intercept with a proxy to do this) Attacker now has session from cheap order stored with successful payment results Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ ]= price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker recipientID = payerID = orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_Succeeded ExpResults[ Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 ]= ]= currentOrderID = 1 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 1 customer = Attacker orderID = 0 Attacker Variables myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) = 0 42 6. Create Expensive Order (Second Session) • In a new tab or second browser, the Attacker starts a second order • Expensive items are added to the cart 43 6. Create Expensive Order (Second Session) Attacker adds cheap items to cart Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress Attacker creates a second session in a PayPal.com/setExpressCheckout?StoreID new tab or second browser token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID PayPal.com/doExpressPayment?StoreID&payerID result Redirect to: store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker stops the redirect from loading (can intercept with a proxy to do this) Attacker now has session from cheap order stored with successful payment results 44 6. Create Expensive Order 45 myPrice = 200; mySessionID = 1; Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ ]= price = 5 price = tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker recipientID = payerID = orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_Succeeded ExpResults[ Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 ]= ]= currentOrderID = 1 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 1 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 5200 mySessionID = 01 myTokenID = 0 (ORDERID) = 0 46 7. Checkout Expensive Order • On the merchant website, the Attacker clicks the checkout button • On the merchants’ server side, a cart object is created – Is linked to the sessionID 47 7. Checkout Expensive Order store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress 7. Checkout Expensive Order 48 Store_checkout(myPrice,PayPalExpress) createCart(myPrice) carts[currentSessionID].price = myPrice; ExpResults[currentSessionID] = Pay_NotHappened; 2 currentSessionID++; Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ recipientID = StoreID payerID = Attacker recipientID = payerID = orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened ]= Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 currentOrderID = 1 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 1 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 0 (ORDERID) = 0 7.a During Checkout: Store Gets PayPal Token • On the merchants’ server side, a call is made to PayPal Express API method to request a token for the transaction • A token is returned to the merchants’ server • The Attacker does not have access to this communication; it is directly between the merchant and PayPal Express 49 50 7.a During Checkout: Store Gets PayPal Token store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token 7.a During Checkout: Store Gets PayPal Token PayPal_setExpressCheckout(StoreID); tokens[currentTokenID].recipientID = StoreID; tokens[currentTokenID].payer = Not Set; currentTokenID++; //payer not valid yet Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not Set orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 currentOrderID = 1 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 12 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 0 (ORDERID) = 0 51 8. Store Redirects to PayPal. Attacker Skips Payment • The merchant redirects the Attacker to PayPal Express to make a payment – Attacker does not pay for the order • The token received from PayPal Express in step 2.a is included as a parameter in this new page request 52 53 8. Store Redirects to PayPal. Attacker Skips Payment store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Attacker skips payment 8. Store Redirects to PayPal. Attacker Skips Payment 54 PayPal_Pay(myTokenID, myCustomer); Attacker skips payment Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not Set orders[ 0 ] = orders[ ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = price = status = paymentType = ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 currentOrderID = 1 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 12 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 0 1 (ORDERID) = 0 9. PayPal Redirects to Store. Store Still Creates Order • Payment is not made, but PayPal Express still redirects the Attacker back to the merchant to continue the checkout process • The merchant creates an order object on the server – The payment status of the order is set to pending – Price is retrieved from cart stored on the server, not from an API parameter – Is not linked to the sessionID 55 56 9. PayPal Redirects to Store. Store Still Creates Order store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID 9. Store Creates Order 57 Store_finishOrder(myTokenID, myCustomer, mySessionID); if (mySessionID < 0 || mySessionID >= currentSessionID) return; //if invalid sessionID orderID = createOrder( carts[mySessionID].price, PENDING, PayPalExpress); orders[currentOrderID].orderID = currentOrderID; orders[currentOrderID].price = carts[mySessionID].price; orders[currentOrderID].status = PENDING; orders[currentOrderID].paymentType = PayPalExpress currentOrderID++; return currentOrderID-1; Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not_Set orders[ 0 ] = orders[ 1 ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = 1 price = 200 status = PENDING paymentType = PayPalExpress ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 currentOrderID = 12 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 2 customer = Attacker orderID = 1 Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 1 (ORDERID) = 0 58 9.a Store Checks if Payment was Made • The merchant asks PayPal Express if payment was made • PayPal Express records the payment on their server • PayPal Express returns the result of payment – Payment Failed – Is linked to the sessionID • The merchant digitally signs an orderID with information about the order, whether or not payment suceeded 59 9.a Store Checks if Payment was Made store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID PayPal.com/doExpressPayment?StoreID&payerID result (payment failed) 9.a Store Checks if Payment was Made 60 ExpResults[mySessionID] = PayPal_DoExpressPayment(myCustomer, StoreID, myTokenID, orderID, carts[mySessionID].price); if (myTokenID < 0 || myTokenID >= currentTokenID) return Pay_Failed; //if tokenID invalid if (tokens[myTokenID].payerID == Not_Set) return Pay_Failed; //payer not confirmed sign(orderID); Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables carts[ 0 ] = carts[ 1 ] = payments[ 0 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not_Set orders[ 0 ] = orders[ 1 ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = 1 price = 200 status = PENDING paymentType = PayPal_Express ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened Pay_Failed payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 orderID = 1 currentOrderID = 2 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 2 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 1 (ORDERID) = 0 10. Store Redirects to Update Order Status • After checking payment status with PayPal Express, the merchant redirects the Attacker to a final merchant page to complete the transaction – Signed (ORDERID) is included as a parameter • Order not completed because payment failed\ – Attacker still has access to the signed (ORDERID) parameter 61 62 10. Store Redirects to Update Order Status store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart store.com/checkout?price&PayPalExpress PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID PayPal.com/doExpressPayment?StoreID&payerID result (payment failed) Redirect to: store.com/updateOrderStatus?(ORDERID)=1&sessionID=1 store.com/updateOrderStatus?(ORDERID)=1&sessionID=1 Order not filled because payment verification failed Attacker still gets signed (ORDERID) with information about the expensive order 10. Store Redirects to Update Order Status Store_UpdateOrderStatus((ORDERID), mySessionID); if (mySessionID < 0 || mySessionID >= currentSessionID) return; if ((ORDERID) < 0 || (ORDERID) >= currentOrderID) return; if (orders[(ORDERID)].paymentType != PayPalExpress) return; if (ExpResults[mySessionID] == Pay_NotHappened) return; if (ExpResults[mySessionID] == Pay_Failed) return; //if sessionID is invalid //if orderID is invalid //if payment not from PayPalExp //if payment not started //Payment failed Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not_Set orders[ 0 ] = orders[ 1 ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = 1 price = 200 status = PENDING paymentType = PayPal_Express ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_Failed Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 orderID = 1 currentOrderID = 2 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 2 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 1 63 myTokenID = 1 (ORDERID) = 01 11. Attacker Updates First Session with Second ORDERID • Attacker returns to the first session from step 5, which was paused at the last step of the checkout process • The signed (SESSIONID) from step 5 is replaced with the signed (SESSIONID) from step 10 • The first session is resumed – Payment status is based on sessionID from cheap order, order information is based on signed (SESSIONID) from expensive order – Expensive order completed sucessfully 64 65 11. Attacker Updates First Session with Second ORDERID store.com/updateOrderStatus?(ORDERID)=0&sessionID=0 store.com/updateOrderStatus? (ORDERID)=0&sessionID=0 Attacker adds Expensive items to cart (ORDERID)=1 store.com/checkout?price&PayPalExpress Attacker returns to first session with stored successful payment result, where they stopped the final webpage from loading PayPal.com/setExpressCheckout?StoreID token Redirect to : PayPal.com/pay?token&payerID PayPal.com/pay?token&payerID Expensive Purchase Completed Attacker replaces the first signed ORDERID with the second signed ORDERID by copying and pasting the value in the URL Redirect to: store.com/finishOrder?token&payerID&sessionID store.com/finishOrder?token&payerID&sessionID Attacker loads the webpage PayPal.com/doExpressPayment?StoreID&payerID result (payment failed) Redirect to: store.com/updateOrderStatus?(ORDERID)=1&sessionID=1 store.com/updateOrderStatus?(ORDERID)=1&sessionID=1 11. Attacker Updates 1st Session with 2nd ORDERID mySessionID = 0; Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not_Set orders[ 0 ] = orders[ 1 ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = 1 price = 200 status = PENDING paymentType = PayPal_Express ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_Failed Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 orderID = 1 currentOrderID = 2 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 2 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 10 myTokenID = 1 (ORDERID) = 1 66 11. Attacker Updates 1st Session with 2nd ORDERID Store_UpdateOrderStatus((ORDERID), mySessionID); if (mySessionID < 0 || mySessionID >= currentSessionID) return; if ((ORDERID) < 0 || (ORDERID) >= currentOrderID) return; if (orders[(ORDERID)].paymentType != PayPalExpress) return; if (ExpResults[mySessionID] == Pay_NotHappened) return; if (ExpResults[mySessionID] == Pay_Failed) return; orders[(ORDERID)].status = PAID; ExpResults[mySessionID] = Pay_NotHappened; //if sessionID is invalid //if orderID is invalid //if payment not from PayPalExp //if payment not started //if payment failed //Clear the payment result Program Variables Session 1 (Cheap Order) Session 2 (Expensive Order) carts[ 0 ] = carts[ 1 ] = price = 5 price = 200 tokens[ 0 ] = tokens[ 1 ] = recipientID = StoreID payerID = Attacker recipientID = StoreID payerID = Not_Set orders[ 0 ] = orders[ 1 ] = orderID = 0 price = 5 status = PENDING paymentType = PayPalExpress orderID = 1 price = 200 status = PENDING PAID paymentType = PayPal_Express ExpResults[ 0 ] = Pay_Succeeded Pay_NotHappened ExpResults[ 1 ] = Pay_Failed Payment and other Variables payments[ 0 ] = payerID = Attacker recipientID = StoreID paymentAmount = 5 paymentType = PayPalExpress orderID = 0 orderID = 1 currentOrderID = 2 currentPaymentID = 1 currentSessionID = 1 currentTokenID = 2 customer = Attacker Attacker Variables myCustomer = Attacker myPrice = 200 mySessionID = 0 67 myTokenID = 1 (ORDERID) = 1 Why Does This Work? 69 What Interspire Did Right • Steps 2 & 7 – Sets payment status for session as not paid • Steps 4 & 9 – Verified sessionID value – Price retrieved from a cart object stored on the server side, not from the unsigned price parameter in the URL • This is why only the (ORDERID) parameter needs to be signed • Steps 4.a & 9.a – Verified with PayPal that payment was made, using price stored in cart on server side, not from the unsigned price parameter in the URL – Result parameter from PayPal is never seen by the user 70 What Interspire Did Right • Step 11 – Verified sessionID value – Verified (ORDERID) – Verified payment was attempted – Verified payment did not fail – Session payment status was cleared after order updated to PAID • Prevents replay attacks 71 What PayPal Did Right • Steps 2.a and 7.a – In token returned to Store, payerID set to “not set” • Step 3 – Verified tokenID • Steps 4.a & 9.a – Verified tokenID – Verified payerID was set – Verified the payer is same as the customer on the Store website • Prevent an attacker stealing a customers active session 72 What Interspire Did Wrong • Steps 9.a & 10 – If payment verification from PayPal shows the payment failed in step 9.a, the Store still creates a signed ORDERID • Step 11 – (ORDERID) not associated with a sessionID – Doesn’t verify payment amount made to PayPal is the same amount as the price stored in the order 73 Fixing the Logic Flaw • Connect the orderID and sessionID: • In step 4 and 9, can add another field in the Order object to store the sessionID • In step 10, compare the users’ sessionID to orders[(ORDERID)].sessionID – If they don’t match, return 74 Fixing the Logic Flaw • Verify the payment amount with the price of the order: • The PayPal Express API method DoExpressPayment in steps 4.a and 9.a also returns the payment amount* • A second field could be added to the Expected Results object, to store the payment amount along with the payment status in steps 4.a and 9.a • In step 10 compare orders[(ORDERID)].price to ExpResults[mySessionID].paymentAmount – If they don’t match, return *https://developer.paypal.com/docs/classic/api/merchant/DoExpressCheckoutPay ment_API_Operation_NVP/ 75 Fixing the Logic Flaw • Don’t provide a correct signed (ORDERID) variable if payment failed: • In steps 4.a and 9.a, if the payment status returned from PayPal_DoExpressPayment is Pay_Failed – Don’t send the (ORDERID) parameter when calling UpdateOrderStatus in step 10 • Modify the UpdateOrderStatus method to accept a null value – Set the ORDERID to an invalid number, and still sign it • UpdateOrderStatus method already checks for invalid values 76 Resources R. Wang, S. Chen, X. Wang, S. Qadeer, "How to Shop for Free Online: Security Analysis of Cashier-as-a-Service Based Web Stores," in 2011 IEEE Symposium on Security and Privacy (SP), 22-25 May 2011, Berkeley, CA, pp. 465-480. [Online]. Available: IEEE Xplore, doi: 10.1109/SP.2011.26
© Copyright 2024 Paperzz