"Halo, Checkmarx!" Cara menulis permintaan untuk Checkmarx SAST dan menemukan kerentanan keren



Halo, Habr!

Dalam artikel ini saya ingin berbicara tentang pengalaman kami dalam membuat pertanyaan saya di Checkmarx SAST.

Ketika Anda pertama kali berkenalan dengan analisa ini, Anda mungkin mendapatkan kesan bahwa selain mencari enkripsi / hashing algoritma yang lemah dan tumpukan false positive, itu tidak mengembalikan apa pun. Tetapi ketika dikonfigurasi dengan benar, itu adalah alat yang sangat kuat yang dapat mencari bug serius.

Kami akan memahami seluk-beluk bahasa query Checkmarx SAST dan menulis 2 pertanyaan untuk mencari injeksi SQL dan Referensi Objek Langsung Tidak Aman.


Entri


Setelah lama mencari panduan atau artikel tentang Checkmarx, menjadi jelas bagi saya bahwa selain dokumentasi resmi, tidak ada informasi yang cukup berguna. Dan dokumentasi resmi tidak mengatakan bahwa semuanya menjadi sangat jelas dan dapat dimengerti. Misalnya, saya tidak bisa menemukan praktik terbaik, bagaimana mengatur permintaan override dengan benar, bagaimana menulis kueri "untuk boneka," dll. Ya, ada dokumentasi tentang fungsi Bahasa Query CMx, tapi di sini cara menggabungkan fungsi-fungsi ini menjadi satu permintaan, dokumentasi tidak ditulis.

Mungkin kurangnya artikel dan panduan dari komunitas Checkmarx terkait dengan tingginya biaya alat dan, sebagai akibatnya, audiens yang kecil. Atau mungkin hanya beberapa orang yang repot melakukan fine tuning dan menggunakan solusinya, di luar kotak.

Dalam pengalaman saya, saya melihat lebih banyak bahwa SAST lebih banyak digunakan untuk mematuhi formalitas yang terkait dengan berbagai persyaratan di pihak pelanggan daripada untuk mencari bug nyata. Dengan pendekatan ini, sebagai hasilnya, kami memiliki, paling tidak, sejumlah kecil “kerentanan,” yang hampir secara otomatis dikenal sebagai “tidak dapat dieksploitasi” (karena mereka ada dalam 99,9% kasus).

Perlu dicatat bahwa Checkmarx sendiri berusaha memperbarui kueri mereka sehingga mereka memberikan hasil terbaik di luar kotak. Tapi pertanyaan CMx Query Language disesuaikan dengan "kasus umum". Pencarian awal untuk token didasarkan pada nama. Sebagai contoh, CMx SAST mengasumsikan bahwa semua permintaan ke database akan terlihat seperti ini: * createQuery * atau * createSQLQuery *. Tetapi jika pengembangan internal digunakan untuk bekerja dengan database, dan metode untuk query database disebut berbeda, misalnya * driveMyQuery *, maka semua metode SQL akan dilewati. Misalnya, pelanggan kami menggunakan ORM khusus untuk SQL DB. Dalam hal ini, CMx kueri dari kotak dilewati semua injeksi SQL.

Singkatan dan Definisi


CMx - Checkmarx SAST.
CMxQL - Checkmarx SAST bahasa permintaan
Token - string dengan nilai tertentu adalah hasil kerja penganalisa leksikal (yang juga disebut tokenization)

Aplikasi tes


Untuk menulis artikel, saya membuat sketsa beberapa kode Java, sebuah aplikasi tes kecil. Kode ini adalah salinan perkiraan sebagian kecil dari sistem nyata. Meskipun secara umum kode aplikasi pengujian tidak jauh berbeda dari kode backend HTTP lainnya. Bagian kunci dari kode aplikasi pengujian akan terlihat di tangkapan layar.

Aplikasi tes memiliki struktur sebagai berikut


Kelas WebRouter untuk memproses permintaan HTTP yang masuk; 4 metode untuk memproses URL di dalam:
  • / getTransaction - menerima id transaksi di input dan mengembalikan info tentang itu, id mengambilnya sebagai string, dan meneruskannya ke getTransactionInfo (transactionId) => getTransactionInfo (transactoinId) - membuat transactionId digabungkan dengan permintaan SQL (mis., injeksi SQL diperoleh);
  • / getSecureTransaction - menerima id transaksi sebagai input dan mengembalikan info tentang itu, id mengambilnya sebagai string dan meneruskannya getTransactionInfoSecured () => getTransactionInfoSecured (transactoinId) - pertama-tama melemparkan string transactionId untuk mengetik Long, dan kemudian menggabungkannya dengan query SQL (dalam hal ini kasus injeksi tidak dieksploitasi);
  • / getSettings - menerima userId dan mailboxId sebagai input - dan mengeluarkan pengaturan kotak surat. Tidak memverifikasi bahwa kotak pesan milik pengguna;
  • / getSecureSettings - juga menerima userId dan mailboxId ke input dan menampilkan pengaturan kotak pesan. TETAPI memeriksa bahwa kotak surat itu milik pengguna.


