NoSQL Injection

Bu yazıda NoSQL Injection zafiyetinden bahsedilecektir. Ancak, bu zafiyeti tam olarak kavrayabilmek için öncelikle NoSQL yapısını anlamak ve ardından zafiyetin çalışma mekanizmasını incelemek daha doğru bir yaklaşım olacaktır. Not: Eğer SQL hakkında temel düzey bilgi sahibi değilseniz öncesinde SQL 101 ve SQL Injection makalelerini okumanız konuyu kavramanız için yararlı olacaktır.

NoSQL Veri Tabanları

NoSQL veri tabanlarını kavramak için veri tabanlarının kategorileri ve bu kategorilerin çalışma yapıları bilinmelidir. Veri tabanları iki ana kategoriye ayrılmaktadır:

  1. İlişkisel Veri Tabanları: Bu veri tabanları, saklanabilir, erişilebilir ve değiştirilebilir verilerden oluşur ve bu işlemler RDBMS (İlişkisel Veri Tabanı Yönetim Sistemi) yapısıyla gerçekleşir. SQL dili kullanılarak yazılır. En bilinen örnekler arasında MySQL, Oracle ve PostgreSQL bulunmaktadır.

  2. İlişkisel Olmayan Veri Tabanları: İlişkisel veri tabanlarının aksine, satır ve sütunlardan oluşan tablo şeması bulunmaz. Bunun yerine, depolanacak verinin türüne göre optimize edilmiş depolama türleri kullanılır. Örneğin, veriler JSON belgeleri veya kenar ve köşelerden oluşan grafikler olarak saklanabilir. NoSQL bu sistemle çalışmaktadır.

NoSQL (Not Only SQL), geleneksel SQL veri tabanlarına kıyasla verilerini farklı bir yaklaşımla depolar. Kullanılan teknolojiye göre veriler anahtar-değer çiftleri, belgeler, sütunlar veya grafikler gibi çeşitli biçimlerde düzenlenir. Bu sayede veriler daha verimli şekilde depolanabilir ve erişilebilir hale gelir.

NoSQL veri tabanları, ölçeklenebilirlik açısından SQL veri tabanlarından farklıdır. Yatay olarak ölçeklenebilir şekilde tasarlanmıştır, yani veriler birden fazla sunucuya dağıtılarak çalışır. Bu durumu metaforik olarak açıklayacak olursak, SQL'de tek bir kutu kullanılır ve veri arttıkça daha büyük bir kutu gerekir. NoSQL'de ise birçok kutu kullanılır ve veri arttıkça kutu sayısı da artar.

Bu veri tabanları, yüksek kullanılabilirlik ve düşük hata toleransı sağlar. Sunucu arızalarında veya ağ sorunlarında bile verilere erişim sağlanabilir.

Kısaca NoSQL ve SQL arasındaki farklar şu şekilde özetlenebilir:

ÖzellikSQLNoSQL

Veri Modeli

Sabit ilişkilere sahip, satır ve sütunlardan oluşan tablolar

Esnek, yapılandırılmamış veya yarı yapılandırılmış veriler; anahtar-değer çiftleri, belgeler, sütunlar, grafikler

Ölçeklenebilirlik

Dikey ölçeklenebilirlik (tek sunucuya kaynak ekleme)

Yatay ölçeklenebilirlik (birden fazla sunucuya dağıtma)

Query Dili

SQL (Standart yapılandırılmış sorgu dili)

Özgün sorgu dilleri veya arayüzler (veri modeline göre değişir)

Şema Esnekliği

Katı şemalar (önceden tanımlanmış tablo yapıları ve veri türleri)

Şema esnekliği (önceden tanımlanmamış, dinamik veri yapıları)

Destek

Kapsamlı destek kaynakları ve yaygın satıcı destekli hizmetler

Daha az kapsamlı destek kaynakları; belirli NoSQL veritabanına bağlı satıcı destekli hizmetler


SQL Injection ile NoSQL Injection'u Anlamak

NoSQL Injection saldırıları saldırganların kimlik doğrulamalarını atlamasına, verileri çalmasına ve uygulama üzerinde kontrol sahibi olmasına olanak sağlar.

