Seri: Scapy ile Etik Hacking — Bölüm 2 / 4

Seviye: Başlangıç → Orta

Ön koşul: 101'i oku — katman mantığını ve send/sr farkını bilmen şart

Bu Bölümde Öğreneceklerin

101'de paket mantığını öğrendik. Şimdi o mantığı gerçek işe koşacağız.

Bir sızma testinde "keşif aşaması" (reconnaissance) şu soruları cevaplar:

  • Ağda hangi cihazlar var?
  • Bu cihazlarda hangi portlar açık?
  • Hangi servisler çalışıyor, hangi versiyon?
  • İşletim sistemi ne?

Nmap bu soruları cevaplar — ama imzasını her IDS tanır. Scapy ile aynı işi farklı şekillerde yapabilirsin. Önemli olan arka planda ne döndüğünü anlamak.

Feynman Durağı #1 — TCP El Sıkışması (3-Way Handshake)

Port taramayı anlamadan önce TCP'nin nasıl çalıştığını anlamak şart.

İki insan telefonda konuşmadan önce şunu yapar:

  1. A: "Duyuyor musun?" (SYN)
  2. B: "Duyuyorum, sen duyuyor musun?" (SYN-ACK)
  3. A: "Duyuyorum." (ACK)

TCP de aynısını yapar. Buna 3-way handshake denir.

Aşağıdaki üç durum, SYN taramanın temelidir.

Sen (SYN)  ──────────────►  Hedef
           ◄──────────────  Hedef (SYN-ACK) → Port AÇIK
           ──────────────►  Sen (RST) → Bağlantıyı kes

Sen (SYN)  ──────────────►  Hedef
           ◄──────────────  Hedef (RST-ACK) → Port KAPALI

Sen (SYN)  ──────────────►  Hedef
             (cevap yok)─► Port FİLTRELİ (firewall var)

SYN Tarama — "Yarı Açık" Teknik

SYN taraması neden "stealth" (gizli) sayılır?

Çünkü tam bağlantı kurulmaz. SYN gönderirsin, SYN-ACK alırsın — port açık olduğunu anlarsın — hemen RST gönderirsin, bağlantıyı kapatırsın. Log'a düşme riski azalır.

def syn_scan(hedef, port):
    paket = IP(dst=hedef) / TCP(dport=port, flags="S", sport=RandShort())
    # RandShort() → rastgele kaynak port
    cevap = sr1(paket, timeout=2, verbose=0)
    if cevap is None:
        return "filtered"   # Firewall engelledi ya da cevap yok
    if cevap.haslayer(TCP):
        flag = cevap[TCP].flags
        if flag == "SA":    # SYN-ACK → açık
            # RST gönder → bağlantıyı temizlemek için 
            send(IP(dst=hedef) / TCP(dport=port, flags="R"), verbose=0)
            return "open"
        elif flag == "RA":  # RST-ACK → kapalı
            return "closed"
    return "unknown"

# Tek port testi için
hedef = "10.10.10.5"
sonuc = syn_scan(hedef, 80)
print(f"Port 80: {sonuc}")


Neden böyle yaptım : Tek bir porta SYN gönder, cevabı yorumla. Neden sr1? → Cevap almamız lazım (port açık/kapalı mı) Neden timeout=2? → 2 saniye bekle, cevap gelmezse filtered kabul et

Şimdi bunu bir port listesine uygula:

def port_tara(hedef, portlar):
    acik = []
    for port in portlar:
        durum = syn_scan(hedef, port)
        if durum == "open":
            print(f"[AÇIK]     {port}/tcp")
            acik.append(port)
        elif durum == "filtered":
            print(f"[FİLTRELİ] {port}/tcp")
    return acik
# Yaygın portları tara
yaygin = [21, 22, 23, 25, 53, 80, 110, 135, 139, 143, 443, 445, 3389, 8080]
acik_portlar = port_tara("10.10.10.5", yaygin)

Feynman Durağı #2 — Neden "SA" ve "RA"?

TCP flag'leri bitlerle ifade edilir. Scapy bunları harf kombinasyonuyla gösterir:

S  = SYN
A  = ACK
R  = RST
F  = FIN
P  = PSH
U  = URG

SA = SYN + ACK  → "Bağlantıya hazırım"
RA = RST + ACK  → "Bu port kapalı, git"

Bunu bir kez anladıktan sonra flags == "SA" kontrolü ezbere gerek kalmadan mantıklı gelir.

Ek bilgi ve hatırlatma:

ACK (Acknowledgment): Onay mekanizmasıdır. Gönderilen bir verinin veya bağlantı isteğinin başarıyla alındığını karşı tarafa bildirmek için kullanılır.

RST (Reset): Bağlantıyı zorla sonlandırır. Bir hata oluştuğunda veya geçersiz bir paket alındığında bağlantıyı anında kesmek için kullanılır.

FIN (Finish): Bağlantıyı düzgünce kapatma isteğidir. Gönderen tarafın artık gönderecek verisi kalmadığını belirtir.