CMx: Informasi Umum dan Definisi Dasar


Sebelum Anda mulai mengembangkan kueri


Pengembangan kueri dilakukan dalam program terpisah CxAuditor. Di CxAuditor Anda perlu memindai semua kode (buat proyek lokal), yang akan kami tulis pertanyaannya. Setelah itu, Anda dapat menulis dan menjalankan kueri baru. Dengan basis kode yang besar, pemindaian primer dapat memakan waktu berjam-jam dan memori sebesar gigabita. Setelah itu, setiap permintaan tidak akan dieksekusi cukup cepat. Ini sama sekali tidak cocok untuk pengembangan.

Oleh karena itu, Anda dapat mengambil satu set kecil file dari proyek, idealnya dengan bug yang ditemukan dalam kode di depan jenis di mana kami menulis permintaan (atau menempatkan bug di sana secara manual) dan memindai hanya set file ini. Tidak perlu mematuhi struktur file proyek. Yaitu, jika Anda memiliki paket Java A dan B, dan kelas-kelas dalam paket B menggunakan kelas-kelas dan metode-metode paket A, Anda dapat meletakkan semua ini ke dalam satu direktori, dan CMx akan tetap memahami hubungan dan membangun rantai panggilan antar file dengan benar (baik, atau hampir selalu benar, meskipun kesalahan hampir tidak terkait dengan struktur file proyek).

Definisi dasar


Cxlist


Jenis data utama dalam CMx. Hasil dari hampir semua fungsi CMxQL adalah CxList . Ini banyak elemen dengan properti tertentu. Properti yang paling berguna untuk pengembangan akan dipertimbangkan di bawah ini.

hasil


CMxQL memiliki hasil variabel bawaan. Set yang berisi variabel hasil , setelah eksekusi seluruh kueri akan ditampilkan sebagai hasilnya.

Artinya, operasi terakhir dari setiap kueri harus berupa hasil string = WHATEVER , misalnya:
result = All.FindByName("anyname"); 

elemen aliran dan kode


Sebagian besar fungsi CMxQL berdasarkan jenis nilai yang dikembalikan dibagi menjadi 2, yang mengembalikan "elemen kode" dan yang mengembalikan Flow. Dalam kedua kasus, hasilnya adalah CxList . Tetapi isinya akan sedikit berbeda untuk elemen Flow dan kode.
  • Elemen kode - token - misalnya, variabel, pemanggilan metode, penugasan, dll.;
  • Flow - hubungan antara token yang diberikan.


Semua dan “sub” Semua


Setiap fungsi CMxQL dapat dilakukan pada set Semua (itu berisi semua token dari seluruh kode yang dipindai, kita sudah melihat contoh dengan hasil ) atau pada set CxList , yang pada gilirannya diperoleh sebagai hasil dari beberapa operasi dalam permintaan, misalnya, permintaan:
 CxList newList = CxList.New(); 

akan membuat set kosong, yang kemudian bisa kita isi dengan elemen menggunakan metode Add () , dan kemudian cari dengan elemen set baru:
 CxList newFind = newList.FindByName("narrowedScope"); 

Properti item yang ditemukan


Setiap elemen dari set CxList memiliki beberapa properti. Saat menganalisis hasil untuk menulis kueri, yang paling berguna adalah:

  • SourceFile - nama file yang berisi elemen ini;
  • Source Line - nomor baris dengan token;
  • Nama Sumber - nama token. Setara dengan token, yaitu jika variabel tersebut disebut var1, maka Sumber Nama = var1;
  • Jenis Sumber - jenis token. Misalnya, jika itu adalah string, maka itu akan menjadi StringLiteral, jika metode ini disebut, maka MethodInvokeExpr, dan banyak lainnya;
  • File tujuan
  • Jalur Tujuan;
  • Nama Tujuan;
  • Jenis Tujuan.


Sumber dan Tujuan akan berbeda jika elemen dari set hasil adalah Flow, dan sebaliknya mereka akan cocok jika hasilnya adalah elemen kode.

Mulai buat kueri


