Client side validation strikes again: PIN code bypass !


Client side validation

Client side validation is a common weakness found during penetration tests and security audits performed by Randorisec.

Because client side is by definition… on the user side, it can be altered by the user and sometimes it can be done quite easily.

Netflix Parental Control PIN

A few months ago we figured out that the Netflix parental control PIN was very easy to bypass:

 

The PIN check is done remotely by a Netflix server (good !) and the status (false for an incorrect PIN or true for a correct one) is sent to the client:

If the PIN is correct the player starts ! “TADA” sound 😉

You can easily « bypass the check » by intercepting the Netflix server answer with an intercepting proxy like Burpsuite and replace the “false” status by « true ».

It can be even simpler with a bit of JavaScript as @ska_vans said.

The vulnerability is affecting the iOS application, the Android one and every other Netflix client.

 

We reported this vulnerability using Netflix bug-bounty program but unfortunately they don’t want to correct that and said »(…) The intent of the parental control PIN is to be a barrier to children, so this kind of local bypass is a Won’t Fix. (…) ».

Hmm not sure this barrier is strong enough for kids but anyway let’s move on another one.

Linxo banking app

A few weeks ago, we came across another PIN issue.This time the problem was coming from an iOS banking application widely used in France: Linxo.

When launching the app for the first time, Linxo users are asked to set a PIN code to protect the app.

Then the PIN is asked each time you launch the app. When you switch to another app and then go back to Linxo the PIN is asked again.

 

Unfortunately this PIN code is only checked… on the device so by modifying the client side code of the app or the true/false return value, like we did for Netflix, a malicious user is able to bypass the PIN code check.

This can be done by using Frida or Cycript tools on all iOS devices (jailbroken and non-jailbroken).

 

Pre-requisites:

On a jailbroken device On a non jailbroken device (non-trivial)
* An unlocked device

* Frida installed on the device (using Cydia and the https://build.frida.re repository)

* Frida client on a computer

* An unlocked device

* An Apple developer account

* The application needs to be re-packaged with Frida as a dylib and re-signed with the Apple developer account (using IPAPatch or Resign)

* Frida client on a computer

 

PIN code check is done by the pinMatch instance method of the LXKeychain class.

 

Bad PIN entered (1234):

Good PIN entered (7777):

As we can see, pinMatch method returns “1” when the PIN code is the good one.

So by intercepting the result of the check and returning 1 the check will always be true.

This can be done with frida-trace:

frida-trace -U `frida-ps -U | grep Linxo | awk '{print $1}'` -m "-[LXKeychain pinMatch:]"

And a bit of JavaScript:

onLeave: function (log, retval, state) {

console.log("Function [LXKeychain pinMatch:]] originally returned:"+ retval);

retval.replace(1);

console.log("Changing the return value to:"+retval);

}

Now the app will be unlocked even if a bad PIN is entered:

 

 

We disclosed that using Linxo bug-bounty program and had an answer a few minutes later (impressive !).

They choose to take the problem seriously and correct it using different layers:

  1. switch the class from ObjectiveC to Swift (as security tools are not comfortable in Swift speaking)
  2. do jailbreak detection
  3. do debug detection
  4. do the check on the server side

Step one has already be done since 6.1.1 version.

We’ve tested the 6.1.2 version and can confirm the PIN check is done in Swift. Of course this is far from perfect as security tools are evolving constantly and becoming fluent in Swift (thanks to Malte Kraus: https://github.com/maltek/swift-frida) but this is a first step.

Step two is planned for January and step three and four are in progress.

Conclusion

Here are a few ideas to improve PIN code checks

  • Do the check on server-side
  • Increase the complexity of reverse-engineering the app:
  • add anti-hooking
  • add anti-debugging
  • add anti-tamper (binary and runtime)
  • obfuscation of the source code

But on the other hand it is important to keep in mind that ptrace, used for anti-debugging techniques, is not part of the public iOS API. Non-public APIs are prohibited, and the App Store may reject apps that include them.