PSH (Push): Verinin bekletilmeden hemen uygulamaya iletilmesini sağlar. Tampon belleğin (buffer) dolmasını beklemeden veriyi "ittirir".

URG (Urgent): Acil veri bildirimidir. Paketin içindeki belirli bir verinin öncelikli olduğunu ve diğer verilerden önce işlenmesi gerektiğini belirtir.

Nmapte:
Xmas Scan: FIN, PSH ve URG bayraklarının aynı anda gönderildiği tarama türüdür. Paketin içi bir yılbaşı ağacı gibi "ışıldadığı" için bu ismi alır.
Maimon Scan: FIN ve ACK yanına bazen URG eklenerek cihazın standartlara ne kadar uyduğu test edilir.

CUE NOTU — Flag Okuma Şeması

Gönderdin: SYN (S)
    │
    ├── SA geldi?   → Port AÇIK  (el sıkış teklif kabul)
    ├── RA geldi?   → Port KAPALI (reddedildi)
    └── Cevap yok?  → FİLTRELİ (firewall düşürdü)

ARP Sweep - Aynı Ağda Kim Var?

ICMP (ping) her zaman çalışmaz — bazı hostlar ping'e cevap vermez. Ama ARP'a cevap vermek zorundalar, çünkü ARP Layer 2'de çalışır ve engellemek çok daha zordur.

ARP nasıl çalışır?

"Bu IP adresi kimin? MAC adresini söyle!" diye broadcast (herkese) soru soran bir protokol.

Sen: "192.168.1.10 kimde?" (ff:ff:ff:ff:ff:ff'ye broadcast)
Hedef: "Bende! MAC adresim 00:11:22:33:44:55"
def arp_sweep(network):
  
    paket = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=network)
    # iface → doğru interface'i seç (101'den hatırlıyoruz)
    iface = conf.iface
    print(f"[*] {network} taranıyor → {iface} ({get_if_addr(iface)})")

    cevaplar, _ = srp(paket, iface=iface, timeout=3, verbose=0)
    print(f"\n{'IP':<20} {'MAC':<22}")
    print("-" * 42)
    for gonderilen, gelen in cevaplar:
        ip  = gelen[ARP].psrc    # Cevap verenin IP'si
        mac = gelen[Ether].src   # Cevap verenin MAC'i
        print(f"{ip:<20} {mac:<22}")
    print(f"\n[+] {len(cevaplar)} host bulundu")
arp_sweep("192.168.1.0/24")
None

Neden? Bir /24 ağındaki tüm cihazları ARP ile bul. Neden srp? → Layer 2 (Ethernet), cevap almamız lazım Neden Ether(dst="ff:ff:ff:ff:ff:ff")? → Broadcast, herkese gidiyor

Interface Seçimi — Birden Fazla Ağ Kartı Varsa

101'de get_if_list() öğrendik. Şimdi bunu tarama koduna entegre edelim:

# Foothold aldın, hangi ağ kartları var?
for iface in get_if_list():
    if iface == "lo":   # loopback'i atla
        continue
    try:
        ip  = get_if_addr(iface)
        mac = get_if_hwaddr(iface)
        if ip == "0.0.0.0":
            continue
        print(f"{iface:<12} IP: {ip:<18} MAC: {mac}")
    except:
        pass
None

Banner Grabbing — Servis Versiyonunu Öğren

Port açık olduğunu buldun. Peki hangi servis, hangi versiyon? Banner grabbing ile servise bağlan, kendini tanıtmasını bekle.

import socket
PROBLAR = {
    21:   None,
    22:   None,
    25:   b"EHLO test\r\n",
    80:   b"HEAD / HTTP/1.0\r\n\r\n",
    110:  None,
    3306: None,
}

def banner_al(ip, port, probe=None):
    try:
        with socket.socket() as s:
            s.settimeout(3)
            s.connect((ip, port))
            if probe:
                s.send(probe)
            banner = s.recv(1024).decode(errors="ignore").strip()
            return banner.split("\n")[0][:80]
    except (socket.timeout, ConnectionRefusedError, OSError):
        return None

# Taranack IP
hedef_ip = input("Hedef IP: ").strip()

print(f"\n[*] {hedef_ip} taranıyor...\n")

for port in PROBLAR:
    b = banner_al(hedef_ip, port, PROBLAR.get(port))
    durum = f"Banner: {b}" if b else "cevap yok"
    print(f"  Port {port:<6} {durum}")
None

OS Fingerprinting — TTL ile İşletim Sistemi Tahmini

101'de TTL'den bahsetmiştik. Şimdi bunu işe yarar hale getirelim.

Neden TTL işe yarar?

Her işletim sistemi paketi farklı TTL ile gönderir:

  • Windows → 128 (genellikle)
  • Linux/macOS → 64
  • Cisco IOS → 255

Paket ağda her hop'ta 1 TTL kaybeder. Sana ulaşan TTL'i bilince başlangıç TTL'ini tahmin edebilirsin.

