URL-injektio

Epäturvallisesti rakennetut URL-osoitteet tai URL-injektio

Helppo
45 min

Injektiot yleisesti

Injektio on haavoittuvuus, jossa yritetään parameterisoida jotain protokollaa, mutta parametriksi tarkoitettu data pääseekin murtautumaan ulos parametrin paikastaan ja muodostuu osaksi protokollan rakennetta.

URL-injektio

URL-injektio on tästä yksinkertainen esimerkki. Kun sovellus rakentaa URL-osoitteita, esimerkiksi tehdäkseen taustajärjestelmässä kutsun toiseen (sisäiseen) taustajärjestelmään, ja lisää rakennettuun URL-osoitteeseen jonkun polun tai parametrin esimerkiksi käyttäjän syötteestä, on riski URL-injektioon jos syötettä ei ole enkoodattu oikein.

Esimerkki

Seuraava kuva kuvastaa tilannetta, jossa URL-injektioita voi sijaita.

Yllä olevassa kuvassa verkkosivu suorittaa taustapalveluun HTTP-pyynnön käyttämällä käyttäjän antamaa tunnusta osana URL-osoitteen polkua.

url = "/varattu/" + kayttajan_antama_syote

Tämä vaikuttaa täysin turvalliselta sekä viattomalta, mutta mitä tapahtuu, jos käyttäjä antaakin tunnuksen sijaan arvon, joka uudelleenmäärittää verkkosivun tekemän kyselyn lopullisen sijainnin. Jos verkkosivu ei rakenna URL-osoitteita turvallisesti, niin tämä voi mahdollistaa esimerkiksi ../ merkkijonon käyttämistä syötteessä, jolla voidaan joskus matkustaa URL-polussa taaksepäin.

Yllä olevassa kyselyssä, käyttäjä antoi syötteenä ../kayttajat, joka muokkasi verkkosivun tekemän kyselyn lopullista sijaintia, jolloin sovellus päätyikin tekemään sisäisen HTTP-pyynnön eri polkuun kuin kehittäjä oli tarkoittanut.

Harjoitus

Käynnistä nyt alla oleva harjoitus ja lue eteenpäin.

URL-injektio harjoitus

Etsi ja tunnista URL-injektio haavoittuvuus ja hyväksikäytä tätä.

Tehtävässä käytetyn sovelluksen lähdekoodit:

import os
import flask
import requests

from urllib.parse import urlparse
from modules.database import Database
from flask import Flask, render_template, request, flash, abort, jsonify

database = Database()

app = Flask(__name__)

### RAJAPINNAT VAIN SISÄISILLE KYSELYILLE ###
# Rajapinta kehityksen auttamiseksi
@app.route("/api/users", methods=['GET'])
def users_api():
    ip_address = flask.request.remote_addr
    if ip_address not in ('127.0.0.1', '::1', 'localhost'):
      abort(404)
    
    users = database.get_users()
    return jsonify({ 'Users': users })

# Tarkistetaan onko sposti jo kannassa
@app.route("/api/user/<user_mail>")
def user_api(user_mail):
    ip_address = flask.request.remote_addr
    if ip_address not in ('127.0.0.1', '::1', 'localhost'):
      abort(404)
    user_mail = request.view_args["user_mail"]
    ret_user = database.get_user(user_mail)
    return jsonify({ 'User': ret_user })

# ULKOISET RAJAPINNAT
@app.route("/varmista", methods=['POST'])
def varmista():
  sposti = request.form.get("sposti")    
  responssi = requests.get("http://127.0.0.1:5000/api/user/"+sposti)
  return jsonify(responssi.json())

@app.route("/", methods=["GET"])
def index():
  return render_template("index.html")


if __name__ == '__main__':
  app.run(debug=False, host='0.0.0.0')

Tehtävät

Flag

Löydä lippu (flag) labraympäristöstä ja syötä se alle.

Aloitamme suorittamalla sovelluksessa olevan rekisteröitymisen. Tarkistamme BurpSuite-ohjelmasta, tästä aiheutuneen HTTP-pyynnön.