Semua fungsi CMxQL dapat dibagi menjadi beberapa jenis. Di sini, menurut pendapat saya, orang dapat mencatat kelemahan utama dari dokumentasi CMxQL, semua fungsi di dock dijelaskan secara sederhana dalam urutan abjad, sementara akan jauh lebih mudah untuk menyusunnya berdasarkan fungsi dan hanya berdasarkan abjad.

  • Fungsi pencarian - hampir semua fungsi CMxQL dengan nama FindBy * dan GetBy * ;
  • Fungsi operasi pada set adalah penambahan, pengurangan, persimpangan, iterasi atas elemen, dll.
  • Fungsi Analisis - Ini pada dasarnya adalah * Dipengaruhi oleh * * Mempengaruhi fungsi * .


Prinsip dasar pertanyaan adalah pergantian jenis fungsi ini. Pertama, menggunakan fungsi pencarian, kami hanya memilih token yang menarik bagi kami oleh properti tertentu. Menggunakan operasi pada set, kita dapat menggabungkan set yang berbeda dengan sifat token yang berbeda menjadi satu, atau sebaliknya, kurangi yang lain dari yang satu. Kemudian, menggunakan fungsi analisis, kami membangun Aliran Kode dan mencoba memahami apakah kerentanan potensial bergantung pada parameter di titik masuk.

Pilihan tempat untuk memulai pencarian, dan secara umum seluruh jalur pencarian, tergantung pada kode spesifik, dan lebih tepatnya, bahkan pada "teks". Dalam beberapa kasus, lebih mudah untuk mencari pertanyaan pengguna dari titik masuk, dalam beberapa kasus lebih mudah untuk memulai dari "akhir" atau bahkan dari tengah. Itu semua tergantung pada kode spesifik dan Anda perlu mendekati masing-masing repositori.

Contoh: Cari injeksi SQL


Paket pencarian, dalam kurung saya menunjukkan nama set (variabel dalam kueri):

  1. Tetapkan pengecualian - token yang dapat segera dibuang dari lingkup pencarian ( exclusionList );
  2. Menentukan lokasi pemeriksaan sanitasi / keamanan ( sanitasi );
  3. Temukan semua tempat tingkat rendah dengan eksekusi permintaan di database ( runSuperSecureSQLQuery );
  4. Temukan semua parameter dari metode yang disebut runSuperSecureSQLQuery ( runSSSQParams );
  5. Temukan titik entri (metode induk dan parameternya) untuk tempat pelaksanaan kueri dalam database ( entryPointsParameters );
  6. Temukan dependensi parameter runSSSQParams pada entryPoints , sementara hanya tempat-tempat di mana tidak ada sanitasi sanitasi input sanitasi .


Akibatnya, kami mendapatkan metode tingkat rendah dengan kueri SQL, di mana parameter kueri SQL:

  • tergantung pada parameter metode;
  • parameter diterima sebagai string;
  • parameter digabungkan dengan permintaan.

Kami tidak akan memeriksa apakah kami dapat mengontrol parameter ini, seperti kami percaya bahwa ada mekanisme untuk memetakan variabel ke dalam kueri dan ada pemain ke tipe numerik untuk angka, dan penggabungan string selalu dianggap berbahaya. Bahkan jika tidak ada kontrol atas saluran sekarang, itu mungkin muncul di rilis baru.

SQLi: Langkah 1. Mendefinisikan Pengecualian


Dalam pengecualian, Anda perlu menambahkan kelas-kelas atau file di mana nama token dapat cocok dengan yang Anda cari, karena token ini akan menyebabkan entri yang tidak valid.

Misalnya, metode untuk mengakses database disebut runSuperSecureSQLquery . Kami berasumsi bahwa metode runSuperSecureSQLquery di dalam diimplementasikan dengan aman. Dan tugas kita adalah menemukan tempat-tempat yang tidak aman menggunakan metode itu sendiri. Untuk injeksi SQL, tempat penyatuan parameter yang dikontrol pengguna tidak akan menjadi tempat yang aman. Dan tempat-tempat yang aman untuk memetakan parameter ke dalam struktur ORM atau, misalnya, untuk parameter numerik, ini merupakan cetakan untuk tipe yang sesuai. Kita tidak perlu memindai semua kode yang terletak "lebih dalam" daripada runSuperSecureSQLquery , yang berarti lebih baik untuk mengecualikannya untuk menghindari temuan yang tidak berguna.

Untuk mencari pengecualian seperti itu, nyaman untuk menggunakan fungsi CMxQL:
  • FindByFileName () - akan menemukan set semua token dalam file tertentu;
  • GetByClass () - akan menemukan set semua token di kelas dengan nama yang diberikan.


