no-copy-allowed (267 pts, 40 solved)

keep doing this until you get bored.

http://ctf.b01lers.com:5125

Author: Dx

Solution

Discovery Phase

Navigate on website, you will have a value to copy and paste in the form.

<html>
	<head>
        <style>
            @font-face {font-family:b;src:url("index.ttf")}
            p, input { font-size:3vw; }
            span { font-family:b;font-size:2vw; }
            input { border: solid 0.4vw;width:60vw; }
        </style>
	</head>
	<body>
		<table width="100%" height="100%"><tbody><tr><td><center>
            <p>Enter "<span>EIEjtvPAY0fxF4sviaIR90pgg9ob6gFGdBEUihkc</span>" to continue</p><input>
		</center></td></tr></tbody></table>
        <script>
            var input = document.querySelector("input");
            input.addEventListener("keypress", function(e) {
                if (e.keyCode == 13) {
                    window.location.href = input.value + ".html";
                }
            });
        </script>
	</body>
</html>

We need to navigate in http://ctf.b01lers.com:5125/EIEjtvPAY0fxF4sviaIR90pgg9ob6gFGdBEUihkc.html to get next page, and we have a new value to copy and paste in the form, until we get flag.

Exploiting Phase

Scripting

Let’s create a script to do this for us.

import bs4
import requests
import sys

base = "http://ctf.b01lers.com:5125/"
path = "index"
while True:
    r = requests.get(base + path + ".html")
    soup = bs4.BeautifulSoup(r.text, "html.parser")
    path = soup.find("span").text
    print(path)
    if "bctf" in path:
        print("Found flag: {}".format(path))
        sys.exit(0)

Problem

Code looks running until http://ctf.b01lers.com:5125/x70OK5gxhlPEbhmRRmqhimTOSkTz3oAJUTo2VoC8.html page, something goes wrong…

no-copy-allowed

Let’s check the source code of the page:

<html>
	<head>
        <style>
            @font-face {font-family:b;src:url("x70OK5gxhlPEbhmRRmqhimTOSkTz3oAJUTo2VoC8.ttf")}
            p, input { font-size:3vw; }
            span { font-family:b;font-size:2vw; }
            input { border: solid 0.4vw;width:60vw; }
        </style>
	</head>
	<body>
		<table width="100%" height="100%"><tbody><tr><td><center>
            <p>Enter "<span>vBBEO.CpefN.TBsgS.HfjLG.wcZee.qZrhY.TDFdp.mBbei.IbHlG.tmXTZ.XqBtD.LYzBt.upRSj.EOzlj.izClL.oRdKf.CpefN.XceXS.mBbei.XqBtD.qPcfO.IbHlG.qZrhY.TDFdp.DMmXT.adNyQ.JkxgL.hhGEG.hhGEG.kcXxq.tmXTZ.yWzOK.EtLOl.adNyQ.NliRt.hMtRY.jNjpP.rAufC.EOzlj.yRfgC.</span>" to continue</p><input>
		</center></td></tr></tbody></table>
        <script>
            var input = document.querySelector("input");
            input.addEventListener("keypress", function(e) {
                if (e.keyCode == 13) {
                    window.location.href = input.value + ".html";
                }
            });
        </script>
	</body>
</html>

Span element contains a long string, instead of next page path. Why?

Font Ligatures

Analyzing the source code, we can see that the font used is a custom font, with a custom name. Let’s check the font file using FontDrop tool, and we can see that the font contains a lot of ligatures:

no-copy-allowed

Span element use a custom font, with ligatures.

Ligatures are a way to combine two or more characters into a single glyph. For example, the “fi” ligature combines the letters “f” and “i” into a single glyph. This is useful for reducing the number of glyphs in a font, and for making text more readable.

Solution

Define some usefull function for resolve ligatures:

import bs4
import requests
import sys
from fontTools.ttLib import TTFont

# This is a list of all particular glyphs in the font
replace_map = {
    'zero': '0',
    'one': '1',
    'two': '2',
    'three': '3',
    'four': '4',
    'five': '5',
    'six': '6',
    'seven': '7',
    'eight': '8',
    'nine': '9',
    'underscore': '_',
    'braceleft': '{',
    'braceright': '}',
    'space': ' ',
    'comma': ',',
    'period': '.',
    'exclam': '!',
    'question': '?',
    'hyphen': '-',
}

# This function resolves the glyphs to their real text
def resolve_text(text):
    for x in replace_map:
        text = text.replace(x, replace_map[x])
    return text

# This function gets all the ligatures from the font in a dictionary
def get_ligatures(font_path):
    font = TTFont(font_path)
    ligatures = {}
    for table in font['GSUB'].table.LookupList.Lookup:
        if table.LookupType == 4:
            for subtable in table.SubTable:
                for key, value in subtable.ligatures.items():
                    for glyph in value:
                        glyph_text = resolve_text(key)
                        for x in glyph.Component:
                            glyph_text += resolve_text(x)
                        ligatures[glyph_text] = resolve_text(glyph.LigGlyph)
    return ligatures

# This function resolves the path to the real path using font ligatures
def resolvePath(path, font="font.ttf"):
    ligature_table = get_ligatures(font)
    for key in ligature_table:
        path = path.replace(key, ligature_table[key])
    return path

Starting from last point, resolve next steps until we get the flag.

base = "http://ctf.b01lers.com:5125/"
path = "x70OK5gxhlPEbhmRRmqhimTOSkTz3oAJUTo2VoC8"

while True:

    # Download the font
    r = requests.get((base + path + ".ttf"), allow_redirects=True)
    open('font.ttf', 'wb').write(r.content)

    # Get the next path
    r = requests.get(base + path + ".html")
    soup = bs4.BeautifulSoup(r.text, "html.parser")
    path = resolvePath(soup.find("span").text)
    
    print(path)
    if "bctf" in path:
        print("Found flag: {}".format(path))
        sys.exit(0)

Script runs until http://ctf.b01lers.com:5125/wDcIyytPUCxMxQB2YGrlQPDuASUp3ueyjeOnDswA.html is reached.

Something is wrong, we have to check the url. Flag is in <p> instead <span>

base = "http://ctf.b01lers.com:5125/"
path = "wDcIyytPUCxMxQB2YGrlQPDuASUp3ueyjeOnDswA"

# Download the font
r = requests.get((base + path + ".ttf"), allow_redirects=True)
open('font.ttf', 'wb').write(r.content)

# Get the flag path
r = requests.get(base + path + ".html")
soup = bs4.BeautifulSoup(r.text, "html.parser")
path = resolvePath(soup.find("p").text)

print(path)

bctf{l1gatur3_4bus3_15_fun_X0UOBDvfRkKa99fEVloY0iYuaxzS9hj4rIFXlA3B}