def os_tahmin(ip):
    # Hem TCP hem ICMP dene — birisi cevap verir
    cevap = sr1(IP(dst=ip) / TCP(dport=80, flags="S"), timeout=1, verbose=0)
    if not cevap:
        cevap = sr1(IP(dst=ip) / ICMP(), timeout=1, verbose=0)
    if not cevap:
        print(f"[-] {ip} cevap vermedi")
        return
    ttl = cevap.ttl
    # Gelen TTL'e bakarak başlangıç TTL'ini tahmin et
    if ttl <= 64:
        os_tahmini = "Linux / macOS"
    elif ttl <= 128:
        os_tahmini = "Windows"
    elif ttl <= 255:
        os_tahmini = "Cisco / Network cihazı"
    else:
        os_tahmini = "Bilinmiyor"
    print(f"[+] {ip:<18} TTL={ttl:<4} → muhtemelen {os_tahmini}")
# Birden fazla hosta uygula
for host in ["10.10.10.5", "10.10.10.10", "10.10.10.1"]:
    os_tahmin(host)

Hepsini Birleştir — İç Ağ Keşif Scripti

Şu ana kadar öğrendiklerin: interface keşfi, ARP sweep, SYN tarama, banner grabbing, OS fingerprint.

Bunları birleştirince elimde ne olur?

import ipaddress
def ic_ag_kesfi(network=None):
    print("  İÇ AĞ KEŞİF - SCAPY")
    print("=" * 50)
    print("\n[1] AĞ KARTLARI:")

    for iface in get_if_list():
        if iface == "lo": continue
        try:
            ip  = get_if_addr(iface)
            mac = get_if_hwaddr(iface)
            if ip == "0.0.0.0": continue
            print(f"    {iface}: {ip} / {mac}")
            # Network belirtilmemişse ilk aktif interface'i kullan
            if not network:
                network = str(ipaddress.IPv4Network(f"{ip}/24", strict=False))
        except: pass

    # ARP sweep
    print(f"\n[2] ARP SWEEP: {network}")
    paket = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=network)
    cevaplar, _ = srp(paket, timeout=2, verbose=0)
    hostlar = [(g[ARP].psrc, g[Ether].src) for _, g in cevaplar]
    for ip, mac in hostlar:
        print(f"    LIVE: {ip:<18} {mac}")

    # Açık portlar + OS tahmini
    print("\n[3] PORT + OS ANALİZİ:")
    kontrol_portlari = [22, 80, 443, 445, 3389]

    for ip, _ in hostlar:
        acik = []
        ttl_deger = None
        for port in kontrol_portlari:
            r = sr1(IP(dst=ip)/TCP(dport=port,flags="S",sport=RandShort()),
                    timeout=0.5, verbose=0)
            if r and r.haslayer(TCP) and r[TCP].flags == "SA":
                acik.append(port)
                if not ttl_deger:
                    ttl_deger = r.ttl
                send(IP(dst=ip)/TCP(dport=port,flags="R"), verbose=0)
        os_t = "Linux" if ttl_deger and ttl_deger<=64 else "Windows" if ttl_deger and ttl_deger<=128 else "?"
        print(f"    {ip:<18} Portlar={acik}  OS≈{os_t}  TTL={ttl_deger}")

ic_ag_kesfi()

CUE NOTU — 102 Özeti

┌────────────────────────────────────────────────┐
│  SCAPY 102 — Aktif Keşif Özeti                 │
│                                                │
│  SYN SCAN:                                     │
│    SYN gönder → SA geldi = AÇIK                │
│                 RA geldi = KAPALI              │
│                 Yok      = FİLTRELİ            │
│                                                │
│  ARP SWEEP:                                    │
│    srp(Ether broadcast / ARP(pdst=network))    │
│    Layer 2 → ICMP'den güvenilir                │
│                                                │
│  BANNER GRAB:                                  │
│    socket.connect → recv(1024) → ilk satır     │
│                                                │
│  OS TAHMİNİ:                                   │
│    TTL ≤ 64  → Linux/macOS                     │
│    TTL ≤ 128 → Windows                         │
│    TTL ≤ 255 → Cisco                           │
└────────────────────────────────────────────────┘

Feynman Son Testi

  1. SYN taramada neden ACK yerine RST gönderiyoruz?
  2. ARP sweep için neden srp() kullanıyoruz, sr1() değil?
  3. Banner grabbing ile servis versiyonu öğrenmek neden önemli?
  4. get_if_addr() ve get_if_hwaddr() ne döndürür?
  5. Bir host TTL=110 ile cevap verdi. OS'u ne tahmin edersin?

Sıradaki Bölüm

Scapy 201: Trafik Analizi ve MITM — Sniffing, ARP poisoning ve ağ trafiğini araya girme. Aktif sorgulamadan passif dinlemeye geçiyoruz.

⚠️ Tüm teknikler yalnızca izin alınmış sistemlerde uygulanmalıdır.

Scapy ile Etik Hacking Serisi — Bölüm 2/4

☕️ Support me on Buy Me a Coffee