Skimming Credit Card and ACH Payment Details from Tigerpaw Software Clients

by Victor

Tigerpaw Software develops and sells business software to run day-to-day operations :everything from sales to service; inventory to invoicing.

They'd recently integrated payment processing by credit card or Automated Clearing House (ACH) into the stack.

Tigerpaw, it must be noted here, is used primarily on Windows PCs.  While a web version can be installed and used, this article is focused on the desktop client.  This is a story of how I came to discover a flaw in their credit card processing implementation.  It was discovered on version 1.17.1.01, which may not have been the latest atthat time, so this could affect versions slightly older than that, but definitely not versions 18+.

Bear with me while I cover some background about Tigerpaw and how I became curious about their online payment tools.  Tigerpaw's payment processing is handled through a third-party company who provides an API to access a "vault" for accepting sensitive financial details and payment processing.  The typical vault is designed so that sensitive details are not stored locally.  Sensitive details are instead securely passed to the third-party company.  A token is received back from the third-party company which represents the payment method stored in the vault.  This token is used to charge the customer in future transactions.  The token is worthless to anyone other than the business, so the sensitive financial details belonging to customers can't be stolen in the event of a data breach.  This is all good!  Using this type of payment vault reduces a company's PCI compliance requirements.

We signed up with this third-party company, BNG Payments, who provides the vaulted payment processing through Tigerpaw.  To use their service we were required to file a "Self-Assessment Questionnaire" (SAQ) to be considered PCI compliant.  Alternatively, we could pay an additional $25 a month if we didn't file a SAQ, but, amusingly, we are not absolved from liability or responsibility if we opt to pay the monthly penalty instead of attesting to our level of compliance.  I digress, but this agreement was with yet another third-party company to BNG, so we're three middlemen deep at this point, which made it difficult to get through to someone who understood (or cared about) our concern.

I believe we were originally presented with SAQ-C, a weighty document covering all kinds of scenarios and policies which made little sense for such a small company.  We had processed credit cards through QuickBooks, PayPal, and Stripe which worked similarly for years without any agreement.  Among the many questions that didn't seem to apply tous was one asking if all sensitive details were transmitted securely.  We sure hoped so, but we weren't the ones who wrote the software, so how could we attest this to be true?  At this point, I was given the green light to spend some time investigating!

The first thing I decided to do was log all network activity while creating a secure vault entry.  I was lazy, so instead of a packet capture I went on the firewall and made a rule to log all network connections emanating from my desktop.  I quickly learned that Tigerpaw made web requests during the exchange.  There were no fancy services running on dedicated ports, just a few web requests.

I moved onto proxy logs on the firewall to see if I could identify specific web request types and URLs.  One interesting entry stands out: an unencrypted GET.  Curious to see what was being pulled, I directed a browser to load the URL.  It was a form to enter credit card or ACH details!  It turns out that Tigerpaw just embedded a web form into its desktop GUI.  I took a peek at the source code for this page and the form POSTed sensitive details to an HTTPS URL, so at least sensitive details were being encrypted, but I was pretty certain this single unencrypted web request could be abused.

At this point, I decided to spin up a virtual machine and get some tools to thoroughly inspect the process.  I installed mitmproxy, ARPspoof, and some Python frameworks, namely Flask, so I could serve web pages; I configured the attack VM to allow packet forwarding.  Then, from the attack VM, I poisoned my desktop's ARP cache with ARPspoof, making the VM the man in the middle, so all traffic going through the gateway passed through the VM first.  Finally, I configured the VM to intercept and send all unsecured web requests to mitmproxy and I was ready to try serving arbitrary content in place of the payment detail form.

mitmproxy allows you to write rules to handle specific requests.  So, for example, when a request for tigerpawbng.azurewebsites.net is received, I can direct that request to my Flask server instead of the real destination.  I started a Flask project returning a simple "Hello World" and began creating a new customer payment method on my desktop again.

  • Tigerpaw requests the credit card entry form.
  • The request goes to my VM, the "man-in-the-middle," instead of the real gateway.
  • The VM's mitmproxy sees a request for tigerpawbng.azurewebsites.net and directs the request to the Flask server.
  • The Flask server serves up a "Hello World!" page.

It worked!

Tigerpaw showed "Hello World!" instead of the credit card form!

My next task was to grab the source for the real credit card entry form, so I could create an indiscernible fake with one change: modify the URL to which this form submits so it went to my Flask server instead of the real destination.

Further success: with a few print() lines added to my Flask project I could now log credit card or banking routing numbers that a user entered into Tigerpaw!

This is pretty bad on its own but, as the project stands, my server only receives sensitive details and leaves the Tigerpaw client in a broken state.  I had a few ideas to fix, this but none of them were entirely transparent.  I wanted to be the middleman for the whole transaction - capturing sensitive details while not breaking the process.  At this point, I have to admit, I spent a whole lot of time that ultimately did not contribute to the final solution.  There were some TLS encrypted exchanges in the full process which contained details necessary to handle the entire transaction myself.  After quite some time I decided it wasn't possible to accept the sensitive details then go on to complete the transaction, at least not without installing a fake TLS certificate on the client.

I had given up for the day, but the problem occupied my mind through the night.

It dawned on me at some point that the Tigerpaw desktop GUI was rendering a web page in an embedded browser and that browser would likely run JavaScript.  I wondered if I could:

  • Direct the secure POST request to my Flask server as before.
  • Log the sensitive details as before.
  • Reflect the original form back to the client with their payment details filled so the next time it's submitted it will go to the real destination.
  • Include some JavaScript to make the client click the submit button after the page loads.

This worked and was completely transparent to the user!

I wrote up the pertinent details and got the attention of the company's president and lead developer to report the problem.  It has since been fixed.

This bug, in my opinion, was most likely a development holdover.  The form was available through HTTPS, it just wasn't pulling from that URL.

It could have also been a misunderstanding that since the details were ultimately POSTed securely that serving an unsecured form wasn't dangerous.

As for the document BNG and their third-party required us to sign, we finally were able to reach someone who understood that we were not directly processing payment details and, since we were using their secure vault, a less stringent agreement, SAQ-A, was sufficient.  It could just be a misunderstanding, but I suspect they purposely push the full SAQ to customers to reduce their liability by offloading it onto clients.

While I'd theoretically known how all these components worked for years, this was the first time I combined an array of tricks to discover and craft a real world exploit with criminal potential.  I also learned that the small amount of time it takes to look for bugs in obvious places can have a big payoff.

Return to $2600 Index