Light
Esenlikler,
Bugünkü yazımda, TryHackMe platformunda yer alan Light isimli senaryoyu çözmeye çalışacağım. Görev, "kolay" seviyede olarak değerlendirilmiş. Şimdiden ilginiz için teşekkür ederim.
Light Hakkında
Light ile ilgili olarak verilen açıklamada şu ifadeler yer alıyor:
- "I am working on a database application called Light! Would you like to try it out?
- If so, the application is running on port 1337. You can connect to it using
nc <ip> 1337
- You can use the username smokey in order to get started."
Kısaca, bir veritabanı uygulaması geliştirildiği ve bu uygulamaya belirtilen IP adresi ve 1337 portu üzerinden nc
(netcat) ile bağlanılabileceği belirtiliyor. Ayrıca uygulamaya başlamak için smokey kullanıcı adını kullanmamız gerektiği ifade ediliyor.
Görev Hakkında
TryHackMe platformu bu senaryo için bize üç görev sunuyor:
- Admin'in kullanıcı adı nedir?
- Admin'in parolası nedir?
- Flag nedir?
Uygulamayı Tanımak
Uygulamayı daha iyi anlayabilmek adına, ilk olarak normal bir kullanıcı gibi davranarak uygulamayı kullanmayı deniyorum.
Uygulama, oldukça basit bir mantıkla çalışıyor. Verdiğimiz kullanıcı adının parolasını doğrudan ekrana yazdırıyor. Gerçekten oldukça basit bir yapı.
Tahminen arka planda aşağıdaki gibi bir kod çalışıyor olabilir:
def get_user_password(username: str) -> str:
conn = sqlite3.connect('db_name')
curr = conn.cursor()
query = f"SELECT password FROM table_name WHERE username='{username}'"
curr.execute(query)
password = curr.fetchone()
return password
Eğer gerçekten bu mantıkla çalışıyorsa, uygulama SQL Injection saldırılarına karşı son derece savunmasız demektir. Kullanıcı girdisi doğrudan SQL sorgusuna eklendiği için, kötü niyetli bir kullanıcı sorgunun yapısını kolayca manipüle edebilir.
SQLi
Hedef sistemde SQL Injection (SQLi) açığını test etmek için, sorguyu manipüle etmeyi deneyeceğim. Bunun için kullanıcı adı inputuna aşağıdaki değeri vereceğim:
smokey' UNION SELECT 1 --
Bu girişle birlikte arka plandaki SQL sorgusu şu hale gelecek:
SELECT password FROM table_name WHERE username='smokey' UNION SELECT 1 --'
Burada --
ifadesi SQL'de yorum satırı anlamına geldiği için, sorgunun geri kalan kısmı devre dışı bırakılır. Böylece sonundaki tek tırnak ('
) karakteri de etkisiz hale getirilmiş olur.
Tabii, hedef sistemde bu tür saldırılara karşı bir önlem alınmış. Ancak bu önlem yetersiz, çünkü yorum satırı (--
) kullanmadan da sorguyu manipüle etmek mümkün. Örneğin, aşağıdaki gibi bir input vererek sorgunun yapısını yine değiştirebiliriz:
SELECT password FROM table_name WHERE username='smokey' UNION SELECT '1
Bu sayede, sorgunun sonunda yer alan tek tırnaktan yine kurtulmuş oluruz.
Bu noktada hedef sistem, "bazı sevmediğim kelimeler var" diyerek bizi uyarıyor. Burada bahsedilen muhtemelen UNION
ifadesinin kullanımı. Bu sınırlamayı aşmak için deneyeceğim ilk yöntem, eğer hedef sistemde yeterince güçlü bir filtreleme yapılmamışsa, UNION
ifadesini farklı bir biçimde kullanmak olacaktır.
Çıktıya baktığımızda bir koşul kontrolü yapıldığını anlıyoruz çünkü UNION
kullanıldığında sistem bize "Ahh there is a word in there I don't like :(" mesajını gösteriyor.
Yine arka planda çalışan kodu tahmini olarak şöyle yazabilirim:
def get_user_password(username: str) -> str:
if "UNION" in username:
return "Ahh there is a word in there I don't like :("
if "--" in username:
return "For strange reasons I can't explain, any input containing /*, -- or ,%0b is not allowed :)"
conn = sqlite3.connect('db_name')
curr = conn.cursor()
query = f"SELECT password FROM table_name WHERE username='{username}'"
curr.execute(query)
password = curr.fetchone()
return password
Buradan hareketle yorum yapacak olursam; eğer kullanıcıdan gelen input, if
koşulu ile tek tek kontrol ediliyorsa, hedef sistem büyük/küçük harf duyarlılığına bakmıyor olabilir. Bu yüzden sorguyu şu şekilde değiştirirsem:
SELECT password FROM table_name WHERE username='smokey' Union Select '1
istenilen sonucu elde edebilirim.
Haklı olmak güzel şey.
Şimdi sırada, hedef sistemde hangi tabloların bulunduğunu öğrenmek var.
Biraz araştırma yaptığımda, SQLite içerisinde bu amaçla kullanılan özel bir tablo olduğunu gördüm. Bu tablo, SQL Server’daki information_schema
veya MySQL’deki INFORMATION_SCHEMA
tablolarına benzer şekilde çalışır. Yani, SQLite’a bağlanan uygulamalar mevcut tabloları listelemek için bu özel tabloyu kullanırlar.
Bu özel tablo ise sqlite_master
tablosudur.
Şimdi ilk adım olarak, veritabanında hangi tabloların yer aldığını sqlite_master
üzerinden öğrenmeye çalışacağız.
Bunun için kullanılacak SQL sorgusu şu şekildedir:
SELECT password FROM table_name WHERE username='smokey' UNION SELECT GROUP_CONCAT(name) FROM sqlite_master WHERE type='table'
li Bu sorgunun çalışması için aşağıdaki input'u vermemiz yeterli olacaktır:
smokey' UNION SELECT GROUP_CONCAT(name) FROM sqlite_master WHERE type='table
GROUP_CONCAT
fonksiyonunu kullanmamın nedeni, veritabanında yer alan tüm tablo adlarını tek bir satırda görebilmek.
Bu sorgunun sonucunda, hedef sistemde usertable
ve admintable
adında iki tablo olduğunu öğrenmiş olduk.
Peki, bu tablolarda hangi kolonların olduğunu nasıl öğrenebiliriz? Biraz araştırma yaptıktan sonra, SQLite’ta tablo yapısı hakkında bilgi almak için pragma_table_info
fonksiyonunun kullanılabileceğini öğrendim. Bu yapı, verilen tabloya ait kolon bilgilerini döndürür.
Dolayısıyla aşağıdaki sorguyla admintable
tablosunun kolonlarını öğrenebiliriz:
SELECT password FROM admintable WHERE username='smokey' UNION SELECT GROUP_CONCAT(name) FROM pragma_table_info('admintable') WHERE '1
Burada:
GROUP_CONCAT
fonksiyonunu, tüm kolon adlarını tek bir satırda görebilmek için kullandım.
WHERE '1
ifadesini ise sorgunun düzgün tamamlanmasını sağlamak için ekledim.
Aynı yöntemi usertable
için de uygulayabiliriz:
SELECT password FROM usertable WHERE username='smokey' UNION SELECT GROUP_CONCAT(name) FROM pragma_table_info('usertable') WHERE '1
İki tabloda da id
, username
ve password
kolonlarının bulunduğunu öğrenmiş olduk.
Şimdi geriye sadece bu tabloların içerisindeki verileri okumak kaldı. Bunun için sorguyu şu şekilde kullanabiliriz:
SELECT password FROM admintable WHERE username='smokey' UNION SELECT GROUP_CONCAT(id || ':' || username || ':' || password) FROM admintable WHERE '1'
Aynı işlemi usertable
için de uygulayabiliriz.
Admintable içerisinde TryHackMe'nin verdiği 3 task'ın da cevaplarını görebiliyoruz. ve bu sayede bu challenge'yi de tamamlamış oluyoruz.
Teşekkürler
Yazımı buraya kadar okuduğunuz için teşekkür ederim. Bir sonraki test raporunda görüşmek dileğiyle. Kendinize iyi bakın, esenlikler.