Coordinated Disclosure Timeline

Summary

Pay, a payments engine for Ruby on Rails, comes with a payment info page which was susceptible to Cross-site scripting. This allowed a remote attacker to execute JavaScript code in the context of the current user. An attacker could have used this vulnerability to execute requests in the name of a logged-in user or potentially collect information about the attacked user by displaying a malicious form.

Product

Pay

Tested Version

v6.2.4

Details

Cross-site scripting in payments display page (GHSL-2023-084)

A payments info page of Pay was susceptible to reflected Cross-site scripting. An attacker could have created a working URL that renders a javascript link to a user on a Rails application that integrates Pay. This URL could have been distributed via email to specifically target certain individuals. If the targeted application contained a functionality to submit user-generated content (such as comments) the attacker could have even distributed the URL using that functionality.

In the show method of the PaymentsController class, the user-controlled value of the parameter named back is assigned to the instance variable named @redirect_to:

@redirect_to = params[:back].presence || root_path

This value is then rendered using a link_to helper function in the corresponding view (show.html.erb):

<%= link_to t("pay.back"), @redirect_to, [..]

The link_to view helper does not check the protocol of the provided URL, this makes it possible to provide an URL starting with javascript:.

This vulnerability was found using CodeQL’s reflected cross-site scripting query for Ruby.

Proof of Concept

Precondition: The attacker needs to know a valid Stripe Payment Intent ID for the targeted page. This Payment Intent ID doesn’t need to be newly created to work. (So the attacker can start a payment themselves and copy the payment intent ID.)

The attacker can then construct an URL such as:

http://<host-using-pay>/pay/payments/<PaymentIntentID>?back=javascript:alert(document.location)

E.g., such a URL could look like this:

http://127.0.0.1:3000/pay/payments/pi_3Mw23E2wqvxfBLhL1dzvJ3Xn?back=javascript:alert(document.location)

If an existing payment intent ID for the targeted site is used, the following text is displayed to the user “This payment was already successfully confirmed.”. Below this text a button is displayed that points to the JavaScript link of the attacker.

If a targeted user clicks on this link using the “Go back” button the JavaScript is executed in context of the actual website. This means the attacker can use the logged-in user to perform actions on their behalf.

Impact

This issue may lead to Sensitive data disclosure due to reflected Cross-site scripting (XSS).

Resources

Cross Site Scripting: link to HREF

CVE

Credit

This issue was discovered and reported by GHSL team member @p- (Peter Stöckli).

Contact

You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2023-084 in any communication regarding this issue.