- Introduction
- How? (For the Most Curious)
- Installation and Configuration
- Usage
- Demo
- Known Limitations
- Contacts
- Disclaimer
A friend introduced me to Red Bull's contests. The idea is simple: play the game they provide and, if you get a high score, you can win prizes. I am not very interested in the prizes themselves. What I truly enjoy is exploring their code and finding creative ways to push its limits.
This year's contest involved answering six questions correctly within the given time limit. Players also had access to a powerup that could boost their chances of winning or increase their final score.
You can try it yourself by visiting Red Bull Quiz Stop 2.0.
I created this tool as a simple MITM (man-in-the-middle) proxy. It intercepts the requests the client sends to the server while the user is playing, modifies them (as explained in the How? (For the Most Curious) section), and achieves the highest possible score.
There are many ways this could be implemented, but I chose a MITM approach for simplicity. If a user wants to save their result, they still need to log in and submit the form, so this method avoids having to implement login and form submission logic, which is outside the scope of this project.
In a previous contest (see Red Bull Giro Veloce for more info), the trick was simple: modify the game logic directly inside the JavaScript loaded by the page. This time things have changed, and the challenge was more interesting. To pull this off, I started with a lot of debugging.
I first played the game and captured all requests and responses exchanged with the server. I wanted to know whether submitting an answer actually triggered a request beyond analytics. To my surprise, it did. In the older contest, everything was handled client-side. Now, requests were sent to the server and the payloads were encrypted.
At this point, I needed to figure out how the requests were encrypted and whether decrypting them would help. Using my browser's developer tools, I inspected the call stack for these requests and traced them to bundle.js
. The file was mostly readable except for a section that had clearly been obfuscated, so I focused my efforts there. After several failed attempts at deobfuscation, I had a simple but effective idea: ask ChatGPT to help me break the problem into smaller parts. To my surprise, it worked, gaining valuable insights about the obfuscated code, including the encryption function.
Note on AI: I believe AI tools can be very useful when used thoughtfully and with a bit of critical thinking. I am against relying on them to do everything and simply copying and pasting the results, hoping they work without understanding why, when, or how. At the same time, they can be incredibly helpful when used as support. In this case, I asked for help breaking the problem into smaller pieces. The goal was not to have the AI do all the work but to gain insights into how things functioned so I could continue with my work.
function Mr(t) {
var e = Pr,
r = arguments[e(347)] > 1 && void 0 !== arguments[1] ? arguments[1] : null,
n = Math[e(266)](1e8 * Math[e(414)]() + (new Date)[e(265)]() / 3),
i = Or.A.PK + Math[e(270)](n / 2);
return Dr({
t: n,
d: Lr()[e(285)].encrypt(JSON.stringify(t), i)[e(397)]()
}, r && {
z: r
})
}
The function used AES-256-CBC with an encryption key in the format 932748hfidufdwofuyfiuys8ryf7r8XXXXXXXXXXXX
, where 932748hfidufdwofuyfiuys8ryf7r8
is the base key and XXXXXXXXXXXX
is a twelve-digit number generated partly from the datetime and partly at random.
Instead of manually searching for the base key in the code, I used a tool to replace bundle.js
when the webpage requested it from the server. My modified version simply printed all the keys whenever something was encrypted. This saved me a lot of headaches.
With decryption working, I found two key fields in the client payloads:
gameid
: identifies the current game sessionactions
: lists all actions performed by the player
At this point, I knew I had to modify the payload to submit my own answers. The remaining question was how to determine the correct answer.
The game has two types of challenges:
- Questions: multiple choice questions. The server expects
"value": 0
in the payload for a correct answer. - Pairs: you must reorganize items based on images. The server expects the submitted array to be in ascending order
"value": [0, 1, 2, 3, 4, 5]
.
This was enough to always submit the correct answer.
Submitting correct answers was not enough. I wanted the highest possible score. After some testing, I discovered that the score is determined by two other factors: Powerup (obviously) and most importantly Speed of Answer.
-
Powerups: there are three powerups:
time
,double
, andremove
.time
andremove
are irrelevant since we control submissions, butdouble
is very valuable as it doubles the score. Interestingly, using it on questions gave me more points than using it on pairs. Keep in mind that powerups can only be used once per game. Using them multiple times causes a server error. -
Speed: the speed is derived from the
frame
field in theactions
array, which tracks when each interaction occurred. Fewer and faster actions give more points. By minimizing actions to just two (select and submit), I could simulate a very fast player. However, frames that were too small (like0
or1
) were rejected by the server, likely as anti-cheat protection. After a quick binary search, I found the valid frame values: for questions, select at11
and submit at15
; for pairs, select at1
and submit at60
. The difference makes sense since pairs take more time to complete physically.
Here is the process I used to achieve a near-perfect score:
- Find a game that gives you the double powerup (this may take a few tries).
- Submit answers programmatically using the discovered encryption and decryption method.
- Set the frames to the optimal values (
11
and15
for questions,1
and60
for pairs). - Use the powerup only once and apply it to a question to maximize your score.
- Set the frames to the optimal values (
Following this strategy, I reached a final score of 67.833.
If you manage to score even higher, I would love to know how you did it!
First, clone this repository by running git clone https://github.com/christiansassi/redpy
- Download and install mitmproxy from here.
- Install the mitmproxy certificate:
- Start mitmproxy by running
mitmdump
in the terminal. - Set the proxy of your device to
http://127.0.0.1:8080
(or whatever mitmproxy indicates). Alternatively, and this is strongly recommended, since everything in this project relies on the browser, you can start your browser with the appropriate proxy flags. For Chrome, for example, the flag is--proxy-server="http://127.0.0.1:8080"
. - Open mitm.it and download the certificate.
- Install the certificate you just downloaded.
- Start mitmproxy by running
Install the required packages by running pip install -r requirements.txt
.
- Run the script with
python redpy.py
and wait until you see[XXXX-XX-XX XX:XX:XX] Ready! https://www.redbull.com/it-it/projects/red-bull-quiz-stop-motorsport
. - Open your browser with the proxy activated and pointing to mitmproxy.
- Navigate to Red Bull Quiz Stop 2.0.
- Start a new game and play freely. Your answers do not matter because the script will intercept and submit the correct payload automatically.
- Once finished, save the result and complete the form.
Tip
It is recommended to do everything while logged in so that the submitted form is linked to your account. If you plan to do this, log in before step 4.
- Once the script is executed, it will be valid for only one game session. If you want to play multiple sessions, restart the script. You can keep the browser open but avoid browsing other websites, as the proxy will be disabled and you will not be able to connect.
- If you close the game window by clicking the x, the current session will be invalidated. You must restart the script to obtain a new valid game session.
This tool currently has two main limitations:
-
Speed: as explained earlier, frame values are set to the lowest possible values that are still accepted by the server as valid. While this maximizes the score, a human reviewer could easily notice that the actions were performed unnaturally fast. You can modify the source code to experiment with more "human-like" frame values, but make sure they are realistic and reproducible by an actual player.
-
Pairs Answers: at the moment, the script simulates a single move that results in the correct final order, then submits the solution. This works but is not realistic for puzzles that would normally require multiple moves. Again, a manual review could detect this discrepancy. You could improve this by modifying the code to calculate the minimum number of moves needed to solve the puzzle and submit them sequentially, better simulating a real player's behavior.
For questions, feedback, or just curiosity about this project, feel free to reach out:
Christian Sassi - sassi.christian@gmail.com
This project is for educational and research purposes only. It demonstrates how network requests can be intercepted, analyzed, and modified, and how client–server communication and game logic can be studied through reverse engineering.
Using this tool to gain unfair advantages in contests, games, or platforms you do not own or have permission to test may violate terms of service or laws. The author assumes no responsibility for misuse. By using this project, you are responsible for ensuring your actions are legal and ethical.