0%

IronCTF2024Writeup

Omar Mohamed
Thanks for sharing!

بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ

Iron CTF 2024 Writeup
Hello there! Today, I'm going to share some of the challenges I solved in IronCTF 2024.

Content

Let's start with some warm-up challenges.

JWT Hunt

JWT Hunt
Upon visiting the website, I saw register and login buttons. Knowing this was a warm-up challenge, I fired up Burp Suite to capture the traffic.
I registered and logged in, receiving a welcome message:
JWT Hunt Welcome
I checked the page source—nothing. Then, I navigated to /robots.txt and found the first part of the secret key: 6yH$#v9Wq3e&Zf8L, along with a path to the fourth part: /secretkeypart4.
JWT Hunt Key 1
Next, I tried the common file /sitemap.xml, and found the third part of the key: 2C@mQjUwEbGoIhNy.
JWT Hunt Key 3
Checking Burp Suite's history, I found the second part of the key in a cookie: pRt1%Y4nJ^aPk7Sd.
JWT Hunt Key 2
Now, for the last key, I navigated to /secretkeypart4 but got a Bad Request:
JWT Hunt Bad Request
I sent the request to Burp Suite's Repeater and changed the request methods. Eventually, using HEAD, I received a 200 OK and the fourth key: 0T!BxlVz5uMKA#Yp.
JWT Hunt Key 4
Combining all the parts, the full secret key is:
6yH$#v9Wq3e&Zf8LpRt1%Y4nJ^aPk7Sd2C@mQjUwEbGoIhNy0T!BxlVz5uMKA#Yp
Since the challenge involves JWT, I copied my token and went to jwt.io. I changed the username field to admin and used the secret key:
JWT Hunt jwt.io
Using the modified token in a request to /dashboard, I was redirected and obtained the flag:
JWT Hunt Flag
ironCTF{W0w_U_R34lly_Kn0w_4_L07_Ab0ut_JWT_3xp10r4710n!}

Now for some Web challenges.

Loan App

Loan App

Discovering the Website

Upon visiting the website, I found a login form. I registered and logged in—nothing special here, except that both the username and password had to be uuidV4. So, I generated one and logged in.
Now, there's a form with two fields: Amount and Reason. I submitted random values and got a ticket like this:
Loan Form
q
Loan ID: 67018c083b619fc5054d3828
Amount: $1337
Status: pending

Checking the Source Code

Everything looked normal, except for one endpoint:
Vulnerable Endpoint
This endpoint allowed loan approval, and the flag was included in the response. The problem was that it had no authorization! So, I made a request to the endpoint, but got hit with a 403 Forbidden:
403 Forbidden
Looking at the code, this wasn't supposed to happen—there must be some middleware involved. After inspecting the files, I found the haproxy.cfg configuration:
q
global
log stdout format raw local0
maxconn 2000
user root
group root
daemon

defaults
log global
option httplog
timeout client 30s
timeout server 30s
timeout connect 30s

frontend http_front
mode http
bind :80
acl is_admin path_beg /admin
http-request deny if is_admin
default_backend gunicorn

backend gunicorn
mode http
balance roundrobin
server loanserver loanapp:8000 maxconn 32
As we can see, it denies access to any path starting with /admin:
q
acl is_admin path_beg /admin
http-request deny if is_admin
I tried various bypass techniques: making some letters uppercase, URL encoding, adding slashes, but nothing worked.

Finding the Vulnerability

Going back to the files, particularly the docker-compose.yml, I noticed the version of HAProxy:
Old HAProxy
After research, I found that this version is vulnerable to CVE-2021-40346—an HTTP request smuggling vulnerability. I referenced this blog: Critical Vulnerability in HAProxy: CVE-2021-40346.
HTTP request smuggling happens when a carefully crafted request tricks a server into processing it as two requests, allowing the attacker to perform malicious actions.
Here’s the full request:
Full Request
Executing this on the server bypassed the restrictions.

Retrieving the Flag

Returning to the website:
Flag
I finally retrieved the flag:
ironCTF{L04n_4ppr0v3d_f0r_H4ch3r$!!}

b64SiteViewer

b64SiteViewer

Discovering the Website

Upon visiting the website, there was an input field for a URL.
b64 main
I entered https://google.com and received the base64 encoded version of the site.
b64 response
It seems like the server makes a request to the provided URL and returns its base64 encoded content. This behavior hinted at a potential SSRF vulnerability.

Checking the Source Code

Looking at the source code, I noticed a blacklist meant to prevent SSRF attacks:
q
blacklist_scheme=['file','gopher','php','ftp','dict','data']
blacklist_hostname=['127.0.0.1','localhost','0.0.0.0','::1','::ffff:127.0.0.1']
There was also an /admin endpoint:
admin endpoint
This endpoint checks the request's IP address, blocking 127.0.0.1 or localhost. If we bypass this check, the server executes the command passed in the cmd parameter and returns the output. There is also a cmd_blacklist that blocks some commands that we don't have access to.

Hitting a Wall

At first I thought about using some flags like X-Forwarded-For, X-Forwarded-Host ... etc by setting them to 127.0.0.1 or localhost but it didn't work.

Blacklist Bypass

