PPT

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