Untuk aplikasi pengujian, pengecualian ini adalah kelas Sesi , yang berisi implementasi metode runSuperSecureSQLquery .
Contoh permintaan untuk mengecualikan kode di kelas Session (metode GetByClass () akan memeriksa token mana yang diteruskan ke input yang memiliki tipe CMx dari ClassDecl dan akan mengeluarkan banyak token dari kelas ini)

 CxList exclusionList = All.GetByClass(All.FindByName("*Session*")); result = exclusionList; 


Atau cara lain adalah dengan membuang kode di seluruh file Session.java :

 CxList exclusionList = All.FindByFileName("*Session.java"); result = exclusionList; 


Tanda bintang sebelum nama itu penting, karena nama file menyertakan seluruh jalur.
Sekarang kami memiliki banyak token yang dapat dikurangi pada langkah-langkah selanjutnya dari lingkup pencarian.

Hasil pencarian token di dalam kelas Sesi :



SQLi: Langkah 2. Menentukan Tempat Sanitasi


Ada 2 metode API dalam aplikasi pengujian (lihat deskripsi singkat tentang aplikasi pengujian). Perbedaan antara kedua metode API adalah getTransactionInfo () menggabungkan parameter transactionId dalam query SQL, dan getTransactionInfoSecured () pertama-tama mengubah transactionId menjadi Long, dan kemudian meneruskannya sebagai string. Kerentanan (gabungan parameter) tertanam di kedua metode. Tetapi berkat casting ke Long di getTransactionInfoSecured () , metode terakhir tidak rentan terhadap injeksi, karena ketika kita mencoba untuk melewatkan injeksi (string) kita mendapatkan Java Exception.

Dalam contoh ini, kami akan mempertimbangkan pemeran untuk Long sebagai situs sanitasi. Untuk menemukan token ini:

 CxList sanitization = All.FindByName("*Long*"); result = sanitization; 


Contoh hasil:



Hasilnya termasuk token dengan metode YP Long dan metode getValueAsLong , yang secara internal mengkonversi nilai menjadi Long . Anda perlu meninjau hasilnya dengan hati-hati untuk memastikan tidak ada yang berlebihan.

SQLi: Langkah 3. Temukan semua tempat tingkat rendah dengan eksekusi permintaan dalam database


Kueri berikut akan menemukan semua tempat menggunakan token runSuperSecureSQLQuery (yang digunakan untuk mengakses database):

 result = All.FindByName("*runSuperSecureSQLQuery*") 

Hasil pencarian berdasarkan nama token runSuperSecureSQLQuery:


Selain itu, untuk tempat di mana metode ini disebut (kelas Penagihan ), hanya token pemanggilan metode (tipe MethodInvokeExpr ) yang akan ditemukan, dan untuk tempat deklarasi metode (kelas Sesi ), semua token - variabel juga akan ditemukan.

Kami memfilter hanya token panggilan metode:

 CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); result = runSuperSecureSQLQuery; 

Hasil:


Akibatnya, kami mendapat 7 tempat, 4 di antaranya panggilan yang diperlukan untuk metode runSuperSecureSQLQuery () (kelas Penagihan dan Pengguna ). 2 - panggilan ke metode internal runSuperSecureSQLQuery () di dalam kelas Session , dan satu lagi adalah metode add , yang merupakan sejenis keanehan pencarian CMxQL. Anggap saja saya tidak mengharapkannya ada di daftar =) Token di kelas Sesi , seperti yang kami temukan di langkah 1, tidak menarik bagi kami, jadi kami hanya akan mengurangi mereka dari hasilnya:

 CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); result = runSuperSecureSQLQuery - exclusionList; 

Kami mendapatkan daftar panggilan yang valid ke metode yang diperlukan:



Perhatikan fungsi FindByType () dan typeof () di kueri sebelumnya. Jika kita ingin mencari berdasarkan tipe CMx, yaitu, dengan properti CxList “Source Type” - maka kita menggunakan typeof (Source Type) . Jika kita ingin melakukan pencarian berdasarkan tipe data, maka kita perlu melewatkan parameter hanya sebagai string. Sebagai contoh:

 result = All.FindByType("String"); 

akan menemukan semua token java dengan tipe String.

SQLi: Langkah 4. Temukan semua parameter metode yang disebut runSuperSecureSQLQuery


Untuk mencari parameter metode, fungsi CMxQL GetParameters () digunakan :

 CxList runSSSQParams = All.GetParameters(runSuperSecureSQLQuery); result = runSSSQParams; 

Hasil:



SQLi: Langkah 5. Temukan titik masuk untuk lokasi eksekusi kueri dalam database


