Fetch The Flag 2023 Quick Maths Writeup

Published: March 11, 2025, updated: March 11, 2025

This is a writeup for the Fetch The Flag 2023 Quick Maths challenge.

Challenge notes

To try and get better grades in math, I made a program that gave me timed quizzes. Funny thing is that as I got better at the questions in this test, I got worse grades on my math tests.

NOTE: Float answers are rounded to 1 decimal points.

NOTE: And here’s another twist… the answers to division questions depend on integer division or double division. I.e., 3/5 = 0 3/5.0 = .6

Press the Start button in the top-right to begin this challenge. Connect with:

nc challenge.ctf.games 32237

Connecting to the challenge server

Using the command supplied in the challenge notes, I use nc to connect to the challenge server. The server prompts you to solve math challenges and times you out when you don’t answer fast enough.

Welcome! To be honest, I am a Computer Science major but I was never any good at math in school. I seemed to always get Cs.

Note to self: Round all answers to 1 decimal point, where applicable.

Do you want to give it a chance? (Y/n): y
Awesome, good luck!
What is 97.2 / 33.7?
Too Slow!!!
Good bye :(

I decided to use this as an opportunity to test out the nclib library in Python. You can use nclib to automate interacting with telnet-like interfaces over the network.

The protocol

The challenge server writes 6 lines of text and asks the user to enter y. After that, the server prompts you to solve a long chain of arithmetic questions.

Here is one more transcript from the server:

Welcome! To be honest, I am a Computer Science major but I was never any good at math in school. I seemed to always get Cs.

Note to self: Round all answers to 1 decimal point, where applicable.

Do you want to give it a chance? (Y/n): y
Awesome, good luck!
What is 3 / 5?
What is 3 / 5.0?
Too Slow!!!
Good bye :(

As noted in the challenge notes, the server cares about integer and float division.

The solution script

I first define a few test cases in pytest to make sure that I get the integer versus float division right:

from calculator import calculate


def test_all() -> None:
    assert calculate("What is 3 / 5?") == 0
    assert calculate("What is 3 / 5.0?") == 0.6
    assert calculate("What is 76.0 * 36.1?") == 2743.6
    assert calculate("Correct!\nWhat is 76.0 * 36.1?") == 2743.6

This is the calculate function in Python:

import re, operator

FORMULA_RE = re.compile(
    r"(?:.+\n)?What is ([0-9.]+) ([*/+-]) ([0-9.]+)\?",
    re.MULTILINE,
)
OPERATORS = {
    "*": operator.mul,
    "/": operator.truediv,
    "+": operator.add,
    "-": operator.sub,
}

def calculate(formula: str) -> Union[int, float]:
    """Evaluate a formula."""
    match = FORMULA_RE.match(formula)
    assert match is not None, f"Couldn't match {formula}"
    left, op_chr, right = match.group(1, 2, 3)
    floats = '.' in left or '.' in right
    op = OPERATORS[op_chr]
    if floats:
        return round(op(float(left), float(right)), 1)
    return int(op(int(left), int(right)))

The calculate function performs the following tasks:

  1. Use a regular expression to parse the math formula from the server’s question.
  2. Pick the right operator from a dictionary called OPERATORS
  3. Determine if the answer is expected to be a floating point number or integer.
  4. If a floating point number is required, run the calculation and return the result rounded to 1 decimal point
  5. If an integer is required, round the result and return it.

Here’s the full script that solved the challenge:

#!/usr/bin/env python3
import operator
import re
from typing import Union

import nclib

HOST = "challenge.ctf.games", 32237

FORMULA_RE = re.compile(
    r"(?:.+\n)?What is ([0-9.]+) ([*/+-]) ([0-9.]+)\?",
    re.MULTILINE,
)
OPERATORS = {
    "*": operator.mul,
    "/": operator.truediv,
    "+": operator.add,
    "-": operator.sub,
}

def calculate(formula: str) -> Union[int, float]:
    """Evaluate a formula."""
    match = FORMULA_RE.match(formula)
    assert match is not None, f"Couldn't match {formula}"
    left, op_chr, right = match.group(1, 2, 3)
    floats = '.' in left or '.' in right
    op = OPERATORS[op_chr]
    if floats:
        return round(op(float(left), float(right)), 1)
    return int(op(int(left), int(right)))

def main():
    nc = nclib.Netcat(HOST)
    print(nc.recv_until("(Y/n): ").decode())
    nc.send_line("y")
    print(nc.recv_until("Awesome, good luck!\n").decode())
    while True:
        line = nc.recv_until("? ").decode()
        print("Next question:", line)
        line = line.strip()
        result = calculate(line)
        print("Calculated the result:", result, "-- sending now")
        nc.send_line(str(result))


if __name__ == "__main__":
    main()

Tags

I would be thrilled to hear from you! Please share your thoughts and ideas with me via email.

Back to Index