Challenge Name: ConCmarks

Mohamed Samir
5 min read1 day ago

--

it might be useful to find a mark.

this is our target:

Okay guys, I guess this challenge It depends on exploring something mysterious, maybe a comment or something in the page source or something I will figure it out when I inspect the page, so let’s open the page source:

Yea as we can see, there is nothing interesting here just very basic HTML Codes.

let’s inspect the main page and see :

yea we got this pretty comment, let’s explain it :

  • <!--FILE= sourceXXXX -->: Suggests a file or resource named "sourceXXXX," where "XXXX" is likely the part I need to figure out.
  • <!--XXXX are numbers > 7000 & < 9000 -->: Indicates that "XXXX" is a number between 7001 and 8999.

So I need a tool to try this on the target so, I’ll use burpsuite the intruder model, I’ll edit the url and add sourceXXXX and I will replace XXXX with a different number starts from 7000 until 8999 and I will make a cup of tea until it will finish it’s job, so let’s do it:

this is the url before adding the file source in the directory:

http://wcamxwl32pue3e6mekgvd1gf9zrqqyz8wqrwf9vw-web.cybertalentslabs.com/

I will add sourceXXXX

http://wcamxwl32pue3e6mekgvd1gf9zrqqyz8wqrwf9vw-web.cybertalentslabs.com/sourceXXXX

As u see here I edited XXXX and I added it between §§ because it’s the part that I will change in every request, so let’s create our payload:

Oookkayy actually burpsuite will take too much time to do that, so I’ll use a python script to do this task for me to just save time :)

import requests
import time

# Base URL provided in the challenge
url = "http://wcamxwl32pue3e6mekgvd1gf9zrqqyz8wqrwf9vw-web.cybertalentslabs.com/source"

for number in range(7001, 9000): # Iterate through the range 7001 to 8999
full_url = f"{url}{number}" # Construct the full URL
try:
response = requests.get(full_url) # Send a GET request
if response.status_code == 200: # Check for successful response
print(f"Valid file found: source{number}")
else:
print(f"Checked: source{number} - Status Code: {response.status_code}")
time.sleep(0.2) # Add a small delay to avoid rate-limiting issues
except requests.exceptions.RequestException as e: # Handle any errors
print(f"Error with {full_url}: {e}")
  • Imports:
  • requests: For sending HTTP requests.
  • time: For adding delays between requests.
  • Base URL:
  • url: The starting URL provided in the challenge.
  • Loop:
  • for number in range(7001, 9000): Iterates through numbers from 7001 to 8999.
  • Full URL:
  • full_url = f"{url}{number}": Constructs the full URL by appending the number.
  • HTTP Request:
  • response = requests.get(full_url): Sends a GET request to the constructed URL.
  • Check Status:
  • If response.status_code == 200, it prints the valid file name.
  • Else, it logs the status code for debugging.
  • Delay:
  • time.sleep(0.2): Pauses 0.2 seconds between requests to avoid rate-limiting.
  • Error Handling:
  • Catches and logs any exceptions during the requests.

this is the script that I will use, I will save it as a check.py file

then, I will run the code from my terminal using this command

python check.py

it’s working but still without 200 OK response :(

and yeaaa finally we got the right source :

let’s add it into the url and see the result…

and we have been redirected to this page, let’s explain this code..

The PHP code checks two inputs, n1 and n2, ensuring they are different but produce the same MD5 hash when concatenated with a $salt. To solve this challenge, we need to find two distinct values for n1 and n2 that cause an MD5 hash collision with the given $salt.

let’s try to add a random value into n1&n2 and see:

as we can see:

Sorry this value not valid.

So I will make a quick research about how could I bypass this condition.

this condition treats the inputs as a string but what if we let it treat it as an array?

so in this case we could bypass it, to be more clear we will bypass it logically :

  • n1[] as an Array:
  • By passing n1[]=, $_GET['n1'] becomes an array instead of a string.

n2[]=1:

  • Similarly, $_GET['n2'] becomes an array.

Bypass Logic:

  • The !== operator compares input1 (array) with input2 (array) and evaluates them as not strictly equal, even if their content matches.
  • Hashing an array with @hash("md5", $salt.$input1) results in NULL, which is equal for both n1[] and n2[] due to the silent error suppression (@).

let’s try it and see the result, our payload will be like

http://wcamxwl32pue3e6mekgvd1gf9zrqqyz8wqrwf9vw-web.cybertalentslabs.com/?n1[]=&n2[]=1

and yea we got the flag!

FLAG{K0nC473n4710N_!5_50_C00l}

thanks for reading my writeup!

See u in the next CTF Challenge…

--

--

Mohamed Samir
Mohamed Samir

Written by Mohamed Samir

second-year Computer Science | Web PenTester | Offensive | Software Tester Trainee at @CLS Learning Solutions | CTF Player at THM | Top 4% on tryhackme