Untuk melakukan ini, pertama kita mendapatkan nama-nama metode induk, yang di dalamnya adalah panggilan ke database runSuperSecureSQLQuery , dan kemudian kita mendapatkan parameternya. Untuk mencari token induk, fungsi CMxQL GetAncOfType () digunakan :

 CxList entryPoints = runSuperSecureSQLQuery.GetAncOfType(typeof(MethodDecl)); result = entryPoints; 


Dalam kueri ini, untuk set runSuperSecureSQLQuery, kembalikan semua token induk dari tipe MethodDecl - ini adalah metode sebelumnya dalam tumpukan panggilan:



Untuk mencari parameter metode, kami juga menggunakan GetParameters () :

 CxList entryPointsParameters = All.GetParameters(entryPoints).FindByType("String"); 


Kueri akan mengembalikan parameter subset dari entryPoints dengan Java type String:



SQLi: Langkah 6. Cari dependensi parameter runSSSQParams pada entryPointsParameters, sementara hanya tempat-tempat di mana tidak ada input input sanitasi


Pada langkah ini, kami menggunakan fungsi analisis. Fungsi-fungsi berikut digunakan untuk menganalisis kode aliran:

  • Dipengaruhi oleh ()
  • Dipengaruhi oleh BlyAndNotSanitized ()
  • Pengaruh pada ()
  • InfluencingOnAndNotSanitized ()
  • NotInfluencedBy ()
  • NotInfluencingOn ()


Untuk menemukan Alur parameter permintaan runSSSQParams tergantung pada parameter metode induk entryPointsParameters dan mengecualikan token sanitasi:

 CxList dataInflOnTable = runSSSQParams.InfluencedByAndNotSanitized(entryPointsParameters, sanitization); 


Namun, saya tidak yakin apakah fungsi * AndNotSanitized di dalam melakukan beberapa sulap, dan itu lebih mirip metode hanya mengurangi set sanitasi dari hasilnya. Yaitu, jika Anda melakukannya:

 CxList dataInflOnTable = runSSSQParams.InfluencedBy(entryPointsParameters) - sanitization; 


ternyata hal yang sama. Walaupun mungkin saya hanya tidak menemukan opsi ketika masih ada perbedaan.

Hasil kueri memberi kita Aliran yang dibangun dengan benar:



Got Flow dengan potensi injeksi SQL. Seperti yang bisa dilihat dari tangkapan layar, Checkmarx mengembalikan 3 Flow. Aliran dalam tangkapan layar adalah yang terpendek, dimulai dan diakhiri dalam satu file dan satu metode. Aliran berikutnya berangkat sudah di kelas Sesi. Perhatikan Sumber / Tujuan. Dan yang terakhir adalah metode lain di kelas Sesi. Aliran di dalam Sesi akan terlihat seperti ini:



Untuk memilih satu aliran, metode ReduceFlow (CxList.ReduceFlowType flowType) digunakan , di mana flowType dapat:

  • CxList.ReduceFlowType.ReduceBigFlow - pilih Flow terpendek
  • CxList.ReduceFlowType.ReduceSmallFlow - pilih Flow terpanjang


SQLi: Permintaan terakhir untuk menemukan injeksi SQL


 // 1.   CxList exclusionList = All.GetByClass(All.FindByName("*Session*")); // 2.    CxList sanitization = All.FindByName("*Long*"); // 3.    runSuperSecureSQLQuery() CxList runSuperSecureSQLQuery = All.FindByName("*runSuperSecureSQLQuery*").FindByType(typeof(MethodInvokeExpr)); runSuperSecureSQLQuery -= exclusionList; // 4.     runSuperSecureSQLQuery() CxList runSSSQParams = All.GetParameters(runSuperSecureSQLQuery); // 5.   ,     runSuperSecureSQLQuery() CxList entryPoints = runSuperSecureSQLQuery.GetAncOfType(typeof(MethodDecl)); CxList entryPointsParameters = All.GetParameters(entryPoints).FindByType("String"); // 6.       (runSuperSecureSQLQuery)     CxList dataInflOnTable = runSSSQParams.InfluencedByAndNotSanitized(entryPointsParameters, sanitization); // 7.   result = dataInflOnTable.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow); 


Contoh 2: Mencari Referensi Objek Langsung Tidak Aman


Dalam permintaan ini, kami akan mencari semua tempat di mana bekerja dengan objek terjadi tanpa memeriksa pemilik objek. Dalam kasus ini, nama parameter HTTP yang berbeda untuk kotak surat dapat digunakan (kami menganggap ini Legacy), dan verifikasi itu sendiri dapat terjadi pada tahapan yang berbeda: di suatu tempat tepat di titik API entri HTTP, di suatu tempat sebelum permintaan ke database, dan kadang-kadang dalam metode menengah.

