Challenge:
crackmes.one
İyi günler, iyi akşamlar. Dün karşıma çıkan güzel bir shellcode mantığı içeren "Password or Payload" challenge'ını inceleyeceğiz. Program bizden bir şifre istiyor ama author'un bahsettiği üzere "Input injector yok, sadece klavye serbest" demiş. Yani elimizdeki kısıtlı karakter setiyle (printable ASCII) programın akışını yönlendirmemiz lazım. Klasik bir "şifreyi bul" sorusu değil, daha çok "geçerli bir kod yaz ve çalıştır" sorusu.
Bu mesajlar, ilk bakışta programın standart bir "şifre doğrulama" işlemi yapacağı izlenimini veriyor. Ancak bu varsayımı doğrulamadan önce programın hangi API'leri kullandığına bakmamız şart.
Import Table'ı incelediğimizde dikkat çekici bir detayla karşılaşıyoruz:
Program, VirtualAlloc fonksiyonunu import ediyor. Basit bir metin karşılaştırma programının, Windows'tan manuel olarak bellek tahsis etmesi (memory allocation) olağan bir durum değil. Bu API genellikle dinamik kod çalıştırma veya unpacking işlemlerinde kullanılır. Bu aşamada, programın basit bir crackme'den fazlası olabileceğine dair ilk ipucumuzu alıyoruz.
A. Input Alımı ve Bellek Tahsisi
Önce fgets ile bizden input alınıyor.
Hemen ardından VirtualAlloc çağrılıyor. Burada en kritik detay r9d register'ına atanan değerdir.
"mov r9d, 40h" Bu 0x40 değeri, Windows API'sinde PAGE_EXECUTE_READWRITE iznine denk gelir. Yani bu bellek alanı, içine veri yazıp kod çalıştırabileceğimiz özel bir alandır.
B. Shellcode Çalıştırma ve Karar Mekanizması
Program, bizim input'umuzu RWX belleğe kopyaladıktan sonra onu kod olarak çalıştırıyor. Aşağıdaki grafikte bu akışı net görebilirsiniz:
Grafiği adım adım inceleyelim:
Özetle program bizden şunu istiyor. "Bana öyle karakterler ver ki, ben bunları makine kodu (opcode) olarak çalıştırdığımda, bana 0 olmayan bir sonuç (return value) ile geri dön."
Bu kodun makine dili karşılığı: B0 01 C3.
Fakat daha önce bahsettiğim üzere. Sadece klavye kullanabiliriz. B0 (mov al) byte'ı, standart ASCII tablosunda basılabilir bir karaktere denk gelmiyor. Hex editör kullanamadığımız için, sadece klavyedeki harflerle aynı işi yapan alternatif bir opcode zinciri kurmamız gerekiyor.
ADIM 1: push 0x31
ADIM 2: pop rax
ADIM 3: ret
Gördüğünüz üzere payloadımız çalıştı. Eğer buraya kadar okuduysanız ve aklınızda herhangi bir soru varsa sorabilirsiniz.
Not: Bunu çözerken bir kaç kaynaktan yardım almıştım ayrıca. Göz atmak isteyen varsa onları da ekliyorum.
Shellcode ve Process Injection
x86-64 Opcode Referansları
Alphanumeric / Printable Shellcode
UTF-8 ve Unicode Shellcode
koboldschwaengel's Password or Payload?
I guess there ist only one way to find the answer. Have fun!
İyi günler, iyi akşamlar. Dün karşıma çıkan güzel bir shellcode mantığı içeren "Password or Payload" challenge'ını inceleyeceğiz. Program bizden bir şifre istiyor ama author'un bahsettiği üzere "Input injector yok, sadece klavye serbest" demiş. Yani elimizdeki kısıtlı karakter setiyle (printable ASCII) programın akışını yönlendirmemiz lazım. Klasik bir "şifreyi bul" sorusu değil, daha çok "geçerli bir kod yaz ve çalıştır" sorusu.
1. Recon
Binary'yi IDA Pro ile açarak analize başlıyoruz. İlk olarak programın yapısını anlamak için Strings penceresini kontrol ediyoruz. Burada, programın çalışma zamanında kullanıcıya gösterdiği "Enter the Password:", "pass is correct!" ve "passwort is incorrect" gibi geri bildirim mesajları açıkça görülüyor.
Bu mesajlar, ilk bakışta programın standart bir "şifre doğrulama" işlemi yapacağı izlenimini veriyor. Ancak bu varsayımı doğrulamadan önce programın hangi API'leri kullandığına bakmamız şart.
Import Table'ı incelediğimizde dikkat çekici bir detayla karşılaşıyoruz:
Program, VirtualAlloc fonksiyonunu import ediyor. Basit bir metin karşılaştırma programının, Windows'tan manuel olarak bellek tahsis etmesi (memory allocation) olağan bir durum değil. Bu API genellikle dinamik kod çalıştırma veya unpacking işlemlerinde kullanılır. Bu aşamada, programın basit bir crackme'den fazlası olabileceğine dair ilk ipucumuzu alıyoruz.
2. Derin Analiz: Code Execution Mantığı
Şüphelerimizi doğrulamak için main fonksiyonunu inceliyoruz. Kod akışının, standart bir karşılaştırma (if password == input) algoritmasından çok farklı olduğunu görüyoruz.A. Input Alımı ve Bellek Tahsisi
Önce fgets ile bizden input alınıyor.
Hemen ardından VirtualAlloc çağrılıyor. Burada en kritik detay r9d register'ına atanan değerdir.
"mov r9d, 40h" Bu 0x40 değeri, Windows API'sinde PAGE_EXECUTE_READWRITE iznine denk gelir. Yani bu bellek alanı, içine veri yazıp kod çalıştırabileceğimiz özel bir alandır.
B. Shellcode Çalıştırma ve Karar Mekanizması
Program, bizim input'umuzu RWX belleğe kopyaladıktan sonra onu kod olarak çalıştırıyor. Aşağıdaki grafikte bu akışı net görebilirsiniz:
Grafiği adım adım inceleyelim:
| call rdi | rdi register'ında tutulan adrese (bizim input'umuzun kopyalandığı RWX bellek) dallan ve oradaki kodu çalıştır. |
| test al, al | Çalıştırılan kodun dönüş değerini (AL register) kontrol et. AL, RAX'ın alt 8 bit'idir. |
| lea rcx, "pass is correct!" | Önce success string'ini RCX'e yükle (puts için hazırlık). |
| jnz short loc_140001125 | Eğer AL ≠ 0 ise puts'a (loc_140001125) atla ve "pass is correct!" yazdır. |
| lea rcx, "passwort is incorrect" | Eğer AL = 0 ise bu satıra düşer ve fail string'i yüklenir. |
Özetle program bizden şunu istiyor. "Bana öyle karakterler ver ki, ben bunları makine kodu (opcode) olarak çalıştırdığımda, bana 0 olmayan bir sonuç (return value) ile geri dön."
3. Keyboard-Only Shellcode
Normal şartlarda, bir fonksiyondan 1 (true) döndürmek için Assembly'de şu basit iki satırı yazarız:| mov al, 1 | AL = 1 |
| ret | Return |
Bu kodun makine dili karşılığı: B0 01 C3.
Fakat daha önce bahsettiğim üzere. Sadece klavye kullanabiliriz. B0 (mov al) byte'ı, standart ASCII tablosunda basılabilir bir karaktere denk gelmiyor. Hex editör kullanamadığımız için, sadece klavyedeki harflerle aynı işi yapan alternatif bir opcode zinciri kurmamız gerekiyor.
Çözüm Matrisi
Hedefimiz yukarıda bahsettiğimiz AL register'ını 0 dışı bir değer yapmak ve ret ile çıkmak.ADIM 1: push 0x31
| Opcode | 6A 31 |
| Klavye | j1 |
| Ne yapıyor? | 0x6A (push imm8) komutu j harfine denk gelir. Arkasındaki 0x31 ise 1 karakteri. Stack'e 0x31 (decimal 49) değerini atıyoruz. |
ADIM 2: pop rax
| Opcode | 58 |
| Klavye | X |
| Ne yapıyor? | Stack'teki 0x31 değerini RAX register'ına çekiyoruz. RAX'ın alt 8 bit'i AL olduğu için artık AL = 0x31 (sıfır değil!). |
ADIM 3: ret
| Opcode | C3 |
| Klavye | À (Alt+0192 veya kopyala-yapıştır) |
| Ne yapıyor? | İşin zor kısmı burası.... Program UTF-8 modunda çalışıyor (SetConsoleCP(65001)). À karakteri UTF-8'de C3 80 olarak kodlanır. İlk byte C3 = ret komutu, fonksiyondan çıkıyoruz. İkinci byte (80) ise ret'ten sonra geldiği için CPU buraya hiç ulaşmıyor yani önemsiz. |
4. Payload ve Sonuç
Final payload'ımız şu 4 karakterden oluşuyor: j1XÀ
Gördüğünüz üzere payloadımız çalıştı. Eğer buraya kadar okuduysanız ve aklınızda herhangi bir soru varsa sorabilirsiniz.
Not: Bunu çözerken bir kaç kaynaktan yardım almıştım ayrıca. Göz atmak isteyen varsa onları da ekliyorum.
Shellcode ve Process Injection
- Windows Process Injection Fundamentals — VirtualAlloc, WriteProcessMemory ve shellcode çalıştırma temelleri.
- Loading and Executing Shellcode From PE Resources (ired.team) — Visual Studio ile shellcode yükleme ve çalıştırma.
- What is Process Injection? Techniques & Preventions (SentinelOne) — Process injection tekniklerine genel bakış.
x86-64 Opcode Referansları
- X86 Opcode and Instruction Reference (coder64) — 64-bit opcode tablosu, her instruction'ın byte karşılıkları.
- Intel x86 Opcode Table (shell-storm) — Kapsamlı Intel opcode referansı.
- Stanford CS107 x86-64 Reference Sheet (PDF) — Hızlı başvuru için özet tablo.
Alphanumeric / Printable Shellcode
- Alphanumeric Shellcode (Offensive Security) — Metasploit ile alphanumeric shellcode üretimi.
- ASCII Shellcode (NetSec) — Printable karakter kısıtlamalarıyla shellcode yazma.
- Linux Alphanumeric Shellcode — Sadece alfanumerik karakterlerle execve() shellcode.
UTF-8 ve Unicode Shellcode
- UTF-8 Shellcode (Phrack #62) — UTF-8 encoding ile shellcode bypass teknikleri. (Klasik makale)
- Writing Unicode Payloads (Venetian Shellcode) — Unicode kullanan uygulamalarda exploit geliştirme.


