While doing some research for Bankdroid during the hot summer days I decided to take a look at the increasingly popular payment app Swish. Swish, developed by HiQ for Sweden's six major banks (Danske Bank, Handelsbanken, Länsförsäkringar Bank, Nordea, SEB and Swedbank/Sparbankerna), lets its users send and receive instant payments without the hassle of bank transfers.
My first thought was to set up a transparent proxy to be able to observe the traffic between the app and the Swish backend, but that didn't go as smooth as I had hoped. It turned out that they were using a self-signed certificate and had therefore implemented certificate pinning (Moxie Marlinspike's1 AndroidPinning to be precise, licensed under the GPLv3 - meaning that Getswish AB is obligated to hand over a copy of the Android app's source code to anyone who requests it) in their apps. Whether planned or not this solution made the app more secure by mitigating man-in-the-middle attacks - it also meant that I had a new obstacle to overcome as I wouldn't be able to simply connect through my proxy.
After a bit of
twerking tweaking and tinkering I was finally able to see all the requests in clear text.
At first glance nothing looked out of the ordinary. A simple XML API with a handful of endpoints and, as HiQ boasts on their website2, using Mobilt BankID for authentication:
Betalningarna godkänns med hjälp av mobilt BankID, vilket gör att lösningen är lika säker som de olika bankernas internettjänster.
The way mobilt BankID was implemented was that a request that required authentication would return a BankID reference number and the app would, after the user had successfully signed with mobilt BankID, execute the same request again (this time including the reference number).
This seemed like straightforward and secure enough solution.
A thing that caught my attention was that the user's MSISDN/phone number was included in the payment history request:
Why would the server need the MSISDN if we were already authenticated?
What would happen if the MSISDN was changed to another user's?
Apparently, in the case of payment history requests, mobilt BankID was only used for authentication and not for authorization.
An authenticated user could retrieve any other users complete transaction history simply by changing the MSISDN in the request. The Swish server never checked whether the user was authorized to make that request or not.
The transaction history includes phone numbers, full names, datetimes, amounts and messages for every person you've ever sent/received a Swish payment to/from.
As the developers were completely unaware of this vulnerability it is quite safe to assume that is has existed since Swish was launched nearly 2 years ago (December 2012) and at the time of discovery affected over 1.4 million Swish users3.
Getting a hold of someone at Swish was another challenge. There's no contact info on their website (only contact info for the connected banks) and their PR folks on Twitter weren't very helpful.
After emailing all of the affected banks and describing the problem a couple of them actually replied and a day later I was contacted by someone in charge of the project. A short week later the vulnerability was fixed and everyone was happy again.
TLDR: Vulnerability in Swish let any user access any other user's complete transaction history.
Update regarding use of GPL code in the Swish payments app
31 Oct 2014
There has been some discussion regarding the claim that Getswish AB is using GPL licensed code in their application. Getswish AB (via their press managers at The World Loves) are denying4,5,6 that any GPL licensed code is used in the Swish payments app. Myself and others who have examined the app are confident that (a modified version of) AndroidPinning has been used.
Let's take a look at the code from both projects. PinningTrustManager from AndroidPinning can be found at https://github.com/moxie0/AndroidPinning/blob/master/src/org/thoughtcrime/ssl/pinning/PinningTrustManager.java
The code from the Swish app was converted from dex format7 to java .class files with the help of dex2jar and the java code was reconstructed from the .class files using JD Project. The source for the reconstructed code can be found at https://gist.github.com/liato/1e8f11c017353109dd82
It's important to note that reconstructing java code from a .class file doesn't always produce the same code that was compiled but all method signatures and string literals are left untouched.
As you can see by comparing the two classes there are too many similarities for it to be an original piece of code.
For starters, both the class from AndroidPinning and the one from Swish is called PinningTrustManager.
Identical method signatures that are not inherited from the X509TrustManager interface include:
private byte hexStringToByteArray(String)
private TrustManager initializeSystemTrustManagers()
private boolean isValidPin(X509Certificate)
Identical field declarations include:
private final List<byte> pins
private final SystemKeyStore systemKeyStore
They even have the same exact exception message for
throw new CertificateException("Client certificates not supported!");
The examined apk was downloaded 4th of July 2014 and can be found at http://nullbyte.eu/se.bankgirot.swish_2.0_6023.apk