Käyttöjärjestelmäkomennot
Käytännössä kaikissa käyttöjärjestelmissä on jonkunlainen komentotulkki, tekstipohjainen käyttöliittymä. Windowsissa se on komentokehoite (cmd.exe) tai powershell, Linuxissa ja Macissa se on bash, sh, zsh, jne. Ja näistä komentotulkeista voi käynnistää tekstipohjaisia ohjelmia ja komentoja. Esimerkiksi, voimme pingata Googlea:
$ ping google.com
PING google.com (209.85.233.101) 56(84) bytes of data.
64 bytes from lr-in-f101.1e100.net (209.85.233.101): icmp_seq=1 ttl=114 time=82.4 ms
Käyttöjärjestelmäkomennot ohjelmoinnissa
Joskus sovellukset hyödyntävät taustalla konsolityökaluja. Meillä voisi olla vaikka Pythonilla tehty web-sovellus, jonka tarkoitus on pingata tiettyä IP-osoitetta ja näyttää sivustolla pingauksen tulos. Koodi voisi näyttää tältä:
def do_GET(request):
ip = request.get_parameter("ip")
result = os.popen(f"ping -c1 {ip}").read()
return {"result": result}
Injektio
Ongelma tässä on, että käyttäjältä tulevan syötteen lisääminen käyttöjärjestelmäkomentoon on hirvittävän vaarallista. Lisäämällä tiettyjä erikoismerkkejä "IP-osoitteeseen", hyökkääjän on mahdollista huijata sovellus ajamaan ihan eri käyttöjärjestelmäkomentoja kuin ping.
Komentosubstituutio
Otetaan vaikka komentosubstituutio (command substitution), jolla voidaan ottaa toisen komennon tuloste osaksi komentoa. Substituutio voidaan tehdä syntaksilla $(komento) tai `komento`.
$ pwd
/tmp
$ echo "pwd on: $(pwd)"
pwd on: /tmp
$ echo "pwd on: `pwd`"
pwd on: /tmp
Eli syöttämällä IP-osoite kuten 1.2.3.4$(id) voisimme saada vastauksen "Ping 1.2.3.4$(id) tulos: ping: uid=0(root) gid=0(root) groups=0(root): Name or service not known." Kyseessä on siis id-komennon tuloste (tässä tapauksessa käyttäjä oli root).
Mielikuvitus on rajana
On lukemattomia tapoja saada omia komentoja ujutettua väliin tai perään jos pääsee kirjoittamaan käyttöjärjestelmäkomennon sisälle. Joitakin esimerkkejä:
# Puolipiste, aloittaa uuden komennon.
echo abc; id
# Yksi putki, ottaa edellisen komennon tulosteen ja ohjaa sen uuteen komentoon
echo abc | id;
# Kaksi et-merkkiä, aloittaa uuden komennon jos edellinen komento onnistuu.
echo abc && id;
Harjoitus
Labrassa on verkkodiagnostiikkatyökalu, joka tarkistaa onko nettisivu ylhäällä. Taustalla työkalu käyttää curl-komentoa. Näet sivun ylälaidasta mitä komentoja palvelimella ajetaan.
Yritä saada luettua tiedosto /flag.txt.
Käyttöjärjestelmäkomentojen ajamista kannattaa lähtökohtaisesti välttää, ja pyrkiä saavuttamaan sama asia koodilla. Jos tämä ei ole mieluinen vaihtoehto, niin on tärkeää että:
1. Käyttöjärjestelmäkomentoja ei koskaan rakenneta tekstiä yhdistelemällä, kuten:
os.system("ping " + ip)
Komennot tulee rakentaa sellaisella ohjelmistokirjastolla, joka osaa turvallisesti huolehtia, ettei syöte leviä kyseisen parametrin ulkopuolelle:
subprocess.Popen(['ping', ip])
2. Parametri, joka tulee mistään ohjelman ulkopuolelta (kuten käyttäjän syötteestä, tietokannasta, rajapinnasta, jne), tulee validoida mahdollisimman tarkasti.
3. Parametri, johon esimerkiksi sovelluksen käyttäjän annetaan vaikuttaa, tulee ymmärtää kunnolla. Otetaan esimerkiksi curl. Riittääkö, että käytetään turvallista kirjastoa muodostamaan curl-kutsu, ja annetaan käyttäjän vain päättää URL-osoite? Jos vielä validoidaan että URL on oikeanmuotoinen?
Ei riitä! Curlilla voi lukea tiedostoja palvelimen levyltä.
$ curl file:///etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
Entäs jos varmistaa että URL-osoitteet alkavat http:// tai https://? Ei riitä! Curlilla voisi kutsua palvelimella olevia paikallisia tai sisäverkon HTTP-rajapintoja, joiden ei ollut tarkoitus olla auki Internetiin.
Pointti on, että käyttöjärjestelmäkomennoilla on usein sellaisia kyvykkyyksiä joita ei olisi tullut ajatelleeksi. Jos niitä on pakko käyttää, on syytä noudattaa varovaisuutta.
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ä.