Calibrator
6 minutes to read
We are given a remote instance to connect to:
$ nc 165.227.224.40 31139
[OK] Memory check
[OK] Syncing filesystem
[OK] Detecting sensors
[OK] Module loader
[OK] Reading configurations
Inititing calibration process ...
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββΌββββββββββββββββββββΌβββββββββββββββββββββββββΌβΌβββββββββββββββββββββββΌβ
ββ XenoCal 2000 β . ββ ββ
ββΌββββββββββββββββββββ€ βββ xββ . . ββ
ββ Iteration: 42 β x βΊ βββ ββ x ββ
ββΌββββββββββ¬ββββββββββ€ ββ ββ
ββ X:1337 β Y:65189 β . x βββββΌβΌββββ ββ
ββΌββββββββββ΄ββββββββββ x βββ ββ βββ x ββ
ββ . βββ ββ βββ ββ
ββ βββ x βΌ ββ . ββ ββ x ββ
ββ βββ . ββ ββ ββ ββ
ββ . β ββ . β ββ
ββΌβββββββββββββββββββββββββββββββββββΌββββββββββΌβΌββββββββββΌβββββββββββββΌβ
ββ x β x ββ β βΌ ββ
ββ x βββΊx ββ ββ ββ ββ
ββ . βΌ ββ ββ ββ . ββ
ββ . βββ ββ βββ ββ
ββ βββ x βββ ββ βββ x ββ
ββ βββ βββ βββ βββββΌβΌββββ ββ
ββ βββ βββ ββ ββ
ββ x . . ββ . ββ
ββ . x ββ x ββ
ββ x βΌ x . ββ x ββ
ββ ββ ββ
ββΌβββββββββββββββββββββββββββββββββββββββββββββΌβΌβββββββββββββββββββββββΌβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ
β
βββ¬ββββββββββββββββββββββββββββ¬ββ ββββββββ βββββΌβ
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β β[=()=]β ββββββ€
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β ββββΌβββββββΌβββ β β
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β ββββΌβΌβΌβΌβΌβΌβΌβΌβ€βΊβ β β
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β ββββΌβββββββΌβββ ββββββ€
βββ΄ββββββββββββββββββββββββββββ΄ββ β[=()=]β ββββββ
ββββββββ
Iteration 0:
>
Source code analysis
And we also have the source code in Python:
from FLAG import flag
import random
import math
import time
ITERATIONS = 47
SIDE_LENGTH = 2 * 10 ** 9
ATTEMPTS = 300
HI = SIDE_LENGTH // 2
LO = -SIDE_LENGTH // 2
def banner():
banner = """
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββΌββββββββββββββββββββΌβββββββββββββββββββββββββΌβΌβββββββββββββββββββββββΌβ
ββ XenoCal 2000 β . ββ ββ
ββΌββββββββββββββββββββ€ βββ xββ . . ββ
ββ Iteration: 42 β x βΊ βββ ββ x ββ
ββΌββββββββββ¬ββββββββββ€ ββ ββ
ββ X:1337 β Y:65189 β . x βββββΌβΌββββ ββ
ββΌββββββββββ΄ββββββββββ x βββ ββ βββ x ββ
ββ . βββ ββ βββ ββ
ββ βββ x βΌ ββ . ββ ββ x ββ
ββ βββ . ββ ββ ββ ββ
ββ . β ββ . β ββ
ββΌβββββββββββββββββββββββββββββββββββΌββββββββββΌβΌββββββββββΌβββββββββββββΌβ
ββ x β x ββ β βΌ ββ
ββ x βββΊx ββ ββ ββ ββ
ββ . βΌ ββ ββ ββ . ββ
ββ . βββ ββ βββ ββ
ββ βββ x βββ ββ βββ x ββ
ββ βββ βββ βββ βββββΌβΌββββ ββ
ββ βββ βββ ββ ββ
ββ x . . ββ . ββ
ββ . x ββ x ββ
ββ x βΌ x . ββ x ββ
ββ ββ ββ
ββΌβββββββββββββββββββββββββββββββββββββββββββββΌβΌβββββββββββββββββββββββΌβ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββ
β
βββ¬ββββββββββββββββββββββββββββ¬ββ ββββββββ βββββΌβ
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β β[=()=]β ββββββ€
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β ββββΌβββββββΌβββ β β
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β ββββΌβΌβΌβΌβΌβΌβΌβΌβ€βΊβ β β
β ββΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβΌβ β ββββΌβββββββΌβββ ββββββ€
βββ΄ββββββββββββββββββββββββββββ΄ββ β[=()=]β ββββββ
ββββββββ
"""
print(banner)
def load():
boot_messages = \
"""
[\033[92mOK\033[0m] Memory check
[\033[92mOK\033[0m] Syncing filesystem
[\033[92mOK\033[0m] Detecting sensors
[\033[92mOK\033[0m] Module loader
[\033[92mOK\033[0m] Reading configurations
Inititing calibration process ...
""".split('\n')
for m in boot_messages:
print(m)
time.sleep(random.random())
# Calibrator's error acceptance threshold
e = 2
if __name__ == '__main__':
load()
banner()
for i in range(ITERATIONS):
print(f"Iteration {i}:")
R = random.randint(SIDE_LENGTH // 4, SIDE_LENGTH // 2)
X = random.randint(LO + R, HI - R)
Y = random.randint(LO + R, HI - R)
for a in range(ATTEMPTS):
line = input("> ")
x, y = [int(n) for n in line.split(' ')]
D = math.sqrt((X - x) ** 2 + (Y - y) ** 2)
if D <= e:
print("\033[94mREFERENCE", end="\n\033[0m")
break
elif D <= R:
print("\033[92mDETECTED", end="\n\033[0m")
else:
print("\033[91mUNDETECTED", end="\n\033[0m")
else:
exit(0)
print(flag)
The challenge runs 47 iterations, and in each iteration, we are given 300 attempts to input a point for
-else
syntax existed in Python. The else
blocks executes when the for
loop is finished (more information at www.w3schools.com), but not if a break
statement exits the loop.
So, looking again at the challenge code, we need to enter the if
statement where D <= e
, so that break
is called and the server does not call exit(0)
.
Drawing the problem
On each iteration, we must enter a point LO
, HI
and SIDE_LENGTH
).
In order to pass the iteration, we must enter a point DETECTED
. And if UNDETECTED
.
The following figure shows all these sections:
Notice that we donβt know any of DETECTED
, UNDETECTED
to somehow find a point REFERENCE
.
Binary search
Since we have only 300 attempts, we must find a quick searching algorithm that allows us to find a REFERENCE
point. Since we have a βbinaryβ oracle (DETECTED
or UNDETECTED
), we will try to find a way to apply binary search.
The objective is to find 0
to HI
), so that
Then, we will do another binary search on point LO
to 0
), so that
Analogously, the other two binary search algorithms will output
Therefore, we can construct this system of equations:
Subtracting the first two equations and the last two, we have this reduced system of equations:
So, we can isolate
Once we know the point
Flag
After programming the above procedure, we execute the script and eventually find the flag:
$ python3 solve.py 165.227.224.40:31139
[+] Opening connection to 165.227.224.40 on port 31139: Done
[+] Round: Done
[+] HTB{b1n4ry_s34rch_15_und3rr4t3d}
[*] Closed connection to 165.227.224.40 port 31139
The full script can be found in here: solve.py
.