Another way was to make the server itself make the request for us, and how can we do that? from the input field we found earlier! So we have to bypass the blacklist_hostname as menthoined above.
A small trick which not everyone knows, is that 127.1 is the same as 127.0.0.1 in the server. and it is not in the blacklist_hostname. That's our way in.
We know that the website is running on port 5000 from the source code.
website port
So, I tried the URL https://127.1:5000/admin?cmd=ls, and received the following base64-encoded response:
q
YXBwLnB5CnJlcXVpcmVtZW50cy50eHQKcnVuLnNoCnRlbXBsYXRlcwo=
Which decodes to:
q
app.py
requirements.txt
run.sh
templates
Next, I used https://127.1:5000/admin?cmd=cat+*, but I got:
q
Q29tbWFuZCBibG9ja2Vk
which decodes to: Command blocked. It seemed I hit the cmd_blacklist, which wasn’t visible in the source code.

Bypassing the Blacklist and Getting the Flag

I tried alternative commands, some didn't work like less, but finally, I had success with head. I used the following URL: https://127.1:5000/admin?cmd=head+*.
The response was:
base64
PT0+IGFwcC5weSA8PT0KZnJvbSBmbGFzayBpbXBvcnQgcmVuZGVyX3RlbXBsYXRlLHJlbmRlcl90ZW1wbGF0ZV9zdHJpbmcsRmxhc2sscmVxdWVzdApmcm9tIHVybGxpYi5wYXJzZSBpbXBvcnQgdXJscGFyc2UKaW1wb3J0IHVybGxpYi5yZXF1ZXN0CmltcG9ydCByYW5kb20KaW1wb3J0IG9zCmltcG9ydCBzdWJwcm9jZXNzCmltcG9ydCBiYXNlNjQKYXBwPUZsYXNrKF9fbmFtZV9fKQphcHAuc2VjcmV0X2tleT1vcy51cmFuZG9tKDE2KQoKCj09PiByZXF1aXJlbWVudHMudHh0IDw9PQpGbGFzaz09My4wLjMKCj09PiBydW4uc2ggPD09CiMhL2Jpbi9iYXNoCmV4cG9ydCBmbGFnPSJpcm9uQ1RGe3kwdTRyM3IwY2sxbjZrMzNwaDRjazFuNn0iCmNkIC9ob21lL3VzZXIKcHl0aG9uIC1tIGFwcAo9PT4gdGVtcGxhdGVzIDw9PQo=
which decodes to:
bash
==> app.py <==
from flask import render_template,render_template_string,Flask,request
from urllib.parse import urlparse
import urllib.request
import random
import os
import subprocess
import base64
app=Flask(**name**)
app.secret_key=os.urandom(16)

==> requirements.txt <==
Flask==3.0.3

==> run.sh <==
#!/bin/bash
export flag="ironCTF{y0u4r3r0ck1n6k33ph4ck1n6}"
cd /home/user
python -m app
==> templates <==
And here is the flag:
ironCTF{y0u4r3r0ck1n6k33ph4ck1n6}

MovieReviewApp

MovieReviewApp

Discovering the Website

Upon visiting the website, I found buttons that navigate to random movie posters. The URL revealed we were not in the root directory:
https://movie-review.1nf1n1ty.team/movies/

Git Repository

Returning to the root directory, I discovered a directory listing containing a .git directory.
Movie Review App Directory Listing
Using git-dumper, I dumped the git files:
q
git-dumper http://movie-review.1nf1n1ty.team/.git /movies
Next, I checked previous commits for useful information. The command git log revealed past commits.
Movie Review App Git Log
Inspecting the commit with hash 66469c24090e50c6a4a955f679c6cfff2f2380da, I found hardcoded credentials and a route:
Movie Review App Creds
q
+ADMIN_USERNAME = 'superadmin'
+ADMIN_PASSWORD = 'Sup3rS3cR3TAdminP@ssw0rd$!'
/admin

Admin Panel Access

Here I made a mistake by going right away to http://movie-review.1nf1n1ty.team/admin. I got 404. The right path was http://movie-review.1nf1n1ty.team/servermonitor/admin/ as you can see in file path
Movie Review App admin path
After logging in, I got a form for entering an IP address and a Ping Count.
Movie Review App Admin
When attempting to ping an IP, I received an error. I reviewed the commit responsible for this in the git log:
Movie Review App Ping Commit
In the code, we can see it is validating the ip but not the count. so count is our way in!.
Movie Review App Ping Code

Exploiting the Vulnerability

Since the front end just accepting numbers, I used burp to manipulate count That's our command:
q
ping -c {count} {ip}
We add a ; to terminate the ping -c part and execute our command. We can also add ; after it to exclude the ip, but it is not necessary.
q
;ls;
This returned the contents of the directory.
Movie Review App ls
After exploring the files, I couldn’t locate the flag.

Retrieving the Flag

navigated a bit in the machine until I found the flag in / directory
q
;cat ../../flag.txt;
This revealed the flag:
ironCTF{4lways_b3_c4ar3ful_w1th_G1t!}
I had a blast participating in this CTF with all its challenges, and it was awesome getting to know some cool people along the way. Don't forget to check out my X (formerly Twitter) for more updates. Catch you next time!
Peace!

You might also like