FlawOpen

flawopen.com/Incidents/equifax-struts-ognl-rce-july-2017

● Equifax · CVE-2017-5638 · July 2017 · CVSS 9.8 · Kritis
Riset Keamanan · FlawOpen

Equifax: data hampir 150 juta orang terdampak

CVE-2017-5638 memungkinkan injeksi OGNL melalui header Content-Type pada portal Struts Equifax yang belum diperbaiki. Serangan berlangsung sekitar 76 hari dan memengaruhi data hampir 150 juta orang.[1], [4]

💡 Penjelasan Sederhana (ELI5)

Bayangkan perangkat lunak pengiriman memasukkan label paket yang ditolak ke dalam pesan kesalahan. Jika pesan itu memperlakukan label sebagai templat instruksi yang dapat dijalankan, pengirim dapat membuat perangkat lunak mengikuti instruksi, bukan sekadar melaporkan alamat yang salah. Patch Struts sudah tersedia, tetapi pemberitahuan Equifax dikirim ke daftar administrator yang usang dan pemindaian jaringan tidak mendeteksi kerentanan pada portal tersebut.[1], [4]

Konsep Kunci & Istilah

OGNL
Object-Graph Navigation Language, bahasa ekspresi yang dipakai Struts untuk membaca dan mengatur properti objek Java. Mengevaluasi teks penyerang sebagai OGNL berarti menjalankan kode penyerang.[1], [6]
Parser multipart Jakarta
Komponen Struts yang menangani unggahan berkas. Saat menolak Content-Type yang salah bentuk, komponen ini menyusun pesan galat yang kemudian dievaluasi.[1], [3], [2]
Injeksi ekspresi (CWE-917)
Masukan tak tepercaya masuk ke dalam ekspresi yang dievaluasi interpreter, sehingga data berubah menjadi instruksi.[6]
Inventaris aset
Catatan perangkat lunak dan versi apa yang berjalan di sistem mana. Tanpanya, pemberitahuan patch tidak bisa dicocokkan dengan mesin yang membutuhkannya.[4]

Pemetaan kerangka kerja

Kronologi Insiden

2017-03-07

Apache menerbitkan buletin keamanan S2-045 dan versi perbaikan Struts 2.3.32 serta 2.5.10.1.[1]

2017-05-13

Equifax kemudian memberi tahu GAO bahwa intrusi terpisah dimulai pada 13 Mei dan penyerang mulai mengambil data pribadi; akses tanpa izin yang lebih awal sudah terjadi, tetapi belum ada data yang diambil.[4]

2017-07-29

Equifax mendeteksi lalu lintas mencurigakan setelah memperbarui sertifikat kedaluwarsa pada perangkat inspeksi lalu lintas.[4]

2017-09-07

Equifax mengumumkan pembobolan secara publik. GAO kemudian melaporkan bahwa data setidaknya 145,5 juta orang di AS dan hampir 1 juta orang di luar AS terdampak.[4]

Alur Serangan Langkah demi Langkah

Langkah 1

Header Content-Type rekaan

Penyerang mengirim permintaan unggahan yang header Content-Type-nya berisi ekspresi OGNL, bukan tipe media.[1]

Langkah 2

Pesan galat membawa header

Parser Jakarta menolak header itu dan menyusun pesan galat terlokalisasi yang memuat teks mentah header tersebut.[1], [3], [2]

Langkah 3

Pesan dievaluasi

Struts mengevaluasi ekspresi di dalam pesan itu, sehingga OGNL penyerang berjalan sebagai kode Java dengan hak istimewa aplikasi web.[1], [3], [2], [6]

Langkah 4

Pencurian data yang pelan dan senyap

Penyerang menggunakan kredensial yang ditemukan di repositori data untuk memperluas akses dari tiga basis data portal ke 48 basis data lain yang tidak terkait dengan portal. GAO melaporkan sekitar 9.000 kueri dan serangan selama kira-kira 76 hari; sertifikat kedaluwarsa membuat lalu lintas terenkripsi tidak dapat diperiksa.[4]

Kode Sumber: Rentan vs Aman

Java ilustratif, bukan kode sumber Struts; perbaikan sebenarnya di 2.3.32 dan 2.5.10.1 mengubah cara galat multipart disusun. Versi yang diperkuat memakai kunci pesan tetap dan meneruskan header hanya sebagai argumen posisi, sehingga ia diformat sebagai teks dan tidak pernah menjadi bagian dari ekspresi. Versi ini juga mengganti nilai apa pun yang tidak tampak seperti tipe media dengan penanda, sehingga perubahan pesan di masa depan pun tidak dapat mengembalikan header ke dalam string yang dievaluasi.[3], [2], [6]

IMPLEMENTASI RENTAN
import com.opensymphony.xwork2.TextProvider;
import javax.servlet.http.HttpServletRequest;

public final class UploadErrors {
    private final TextProvider text;   // resolves messages and evaluates expressions in them

    public UploadErrors(TextProvider text) {
        this.text = text;
    }

    public String describe(HttpServletRequest request) {
        String contentType = request.getHeader("Content-Type");
        // Header mentah menjadi bagian dari pesan yang dievaluasi.
        return text.getText("Invalid content type: " + contentType);
    }
}
PERBAIKAN AMAN & KUAT
import com.opensymphony.xwork2.TextProvider;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;

public final class UploadErrors {
    private static final Pattern MEDIA_TYPE = Pattern.compile(
        "^[a-z]+/[a-z0-9.+-]+(;\\s*[a-z0-9-]+=[a-z0-9._-]+)*$");
    private final TextProvider text;

    public UploadErrors(TextProvider text) {
        this.text = text;
    }

    public String describe(HttpServletRequest request) {
        String contentType = request.getHeader("Content-Type");
        boolean wellFormed = contentType != null && contentType.length() <= 256
            && MEDIA_TYPE.matcher(contentType.toLowerCase(Locale.ROOT)).matches();
        // Gunakan kunci pesan tetap; header hanya boleh menjadi argumen posisi,
        // dan itu pun hanya jika bentuknya menyerupai tipe media.
        return text.getText("upload.invalid.content.type",
            new String[] { wellFormed ? contentType : "(malformed)" });
    }
}

Daftar Periksa Penguatan Sistem Rekayasa

Sumber