Huomaamme, että rekisteröityminen aiheuttaa HTTP-pyynnön rajapintaan /varmista. Varmistetaan rajapinnan toiminta lähdekoodista.

@app.route("/varmista", methods=['POST'])
def varmista():
  sposti = request.form.get("sposti")    
  responssi = requests.get("http://127.0.0.1:5000/api/user/"+sposti)
  return jsonify(responssi.json())

Rajapinnan käyttämästä koodista näkee, että sovellus ottaa vastaan annetun sähköpostiosoitteen ja suorittaa uuden kyselyn rajapintaan /api/user/ + sposti. Lähdekoodista näemme myös, että sovellus ei suorita minkäänlaista formatointia annetulle sähköpostiosoitteelle, mikä tarkoittaa, että sovellus on haavoittuvainen URL-injektio hyökkäyksille.

Jos tutkimme kyseisen rajapinnan koodia, huomaamme, ettei rajapintaan voi suorittaa itse kyselyitä, sillä rajapinta tarkistaa, että kyselyt saapuvat osoitteesta 127.0.0.1.

@app.route("/api/user/<user_mail>")
def user_api(user_mail):
    ip_address = flask.request.remote_addr
    if ip_address not in ('127.0.0.1', '::1', 'localhost'):
    ...

Tutkimalla lähdekoodia lisää, voimme todeta, että sovelluksessa on myös toinen sisäinen rajapinta, jossa tehdään IP-osoitteen tarkistus.

@app.route("/api/users", methods=['GET'])
def users_api():
    ip_address = flask.request.remote_addr
    if ip_address not in ('127.0.0.1', '::1', 'localhost'):
      abort(404)
    
    users = database.get_users()
    return jsonify({ 'Users': users })

Kyseinen rajapinta näyttää yksinkertaisesti palauttavan kaikki sovelluksen käyttäjät tietokannasta. Käytetään löydettyä URL-injektio haavoittuvuutta ja uudelleenohjataan /varmista rajapinnan tekemä kysely, yllä olevaan /api/users rajapintaan.

Voila! - Onnistuimme tekemään HTTP-pyynnön, jossa uudelleenohjasimme palvelimen suorittaman HTTP-pyynnön toiseen rajapintaan ja tämä palautti meille Flag:in.

Kuinka rakennan URL-osoitteet turvallisesti?

Tärkeintä on muistaa, että emme halua käyttäjän kykenevän uudelleenmäärittämään sovelluksessa tehtävän HTTP-pyynnön rakennetta. Tähän tarkoitukseen voidaan käyttää URL-enkoodausta, johon löytyy erilaisia kirjastoja, riippuen hieman, mitä ratkaisuja käytät. Esimerkiksi, Node.js sovelluksissa voit käyttää encodeURIComponent funktiota, johon syötät käyttäjältä saadun tiedon, ennen kuin asetat tämän lopulliseen URL-osoitteeseen.

//Haavoittuv:
var url = "http://taustapalvelu.com/api/esim/" + tunnus; 

//Ei haavoittuva
var url = "http://taustapalvelu.com/api/esim/" + encodeURIComponent(tunnus);

Tämä täytyy myös muistaa, kun käytät käyttäjältä saatua tietoa parametreissä, ettei hyökkääjä kykene manipuloimaan kyselyissä käytettyjä parametrejä.

//Haavoittuva
var url = "http://taustapalvelu.com/api/tunnus?nimi=" + nimi;
// nimi parametri sisältää "bob&poista=true"

//Ei haavoittuva
var url = "http://taustapalvelu.com/api/tunnus?nimi="+ encodeURIComponent(nimi);
hakatemia pro

Valmis ryhtymään eettiseksi hakkeriksi?
Aloita jo tänään.

Hakatemian jäsenenä saat rajoittamattoman pääsyn Hakatemian moduuleihin, harjoituksiin ja työkaluihin, sekä pääset discord-kanavalle jossa voit pyytää apua sekä ohjaajilta että muilta Hakatemian jäseniltä.