NoSQL Injection'u SQL Injection ile karşılaştırarak anlatmak, konunun anlaşılması açısından daha etkili bir yaklaşım olacaktır. Farklılıklar birkaç kategoride açıklanabilir:

  1. Sorgu Yapısı SQL veri tabanlarında sorgular, SQL dilinde yazılır. Buna karşın NoSQL veri tabanlarında çok çeşitli sorgu dilleri kullanılabilir veya bazı NoSQL veri tabanları hiç sorgu dili kullanmayabilir. Bu sebeple, SQL Injection saldırılarında kullanılan teknikler NoSQL veri tabanlarında geçerli değildir.

  2. Sorgu Parametreleri SQL Injection saldırılarında saldırganlar sorgu parametrelerinden kaynaklanan güvenlik açıklarına odaklanır. NoSQL Injection'da ise saldırganlar doğrudan veri tabanaı sorgularında veya veri işleme operasyonlarında kullanılan giriş verilerinde açık arar.

  3. Enjeksiyon Teknikleri SQL Injection sorguları değiştirip manipüle ederken NoSQL Injection ise yapılandırılmamış verileri veya NOSQL'de kullanılan sorgu yapılarını manipüle etmeyi amaçlar.

  4. Veri Manipülasyonu SQL Injection, genellikle ilişkisel veri tabanlarındaki verilere erişim sağlamak, verileri değiştirmek veya silmek amacıyla kullanılır. Buna karşılık, NoSQL Injection saldırılarında saldırganlar, veri yapısını veya veri nesnelerini manipüle ederek potansiyel olarak verilerin bozulmasına veya ifşasına neden olabilir.

    SQL Sorgusu:

    SELECT * FROM users WHERE username = 'user_input' AND password = 'user_input';

    Yukarıdaki sorgu, SQL Injection zafiyeti içererek kullanıcı adı ve parolanın doğrulanması için kullanılmaktadır.

    SQL Injection Sonrası Oluşan Sorgu:

    SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '' OR '1'='1';

    Bu sorguda, kullanıcı girişi olan ' OR '1' = '1' ifadesi, filtreleme yapılmadan doğrudan sorguya dahil edilmiştir. Bu durum, SQL Injection saldırısı ortaya çıkarmıştır.


    NoSQL Sorgusu (MongoDB):

    db.users.find({ username: user_input, password: user_input });

    Yukarıdaki sorgu SQL örneğinde olduğu gibi zafiyet içeren kullanıcı adı ve parola doğrulaması yapan JavaScript ile yazılmış NoSQL sorgusudur.

    NoSQL Injection Sonrası Oluşan Sorgu:

    db.users.find({ username: { "$ne": null }, password: { "$ne": null } });

    SQL Injection'da olduğu gibi input'ların hiçbir filtrelemeye tabi olmadan direkt olarak { "$ne": null } ifadesini sorguya dahil etmesinden kaynaklı NoSQL Injection saldırısı ortaya çıkmıştır.


NoSQL Injection Tespiti

Syntax Injection

Bu enjeksiyon türü NoSQL sorgusunu bozarak kendi payloadlarımızı enjekte ettiğimiz durumlarda ortaya çıkmaktadır. Metodolojik olarak SQL Injection'a benzemektedir. Ancak saldırı büyük ölçüde farklılık göstermektedir çünkü NoSQL veri tabanları farklı sorgu dilleri, farklı syntaxlar kullanmaktadır.

NoSQL enjeksiyonu açıklarını tespit etmek için sorgu sözdizimini bozmaya yönelik testler gerçekleştirilebilir. Bu doğrultuda, uygulama tarafından yeterince temizlenmemiş veya filtrelenmemiş fuzz (rastgele karakter dizileri) dizileri ve özel karakterler gönderilerek her bir giriş sistematik bir şekilde test edilir. Bu tür fuzz dizileri ve karakterler, veritabanı hatası tetikleyebilir veya başka tespit edilebilir davranışlar sergileyebilir.

Hedef veritabanının API dili biliniyorsa, o dil için uygun özel karakterleri ve fuzz dizilerini kullanmak önemlidir. Bilinmeyen API dilleri için ise çeşitli fuzz dizileri kullanarak geniş bir test kapsamı sağlanabilir.

Syntax Injection Tespiti

