Cyber Security Articles

Encrypter QNQSec - Embedded Shellcode

Encrypter - Decrpyter

Encrypter Dosyası

Encrypter dosyası çalıştırılabilir bir ELF uygulamasıdır. Bu uygulamanın mantığı bir şifreleme fonksiyonunu AES-CBC olarak verilen bir dosyası şifrelediği ve bu dosyanın tekrardan çözümlenmesi için gerekli olan tersine mühendislik işlemleri ile birlikte uygulamanın kırılarak şifrelenmiş dosyaları geri kurtarmaktır. Burada üzerinde duracağımız en önemli nokta ise KEY - IV değerlerini elde edibilmemizdir. Bunları elde ettiğimizde şifrelenmiş dosyaları geri Decrypt işlemi yaparak kurtarabiliriz. Ayrıntılı olarak bahsedeceğimiz bu çözümde uygulamayı ayrıntılı olarak analiz edeceğiz.

İlk Çalıştırma

./encrypter encrpyt

Komutu verildiğinde bize bir dosya şifreleme işlemi yapılmaktadır.

      iVar1 = strcmp((char *)param_2[1],"encrypt");
      if (iVar1 == 0) {
        uVar3 = encrypt_file("flag.txt","flag.enc",(uchar *)&local_38,(uchar *)&local_48);
        if ((int)uVar3 == 0) {
          puts("Encrypt failed");
          uVar2 = 3;

Bu işlem burada gerçekleştirilmektedir. Ghidra ile uygulamayı incelediğimizda C kodunda strcmp fonksiyonu param_2 [1] ile parametre kontrolü yapmaktadır. Eğer “encrypt” değerine eşit bir veri girildiyse şifreleme işlemi başlamaktadır. Burada yapılan şifreleme işlemini derinlemesine incelemeden önce değişkenlerimiz local_38 = KEY ve &local_48 = IV dir. Burada bir simetrik şifreleme işlemi yapılıyor diyebiliriz çünkü elimizde iki adet bir değişken var ve bu değişken değerlerine göre şifreleme işlemi yapılmaktadır. Flag.txt dosyasını alıp flag.enc adıyla bir dosya yaratmaktadır. Burada ki amacımız bu değerlere ulaşıp işlemi tersine çevirmektir.

encrypt_file() Fonksiyonunun İncelenmesi

Bu fonksiyon incelediğinde içerisinde do_crypto adında bir fonksiyon görmekteyiz bunu detaylı inceleyerek yani bu fonksiyona atlama yaparak içeriğini incelediğimizde ne tür bir şifreleme tekniği kullandığını görebiliriz.

  else {
    uVar1 = do_crypto(__stream,__stream_00,param_3,param_4,1);
    fclose(__stream);
    fclose(__stream_00);
    uVar1 = uVar1 & 0xffffffff;
  }

do_crypto() Fonksiyonunun İncelenmesi

Fonksiyonu incelediğimizde uzun bir kod bloğu bizi karşılıyor ama bir aralık dikkatimizi çekiyor ve burada ise AES-CBC-256 şifrelemesi olduğunu görmekteyiz.

  else {
    local_840 = EVP_aes_256_cbc();
    if (param_5 == 0) {
      iVar1 = EVP_DecryptInit_ex(local_848,local_840,(ENGINE *)0x0,param_3,param_4);
      if (iVar1 != 1) {
        handle_errors();
      }
    }

EVP_aes_256_cbc() fonksiyonu bize KEY ve IV değerleri ile bir şifreleme yapıldığının ipucunu vermektedir.

encrypt_file("flag.txt","flag.enc",(uchar *)&local_38,(uchar *)&local_48); Burada bulunan Local_38 ve Local_48 KEY - IV İkilisi olduğunu böylelikte öğrenmiş olduk.

Fakat bu öğrendiğimiz sadece bir şifreleme yöntemi ve bu değerlere nasıl atama yapılıyor ve ne tür bir işlem akışı var bunu çözmemiz gerekmektedir.

Kod Bloklarının İncelenmesi - &local_48

Detaylıca incelediğimizde görüyoruz ki “1337” stringini local_48’in başladığı adrese kopyalar. 0x10 = 16 byte. Yani 16 byte’lık bir alana “1337” yazılır. IV (16 byte): '1', '3', '3', '7', '\0', '\0', … (Toplam 16 byte) 1337’den geriye kalan değerlere ise \x00 Bytelar ile doldurulur. Burada dikkatimiz bir alan daha çekiyor o ise call_embedded_shellcode() fonksiyonu bu ise &local_38 Pointer değişkeni ile alakalı bir durumdur.

    local_48 = 0;
    local_40 = 0;
    strncpy((char *)&local_48,"1337",0x10);
    local_38 = 0;
    local_30 = 0;
    local_28 = 0;
    local_20 = 0;
    uVar2 = call_embedded_shellcode(&local_38,0x20);
    if ((int)uVar2 == 0) {
      fwrite("Failed to produce key via shellcode\n",1,0x24,stderr);
      uVar2 = 2;
    }

Kod Bloklarının İncelenmesi - &local_38

call_embedded_shellcode() fonksiyonu incelendiğinde $rbp ‘ ye erişimi &Local_38 değişkeni üzerinden gerçekleşmektedir. 0x20 Hexadecimal değeri ise &local_38 değerinin 32 Byte olduğu anlaşılmaktadır.

Bu iki değişken yapısını incelerken $rbp - 0x48 ve $rbp - 0x38 olarak ele alırsak daha detaylı bir inceleme gerçekleştirebiliriz.

Kod Bloklarının İncelenmesi - Flag

Shellcode &local_38 KEY (Anahtar) ile doldurulduktan sonra ise &local_48 IV (Initialization Vector) ile şifreleme işlemi yapılmaktır. Flag.txt adında bir dosyanın yerel dizinde bulunması gerekmektedir yani şifreleme yapmadan önce dosyamızın hazır olması gerekmektedir. ./encrypter encrypt komutu yazıldığında ise Flag.enc olarak dosya yaratılmaktadır.


    if ((int)uVar2 == 0) {
      fwrite("Failed to produce key via shellcode\n",1,0x24,stderr);
      uVar2 = 2;
    }
    else {
      iVar1 = strcmp((char *)param_2[1],"encrypt");
      if (iVar1 == 0) {
        uVar3 = encrypt_file("flag.txt","flag.enc",(uchar *)&local_38,(uchar *)&local_48);
        if ((int)uVar3 == 0) {
          puts("Encrypt failed");
          uVar2 = 3;
        }
        else {
          puts("Encrypted -> flag.enc");
          uVar2 = 0;
        }
      }
      else {
        uVar2 = 0;
		

Dinamik Analiz

Ltrace ile yaptığımız dinamik analizde programın akışı aşağıdaki gibidir ayrıca detaylıca inceleyeceğiz. Burada aslında bir başka değinmek istediğimiz nokta ise programların inceme aşamasında daha detalıca nasıl anlamlandırabiliriz bunu belirtmekteyim.


(1)  strncpy(0x7fffad711250, "1337", 16)                                                    = 0x7fffad711250
(2)  sysconf(_SC_PAGE_SIZE)                                                                 = 4096
(3)  mmap(nil, 4096, 0b111, 0x22, -1, 0)                                                    = 0x7f87983e2000
(4)  memcpy(0x7f87983e2000, "\306G\0t\306G\001h\306G\0021\306G\003_\306G\0041\306G\005s\306G\006_\306G\at"..., 129) = 0x7f87983e2000
(5)  munmap(0x7f87983e2000, 4096)                                                           = 0
(6)  strcmp("encrypt", "encrypt")                                                           = 0
(7)  fopen("flag.txt", "rb")                                                                = 0x56503376b2a0
(8)  fopen("flag.enc", "wb")                                                                = 0x56503376b480
(9)  EVP_CIPHER_CTX_new(0x56503376b2a0, 0x56503376b480, 0x7fffad711260, 0x7fffad711250)     = 0x56503376b660
(10) EVP_aes_256_cbc(0, 0, 0, 0)                                                            = 0x7f87981b9bc0
(11) EVP_EncryptInit_ex(0x56503376b660, 0x7f87981b9bc0, 0, 0x7fffad711260)                  = 1
(12) fread(0x7fffad7109a0, 1, 1024, 0x56503376b2a0)                                         = 19
(13) EVP_EncryptUpdate(0x56503376b660, 0x7fffad710da0, 0x7fffad710988, 0x7fffad7109a0)      = 1
(14) fwrite("j\304g\304\003\216\257\316uiN\257\350\323\325\006\001", 1, 16, 0x56503376b480) = 16
(15) fread(0x7fffad7109a0, 1, 1024, 0x56503376b2a0)                                         = 0
(16) EVP_EncryptFinal_ex(0x56503376b660, 0x7fffad710da0, 0x7fffad710988, 0x7fffad710da0)    = 1
(17) fwrite("\331\177\323\320\0018\234\036\361\222\342d\3358\265-\001", 1, 16, 0x56503376b480) = 16
(18) EVP_CIPHER_CTX_free(0x56503376b660, 0x7fffad710da0, 0, 0x56503376b480)                 = 1
(19) fclose(0x56503376b2a0)                                                                 = 0
(20) fclose(0x56503376b480)                                                                 = 0
(21) puts("Encrypted -> flag.enc"Encrypted -> flag.enc

Burada incelediğimizde (1) kısmında strncpy ile 1337+\x00 kalan Bytelar doldurulur. 2 ve 3 kısımda ise Hafıza atama işlemleri yapılmaktadır. memcpy() bölümünde yani 4.kısımda ise Shellcode hafızada çalıştırılacak yere kopyalanır. 7 ve 8 bölümünde ise flag.txt dosyasımız açılmaktadır aynı zamanda Flag.enc yazılabilir modda açılmaktadır. 10 bölümde ise EVP_aes_256_cbc bize şifreleme tipinin ayrıca detayını vermektedir. Kalan bölümlerde ise artık şifreleme işleminin tamamlanması yer almaktadır.

Debug

gdb -q ./encrypter -ex 'b call_embedded_shellcode' -ex 'run encrypt' -ex 'finish' -ex 'printf "RBP = %p\n", $rbp' -ex 'x/32bx $rbp-0x38' -ex 'x/16bx $rbp-0x48' -ex 'quit'

komutuyla uygulamayı debug ediyoruz ve call_embedded_shellcode() noktasına bir BreakPoint koyuyoruz. Shellcode bölümüne geldiğimizde program akışı duracaktır. run encrypt ile ise argv[1] olarak bir argüman iletiyoruz. Finish komutu ile ise Fonksiyondan çıkış yaptıktan sonra bitmektedir.

$rbp: “Base Pointer” , main fonksiyonunun stack ( yığın ) çerçevesinin adresini tutar.

-ex 'x/32bx $rbp-0x38' -ex 'x/16bx $rbp-0x48' Burada ise 32 ve 16 Bytelik 38 ve 48 Local değişkenlerin $RBP değerlerine ulaşmak için yazıyoruz.

Çalıştığında ise aşağıdaki gibi veriler gelmiştir ;


RBP = 0x7fffffffdce0
0x7fffffffdca8: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffdcb0: 0x74    0x68    0x31    0x5f    0x31    0x73    0x5f    0x74
0x7fffffffdcb8: 0x68    0x33    0x5f    0x76    0x61    0x6c    0x75    0x33
0x7fffffffdcc0: 0x5f    0x30    0x66    0x5f    0x6b    0x33    0x79    0x00
0x7fffffffdc98: 0xde    0x60    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffdca0: 0x31    0x33    0x33    0x37    0x00    0x00    0x00    0x00

Çözümleme yapıldığında ise

0x7fffffffdca8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 ASCII: "\x00\x00\x00\x00\x00\x00\x00\x00"  (tamamen null baytlar  görünür karakter yok)

0x7fffffffdcb0: 0x74 0x68 0x31 0x5f 0x31 0x73 0x5f 0x74
 ASCII: "t h 1 _ 1 s _ t"   birleştirilmiş: "th1_1s_t"

0x7fffffffdcb8: 0x68 0x33 0x5f 0x76 0x61 0x6c 0x75 0x33
 ASCII: "h 3 _ v a l u 3"   birleştirilmiş: "h3_valu3"

0x7fffffffdcc0: 0x5f 0x30 0x66 0x5f 0x6b 0x33 0x79 0x00
 ASCII: "_ 0 f _ k 3 y \\0"   birleştirilmiş: "_0f_k3y" (sonunda null terminator)

0x7fffffffdc98: 0xde 0x60 0x55 0x55 0x55 0x55 0x00 0x00

0x7fffffffdca0: 0x31 0x33 0x33 0x37 0x00 0x00 0x00 0x00
 ASCII: "1 3 3 7 \\0 \\0 \\0 \\0"   birleştirilmiş: "1337"

Artık elimizde bir KEY ve IV değeri bulunmaktadır bundan sonrası basittir. Bir Python scripti ile çözümlemeyi gerçekleştirebiliriz.

Çözümleme

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

# 1. ADIM: GDB'den bulduğumuz anahtar
# 'th1_1s_th3_valu3_0f_k3y' string'ini 32 byte'a \x00 ile tamamlıyoruz.
KEY = b'th1_1s_th3_valu3_0f_k3y'.ljust(32, b'\x00')

# 2. ADIM: Kodda gördüğümüz IV
# '1337' string'ini 16 byte'a \x00 ile tamamlıyoruz.
IV = b'1337'.ljust(16, b'\x00')

try:
    with open('flag.enc', 'rb') as f:
        ciphertext = f.read()

    # AES-CBC modunda bir cipher nesnesi oluştur
    cipher = AES.new(KEY, AES.MODE_CBC, IV)

    # Şifreli veriyi çöz ve padding'i kaldır
    decrypted_data = unpad(cipher.decrypt(ciphertext), AES.block_size)

    # Sonucu ekrana yazdır
    print("------ AH YALAN DUNYA - PWNED ------")
    print(decrypted_data.decode('utf-8'))
    print("----------------------------")

except Exception as e:
    print(f"Hata oluştu: {e}")
    if "Padding" in str(e):
        print("HATA: Anahtar (KEY) veya IV yanlış. 'Incorrect padding' hatası aldınız.")
    elif "key size" in str(e):
        print(f"HATA: Anahtar tam olarak 32 byte değil. Boyut: {len(KEY)} byte.")

Ayrıca Dosyayı indirmek için > Encrypter İndirmek İçin <

Algoritma

folder_s3