Coindrawer is a “simple cryptocoin wallet and trading platform” that allows users to store and trade Bitcoin/Litecoin, as well as use merchant facilities to accept Bitcoin as payment for online and offline sales.
I am responsibly disclosing a persistent DOM XSS vulnerability that is exploitable at the time of disclosure. Coindrawer were notified of this issue 149 days ago as a submission to their Bug Bounty program. Coindrawer provided a reward for the submission, but have ignored my notifications that their fix for the issue is ineffective. Coindrawer were informed of my intention to publicly disclose this issue 14 days ago. This disclosure is intended to allow Coindrawer users to take mitigating measures to help ensure the security of their accounts and funds.
Update 1 May 2014 - Coindrawer resolved this issue on 26 April 2014.
Estimated severity
Impact: Medium to high (XSS can perform unwanted actions in the context of the logged-in user, such as steal funds)
Difficulty: Requires a medium to high level of user interaction (proceed through a checkout process). Attack might be able to be deployed to an invisible iframe - was not investigated. Attack is relatively unreliable - user account is not necessarily logged in when proceeding through the checkout process.
Mitigations
This vulnerability was discovered and reported to Coindrawer 149 days ago. Before or after this time, it could have been discovered by others and secret knowledge and use of the vulnerability poses a threat to Coindrawer’s users and the security of their accounts and Bitcoin balances. By publicly releasing these details, the playing field is leveled and users can take action to mitigate this risk.
Until Coindrawer fixes this particular issue, potential measures that can be taken to mitigate the risks presented by this issue include:
- Users should not use Coindrawer merchant facilities as purchasers while logged in to Coindrawer. A malicious payment page can perform unwanted actions in the account that a user is logged in to. If users need to use Coindrawer merchant facilities as a purchaser, they should log out of their Coindrawer account first or use a different browser or private/incognito functionality in their browser to isolate the merchant page from their Coindrawer account.
- Users should not click on suspicious links. If a user opens a link to a Coindrawer merchant page, they should close the page and not proceed through the checkout procedure.
Update 1 May 2014 - Coindrawer resolved this issue on 26 April 2014.
Coindrawer and their merchant facilities
Coindrawer is a “simple cryptocoin wallet and trading platform”. Coindrawer provides a digital wallet service to store Bitcoin and Litecoin, a trading platform to trade Bitcoin for Litecoin and vice versa, and merchant tools to accept Bitcoin for online and offline sales. Coindrawer offers a mechanism to buy and sell Bitcoin for USD, but this feature has been suspended at the time of writing citing “banking and regulatory challenges”. Coindrawer claims to be “architected to deliver the lowest cost, most secure Bitcoin transmissions for purchases as well as Bitcoin transfers”.
Coindrawer’s merchant facilities allow Merchants to create Payment Pages and Payment Buttons to accept Bitcoin payments in their checkout workflow. Customer payments are sent to the Merchant’s Coindrawer account. The payment process is customizable, and the Merchant is able to specify URLs that the purchaser is redirected to upon successful or unsuccessful payment (Success URL
and Cancel URL
respectively).
Persistent DOM XSS
Premise
When a purchaser begins the checkout process at a payment page, the merchant facility does the following:
- Generates a new Bitcoin address for the payment to be sent to. This address is then attributed to the Merchant’s Coindrawer account, and any Bitcoin sent to it is added to the Merchant’s balance.
- Returns a page to the purchaser, which:
- Shows the item name and item price in the Merchant’s chosen currency.
- Asks the purchaser to send Bitcoin to the value of the purchased item at the current Bitcoin rate to the generated Bitcoin address.
- Asks the purchaser to click
Confirm Payment
.
Once the purchaser clicks Confirm Payment
they are sent to the page /paycoin/
which repeatedly polls /paycoin/
with background POST requests as follows:
POST /paycoin/ HTTP/1.1
Host: www.coindrawer.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0 Iceweasel/28.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://www.coindrawer.com/paycoin/
Content-Length: 131
Cookie: auth=<REDACTED - present if user is logged in to their account>
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
bcaddr=18qjRZuGYy7WbsD2XAouVA5Aw9tzt5pmBD&merchant_id=<REDACTED>&btnhash=96ea497de815e5acc56d339cefa15316&pageaction=confajax
---
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Vary: Accept-Encoding
Date: Tue, 15 Apr 2014 10:45:21 GMT
Server: Google Frontend
Content-Length: 296
{"coin_address":"18qjRZuGYy7WbsD2XAouVA5Aw9tzt5pmBD","payment_status":"waiting","txnid":null,"cancel_url":"http://www.merchantsite.com/payment_failure.php","success_url":"http://www.merchantsite.com/payment_success.php","amtpaid":"0"}
At this stage, a timeout is started, shown by a progress bar on the page. If the user fails to pay before the timeout expires, then the page is updated using JavaScript to present a Keep Waiting
and Cancel
button. The Keep Waiting
button restarts the timeout and gives the payer more time to send the payment, the Cancel
button sends the user to the Merchant’s Cancel URL
page.
<button class="btn btn-primary" value="confirm" name="pageaction" type="submit">
Keep Waiting
</button>
<a class="btn btn-primary" href="http://www.merchantsite.com/payment_failure.php">
Cancel
</a>
If the user succeeds in paying before the timeout expires, then the page is instead updated to present a Return
button that sends the payer to the Merchant’s Success URL
page.
<p class="text-center">Thank you for your payment.</p>
<p class="text-right">
<a class="btn btn-primary" href="http://www.merchantsite.com/payment_success.php">
Return
</a>
</p>
Vulnerability
The Success URL
and Cancel URL
values, which can be specified by a Merchant, are not sanitized before being output to the page by JavaScript in the event of timeout being reached (Presents the button for Cancel URL
) or payment being completed (Presents the button for Success URL
). An attacker can specify URLs such as the following:
http://attacker.com"><script>alert("JSEC1046 XSS at " + document.domain)</script>
When output to the page, this string breaks out of the href
attribute of the a
tag, closes the a
tag, and inserts script
tags to trigger a popup.
<a class="btn btn-primary" href="http://www.attacker.com">
<script>
alert("JSEC1046 XSS at " + document.domain)
</script>
">Cancel
</a>
XSS can be used to perform unwanted actions on a logged in user’s behalf, such as steal funds. Coindrawer’s cookies are marked as HttpOnly, preventing cookie theft using JavaScript.
Further notes
In my original disclosure to Coindrawer on 19 November 2013, I outlined two attack vectors that take advantage of the lack of sanitization on output to the page:
- The use of a
javascript:
URL in theSuccess URL
andCancel URL
values. This vector requires the user to click on the displayed button. - The use of
">
in theSuccess URL
andCancel URL
values to break out of thea
tag as described above. This vector does not require the user to take any action once the buttons are displayed.
Coindrawer fixed the first vector in 2013, but overlooked the second. I sent several courtesy notifications of this omission after I received a Bug Bounty reward for the submission.
Disclosure Timeline
My communications with Coindrawer regarding this issue have been open and honest. They were made aware of my intention and reason for publicly disclosing the issue. As follows is the disclosure timeline. Coindrawer’s actions are in bold.
- 19 Nov 2013 - Submission of issue (Persistent/DOM XSS in Success/Cancel URL on paycoin page)
- 21 Nov 2013 - Coindrawer acknowledges receipt of issue
- 4 Dec 2013 - Coindrawer sends 1 BTC reward to cover four issues including this one
- 30 Dec 2013 - Notification to Coindrawer that their fix for JSEC1046 was ineffective, and an attack vector outlined in my original report still worked. No response
- 4 Mar 2014 - Reminder to Coindrawer that JSEC1046 is still vulnerable. No response.
- 3 April 2014 - Notification to Coindrawer of disclosure date (17 April 2014)
- 14 April 2014 - Mike Lucente, CTO of Coindrawer acknowledges disclosure date.
- 15 April 2014 - Discussion back and forth regarding disclosure and Coindrawer’s Bug Bounty program I remind Coindrawer that they can ask for an extension to the disclosure date if needed. No response.
- 17 April 2014 - Public disclosure of issue, which remains vulnerable.
- 26 April 2014 - Coindrawer resolves issue by totally removing merchant functionality from platform.
Please refer to the mitigation recommendations at the beginning of this disclosure to help mitigate the risk presented by this vulnerability.
I have a series of posts regarding Coindrawer:
- Coindrawer Bug Bounty experience
- JSEC1046 - Coindrawer Persistent DOM XSS Disclosure (Paycoin Feature)
- JSEC1051 - Coindrawer Payment Replay Disclosure, Create Multiple Merchant Orders
- JSEC1053 - Coindrawer Provide Arbitrary Exchange Rate Disclosure
- JSEC1065 - Coindrawer Non-persistent XSS Disclosure (Buy/sell Orders Feature, Cancel_order Param)
- Coindrawer Bug Bounty finale