Bu örnekte ürünlerin kategorize edilebildiği bir sayfa yer almaktadır. Bu ürünlerin kategorizasyonu ise URL'deki filter?category=Accessories ifadesiyle gerçekleşmektedir. URL'in bu kısmını kullanarak NoSQL Injection zafiyeti gösterilecektir.

Adım 1: Hata almayı deneyin.

	if(this.category == "Accessories"){
		//do something
	}

Sayfanın backend'inde "category" değişkeniyle alınan parametre yukarıdaki gibi bir sorgu ile koda dahil edilmektedir. Bu ifadenin sisteme direkt mi yoksa filtrelenerek mi ifade edildiğini tespit etmek için bazı ifadeleri deneyerek syntax hatası alınmaya çalışmalıdır. Bu ifadeler " ' { } $ ; olabilir.

İfadeler sırasıyla denenip syntax hatası oluşturan ifadeyi yakalamak gerekmektedir.

" işareti denendiğinde syntax konusunda herhangi bir bozulma olmadan olduğu gibi sorguya dahil edilmiştir.

Ancak ' işareti denendiğinde syntax yapısı bozulmakta ve istenen hataya ulaşılmaktadır. Bu hatadan ifadenin yukarıdaki koddaki gibi "Accessories" şeklinde değil de 'Accessories' şeklinde yazıldığı anlaşılmaktadır.

Adım 2: Boolean sorguyu bypass ederek filtrelemeden kaçın.

	if(this.category == "Accessories" && this.limit == 3 ){
		//do something
	}

Yukarıdaki gibi sorguda ekstra koşul ifadeleri yer alıyor olabilir. Bu koşul durumlarından kaçabilmek için bazı sorgu parametreleri denenmelidir, bu sorgu parametreleri aşağıda yer almaktadır:

  • ' && 1 == 1: Sorguya eklendiğinde sayısal değer olan 1 ifadelerinin eşit olup olmadığını kontrol eder ve önceki ifadelerle beraber hepsinin true olması durumunda genel bir true göndererek ifade çalışır.

  • ' && '1' == '1: Sorguya eklendiğinde metinsel değer olan 1 ifadelerinin eşit olup olmadığını kontrol eder ve önceki ifadelerle beraber hepsinin true olması durumunda genel bir true göndererek ifade çalışır.

  • ' || 1 == 1: Sorguya eklendiğinde sayısal değer olan 1 ifadelerinin eşit olup olmadığını kontrol eder ve true gönderir. "||" ifadesi bulunduğunda diğer sorguların kontrolüne gerek kalmaz ve tek bir true bilgisini kullanarak genel bir true göndererek ifade çalışır.

  • ' || '1' == '1: Sorguya eklendiğinde metinsel değer olan 1 ifadelerinin eşit olup olmadığını kontrol eder ve true gönderir. "||" ifadesi bulunduğunda diğer sorguların kontrolüne gerek kalmaz ve tek bir true bilgisini kullanarak genel bir true göndererek ifade çalışır.

  • ' || 1 || ': Birden fazla sorgu yapıldığında ve input alınan sorgu arada kaldığında sorgular birbirine bağlanarak 1 sonucundan doğan true değeri genel true göndererek ifadeyi çalıştırır.

Koşullar sırasıyla denendiğinde ' || '1' == '1 ifadesinin sorgudaki filtrelemeyi devre dışı bırakıp yukarıdaki gibi bir if ifadesinin içerisinden kaçabilmiş olur ve NoSQL Injection tetiklenir.

Operator Injection

NoSQL veritabanlarında, verilerin sorgu sonucuna dahil edilmesi için genellikle birtakım sorgu operatörleri kullanılır. Sıkça karşılaşılan bazı sorgu operatörleri aşağıda yer almaktadır:

  • $where: Bir JavaScript ifadesini karşılayan belgeler eşleştirilir.

  • $ne: Belirtilen bir değere eşit olmayan tüm değerler eşleştirilir.

  • $in: Bir dizide belirtilen tüm değerler eşleştirilir.

  • $regex: Değerlerin belirli bir düzenli ifadeyle eşleştiği belgeler seçilir.

Bu sorgu operatörlerinin sisteme enjekte edilmesi, NoSQL sorgularının manipüle edilmesini sağlar. Bunu yapmak için, sistematik olarak farklı operatörlerin girdi olarak gönderilmesi ve hata mesajlarının veya değişikliklerin incelenmesi gerekmektedir.

