Eksekusi Kode Jarak Jauh dengan XStream

1. Ikhtisar

Dalam tutorial ini, kita akan membedah serangan Eksekusi Kode Jarak Jauh terhadap perpustakaan serialisasi XML XStream. Eksploitasi ini termasuk dalam kategori serangan deserialization tidak tepercaya .

Kita akan belajar kapan XStream rentan terhadap serangan ini, bagaimana serangan itu bekerja, dan bagaimana mencegah serangan tersebut.

2. Dasar-dasar XStream

Sebelum menjelaskan serangannya, mari kita tinjau beberapa dasar XStream. XStream adalah pustaka serialisasi XML yang menerjemahkan antara tipe Java dan XML. Pertimbangkan kelas Person sederhana :

public class Person { private String first; private String last; // standard getters and setters }

Mari kita lihat bagaimana XStream dapat menulis beberapa instance Person ke XML:

XStream xstream = new XStream(); String xml = xstream.toXML(person);

Demikian pula, XStream dapat membaca XML menjadi instance Person :

XStream xstream = new XStream(); xstream.alias("person", Person.class); String xml = "JohnSmith"; Person person = (Person) xstream.fromXML(xml);

Dalam kedua kasus tersebut, XStream menggunakan refleksi Java untuk menerjemahkan tipe Person ke dan dari XML. Serangan itu terjadi saat membaca XML. Saat membaca XML, XStream membuat instance kelas Java menggunakan refleksi.

Instansiasi kelas XStream ditentukan oleh nama elemen XML yang diurai.

Karena kita mengonfigurasi XStream untuk mengetahui tipe Person , XStream membuat instance Person baru saat mengurai elemen XML bernama "person".

Selain tipe yang ditentukan pengguna seperti Person , XStream mengenali tipe inti Java di luar kotak. Misalnya, XStream dapat membaca Peta dari XML:

String xml = "" + "" + " " + " foo" + " 10" + " " + ""; XStream xStream = new XStream(); Map map = (Map) xStream.fromXML(xml); 

Kita akan melihat bagaimana kemampuan XStream untuk membaca XML yang mewakili tipe inti Java akan membantu dalam eksploitasi eksekusi kode jarak jauh.

3. Bagaimana Serangan Bekerja

Serangan eksekusi kode jarak jauh terjadi ketika penyerang memberikan masukan yang pada akhirnya ditafsirkan sebagai kode. Dalam kasus ini, penyerang mengeksploitasi strategi deserialisasi XStream dengan memberikan kode serangan sebagai XML.

Dengan komposisi kelas yang tepat, XStream akhirnya menjalankan kode serangan melalui refleksi Java.

Mari buat serangan contoh.

3.1. Sertakan Kode Serangan di ProcessBuilder

Serangan kami bertujuan untuk memulai proses kalkulator desktop baru. Di macOS, ini adalah "/Applications/Calculator.app". Di Windows, ini adalah "calc.exe". Untuk melakukannya, kami akan mengelabui XStream agar menjalankan proses baru menggunakan ProcessBuilder. Ingat kembali kode Java untuk memulai proses baru:

new ProcessBuilder().command("executable-name-here").start();

Saat membaca XML, XStream hanya memanggil konstruktor dan bidang set. Oleh karena itu, penyerang tidak memiliki cara langsung untuk menjalankan metode ProcessBuilder.start () .

Namun, penyerang pintar dapat menggunakan komposisi yang tepat dari kelas untuk akhirnya mengeksekusi ProcessBuilder 's start () method.

Peneliti keamanan Dinis Cruz menunjukkan kepada kita dalam posting blog mereka bagaimana mereka menggunakan antarmuka Comparable untuk menjalankan kode serangan di copy constructor dari koleksi yang diurutkan TreeSet. Kami akan meringkas pendekatannya di sini.

3.2. Buat Proxy Dinamis yang Dapat Dibandingkan

Ingatlah bahwa penyerang perlu membuat ProcessBuilder dan memanggil metode start () . Untuk melakukannya, kita akan membuat sebuah instance dari Sebanding yang membandingkan metode memanggil ProcessBuilder 's start () method.

Untungnya, Java Dynamic Proxies memungkinkan kita membuat instance Comparable secara dinamis .

Selain itu, kelas EventHandler Java menyediakan penyerang dengan implementasi InvocationHandler yang dapat dikonfigurasi . Penyerang mengkonfigurasi EventHandler untuk memohon ProcessBuilder 's start () method.

Dengan menggabungkan komponen-komponen ini, kami memiliki representasi XML XStream untuk proxy yang sebanding :

