Case Study: Interspire and PayPal Express. Case: Interspire and PayPal Express Interspire is an...
-
Upload
arleen-johns -
Category
Documents
-
view
219 -
download
0
Transcript of Case Study: Interspire and PayPal Express. Case: Interspire and PayPal Express Interspire is an...
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-a-Service 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
11
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)
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);
16
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
17
Checkout Transaction Overview
1. Attacker adds cheap items to cart
2. store.com/checkout?price&PayPalExpress2a. PayPal.com/setExpressCheckout?StoreID
tokenRedirect 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
18
Translation between Case Study and How to Shop for Free
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 step2. 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
22
1. Create Cheap Order (First Session)
Attacker adds cheap items to cart
23
myCustomer = myPrice = mySessionID = myTokenID = (ORDERID) =
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ ] = carts[ ] = payments[ ] =
price = price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ ] = tokens[ ] =
recipientID = payerID =
recipientID = payerID =
orders[ ] = orders[ ] =
orderID =price =status =paymentType =
orderID =price =status =paymentType =
currentOrderID = 0currentPaymentID = 0currentSessionID = 0currentTokenID = 0customer = AttackerExpResults[ ] = ExpResults[ ] =
Program Variables
Attacker Variables
1. Create Cheap Order
myPrice = 5;
mySessionID = 0;
myCustomer = Attacker;
Attacker 5 0
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
25
2. Checkout Cheap Order
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpress
26
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ ] = carts[ ] = payments[ ] =
price = price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ ] = tokens[ ] =
recipientID = payerID =
recipientID = payerID =
orders[ ] = orders[ ] =
orderID =price =status =paymentType =
orderID =price =status =paymentType =
currentOrderID = 0currentPaymentID = 0currentSessionID = 0currentTokenID = 0customer = AttackerExpResults[ ] = ExpResults[ ] =
2. Checkout Cheap Order
createCart(myPrice)carts[currentSessionID].price = myPrice;
ExpResults[currentSessionID] = Pay_NotHappened;
currentSessionID++;
0
0
1
5
Pay_NotHappened
Store_checkout(myPrice,PayPalExpress)
Program Variables
Attacker VariablesmyCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = (ORDERID) =
27
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
28
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
token
2.a During Checkout: Store Gets PayPal Token
29
Session 1 Session 2 Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ ] =
price = 5 price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ ] = tokens[ ] =
recipientID = payerID =
recipientID = payerID =
orders[ ] = orders[ ] =
orderID =price =status =paymentType =
orderID =price =status =paymentType =
currentOrderID = 0currentPaymentID = 0currentSessionID = 1currentTokenID = 0customer = AttackerExpResults[ 0 ] = Pay_NotHappened ExpResults[ ] =
2.a During Checkout: Store Gets PayPal Token
PayPal_setExpressCheckout(StoreID);
tokens[currentTokenID].payer = Not Set;tokens[currentTokenID].recipientID = StoreID;
currentTokenID++;
StoreID 0
Not Set
1
//payer not valid yet
Program Variables
Attacker VariablesmyCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = (ORDERID) =
30
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
31
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
3. Store Redirects to PayPal. Attacker Makes Payment
32
myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = (ORDERID) =
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ ] =
price = 5 price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Not_Set
recipientID = payerID =
orders[ ] = orders[ ] =
orderID =price =status =paymentType =
orderID =price =status =paymentType =
currentOrderID = 0currentPaymentID = 0currentSessionID = 1currentTokenID = 1customer = AttackerExpResults[ 0 ] = Pay_NotHappened ExpResults[ ] =
3. Attacker Makes Payment on PayPal
PayPal_Pay(myTokenID, myCustomer);
tokens[myTokenID].payerID = myCustomer;if (myTokenID < 0 || myTokenID >= currentTokenID) return; //if invalid tokenID
Program Variables
Attacker Variables
Attacker
0
33
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
34
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect: PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
store.com/finishOrder?token&payerID&sessionID
4. PayPal Redirects to Store. Store Creates Order
Redirect to: store.com/finishOrder?token&payerID&sessionID
35
myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) =
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ ] =
price = 5 price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ ] = orders[ ] =
orderID =price =status =paymentType =
orderID =price =status =paymentType =
currentOrderID = 0currentPaymentID = 0currentSessionID = 1currentTokenID = 1customer = Attacker
ExpResults[ 0 ] = Pay_NotHappened ExpResults[ ] =
4. Store Creates OrderStore_finishOrder(myTokenID, myCustomer, mySessionID);
orderID = createOrder( carts[mySessionID].price, PENDING, PayPalExpress);if (mySessionID < 0 || mySessionID >= currentSessionID) return; //if invalid sessionID
Program Variables
Attacker Variables
orderID =
orders[currentOrderID].orderID = currentOrderID;orders[currentOrderID].price = carts[mySessionID].price;orders[currentOrderID].status = PENDING;orders[currentOrderID].paymentType = PayPalExpresscurrentOrderID++;return currentOrderID-1;
0
05PENDING
PayPalExpress
1 0
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
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
PayPal.com/doExpressPayment?StoreID&payerID
result
4.a Store Checks if Payment was Made
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
38
myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) =
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ ] =
price = 5 price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 0currentSessionID = 0currentTokenID = 1customer = Attacker
orderID = 0
ExpResults[ 0 ] = Pay_NotHappened ExpResults[ ] =
4.a Store Checks if Payment was Made
Program Variables
Attacker Variables
ExpResults[mySessionID] = PayPal_doExpressPayment(myCustomer, StoreID, myTokenID, orderID, carts[mySessionID].price);
if (myTokenID < 0 || myTokenID >= currentTokenID) return Pay_Failed; //if tokenID invalidif (tokens[myTokenID].payerID == Not_Set) return Pay_Failed; //if payer not confirmed
if (tokens[myTokenID].payerID != myCustomer) return Pay_Failed; //if payer not the customer
if (tokens[myTokenID].recipientID != StoreID) return Pay_Failed; //if recipient not the store
recordPayment(myCustomer, StoreID, PayPalExpress, carts[mySessionID].price, orderID);
39
myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) =
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ ] =
price = 5 price =payerID =recipientID =paymentAmount =paymentType =orderID =
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 0currentSessionID = 1currentTokenID = 1customer = Attacker
orderID = 0
ExpResults[ 0 ] = Pay_NotHappened ExpResults[ ] =
4.a Store Checks if Payment was Made
Program Variables
Attacker Variables
recordPayment(myCustomer, StoreID, PayPalExpress, carts[mySessionID].price, orderID);payments[currentPaymentID].payerID = myCustomer;payments[currentPaymentID].recipientID = StoreID;
payments[currentPaymentID].paymentType = PayPalExpress;payments[currentPaymentID].paymentAmount = carts[mySessionID].price;
payments[currentPaymentID].orderID = orderID;currentPaymentID++;
return Pay_Succeeded;
0
AttackerStoreID
5PayPalExpress
0
1
Pay_Succeeded
sign(orderID);
40
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
41
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
PayPal.com/doExpressPayment?StoreID&payerID
result
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
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
Redirect to: store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
5. Store Redirects to Update Order Status
42
myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) =
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ 0 ] =
price = 5 price =payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 1currentSessionID = 1currentTokenID = 1customer = Attacker
orderID = 0
ExpResults[ 0 ] = Pay_Succeeded ExpResults[ ] =
Program Variables
Attacker Variables
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
0
43
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
44
Attacker adds cheap items to cart
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
PayPal.com/doExpressPayment?StoreID&payerID
result
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
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
Redirect to: store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
6. Create Expensive Order (Second Session)
Attacker creates a second session in a new tab or second browser
45
myCustomer = Attacker myPrice = 5 mySessionID = 0 myTokenID = 0 (ORDERID) = 0
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ 0 ] =
price = 5 price =payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 1currentSessionID = 1currentTokenID = 1customer = Attacker ExpResults[ 0 ] = Pay_Succeeded ExpResults[ ] =
Program Variables
Attacker Variables
6. Create Expensive OrdermyPrice = 200;
mySessionID = 1;
200 1
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
store.com/checkout?price&PayPalExpress
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
7. Checkout Expensive Order
48
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ ] = payments[ 0 ] =
price = 5 price =payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 1currentSessionID = 1currentTokenID = 1customer = Attacker ExpResults[ 0 ] = Pay_Succeeded ExpResults[ ] =
Program Variables
Attacker Variables
7. Checkout Expensive Order
createCart(myPrice)carts[currentSessionID].price = myPrice;
ExpResults[currentSessionID] = Pay_NotHappened;
currentSessionID++;
Store_checkout(myPrice,PayPalExpress)
1
200
Pay_NotHappened1
2
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 0 (ORDERID) = 0
49
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
50
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
token
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
7.a During Checkout: Store Gets PayPal Token
51
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 1currentSessionID = 1currentTokenID = 1customer = Attacker ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 0 (ORDERID) = 0
Program Variables
Attacker Variables
PayPal_setExpressCheckout(StoreID);
tokens[currentTokenID].payer = Not Set;tokens[currentTokenID].recipientID = StoreID;
currentTokenID++;
StoreID 1
Not Set
2
//payer not valid yet
7.a During Checkout: Store Gets PayPal Token
52
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
53
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
8. Store Redirects to PayPal. Attacker Skips Payment
Attacker skips payment
54
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ ] =
recipientID = StoreID payerID = Attacker
recipientID = payerID =
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 1currentSessionID = 1currentTokenID = 1customer = Attacker ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 0 (ORDERID) = 0
Program Variables
Attacker Variables
StoreID 1
Not Set
2
1
8. Store Redirects to PayPal. Attacker Skips PaymentPayPal_Pay(myTokenID, myCustomer);
Attacker skips payment
55
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
56
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
9. PayPal Redirects to Store. Store Still Creates Order
57
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ 1 ] =
recipientID = StoreID payerID = Attacker
recipientID = StoreIDpayerID = Not_Set
orders[ 0 ] = orders[ ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID =price =status =paymentType =
currentOrderID = 1currentPaymentID = 1currentSessionID = 1currentTokenID = 2customer = Attacker ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 1 (ORDERID) = 0
Program Variables
Attacker Variables
9. Store Creates OrderStore_finishOrder(myTokenID, myCustomer, mySessionID);
orderID = createOrder( carts[mySessionID].price, PENDING, PayPalExpress);if (mySessionID < 0 || mySessionID >= currentSessionID) return; //if invalid sessionID
orders[currentOrderID].orderID = currentOrderID;orders[currentOrderID].price = carts[mySessionID].price;orders[currentOrderID].status = PENDING;orders[currentOrderID].paymentType = PayPalExpresscurrentOrderID++;return currentOrderID-1;
orderID = 1
1200
PENDINGPayPalExpress
12
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
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
PayPal.com/doExpressPayment?StoreID&payerIDresult (payment failed)
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
9.a Store Checks if Payment was Made
60
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ 1 ] =
recipientID = StoreID payerID = Attacker
recipientID = StoreIDpayerID = Not_Set
orders[ 0 ] = orders[ 1 ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID = 1price = 200status = PENDINGpaymentType = PayPal_Express
currentOrderID = 2currentPaymentID = 1currentSessionID = 1currentTokenID = 2customer = Attacker
orderID = 1
ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_NotHappened
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 1 (ORDERID) = 0
Program Variables
Attacker Variables
9.a Store Checks if Payment was MadeExpResults[mySessionID] = PayPal_DoExpressPayment(myCustomer, StoreID, myTokenID, orderID,
carts[mySessionID].price);
if (myTokenID < 0 || myTokenID >= currentTokenID) return Pay_Failed; //if tokenID invalidif (tokens[myTokenID].payerID == Not_Set) return Pay_Failed; //payer not confirmed
Pay_Failed
sign(orderID);
61
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
62
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
PayPal.com/doExpressPayment?StoreID&payerIDresult (payment failed)
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/updateOrderStatus?(ORDERID)=1&sessionID=1
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
10. Store Redirects to Update Order Status
store.com/updateOrderStatus?(ORDERID)=1&sessionID=1
Attacker still gets signed (ORDERID) with information about the expensive order
Order not filled because payment verification failed
63
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ 1 ] =
recipientID = StoreID payerID = Attacker
recipientID = StoreIDpayerID = Not_Set
orders[ 0 ] = orders[ 1 ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID = 1price = 200status = PENDINGpaymentType = PayPal_Express
currentOrderID = 2currentPaymentID = 1currentSessionID = 1currentTokenID = 2customer = Attacker
orderID = 1
ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_Failed
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 1 (ORDERID) = 0
Program Variables
Attacker Variables
Store_UpdateOrderStatus((ORDERID), mySessionID);if (mySessionID < 0 || mySessionID >= currentSessionID) return; //if sessionID is invalidif ((ORDERID) < 0 || (ORDERID) >= currentOrderID) return; //if orderID is invalidif (orders[(ORDERID)].paymentType != PayPalExpress) return; //if payment not from PayPalExpif (ExpResults[mySessionID] == Pay_NotHappened) return; //if payment not startedif (ExpResults[mySessionID] == Pay_Failed) return;
//Payment failed
1
10. Store Redirects to Update Order Status
64
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
65
store.com/checkout?price&PayPalExpressPayPal.com/setExpressCheckout?StoreID
tokenRedirect to : PayPal.com/pay?token&payerID
PayPal.com/pay?token&payerID
PayPal.com/doExpressPayment?StoreID&payerIDresult (payment failed)
store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/finishOrder?token&payerID&sessionID
Redirect to: store.com/updateOrderStatus?(ORDERID)=1&sessionID=1
store.com/updateOrderStatus?(ORDERID)=0&sessionID=0
Attacker adds Expensive items to cart
store.com/updateOrderStatus?(ORDERID)=1&sessionID=1
11. Attacker Updates First Session with Second ORDERID
Attacker returns to first session with stored successful payment result, where they stopped
the final webpage from loading
Attacker replaces the first signed ORDERID with the second signed ORDERID by copying and
pasting the value in the URL
Attacker loads the webpage
store.com/updateOrderStatus? (ORDERID)=0&sessionID=0
(ORDERID)=1
Expensive Purchase Completed
66
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ 1 ] =
recipientID = StoreID payerID = Attacker
recipientID = StoreIDpayerID = Not_Set
orders[ 0 ] = orders[ 1 ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID = 1price = 200status = PENDINGpaymentType = PayPal_Express
currentOrderID = 2currentPaymentID = 1currentSessionID = 1currentTokenID = 2customer = Attacker
orderID = 1
ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_Failed
myCustomer = Attacker myPrice = 200 mySessionID = 1 myTokenID = 1 (ORDERID) = 1
Program Variables
Attacker Variables
mySessionID = 0;
0
11. Attacker Updates 1st Session with 2nd ORDERID
67
Session 1 (Cheap Order) Session 2 (Expensive Order) Payment and other Variables
carts[ 0 ] = carts[ 1 ] = payments[ 0 ] =
price = 5 price = 200payerID = AttackerrecipientID = StoreIDpaymentAmount = 5paymentType = PayPalExpressorderID = 0
tokens[ 0 ] = tokens[ 1 ] =
recipientID = StoreID payerID = Attacker
recipientID = StoreIDpayerID = Not_Set
orders[ 0 ] = orders[ 1 ] =
orderID = 0price = 5status = PENDINGpaymentType = PayPalExpress
orderID = 1price = 200status = PENDINGpaymentType = PayPal_Express
currentOrderID = 2currentPaymentID = 1currentSessionID = 1currentTokenID = 2customer = Attacker
orderID = 1
ExpResults[ 0 ] = Pay_Succeeded ExpResults[ 1 ] = Pay_Failed
myCustomer = Attacker myPrice = 200 mySessionID = 0 myTokenID = 1 (ORDERID) = 1
Program Variables
Attacker Variables
Store_UpdateOrderStatus((ORDERID), mySessionID);if (mySessionID < 0 || mySessionID >= currentSessionID) return; //if sessionID is invalidif ((ORDERID) < 0 || (ORDERID) >= currentOrderID) return; //if orderID is invalidif (orders[(ORDERID)].paymentType != PayPalExpress) return; //if payment not from PayPalExpif (ExpResults[mySessionID] == Pay_NotHappened) return; //if payment not startedif (ExpResults[mySessionID] == Pay_Failed) return; orders[(ORDERID)].status = PAID;
//if payment failed
ExpResults[mySessionID] = Pay_NotHappened; //Clear the payment result
PAID
Pay_NotHappened
11. Attacker Updates 1st Session with 2nd ORDERID
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/
DoExpressCheckoutPayment_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
ResourcesR. 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