0xfat CTF writeup Levels 11-20

2026-09-27

Introduction

I’ve recently restarted solving CTF challenges as a way to get into exploit development and security research. I found this website 0xfat.io to solve some basic to and slightly difficult challenges. For some of the problems I needed help, and searched for writeups. I found this blog, however it had solutions only for levels 1 to 10.

I will be adding the solutions from level 11 onwards in this post. Currently I have added till level 20.

Level 11

The password is given by the output of the following PHP code

function pwCheck($password)
{
    if($password==date("d.m.Y")) //GMT +1
        return true;
    else return false;
}

This is very simple and the password is what the current date is, separated by dots in the format <day>.<month>.<year>

Level 12

The problem states that the flag is the sum of the numbers from 1 to 435. This can be simply computed by using the formula

$$\sum_{i=1}^{n} = \frac{n \times (n+1)}{2}$$

This will give us the sum.

Level 13

Again we are given a piece of PHP code.

function pwCheck($username,$password)
{
    if(!$username || !$password) return false;
    if(strlen($username)==$password)
        return true;
    else return false;
}

We just need to provide a username and use the length of the username as password.

Level 14

This time we are given another code.

function pwCheck($guid,$password)
{
  if(!$guid || !$password) return false;
  $users = implode(file('/data/login_info.json'));
  $json = json_decode($users,true);

  foreach($json['result'] as $data)
    if($data['guid']==$guid && $data['password'] == $password && $data['account_status']=='active')
      return true;
  return false;
}

We see that it reads a JSON file and decodes it. There is some if condition that needs to be met to be accepted. Let’s look at the JSON file first, by going to the path /data/login_info.json at the root of the website i.e 0xf.at/data/login_info.json. Only one of the JSON entries have the account_status as active. We need to use the username and password from this specific entry.

Level 15

After putting in some inputs and checking their outputs, we notice a very straightforward pattern. Let’s say an input string is made of the letters c1, c2, c3… and so on. The algorithm works like this

c1 c2 c3 c4
|  |  |  |
+--|--|--|-> c1
   +--|--|-> c2' (c2 + 1) // adding to the ASCII value of the character
      +--|-> c3' (c3 + 2)
         +-> c4' (c4 + 3)

A python script to give us the input when an output is provided.

#/usr/bin/python3

a = input("> ")
s = a[0]
for i, x in enumerate(a[1:]):
    s += chr(ord(x) - (i+1))
print(s)

Level 16

The PHP code uses Base64 encoding on the password and checks equality with a string. So, to get the password we can perform a decode on the string it is compared with.

echo 'YWQwZTdmNTI2NzA2N2UwOGQxYjM5ZTY3Mw==' | base64 -d

Level 17

We need to find a string that matches the given regex. There can be multiple answers. Refer Regex101 for help with regex.

Level 18

This is a simple morse code decode. Use a simple website like this.

Level 19

This problem needs us to write a small program to get the output before the time runs out.

s = {
    "2": "abc",
    "3": "def",
    "4": "ghi",
    "5": "jkl",
    "6": "mno",
    "7": "pqrs",
    "8": "tuv",
    "9": "wxyz",
}

vs = s.values()
o = 0

a = input("> ")
for x in a:
    if x.isalpha():
        # look for it in the dict values
        v = [d for d in vs if x in d][0]
        # get its key
        k = next((k for k, va in s.items() if va == v), None)
        # get the index
        idx = v.index(x) + 2
        print(f"x = {x}, k = {k}, idx = {idx}")
        #print(type(idx))
        o += idx * int(k)
    else:
        o += int(x)
print(o)

Level 20

We can do a brute force checking all possible combinations from the file and get the concatenated string.

from hashlib import md5

a = open("wordlist.txt").read().split("\n")
for x in a:
    for b in a:
        if x != b:
            conc = x + b
            print(f" checking {conc}")
            if md5(conc.encode("utf-8")).hexdigest() == "9cb70a10d800fe17094b27ebfdc9d3b6":
                print(f"found {conc}")
                break