Rencana pencarian
  1. Tentukan pengecualian ( exclusionList );
  2. Identifikasi tempat untuk pemeriksaan otorisasi ( idorSanitizer );
  3. Temukan titik masuk - tempat untuk pemrosesan utama permintaan HTTP ( webRemoteMethods );
  4. Hanya dengan token titik masuk untuk menemukan lokasi ekstraksi dari parameter HTTP mailboxid ( mailboxidInit );
  5. Temukan semua panggilan dari webRemoteMethods ke metode dan parameter middleware dari panggilan ini ( middlewareMethods );
  6. Temukan metode middleware yang bergantung pada kotak surat ( apiPotentialIDOR );
  7. Temukan semua tempat di mana metode middleware didefinisikan ( middlewareDecl );
  8. Buka semua apiPotentialIDOR dan pilih hanya middlewareDecl yang tidak ada verifikasi pemilik objek mailboxid .


IDOR: Langkah 1. Identifikasi Pengecualian


Dalam hal ini, kecualikan semua token dalam file tertentu:

 CxList exclusionList = All.FindByFileName("*WebMethodContext.java"); result = exclusionList; 

WebMethodContext.java berisi implementasi metode seperti getMailboxId dan getUserId , serta string "mailboxid". Karena nama token akan bertepatan dengan yang kita butuhkan untuk mencari kerentanan, file ini akan mengeluarkan temuan palsu.

IDOR: Langkah 2. Temukan Cek Otorisasi


Dalam aplikasi pengujian, metode validateMailbox () digunakan untuk menentukan apakah objek yang diminta milik pengguna:

 CxList idorSanitizer = All.FindByName("*validateMailbox*"); result = idorSanitizer; 

Hasil:



IDOR: Langkah 3. Temukan titik masuk untuk permintaan HTTP API khusus


Penangan permintaan HTTP memiliki anotasi khusus yang membuatnya mudah ditemukan. Dalam kasus saya, ini adalah "WebRemote"; fungsi CMxQL FindByCustomAttribute () digunakan untuk mencari anotasi. Untuk FindByCustomAttribute () , fungsi pencarian token induk GetAncOfType () akan mengembalikan metode di bawah anotasi:

 CxList webRemoteMethods = All.FindByCustomAttribute("WebRemote") .GetAncOfType(typeof(MethodDecl)); result = webRemoteMethods; 


Hasil permintaan:



IDOR: Langkah 4. Hanya menggunakan token titik masuk, cari lokasi ekstraksi HTTP untuk parameter kotak surat


Untuk menemukan token terkait dengan pemrosesan parameter HTTP mailboxid:

 CxList getMailboxId = All.FindByName("\"mailboxId\"") + All.FindByName("\"mid\"") + All.FindByName("\"boxid\""); result = getMailboxId; 

kami menambahkan 3 set dengan 3 garis yang berbeda, karena menurut legenda, nama parameter HTTP dapat berbeda di berbagai bagian sistem.

Kueri akan menemukan semua tempat di mana mailboxid / mid / boxid ditulis sebagai string (dalam tanda kutip ganda). Tapi kueri ini akan mengembalikan banyak temuan, tk. string semacam itu dapat ditemukan tidak hanya di tempat-tempat di mana parameter HTTP diekstraksi. Jika kami terus bekerja dengan set ini, kami akan mendapatkan sejumlah besar temuan palsu.

Karenanya, kami hanya akan mencari token dari titik masuk ( webRemoteMethods ). Untuk menemukan semua token anak, fungsi CMBQL GetByAncs () digunakan :

 result = All.GetByAncs(webRemoteMethods); 

Permintaan akan mengembalikan semua token milik metode yang dianotasikan sebagai WebRemote . Sudah pada tahap ini, kita dapat memfilter token metode-metode di mana pemilik objek diperiksa. Oleh karena itu, kami menulis ulang kueri sebelumnya untuk mencari token anak sehingga kami hanya memilih token anak dari metode WebRemote , di mana tidak ada pemeriksaan keamanan dari pemilik objek. Untuk melakukan ini, gunakan loop dengan ketentuan:

 //          CxList entry_point_tokens = All.NewCxList(); //      webRemoteMethods foreach (CxList method in webRemoteMethods) { //        CxList method_tokens = All.GetByAncs(method); // ,       ,    owner if (method_tokens.FindByName(idorSanitizer).Count > 0) { //  ,     , ,     } else { //  ,         entry_point_tokens.Add(method_tokens); } } 

