ProxyAsService

Autore: Lord Shiva Aggiornamento: 08.12.2023 Tempo di lettura: 05 min.
 

Prima di tutto, scarico i file zip per analizzare il codice.
Come possiamo vedere, la flag viene inserita in una variabile di ambiente.

# Place flag in environ
ENV FLAG=HTB{f4k3_fl4g_f0r_t3st1ng}

Nel file routes.py il parametro URL viene convalidato se non è vuoto e lo statico SITE_NAME viene aggiunto prima di eseguire una richiesta a questo URL. L'applicazione si aspetta che un utente inserisca un subreddit come "/r/cybersecurity".

from flask import Blueprint, request, Response, jsonify, redirect, url_for
from application.util import is_from_localhost, proxy_req
import random, os

SITE_NAME = 'reddit.com'

proxy_api = Blueprint('proxy_api', __name__)
debug     = Blueprint('debug', __name__)


@proxy_api.route('/', methods=['GET', 'POST'])
def proxy():
    url = request.args.get('url')

    if not url:
        cat_meme_subreddits = [
            '/r/cats/',
            '/r/catpictures',
            '/r/catvideos/'
        ]

        random_subreddit = random.choice(cat_meme_subreddits)

        return redirect(url_for('.proxy', url=random_subreddit))
    
    target_url = f'http://{SITE_NAME}{url}'
    response, headers = proxy_req(target_url)

    return Response(response.content, response.status_code, headers.items())

@debug.route('/environment', methods=['GET'])
@is_from_localhost
def debug_environment():
    environment_info = {
        'Environment variables': dict(os.environ),
        'Request headers': dict(request.headers)
    }

    return jsonify(environment_info)

Esiste una directory /debug/environmentroute che restituisce le variabili di ambiente, solo quando il parametro URL è un URL locale.

@debug.route('/environment', methods=['GET'])
@is_from_localhost
def debug_environment():
    environment_info = {
        'Environment variables': dict(os.environ),
        'Request headers': dict(request.headers)
    }

    return jsonify(environment_info)

Analizzando il file util.py esiste una lista nera che impedisce al proxy di fornire URL locali per prevenire attacchi di falsificazione di richieste lato server. 

RESTRICTED_URLS = ['localhost', '127.', '192.168.', '10.', '172.']

def is_safe_url(url):
    for restricted_url in RESTRICTED_URLS:
        if restricted_url in url:
            return False
    return True

def is_from_localhost(func):
    @functools.wraps(func)
    def check_ip(*args, **kwargs):
        if request.remote_addr != '127.0.0.1':
            return abort(403)
        return func(*args, **kwargs)
    return check_ip

Per acquisire la flag, dobbiamo superare due sfide:
La prima sfida è il metodo di convalida noto come is_from_localhost. Questo controllo è progettato per verificare se l'URL fornito è locale. Aggirarlo non è eccessivamente complesso poiché le liste negate sono spesso incomplete. Esistono numerose tecniche di bypass ed è possibile trovare un elenco completo degli hacktrick. Ad esempio, un esempio potrebbe essere http://0.0.0.0/.

La seconda sfida richiede un po’ più di creatività. Esistono almeno due strategie per affrontarlo. Noi utilizziamo la prima soluzione, lasciando quella un pò più complessa per un'altra volta:
Soluzione 1: utilizzare il simbolo “@”.
Un approccio consiste nello sfruttare la presenza del simbolo “@” all'interno di un URL per l'autenticazione. Inserendo il simbolo “@” nell'URL, il server può interpretare tutto ciò che precede il simbolo “@” come credenziali di autenticazione e ignorarlo, ottenendo così il controllo sull'intero URL.
URL di attacco: http://<ip_target>/?url=@0.0.0.0:1337/debug/environment
In caso di sfruttamento riuscito, avrai accesso alle variabili di ambiente, che potrebbero includere l'ambita bandiera: HTB{fl4g...........1c3}