Operator Injection Tespiti Bu laboratuvarın amacı NoSQL veritabanı kullanılan sistemi manipüle ederek administrator yetkisiyle sisteme giriş yapmaktır.

Adım 1: NoSQL Injection Zafiyetinin Tespiti

Rastegele gönderilen username ve password bilgisi request olarak yakalandığında yukarıdaki gibi bir sonuç ortaya çıkmaktadır.

Zafiyetin tespiti için, önceden verilen kullanıcı bilgilerinden şifre kullanılarak, kullanıcı adı üzerinden sorguya enjeksiyon yapılıp yapılamadığı test edilmektedir.

{"$ne":""} operatörü boş string içermeyen eşleşmeleri seçer ve geriye kalan tüm username bilgilerini döndürür.

Şifrenin önceden bilinmesi nedeniyle, wiener:peter eşleşmesi oluşarak sisteme giriş yapılmasına olanak tanır. Girişin başarıyla gerçekleşmesiyle de NoSQL Injection zafiyetinin olduğu ispatlanır.

Adım 2: $regex Operatörüyle Giriş Yapma

Bu sefer username parametresi {"$regex":"wien.*"} olarak değiştirilip tekrardan giriş yapılmaya çalışışır.

{"$regex":"wien.*"} operatörü belirli bir alandaki değerlerin belirtilen desenle eşleşip eşleşmediğini kontrol eder. wien.* ifadesi wien ile başlayan tüm username bilgilerini döndürür.

"wien" olarak başlayan tek bir kullanıcı olmasından dolayı önceki adımdaki gibi giriş başarıyla gerçekleşir.

Adım 3: Birden Fazla Kullanıcı Doğrulaması

Burada username ve password parametreleri {"$ne":""} olarak doldurulur ve birden fazla kullanıcı olması durumunda hata döndürülmesi beklenir.

Birden fazla kullanıcı olduğundan dolayı sistem yukarıdaki hatayı döndürür.

Adım 4: Administrator Girişi

Gerekli testler gerçekleştikten sonra "administrator" girişi denenebilir durumdadır.

{"$regex":"admin.*"} ifadesiyle "admin" şeklinde başlayan tüm sorgular döndürülür. Bu ifadeyle başlayan tek bir kullanıcı olmasından dolayı tek bir username döner. {"$ne":""} ifadesiyle de boş string olmayan tüm şifreler döndürülür.

Username ve password bilgisi eşleştiğinde giriş başarılı olarak gerçekleşir.


NoSQL Injection Saldırılarından Nasıl Korunulabilir?

Tüm enjeksiyon saldırılarında olduğu gibi NoSQL Injection saldırılarına karşı olarak input alan yerler güvenilmez olarak kabul edilip sterilize edilmeldir. Bu sterilizasyon için aşağıdaki adımlar uygulanabilir:

  • Mongo-sanitize veya mongoose gibi kütüphaneler kullanılabilir.

  • Belirli tiplerde inputlar kabul edilmelidir.

  • MongoDB kullanılması durumunda input alanlarında where, mapReduce ve group gibi operatörler kullanılmamalıdır.

  • Uygulama least privilege prensibiyle çalıştırılmalıdır.


Kaynaklar

  1. https://www.youtube.com/watch?v=V29-GMYRYNs

  2. https://www.komtas.com/glossary/iliskisel-veri-tabani-nedir#:~:text=İlişkisel%20veri%20tabanları%20saklanabilir%2C%20erişilebilir,sistemler%20içerisinde%20SQL%20dili%20kullanılır

  3. https://learn.microsoft.com/tr-tr/azure/architecture/data-guide/big-data/non-relational-data

  4. https://azure.microsoft.com/tr-tr/resources/cloud-computing-dictionary/what-is-nosql-database

  5. https://bilginc.com/tr/blog/sql-ve-nosql-arasindaki-farklar-5869/

  6. https://medium.com/@Land2Cyber/nosql-injection-attacks-how-they-differ-from-sql-injection-d81493852358

  7. https://portswigger.net/web-security/nosql-injection

  8. https://lostar.com.tr/2021/11/nosql-enjeksiyon-saldirilari-nedir-nasil-tedbir-almaliyiz.html

Last updated