Sekarang kita dapat membuat pilihan yang lebih akurat menggunakan parameter kotak surat HTTP:

 CxList getMailboxHTTPParams = entry_point_tokens.FindByName("\"mailboxid\"") + entry_point_tokens.FindByName("\"mid\"") + entry_point_tokens.FindByName("\"boxid\""); result = getMailboxHTTPParams; 

Tetapi kami tidak tertarik pada tempat-tempat di mana parameter HTTP diambil, tetapi pada variabel yang pada akhirnya diberi nilai parameter HTTP. Karena lebih dapat diandalkan untuk mencari Flow secara tepat dengan token variabel.

Fungsi CMxQL FindByInitialization () akan menemukan tempat inisialisasi variabel untuk token yang diberikan:

 CxList mailboxidInit = entry_point_tokens.FindByInitialization(getMailboxHTTPParams); result = mailboxidInit; 

Hasil:



IDOR: Langkah 5. Temukan semua panggilan dari webRemoteMethods ke metode middleware dan parameter panggilan ini


Dengan middleware, maksud saya kode yang lebih dalam dari metode pemrosesan permintaan HTTP API, yaitu, lebih dalam dari titik masuk permintaan pengguna. Misalnya, untuk tangkapan layar di atas, ini adalah metode kelas Pengguna , panggilan ke user.getSettings () dan user.getSecureSettings () :

 CxList middlewareMethods = All.FindByShortName("user").GetRightmostMember(); CxList middlewareMethodsParams = entry_point_tokens.GetParameters(middlewareMethods); result = middlewareMethodsParams; 

Pertama, kami memilih semua token dengan nama pengguna, dan kemudian menggunakan GetRightmostMember () kami memilih token panggilan untuk middleware. GetRightmostMember () dalam rantai panggilan metode akan mengembalikan yang paling kanan. Kemudian kami menurunkan parameter metode yang ditemukan menggunakan GetParameters () .

Hasil:



IDOR: Langkah 6. Temukan metode middleware yang bergantung pada kotak surat


Analisis aliran menggunakan metode * InfluencedBy * dan * InfluncingOn * . Perbedaan di antara mereka jelas berdasarkan nama.

Sebagai contoh:

 All.InfluencedBy(getMailboxHTTPParams) 

akan melewati set All dan menemukan semua token yang bergantung pada getMailboxHTTPParams .

Hal yang sama dapat ditulis dengan cara lain:

 getMailboxHTTPParams.InfluencingOn(All) 


Untuk mencari token yang bergantung pada mailboxidInit :

 CxList apiPotentialIDOR = entry_point_tokens.InfluencedByAndNotSanitized(mailboxidInit, idorSanitizer); result = apiPotentialIDOR; 

Hasil:



IDOR: Langkah 7. Temukan semua tempat untuk mendefinisikan metode middleware


Mari kita temukan definisi dari semua metode perantara yang dapat digunakan di tempat-tempat di mana permintaan pengguna diproses. Untuk melakukan ini, kami menyoroti properti umum mereka, misalnya, dalam semua metode seperti ada penciptaan objek Request () , penciptaan objek adalah tipe CMx ObjectCreateExpr :

 CxList requests = (All - exclusionList).FindByType(typeof(ObjectCreateExpr)).FindByName("*Request*"); CxList middlewareDecl = requests.GetAncOfType(typeof(MethodDecl)); result = middlewareDecl; 


(Semua - exclusionList) - Anda dapat melakukan pengurangan set ini, dan kemudian memanggil fungsi CMxQL yang diinginkan dari hasilnya. Permintaan sekarang berisi semua token dengan nama Permintaan dan jenis yang sesuai dengan pembuatan objek.

Selanjutnya, menggunakan GetAncOfType () yang kita kenal , kita menemukan token induk dari tipe MethodDecl .

Hasil:



IDOR: Langkah 8. Telusuri semua apiPotentialIDOR dan pilih hanya middlewareDecl yang tidak ada verifikasi pemilik objek mailboxid


Di bagian akhir dari permintaan, kami akan menentukan metode middleware mana yang dipanggil langsung dari metode titik masuk dan tidak memeriksa siapa pemilik kotak surat . Kemudian gabungkan Flow untuk analisis hasil yang lebih nyaman.

