6 minutes
QuickR
Note to fellow-HTBers: Only write-ups of retired HTB machines or challenges are allowed.
Machine info ¶
QuickR [by hfz]
Let’s see if you’re a QuickR soldier as you pretend to be
Difficulty: Medium (40 points)
Release: 16 April 2020
Recon ¶
Browsing or cURL’ing the endpoint, we get a QR code in our terminal.
$ curl docker.hackthebox.eu:32169
___ _ __ _______
.' `. (_) [ | _ |_ __ \
/ .-. \ __ _ __ .---. | | / ] | |__) |
| | | | [ | | | [ | / /'`\] | '' < | __ /
\ `-' \_ | \_/ |, | | | \__. | |`\ \ _| | \ \_
`.___.\__|'.__.'_/[___]'.___.'[__| \_]|____| |___|
[*] Hello there! Let's see if you are an QuickR soldier, you got only 3 seconds!
<Prints QR code>
[+] It's important to realise that this is, in a real sense, an illusion: you simply need the true machine value.
[!] Decoded string: [-] Wrong!

There’s also the notion of something being Wrong and having a 3 second limit.
Scanning this QR code with a smartphone, we get an equation:48.530023014695644 - 58.8774162315505 / 214.69427403011673 =
So we can assume the challenge requires us to perform the following steps:
- open connection, read output
- extract QR code
- decode QR code
- calculate equation
- return answer to endpoint
- retrieve flag
Tackling the challenge ¶
We definitely need to automate this whole challenge, since we can’t manually retrieve the QR code, scan it with a smartphone, solve the equation and return the solution.
Capture the QR code ¶
The hardest part of this challenge was finding a way to convert the QR code in the terminal to one that we can scan via a script.
I first looked into a solution to screengrab the terminal, but didn’t immediately find a solution that worked for me.
Next tactic was to read the output and build an image in a kind of search and replace way.
Note that ^[
is actually a control character Ctrl
+ Esc
followed by a square bracket.
Typing this in a console might not work because the terminal captures the control command.
I guess it might also work using \e
or \033
instead, but I haven’t tried.
def qrimg(lines ,filename):
font = ImageFont.truetype('DejaVuSansMono.ttf')
img=Image.new("1", (102,102), 1)
draw = ImageDraw.Draw(img)
y = 0
for line in lines:
line = line.decode("utf-8", "backslashreplace")
line = re.sub(r"^[ \t]*","", line)
line = re.sub(r"\[41m \[0m","*", line)
line = re.sub(r"\[7m \[0m","-", line)
x = 0
for c in line:
if c == "*":
draw.point((x, y), 0)
#draw.point((x, y+1), 0) # drawing 2x2 pixels
#draw.point((x+1, y), 0) # drawing 2x2 pixels
#draw.point((x+1, y+1), 0) # drawing 2x2 pixels
#elif c == "-":
# draw.point((x, y), 1) # Adding white pixel is not needed
x+=1
#x+=2 # drawing 2x2 pixels
y += 1
#y += 2 # drawing 2x2 pixels
draw = ImageDraw.Draw(img)
img.save(filename)
#print("x: {} y: {}".format(x, y))
print("[+] Wrote QR code image to {}".format(filename))
Scan the QR code ¶
Scanning the QR code from an image is easier, since there are Python modules readily available for this.
def qrdecode(filename):
result = ""
qr = qrtools.QR()
if qr.decode(filename):
#conn.send(qr.data)
result = qr.data
print("[+] QR Code decodes to: '{}'".format(result))
return result
Solving the equation ¶
Easy peasy
def solve(equation):
equation = re.sub("x", "*", equation)
equation = re.sub("=", "", equation)
equation = re.sub(r"^[ \t]*", "", equation) # trim whitespace, might not be necessary
equation = re.sub(r"[ \t]*$", "", equation)) # trim whitespace, might not be necessary
print("[+] Cleaned equation: '{}'".format(equation))
result = eval(equation)
print("[+] Equation results to: '{}'".format(result))
return result
Send answer ¶
Finally we send our answer back using the same socket and retrieve the flag.
Make sure you end your output with a newline.
This almost got me pulling my hair out.
conn.sendline("{}".format(result))
print("[+] Send '{}' to {}:{}".format(result, hostname, port))
answer = conn.recvall().decode()
print(answer)
Solution ¶
Combining all of this together and running our final script, we get:
$ ./QuickR.py
[+] Opening connection to docker.hackthebox.eu on port 31607: Done
[+] Wrote QR code text to quickr.txt
[+] Wrote QR code image to quickr.png
[+] QR Code decodes to: '74.22252072924252 - 5.833454784680273 / 379.68114191784696 = '
[+] Cleaned equation: '74.22252072924252 - 5.833454784680273 / 379.68114191784696'
[+] Equation results to: '74.20715664043048'
[+] Send '74.20715664043048' to docker.hackthebox.eu:31607
[+] Receiving all data: Done (263B)
[*] Closed connection to docker.hackthebox.eu port 31607
[+] It's important to realise that this is, in a real sense, an illusion: you simply need the true machine value.
[!] Decoded string: [+] Congratulations! Here is your flag: HTB{@lriGh7_1_tH1nK_y0u`r3_QuickR_s0ldi3r}
And this is the full script:
#!/usr/bin/env python3
### Solution for QuickR by hfz on HackTheBox.eu
### By FlatMarsSociet
### See https://sequr.be/blog/ for more info
import re
import PIL
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
from pwn import *
import qrtools
def qrtxt(lines, filename):
with open(filename,"w") as f:
for line in input:
line = line.decode("utf-8", "backslashreplace")
f.write("{}\n".format(line))
print("[+] Wrote QR code text to {}".format(filename))
def qrimg(lines ,filename):
font = ImageFont.truetype('DejaVuSansMono.ttf')
img=Image.new("1", (102,102), 1)
draw = ImageDraw.Draw(img)
y = 0
for line in lines:
line = line.decode("utf-8", "backslashreplace")
line = re.sub(r"^[ \t]*","", line)
line = re.sub(r"\\[41m \\[0m","*", line)
line = re.sub(r"\\[7m \\[0m","-", line)
x = 0
for c in line:
if c == "*":
draw.point((x, y), 0)
#draw.point((x, y+1), 0) # drawing 2*2 pixels
#draw.point((x+1, y), 0) # drawing 2*2 pixels
#draw.point((x+1, y+1), 0) # drawing 2*2 pixels
#elif c == "-":
# draw.point((x, y), 1) # Adding white pixel is not needed
x+=1
#x+=2 # drawing 2*2 pixels
y += 1
#y += 2 # drawing 2*2 pixels
draw = ImageDraw.Draw(img)
img.save(filename)
#print("x: {} y: {}".format(x, y))
print("[+] Wrote QR code image to {}".format(filename))
def qrdecode(filename):
result = ""
qr = qrtools.QR()
if qr.decode(filename):
#conn.send(qr.data)
result = qr.data
print("[+] QR Code decodes to: '{}'".format(result))
return result
def solve(equation):
equation = re.sub("x", "*", equation)
equation = re.sub("=", "", equation)
equation = re.sub(r"^[ \t]*", "", equation)
equation = re.sub(r"[ \t]*$", "", equation)
print("[+] Cleaned equation: '{}'".format(equation))
result = eval(equation)
print("[+] Equation results to: '{}'".format(result))
return result
def output(lines):
for line in lines:
# line = line.decode("utf-8", "backslashreplace")
print("{}".format(chr(line)), end = '')
hostname = 'docker.hackthebox.eu'
port = 31607
conn = remote(hostname, port)
conn.recvuntil("you got only 3 seconds!")
conn.recvlines(3)
input = conn.recvlines(51)
qrimage = "quickr.png"
qrtext = "quickr.txt"
qrtxt(input, qrtext)
qrimg(input, qrimage)
equation = qrdecode(qrimage)
result = solve(equation)
conn.sendline("{}".format(result))
print("[+] Send '{}' to {}:{}".format(result, hostname, port))
answer = conn.recvall().decode()
print(answer)
conn.close()