This week, my team and I played HackTheBox’s fourth iteration of their Cyber Apocalypse event; this one being named Cyber Apocalypse 2024 - Hacker Royale. We placed 625th out of 5693 teams (~11000 players!), being in the top 11%, and here’s a compilation of the challenges that I’ve solved.
misc / Stop Drop and Roll
The Fray: The Video Game is one of the greatest hits of the last… well, we don’t remember quite how long. Our “computers” these days can’t run much more than that, and it has a tendency to get repetitive…
This challenge is connecting to a remote container, giving us strings containing GORGE, PHREAK, and FIRE. We are supposed to send the correct translation, of which GORGE is STOP, PHREAK, is DROP, and FIRE is ROLL.
Here’s the final script, with added comments for clarity.
This challenge is connecting to a remote container, giving us strings containing GORGE, PHREAK, and FIRE. We are supposed to send the correct translation, of which GORGE is STOP, PHREAK, is DROP, and FIRE is ROLL.
And the flag is:
misc / Unbreakable
Think you can escape my grasp? Challenge accepted! I dare you to try and break free, but beware, it won’t be easy. I’m ready for whatever tricks you have up your sleeve!
We are supposed to read flag.txt (which is in the same directory as the challenge runs), and here is the challenge we’ve been given.
We can’t enter any characters that are in blacklist, and the problems we have are not being able to use the letters b, s, every number, and no spaces.
If we pass the blacklist, it tries to eval() the answer, literally just meaning that it executes the given Python code.
I even wrote a ‘helper’ script made for debugging inputs that I couldn’t see exactly why they were disallowed.
This challenge is actually much easier than it looks like. The trick is just knowing you don’t need a space in this line:
Here was my final solution.
And the flag is:
misc / Character
Security through Induced Boredom is a personal favourite approach of mine. Not as exciting as something like The Fray, but I love making it as tedious as possible to see my secrets, so you can only get one character at a time!
Upon connecting to the remote docker server, we simply get asked for the index of the flag we want, in this format.
So, because I don’t know pwntools that well, I made two separate scripts to extract the flag. Here’s the first one:
And the flag is:
crypto / Iced TEA
Locked within a cabin crafted entirely from ice, you’re enveloped in a chilling silence. Your eyes land upon an old notebook, its pages adorned with thousands of cryptic mathematical symbols. Tasked with deciphering these enigmatic glyphs to secure your escape, you set to work, your fingers tracing each intricate curve and line with determination. As you delve deeper into the mysterious symbols, you notice that patterns appear in several pages and a glimmer of hope begins to emerge. Time is flying and the temperature is dropping, will you make it before you become one with the cabin?
Let’s take a look at what we’re decrypting:
How does this encrypt the flag?
This is actually a modified encryption algorithm based on the Tiny Encryption Algorithm (hence Iced TEA).
The encryption key KEY in this file is split into four 32-bit integers, called [K0, K1, K2, K3], and the message is padded to the block size. Padded means that it ensures it’s length is a multiple of one another. The message is then divided into blocks of 64 bits.
Something interesting is that there’s two modes of encryption here, one if an IV is provided, and one if it isn’t. If the IV is provided, it uses Cipher Block Chaining (CBC), and if the IV isn’t provided, Electronic Codebook (ECB) is used. In this case, ECB is used, so let’s take a look on that.
ECB, in this file means that each block is encrypted independently using the TEA algorithm.
How does the TEA Algorithm encrypt our flag?
The TEA algorithm operates on two 32-bit halves, m0 and m1, which it gets by first padding the message, then dividing it into two blocks, of 32-bit. Each block undergoes 32 rounds of encryption, where for each round it adds a set of bitwise operations, additions, and XOR operations based on the previous key components K0, K1, K2, K3. The result of the TEA algorithm for each block gets transformed into our ciphertext.
How can we decrypt the flag?
Because we are given both the key, and the ciphertext, and with both the key and the ciphertext, we can reverse the message, because the Tiny Encryption Algorithm is designed to be reversible. That’s why they’ve given us more problems. Because they’ve conveniently given us multiple functions, I realized it’s much more simple if we just add the decrypting function myself to the original script. Here’s my solution!
And the flag is:
crypto / Primary Knowledge
Surrounded by an untamed forest and the serene waters of the Primus river, your sole objective is surviving for 24 hours. Yet, survival is far from guaranteed as the area is full of Rattlesnakes, Spiders and Alligators and the weather fluctuates unpredictably, shifting from scorching heat to torrential downpours with each passing hour. Threat is compounded by the existence of a virtual circle which shrinks every minute that passes. Anything caught beyond its bounds, is consumed by flames, leaving only ashes in its wake. As the time sleeps away, you need to prioritise your actions secure your surviving tools. Every decision becomes a matter of life and death. Will you focus on securing a shelter to sleep, protect yourself against the dangers of the wilderness, or seek out means of navigating the Primus’ waters?
Let’s take a look at what we have:
The source file contains this:
This is a modified version of the RSA encryption algorithm, which works by setting N to be a product of 1024-bit prime numbers, named P, and Q, and then encrypting the flag using the public exponent e=65537. The key point is in n = math.prod([getPrime(1024) for _ in range(2**0)]), is that it generates a product of 20 prime numbers, which means that n is prime. The security behind RSA is based on the impossibility of factoring large numbers, but we can easily factor a number if we know that it is prime, to {n: 1}
Euler’s totient function
Euler’s totient function, ϕ(n), is a function that counts the number of positive integers less than n that are coprime to n. It is defined as:
ϕ(n)=n(1−p11)(1−p21)⋯(1−pk1)
If n is a prime number p, the prime factorization is p itself:
ϕ(p)=p(1−p1)=p⋅pp−1=p−1
RSA Decryption
To decrypt a ciphertext C, and recover the message M, use the formula:
M=Cdmodn
Where d is the modular multiplicative inverse of e modulo ϕ(n), which is the Euler’s Totient Function of n. If n is prime, then ϕ(n)=n−1.
In this case, we can calculate the private exponent d as:
d=e−1mod(n−1)
Here’s the solution:
My solution
Alternative solution
RsaCtfTool can also be used to solve this challenge:
TL;DR
RSA encrypted text, with n, e, and a ciphertext encoded. n is weak because it’s prime (so ϕ(N)=N−1. Use Euler’s totient function to decrypt ciphertext into original text.
And the flag is:
crypto / Makeshift
Weak and starved, you struggle to plod on. Food is a commodity at this stage, but you can’t lose your alertness - to do so would spell death. You realise that to survive you will need a weapon, both to kill and to hunt, but the field is bare of stones. As you drop your body to the floor, something sharp sticks out of the undergrowth and into your thigh. As you grab a hold and pull it out, you realise it’s a long stick; not the finest of weapons, but once sharpened could be the difference between dying of hunger and dying with honour in combat.
Let’s take a look at what we’ve been given.
The first thing I noticed, is that we can clearly see HTB, and both curly braces in the encrypted flag ourselves, meaning we have an anagram that we need to solve.
How does this work?
This takes the flag, reverses it, and reorders the flag into groups of three. For example if the flag was ‘FUNNY_FLAG’, this would be the order of operations.
Reversing the flag
Reorders the flag into groups of three letters.
Here’s the solution script:
And the flag is:
crypto / Dynastic
You find yourself trapped inside a sealed gas chamber, and suddenly, the air is pierced by the sound of a distorted voice played through a pre-recorded tape. Through this eerie transmission, you discover that within the next 15 minutes, this very chamber will be inundated with lethal hydrogen cyanide. As the tape’s message concludes, a sudden mechanical whirring fills the chamber, followed by the ominous ticking of a clock. You realise that each beat is one step closer to death. Darkness envelops you, your right hand restrained by handcuffs, and the exit door is locked. Your situation deteriorates as you realise that both the door and the handcuffs demand the same passcode to unlock. Panic is a luxury you cannot afford; swift action is imperative. As you explore your surroundings, your trembling fingers encounter a torch. Instantly, upon flipping the switch, the chamber is bathed in a dim glow, unveiling cryptic letters etched into the walls and a disturbing image of a Roman emperor drawn in blood. Decrypting the letters will provide you the key required to unlock the locks. Use the torch wisely as its battery is almost drained out!
Let’s take a look at what we have.
How does this encrypt our flag?
For each char in the message:
If char is not in the alphabet, it changes nothing to the char.
If char is in the alphabet:
Maps the char into its position in the alphabet (for example a being 01)
Adds offset value to mapped previous number
Converted back to an integer using mod 26, forcing the character to be within range of 0 to 25.
Adds 0x41 (which is the representation of ‘A’), making character to a capital letter. For example:
If a % 26 = 0, adding 0x41 results in 65, which maps towards the ASCII value for ‘A’, same with 66 being ‘B’, etc. This is why all alphabet characters in the encrypted flag are uppercase.
I’ve added comments, to clarify which function is which. This was my solution script.
blockchain / russian roulette
This challenge took a lot of time and work trying to learn web3 and the blockchain. We get attached two .sol files, Setup.sol, and RussianRoulette.sol meaning they’re using Solidity, which is a programming language made for developing Ethereum contracts. Here’s Setup.sol.
Setup
This Setup.sol sends 10 ether to the RussianRoulette.sol contract, and it has an isSolved() function that returns a bool if the RussianRoulette contract is 0. Here’s RussianRoulette.sol.
In the RussianRoulette contract, it has a pullTrigger function that returns a string, and if the blockhash of the previous block is divisible by 10 and the remainder is 7, it self-destructs. When a contract self-destructs, it sends all of its remaining balance to the caller (which is us in this case), and because the isSolved function checks if the balance is 0, we can just keep pulling the trigger until it triggers selfdestruct, and then we can get the flag.
This went on and on, going through multiple ideas, until I finally realized that I needed an ABI to interact with the ETH smart contract, which was RussianRoulette in this case. I got the ABI by simply going back to the Remix IDE, and compiling, and then copying the ABI from there.
What’s an ABI?
An ABI (Application Binary Interface) is a file that contains the contract’s functions and their inputs and outputs. It’s used to interact with the contract from the outside world, and it’s what I needed to interact with the contract from my Python script. You could simply say an ABI acts as a translator between the contract and the outside world.
For example, This is RussianRoulette’s ABI.
Notice how this is just a JSON file, which contains everything about the contract. I noticed how the RussianRoulette contract has a pullTrigger function, and it returns a string. We can use this string to check if the self-destruct event was triggered, and simply pull the trigger until the contract balance is 0.
On the challenge itself, we are provided two ports. The first port simply just says “Sandbox is active”, and this is the sandbox running the ETH contracts.
The second port simply contains a netcat instance of which we can use to get the addresses we need. We get the private key, the address, the target contract, and the setup contract. We can use the private key to interact with the contract, and using the same port, we can get the flag.
In the below script, I will call the sandbox RPC_PORT, and the netcat instance SEND_PORT. Here’s the final script. I’ve added comments if you need help understanding what I’m doing.
Solution
And the flag is:
Reflection
Being the second CTF I’ve played all the way through, I think we did a great job. I have to thank my team D0M BU$TЄR$ for their insights, solving challenges I wouldn’t even know how to start. The CTF itself was extremely fun, and the challenges were well thought out. Kudos to the authors!