Fitur baru yang belum kami gunakan:
GetCxListByPath () - fungsi ini diperlukan untuk beralih lebih dari Aliran, jika TIDAK digunakan, maka CMx akan memampatkan Aliran dalam Elemen Kode (pada simpul aliran pertama)
Concatenate * () - sejumlah fungsi yang diperlukan untuk menggabungkan beberapa aliran menjadi satu
FindByParameters () - temukan metode dengan token parameter tertentu
GetName () - akan mengembalikan string dengan nama token, jika ada lebih dari satu elemen di CxList, maka itu akan mengembalikan yang pertama. Metode ini hanya digunakan ketika iterasi elemen-elemen dari set.

Bagian terakhir dari permintaan:

 //    CxList vulns = All.NewCxList(); //   Flow  apiPotentialIDOR foreach(CxList cxFlow in apiPotentialIDOR.GetCxListByPath()) { //    Flow CxList endNode = cxFlow.GetStartAndEndNodes(CxList.GetStartEndNodesType.EndNodesOnly); //       flow (mailboxid) CxList method_call = entry_point_tokens.FindByParameters(endNode); //     CxList method_decl = middlewareDecl.FindByShortName(method_call.GetName()); //     if (method_decl.Count > 0) { //       CxList _all = (All - exclusionList).GetByAncs(method_decl); //       if (_all.FindByName(idorSanitizer).Count > 0) { //  ,       cxLog.WriteDebugMessage("find sanitized in method: " + method_call.GetName()); //  ,   Flow     vulns } else { //     Flow       vulns.Add(cxFlow.ConcatenatePath(method_call).ConcatenatePath(method_decl)); cxLog.WriteDebugMessage("find NOT sanitized in method: " + method_call.GetName()); } } } 


Hasil:



CocatenatePath , . Code Element Flow

IDOR: IDOR


 // 1.   CxList exclusionList = All.FindByFileName("*WebMethodContext.java"); // 2.     CxList idorSanitizer = All.FindByName("*validateMailbox*"); // 3.    –    HTTP  CxList webRemoteMethods = All.FindByCustomAttribute("WebRemote").GetAncOfType(typeof(MethodDecl)); // 4.         HTTP  mailboxid //     CxList entry_point_tokens = All.NewCxList(); foreach (CxList method in webRemoteMethods) { CxList method_tokens = All.GetByAncs(method); if (method_tokens.FindByName(idorSanitizer).Count > 0) { } else { entry_point_tokens.Add(method_tokens); } } //    HTTP    -  CxList getMailboxHTTPParams = entry_point_tokens.FindByName("\"mailboxId\"") + entry_point_tokens.FindByName("\"mid\"") + entry_point_tokens.FindByName("\"boxid\""); CxList mailboxidInit = entry_point_tokens.FindByInitialization(getMailboxHTTPParams); // 5.      middleware     CxList middlewareMethods = All.FindByShortName("user").GetRightmostMember(); CxList middlewareMethodsParams = entry_point_tokens.GetParameters(middlewareMethods); // 6.  middleware ,     mailboxid CxList apiPotentialIDOR = entry_point_tokens.InfluencedByAndNotSanitized(mailboxidInit, idorSanitizer); // 7.      middleware      CxList requests = (All - exclusionList).FindByType(typeof(ObjectCreateExpr)).FindByName("*Request*"); CxList middlewareDecl = requests.GetAncOfType(typeof(MethodDecl)); // 8.    apiPotentialIDOR     middlewareDecl,      CxList vulns = All.NewCxList(); foreach(CxList cxFlow in apiPotentialIDOR.GetCxListByPath()) { CxList endNode = cxFlow.GetStartAndEndNodes(CxList.GetStartEndNodesType.EndNodesOnly); CxList method_call = entry_point_tokens.FindByParameters(endNode); CxList method_decl = middlewareDecl.FindByShortName(method_call.GetName()); if (method_decl.Count > 0) { CxList _all = (All - exclusionList).GetByAncs(method_decl); if (_all.FindByName(idorSanitizer).Count > 0) { cxLog.WriteDebugMessage("find sanitized in method: " + method_call.GetName()); } else { vulns.Add(cxFlow.ConcatenatePath(method_call).ConcatenatePath(method_decl)); cxLog.WriteDebugMessage("find NOT sanitized in method: " + method_call.GetName()); } } } result = vulns; 


Kesimpulan


Checkmarx , . , , , .. Flow ( ). , , «» .

false positive, :
  • , ( ).
  • , ( ). , «Privacy Violation», , , Web UI. , .. UI . TLS XSS .
  • - , (, ). , XXE , , - , .
  • false positive, , CMxQL FindBy/GetBy. , ( SQL).
  • false positives, , , , , CMx, . , LDAP , . c LDAP- , , .


how-to «hello world» , Checkmarx.

Source: https://habr.com/ru/post/id477742/


All Articles