 java.lang.Comparable    open /Applications/Calculator.app   start  

3.3. Paksa Perbandingan Menggunakan Proxy Dinamis yang Dapat Dibandingkan

Untuk memaksa perbandingan dengan proxy Comparable kami , kami akan membuat koleksi yang diurutkan. Mari buat koleksi TreeSet yang membandingkan dua instance Comparable : String dan proxy kita.

Kami akan menggunakan konstruktor salinan TreeSet untuk membangun koleksi ini. Akhirnya, kami memiliki representasi XML XStream untuk TreeSet baru yang berisi proxy kami dan String :

 foo  java.lang.Comparable    open /Applications/Calculator.app   start    

Akhirnya, serangan itu terjadi ketika XStream membaca XML ini. Sementara pengembang mengharapkan XStream untuk membaca Orang , itu malah mengeksekusi serangan:

String sortedSortAttack = // XML from above XStream xstream = new XStream(); Person person = (Person) xstream.fromXML(sortedSortAttack);

3.4. Ringkasan Serangan

Mari kita rangkum panggilan reflektif yang dilakukan XStream saat mendeserialisasi XML ini

  1. XStream memanggil konstruktor salinan TreeSet dengan Koleksi yang berisi String "foo" dan proxy Sebanding kami .
  2. The TreeSet konstruktor panggilan kami Sebanding proxy compareTo metode untuk menentukan urutan item dalam set yang terurut.
  3. Proksi dinamis sebanding kami mendelegasikan semua panggilan metode ke EventHandler .
  4. The EventHandler is configured to invoke the start() method of the ProcessBuilder it composes.
  5. The ProcessBuilder forks a new process running the command the attacker wishes to execute.

4. When Is XStream Vulnerable?

XStream can be vulnerable to this remote code execution attack when the attacker controls the XML it reads.

For instance, consider a REST API that accepts XML input. If this REST API uses XStream to read XML request bodies, then it may be vulnerable to a remote code execution attack because attackers control the content of the XML sent to the API.

On the other hand, an application that only uses XStream to read trusted XML has a much smaller attack surface.

For example, consider an application that only uses XStream to read XML configuration files set by an application administrator. This application is not exposed to XStream remote code execution because attackers are not in control of the XML the application reads (the admin is).

5. Hardening XStream Against Remote Code Execution Attacks

Fortunately, XStream introduced a security framework in version 1.4.7. We can use the security framework to harden our example against remote code execution attacks. The security framework allows us to configure XStream with a whitelist of types it is allowed to instantiate.

This list will only include basic types and our Person class:

XStream xstream = new XStream(); xstream.addPermission(NoTypePermission.NONE); xstream.addPermission(NullPermission.NULL); xstream.addPermission(PrimitiveTypePermission.PRIMITIVES); xstream.allowTypes(new Class[] { Person.class });

Selain itu, pengguna XStream dapat mempertimbangkan untuk memperkuat sistem mereka menggunakan agen Runtime Application Self-Protection (RASP). Agen RASP menggunakan instrumentasi bytecode pada waktu proses untuk mendeteksi dan memblokir serangan secara otomatis. Teknik ini tidak terlalu rentan terhadap kesalahan daripada membuat daftar jenis yang diizinkan secara manual.

6. Kesimpulan

Dalam artikel ini, kita belajar bagaimana melakukan serangan eksekusi kode jarak jauh pada aplikasi yang menggunakan XStream untuk membaca XML. Karena serangan seperti ini ada, XStream harus diperkuat saat digunakan untuk membaca XML dari sumber yang tidak tepercaya.

Eksploitasi ada karena XStream menggunakan refleksi untuk membuat instance kelas Java yang diidentifikasi oleh XML penyerang.

Seperti biasa, kode untuk contoh dapat ditemukan di GitHub.